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. 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

  2. added theissue type on Sep 7, 2026
  3. 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

  4. 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions