Found while adding a new gate under #10534 follow-up 4 (PR #11149). Filed unassigned; not fixed there, because it is a different defect class from that card and its landing surface is another gate.
The shape
scripts/check-ratchet-remedy-authority.mjs (#8435) sweeps scripts/*.{mjs,mts} — every gate script in the repo — and fails a new one that offers a ratchet-expanding remedy without declaring its authority. So every PR that adds a gate script is in its population, by construction.
scripts/pm/dispatch-gates.mjs names it for none of them. Measured on 47ced12bf:
node scripts/pm/dispatch-gates.mjs scripts/check-auth-mount-ledger.mjs | grep -c ratchet-remedy-authority -> 0
node scripts/pm/dispatch-gates.mjs scripts/check-brand-new-gate.mjs | grep -c ratchet-remedy-authority -> 0
Positive control that the expression is not simply broken: the same grep returns non-zero for the file the gate is derivable from — its own source — and other scripts/**-declaring families (check:entry-guard, check:cross-package-test-inputs) are named for both paths above. So the zeros are real zeros.
--residue puts it in the undetermined bucket: "their sources name no path at all — NOT known irrelevant."
Why
extractWatchHints reads module-scope string literals and refuses any literal with no path separator as too generic. This gate's population is assembled at runtime by readdirSync over the scripts directory, and the only filename-ish literals it carries are its CONTROL corpus keys — bare basenames ('check-engine-double-contract.mjs'), no separator. So it declares no derivable path.
The CONTROL roster is also the shape dispatch-gates' own residue note already warns about: "a list of the files that already exist can never contain one added tomorrow, so its silence is a fact about the roster rather than about your paths." Here it is not even silence — it is undetermined.
What it cost
PR #11149 went red on this gate at head b86bd9e76. The derived union for that change set (27 families) did not name it, and the author did not think to add it by hand. It is the first instance on #10534 where a derivation miss produced an actual red rather than being covered by a hand-named gate — the case the #10309 class is really about, since a gate nobody can derive and nobody thinks to name runs for the first time in CI.
Suggested remedy (not applied here)
The residue note names the escape itself: a gate whose population is a subtree reaches it by declaring the subtree spelling. A module-scope literal carrying a separator — e.g. the glob scripts/*.{mjs,mts} the header already states in prose, or scripts/** — would make it derivable for every gate-script diff. Worth checking in the same pass whether other readdirSync-populated gates share the shape; this was found by tripping over one, not by a sweep, so the extent is unmeasured.
⚠️ Not verified by me: whether hintCovers accepts a brace glob ({mjs,mts}). Whoever takes this should measure that rather than assume it, since a hint that matches nothing is the same silent failure one level down.
Refs
#8435 (the convention) · #10309 (the derivation-miss class) · #10534 / PR #11149 (where it surfaced) · dispatch-gates.mjshintCovers / extractWatchHints docblocks (the refusal rule and its measurements)
Found while adding a new gate under #10534 follow-up 4 (PR #11149). Filed unassigned; not fixed there, because it is a different defect class from that card and its landing surface is another gate.
The shape
scripts/check-ratchet-remedy-authority.mjs(#8435) sweepsscripts/*.{mjs,mts}— every gate script in the repo — and fails a new one that offers a ratchet-expanding remedy without declaring its authority. So every PR that adds a gate script is in its population, by construction.scripts/pm/dispatch-gates.mjsnames it for none of them. Measured on47ced12bf:Positive control that the expression is not simply broken: the same grep returns non-zero for the file the gate is derivable from — its own source — and other
scripts/**-declaring families (check:entry-guard,check:cross-package-test-inputs) are named for both paths above. So the zeros are real zeros.--residueputs it in the undetermined bucket: "their sources name no path at all — NOT known irrelevant."Why
extractWatchHintsreads module-scope string literals and refuses any literal with no path separator as too generic. This gate's population is assembled at runtime byreaddirSyncover the scripts directory, and the only filename-ish literals it carries are itsCONTROLcorpus keys — bare basenames ('check-engine-double-contract.mjs'), no separator. So it declares no derivable path.The
CONTROLroster is also the shapedispatch-gates' own residue note already warns about: "a list of the files that already exist can never contain one added tomorrow, so its silence is a fact about the roster rather than about your paths." Here it is not even silence — it is undetermined.What it cost
PR #11149 went red on this gate at head
b86bd9e76. The derived union for that change set (27 families) did not name it, and the author did not think to add it by hand. It is the first instance on #10534 where a derivation miss produced an actual red rather than being covered by a hand-named gate — the case the #10309 class is really about, since a gate nobody can derive and nobody thinks to name runs for the first time in CI.Suggested remedy (not applied here)
The residue note names the escape itself: a gate whose population is a subtree reaches it by declaring the subtree spelling. A module-scope literal carrying a separator — e.g. the glob
scripts/*.{mjs,mts}the header already states in prose, orscripts/**— would make it derivable for every gate-script diff. Worth checking in the same pass whether otherreaddirSync-populated gates share the shape; this was found by tripping over one, not by a sweep, so the extent is unmeasured.hintCoversaccepts a brace glob ({mjs,mts}). Whoever takes this should measure that rather than assume it, since a hint that matches nothing is the same silent failure one level down.Refs
#8435 (the convention) · #10309 (the derivation-miss class) · #10534 / PR #11149 (where it surfaced) ·
dispatch-gates.mjshintCovers/extractWatchHintsdocblocks (the refusal rule and its measurements)