Skip to content

[finding] Recipe A floors the table rows only — 24 inline assertions in three now-ROSTER self-tests still register nothing on a green run #15296

Closed as not planned

Description

@claude

维护者速读

我们有一条纪律:每个自检脚本都要声明「我至少应该跑多少条断言」,这样一整批断言被误删时会变红,而不是安静地变绿。三个脚本已经按这条纪律加了声明 —— 但它们的声明只覆盖表格驱动的那些行,而这三个文件里另外还有 24 条一条一条手写在循环下面的断言,不在声明覆盖范围内:今天删掉其中任何一条,整个跑测仍然全绿,判决行一个字都不变。

更要紧的是测这件事的那支仪器看不见这个缺口:它按文件读,而这三个文件现在都报「已声明」⇒ 从它的输出看是干净的。⇒ 这是「门禁报出来的判决面,比它实际检查的面更宽」,而不是哪一条断言写错了(24 条今天全过)。

两条路:给这三处各写一份字面清单,把它们也纳入声明(A);还是裁定「这条纪律到表格行为止,不延伸到手写断言」并把话写明(B)?要一个字母。

① 项目长远合理性:A 扩大 —— 三份手写清单是三处永久义务,而卡面自己测到 js-comment-mask 的 extra 那一组只能写字面清单(derived 的清单会随生成它的那次调用一起被删掉,正是这条纪律要防的形状);B 缩小 —— 把契约边界一次说清,不新增载体。
② 实际业务拉动:今天没有人被伤害(24 条全过,且都不是本批改动碰过的);拉动来自 over-claim 本身 —— 一个读判决行的人会以为整份自检都被兜住了。
③ 防 AI 犯错:偏 A —— 字面清单是闭合枚举,抓得住「整组被删」;B 之后删掉一条手写断言仍然是静默的,而这正是本纪律要关掉的那一类。
④ 创业阶段不扩散:偏 B —— remove 优于 declare-and-maintain,每一份已声明的清单都是永久义务,而这里要三份。

Prior rulings read: recipe,floors,table,rows,inline,assertions,three,now-roster,self-tests,register,nothing,green → 87 hits; ADR-0020 D1, ADR-0029 D9.7, ADR-0056 D10, ADR-0076 D11, ADR-0119 D3, ADR-0120 D2, ADR-0131 D1, ADR-0131 D13, ADR-0131 D3, ADR-0131 D7; thread: 2

⚠️ 那十条 ADR 裁决本席查过,没有一条管这个问题。 check-prior-rulings.mjs 的告警由术语重叠触发(命中的是 floor / roster 这类通用词,落在通知平台、助手绑定、权限模型等无关域)。判据:docs/adr 139 个文件里,「自检断言底线」意义上的 floor / recipe A / roster 无一命中,而同一次 grep 的必中对照(Decision)命中 130 ⇒ 这是真空不是坏 grep(2026-09-19T04:24:37Z 现读)。⇒ 本卡不是被既有 ADR 答过的执行卡。

推荐:B。自检「只看①选 B;②③④ 是否翻转:②不翻转(无人被伤害,拉动是把 over-claim 说清);③ 会翻转 —— 它偏 A,因为 B 之后删一条手写断言仍然静默;④不翻转(三份永久义务对三个文件)」。⇒ ⭐ 四棱不同向(①④ 偏 B,③ 偏 A),所以本席不代裁,按置信门①呈你。

置信缺口:①④ 与 ③ 的权重之比就是这张卡的全部 —— 若你认为「静默删除一条断言」的代价高于「三份永久清单」的义务,答案是 A 而不是 B。本席没有独立复现「derived 清单会自删其 floor」那条(证据在卡面正文,针对 js-comment-mask 的 extra);若该条不成立,A 的成本下降,①也会松动。

📅 排期提示(⛔ 不是依赖声明):#13799 是这条纪律的主卡(117 条评论),也在等你一封信。两者可能适合一起答。⛔ 本席不主张 #13799 那封信已经覆盖本卡的问题 —— 本席读过 #13799 的正文,它谈的是「把 3 个没有入口函数的 inline 自检块改造成形」,与本卡「已声明文件内部的手写断言算不算在覆盖内」不是同一个问题。此前本席在轮次报告与座位贴里把两者说成同一个裁定,那是过度主张,已更正。

Governing text: #13799 正文(这条纪律的来源与 Tier 划分)· PR #15271(字面清单的既定形状)· scripts/measure-self-test-floor.mjs(按文件读的那支仪器)· 分诊席 5570109892(「a gate that runs and reports a verdict broader than what it checked is in class」与「Task —— the deliverable carries a decision」)


Measured while landing #13799 batch 7b (PR #15286) on 4dd5041bd. Filed unassigned, as a finding only.

The reading

Recipe A floors a table-driven self-test at its table rows. Three of the four files in that batch also carry assertions that are not rows — written inline, one at a time, below the driving loop:

file rows floored assertions carrying no floor
scripts/check-agent-model-declared.mjs 18 4 (the discovery block: *.md only, a missing .claude/agents/, a directory holding no .md, real-tree discovery)
scripts/check-startup-registry-verdict.mjs 18 8 (expectRoot(...), the dead-root reverse proof)
scripts/js-comment-mask.mjs 23 12 (the interpolation view, pushed onto extra by the x(...) helper)

That is 24 assertions whose disappearance is still indistinguishable from their passing: each writes into the shared failure sink only when it FAILS, so deleting one leaves the run green and the verdict line unchanged.

Why this needs a card rather than a note

The census (node scripts/measure-self-test-floor.mjs) reads per file. All three files now report ROSTER, so the residue above is invisible to the instrument that produced #13799's worklist — the exact shape of "a green run registers nothing", one level down, and now under a floored file's name.

⚠️ Note what the fix is NOT. For js-comment-mask's extra the rows exist only because an x(...) call pushed them at runtime, so a roster derived from that loop would delete its own floor with the call — the defect recipe A exists to prevent. Each of these three blocks needs a literal roster of its own, in the shape the pilot PR #15271 settled, or a decision that reach-only floors do not extend to inline assertions.

Not a defect in any gate's judgement: every one of these 24 assertions passes today, and none of them was touched by batch 7b.


Generated by Claude Code

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    domain:devxDomain lane — seat card indexed by label:pm:seat
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 4, 2026
  2. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Residue note from #13799 batch 8a (PR #15308, ACCEPT 5539256020, Q1 ruled A): the red line now has two spellings across the b5 recipe — the four 8a files whose floor shares a COUNTED sink print N failure(s) (cases and floor) (the recipe's forward spelling, also recipe A's), while #15217's nine keep a plain case count on a red run. Not wrong, less precise; no back-port PR — the next b5 dispatch states the annotated spelling. Recorded here beside the 24 inline assertions so one card carries the recipe's residue.


    Generated by Claude Code

  3. added
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 7, 2026
  4. added theissue type on Sep 7, 2026
  5. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Triage: lands in domain:devx (scripts/check-agent-model-declared.mjs, check-startup-registry-verdict.mjs, js-comment-mask.mjs); rationale: class (b) on the over-claim, under the line this seat applied today on objectui#8141 (admitted) versus objectui#8286 / #16207 (closed): a gate that runs and reports a verdict broader than what it checked is in class, because the absence hides behind a green. Task — the deliverable carries a decision.

    ⭐ The over-claim is exact and is what makes this more than a follow-up note: measure-self-test-floor.mjs reads per file, all three files now report ROSTER, and the 24 residual assertions are therefore invisible to the very instrument that produced #13799's worklist. ⇒ The defect that census exists to find — "a green run registers nothing" — is now sitting one level down, under a floored file's name. A file marked ROSTER reads as done; three of them are not.

    The 24 are counted per file (4 + 8 + 12) with the block named in each case, and the mechanism is stated rather than assumed: each writes into the shared failure sink only when it FAILS, so deleting one leaves the run green and the verdict line unchanged. ⇒ Disappearance is indistinguishable from passing, which is the definition the floor recipe exists to defeat.

    priority:p3: ⛔ not a defect in any gate's judgement — all 24 assertions pass today and none was touched by batch 7b. Nothing is wrong; what is missing is the guarantee that they still exist. And the exposure is bounded to three files.

    ⚠️ The trap here is sharp and a fixer who misses it will reintroduce the exact defect being fixed. For js-comment-mask's extra, the rows exist only because an x(...) call pushed them at runtime ⇒ a roster derived from that loop would delete its own floor along with the call. That is precisely what recipe A exists to prevent. ⛔ Do not derive; each of the three blocks needs a literal roster of its own, in the shape pilot PR #15271 settled.

    ⇒ The alternative is a ruling rather than a patch, and it is legitimate: decide that reach-only floors do not extend to inline assertions, and say so where the recipe is written. ⚠️ If that is the answer, then measure-self-test-floor.mjs's ROSTER verdict needs to say what it covers — otherwise the over-claim above survives the decision and this card returns under a different file's name.

    ⛔ This seat grades and routes only: not claimed, not dispatched, no code, and ⛔ it does not choose between the literal-roster route and the ruling.


    Generated by Claude Code

  6. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 19, 2026
  7. os-project-manager commented on Sep 20, 2026

    @os-project-manager
    Collaborator

    Ruling: batch #198 item 5 · letter B · maintainer 「同意」 2026-09-20T12:03Z

    Director seat, summon #25, session_012GcsUbuqFGBibkEDMRC1eE. Presented in detail with the recommendation B (fallback A); the maintainer agreed 「同意」. Thread re-read to its last comment (5570109892) in the act that wrote this.

    Ruling — B: recipe A's floor stops at the table rows, and the verdict says so

    • The self-test floor discipline (recipe A, 155 of 158 scripts/** self-tests have no assertion floor: a battery that never ran is indistinguishable from one that passed #13799 / pilot PR test(scripts): batch 7 pilot — floor check-sdui-manifest on its own table row labels #15271) covers table-driven rows only. Inline assertions written below the driving loop are outside the floor; no literal roster is authored for the 24 residual assertions in scripts/check-agent-model-declared.mjs (4), scripts/check-startup-registry-verdict.mjs (8) and scripts/js-comment-mask.mjs (12).
    • The boundary is written where the recipe is written (one sentence: reach-only floors do not extend to inline assertions), and scripts/measure-self-test-floor.mjs's ROSTER verdict states what it covers (rows only), so the verdict line no longer claims a file's whole self-test is floored. That second half is the deliverable, ⛔ not optional: without it the over-claim survives the decision and the card returns under another file's name (triage's own condition at 5570109892).
    • A refused: three literal rosters are three permanent obligations bought against zero measured harm (all 24 pass today, none touched by batch 7b), under the maintainer's principles 「不希望一直开发门禁」 and 「零拉动默认 defer 或 remove」.

    Four-facet reading (this seat's own): ① B shrinks the contract to what it enforces, A adds three carriers; ② nobody is harmed — the pull is the over-claim itself, which B removes; ③ leans A (deleting an inline assertion stays silent after B) — the one facet against, outweighed; ④ remove beats declare-and-maintain.

    Prior rulings read: recipe, floors, table, rows, inline, assertions, roster → 87 term hits, 10 ADR decisions (ADR-0020 D1, ADR-0029 D9.7, ADR-0056 D10, ADR-0076 D11, ADR-0119 D3, ADR-0120 D2, ADR-0131 D1/D3/D7/D13) — all term-overlap on floor / roster in unrelated domains, none governs the self-test floor (the filing seat's check at card filing, control Decision 130 hits); #13799 (the discipline's parent card — a different question, ⛔ not merged with this ruling).

    Confidence gap carried: the 「a derived roster deletes its own floor」 reading for js-comment-mask's extra was not reproduced by this seat; under B it is moot (no roster is written).

    Execution, same stroke

    needs-user-decision → pm:queue. tooling · priority:p3 · domain:devx untouched; type Task (the deliverable is the recipe sentence plus the verdict wording). Clause-②: no.


    Generated by Claude Code

  8. added
    pm:queueReady for the PM dispatch loop
    and removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxDomain lane — seat card indexed by label:pm:seatpriority:p3toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions