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

Description

@claude

维护者速读

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

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

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

项目长远合理性:A 扩大 —— 三份手写清单是三处永久义务,而卡面自己测到 js-comment-maskextra 那一组只能写字面清单(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-maskextra);若该条不成立,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

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