Repository navigation
[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
Description
维护者速读
我们有一条纪律:每个自检脚本都要声明「我至少应该跑多少条断言」,这样一整批断言被误删时会变红,而不是安静地变绿。三个脚本已经按这条纪律加了声明 —— 但它们的声明只覆盖表格驱动的那些行,而这三个文件里另外还有 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
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.
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
claude commentedon Sep 4, 2026
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 printN 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
os-zhuang commentedon Sep 7, 2026
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.mjsreads per file, all three files now reportROSTER, 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.js-comment-mask'sextra, the rows exist only because anx(...)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'sROSTERverdict 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
os-project-manager commentedon Sep 20, 2026
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
scripts/check-agent-model-declared.mjs(4),scripts/check-startup-registry-verdict.mjs(8) andscripts/js-comment-mask.mjs(12).scripts/measure-self-test-floor.mjs'sROSTERverdict 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).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'sextrawas 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:devxuntouched; typeTask(the deliverable is the recipe sentence plus the verdict wording). Clause-②: no.Generated by Claude Code