Found while working #12983 (a different paragraph of the same header). Filed rather than folded in: three of the four sites are in scripts/pm/dispatch-gates.mjs, a file other cards are actively claiming (#13126 queues for it), and #12983's triage fenced that card to one paragraph with an explicit "do not hang extra surface on a documentation card" ruling.
The fact
scripts/pm/check-dispatch-gates.mjs carries three module-body path constants, not one:
scripts/pm/check-dispatch-gates.mjs:128 const TOOL = 'scripts/pm/dispatch-gates.mjs';
scripts/pm/check-dispatch-gates.mjs:144 const SURFACE_MODULE = 'scripts/i18n-bundle-surface.mjs';
scripts/pm/check-dispatch-gates.mjs:157 const FRAME_MODULE = 'scripts/check-skill-frame-sync.mjs';
SURFACE_MODULE and FRAME_MODULE were added by #9116, each with its own docblock explaining why. Measured live on a81aa9dd5e, the tool's own extractor returns all three:
node -e "... extractWatchHints(readFileSync('scripts/pm/check-dispatch-gates.mjs','utf8'))"
["scripts/pm/dispatch-gates.mjs","scripts/i18n-bundle-surface.mjs","scripts/check-skill-frame-sync.mjs"]
Positive control on the same reading: module-body appears 16 times in scripts/pm/dispatch-gates.mjs, so the greps below are not empty reads.
The four sites that still say "one"
| site |
text |
why it is wrong now |
scripts/pm/check-dispatch-gates.mjs:85-87 |
"Its watch hints are the one constant below: this gate is matched for a card that edits the tool, and for nothing else" |
the hints are three constants, so the gate is also matched for a card editing scripts/i18n-bundle-surface.mjs or scripts/check-skill-frame-sync.mjs — and for the gate file itself, by identity |
scripts/pm/check-dispatch-gates.mjs:113-115 |
"the one quoted path below is still the one input this gate genuinely has" |
there are three quoted paths below; only one of them is an input the gate reads. The underlying rule ("a module-body literal is unmaskable, so be deliberate about it") survives — the count does not |
scripts/pm/dispatch-gates.mjs:2701-2703 |
"scripts/pm/check-dispatch-gates.mjs is a thin file whose one module-body constant is the tool it runs" |
it is the live specimen the precedence argument leans on, and the specimen has grown two more constants |
scripts/pm/dispatch-gates.mjs:8679-8681 |
self-test comment: "This tool's own gate is one file whose single module-body constant is the tool it runs" |
same claim, in the comment that explains the two pinned cases beside it |
Why it is worth recording
Same class as #12983 and #8604 (closed): a gate header that is cited as the authority carrying a self-description that stopped being true. The direction here is different from #12983's, and slightly worse: "matched for a card that edits the tool, and for nothing else" is not an under-statement, it is a false negative claim about the derivation, in the file whose whole subject is derivation correctness. A reader deciding whether a card editing scripts/i18n-bundle-surface.mjs needs this gate would be told, by the authoritative header, that it does not — while the tool's own self-test pins that it does.
Not a red
Nothing fails. The self-test pins all three constants with covers(...) containment assertions, so it stays green either way; the prose is the only thing that counts them. That is exactly why it went unnoticed across #9116.
Suggested shape
Same repair as #12983 and #12831: describe the shape, not the count. Something on the order of "its watch hints are the module-body constants below — the tool it runs, plus the modules whose edits move this gate's verdict without being discoverable as hints" leaves nothing to re-rot when a fourth constant is declared. The two sites in dispatch-gates.mjs want the specimen sentence to stop asserting a count as well.
Duplicate check
One targeted search_issues scoped to this repo (⚠️ the bare REST search endpoint is not reachable from this session — every repo-scoped REST read returns 403, gh is absent, and an authenticated retry with the session token returned "GitHub access is not enabled for this session"). 71 total, 25 returned; the set is its own positive control, containing #12983 itself plus this file's history — #8604, #8478, #8509, #11511, #9116 — so it is not a silent zero. Nearest neighbours: #8604 is the same file and the same class but a different, already-repaired claim (comment skipping), and it is closed. #12934 and #12808 are the same class on cross-package-test-inputs.mjs, a different file. ⛔ No open card covers these four sites. ⚠️ Not exhaustively deduped outside this query.
Generated by Claude Code
Found while working #12983 (a different paragraph of the same header). Filed rather than folded in: three of the four sites are in
scripts/pm/dispatch-gates.mjs, a file other cards are actively claiming (#13126 queues for it), and #12983's triage fenced that card to one paragraph with an explicit "do not hang extra surface on a documentation card" ruling.The fact
scripts/pm/check-dispatch-gates.mjscarries three module-body path constants, not one:SURFACE_MODULEandFRAME_MODULEwere added by #9116, each with its own docblock explaining why. Measured live ona81aa9dd5e, the tool's own extractor returns all three:Positive control on the same reading:
module-bodyappears 16 times inscripts/pm/dispatch-gates.mjs, so the greps below are not empty reads.The four sites that still say "one"
scripts/pm/check-dispatch-gates.mjs:85-87scripts/i18n-bundle-surface.mjsorscripts/check-skill-frame-sync.mjs— and for the gate file itself, by identityscripts/pm/check-dispatch-gates.mjs:113-115scripts/pm/dispatch-gates.mjs:2701-2703scripts/pm/check-dispatch-gates.mjsis a thin file whose one module-body constant is the tool it runs"scripts/pm/dispatch-gates.mjs:8679-8681Why it is worth recording
Same class as #12983 and #8604 (closed): a gate header that is cited as the authority carrying a self-description that stopped being true. The direction here is different from #12983's, and slightly worse: "matched for a card that edits the tool, and for nothing else" is not an under-statement, it is a false negative claim about the derivation, in the file whose whole subject is derivation correctness. A reader deciding whether a card editing
scripts/i18n-bundle-surface.mjsneeds this gate would be told, by the authoritative header, that it does not — while the tool's own self-test pins that it does.Not a red
Nothing fails. The self-test pins all three constants with
covers(...)containment assertions, so it stays green either way; the prose is the only thing that counts them. That is exactly why it went unnoticed across #9116.Suggested shape
Same repair as #12983 and #12831: describe the shape, not the count. Something on the order of "its watch hints are the module-body constants below — the tool it runs, plus the modules whose edits move this gate's verdict without being discoverable as hints" leaves nothing to re-rot when a fourth constant is declared. The two sites in
dispatch-gates.mjswant the specimen sentence to stop asserting a count as well.Duplicate check
One targeted⚠️ the bare REST search endpoint is not reachable from this session — every repo-scoped REST read returns 403, ⚠️ Not exhaustively deduped outside this query.
search_issuesscoped to this repo (ghis absent, and an authenticated retry with the session token returned "GitHub access is not enabled for this session"). 71 total, 25 returned; the set is its own positive control, containing #12983 itself plus this file's history — #8604, #8478, #8509, #11511, #9116 — so it is not a silent zero. Nearest neighbours: #8604 is the same file and the same class but a different, already-repaired claim (comment skipping), and it is closed. #12934 and #12808 are the same class oncross-package-test-inputs.mjs, a different file. ⛔ No open card covers these four sites.Generated by Claude Code