Found while implementing #13000 (runtime staging dependencies), and it is the same root one door over — so recording it rather than folding it in. Measured on 8b4156139f.
The gap
coveringKey matches a family through four keys: gate-script IDENTITY (entry.files), the workflow trigger, the job filter, and a watch HINT. A first-party module a gate script imports is in none of them. discoverFamilies does follow the import — but only to inherit the module's hints (entry.imports feeds entry.hintOrigin). That answers "what population does this gate watch, once you count what its helper watches". It does not answer "if I edit this helper, which gate can I break", and those are different questions.
So: editing a module reaches the importing family only when some other key happens to cover it — usually a scripts/** hint the gate declares for unrelated reasons. Where no such key exists, the family is silent.
Measured
Over the 181 discovered families, counting (family, imported module) pairs where the module is not in the family's own files and coveringKey(entry, module) returns null today:
families with an IMPORTED module that does NOT reach them: 228
Specimens, each verified by running the derivation on the module path:
| edit this module | this family imports it and is NOT named |
|---|
scripts/invoked-as.mjs | check:nul-bytes, check:console-injection, check:dual-build-cjs-loads, … |
scripts/js-comment-mask.mjs | scripts/check-adr-0087-registration.mjs |
scripts/pm/check-governed-merges.mjs | scripts/pm/check-governed-queue-guard.mjs |
scripts/import-prerequisite.mjs | check:slot-lookup, check:query-options-erasure, check:verify-stand-in |
scripts/pm/dispatch-gates.mjs | scripts/pm/bare-root-worklist.mjs --self-test |
The last row is how it was found: a PR editing scripts/pm/dispatch-gates.mjs derives 14 families, and bare-root-worklist --self-test — which statically imports that very file — is not among them. It was run by hand and was green; nothing in the derived list said to run it.
Reproduce
node -e "import('./scripts/pm/dispatch-gates.mjs').then(async(m)=>{
const {readFileSync,existsSync}=await import('node:fs');
const d=m.discoverFamilies(); const rows=[];
for (const [check,e] of d.byCheck) for (const f of e.files??[]) { if(!existsSync(f)) continue;
for (const mod of m.firstPartyImportTargets(f, readFileSync(f,'utf8'))) {
if ((e.files??[]).includes(mod) || m.coveringKey(e, mod)) continue; rows.push([check,mod,f]); } }
console.log(rows.length); })"
Why it is not folded into #13000
#13000's class is a gate that opens another script's source at a path anchored to its own location; that landed as a program text read key restricted to program-text targets, at a measured cost of 5 new leads across 7288 tracked files. This class is an order of magnitude larger and needs its own pricing decision: 228 pairs is not automatically 228 good leads, and the scripts/**-declaring gates already reach many of these modules by another route, so the honest number is the novel half rather than the raw count. That measurement is the first deliverable for whoever takes this.
⚠️ Related but NOT the same, both closed, both about hint INHERITANCE through imports rather than identity: #11556 and #11511. The open sibling blind spots of the same root are #12983 and #13000; #12956 landed as PR #13049.
Severity is honest and small: like #13000, CI catches these loudly today. The cost is an avoidable red round per occurrence, plus a dev running the wrong local list.
Generated by Claude Code
Found while implementing #13000 (runtime staging dependencies), and it is the same root one door over — so recording it rather than folding it in. Measured on
8b4156139f.The gap
coveringKeymatches a family through four keys: gate-script IDENTITY (entry.files), the workflow trigger, the job filter, and a watch HINT. A first-party module a gate script imports is in none of them.discoverFamiliesdoes follow the import — but only to inherit the module's hints (entry.importsfeedsentry.hintOrigin). That answers "what population does this gate watch, once you count what its helper watches". It does not answer "if I edit this helper, which gate can I break", and those are different questions.So: editing a module reaches the importing family only when some other key happens to cover it — usually a
scripts/**hint the gate declares for unrelated reasons. Where no such key exists, the family is silent.Measured
Over the 181 discovered families, counting (family, imported module) pairs where the module is not in the family's own files and
coveringKey(entry, module)returns null today:Specimens, each verified by running the derivation on the module path:
scripts/invoked-as.mjscheck:nul-bytes,check:console-injection,check:dual-build-cjs-loads, …scripts/js-comment-mask.mjsscripts/check-adr-0087-registration.mjsscripts/pm/check-governed-merges.mjsscripts/pm/check-governed-queue-guard.mjsscripts/import-prerequisite.mjscheck:slot-lookup,check:query-options-erasure,check:verify-stand-inscripts/pm/dispatch-gates.mjsscripts/pm/bare-root-worklist.mjs --self-testThe last row is how it was found: a PR editing
scripts/pm/dispatch-gates.mjsderives 14 families, andbare-root-worklist --self-test— which statically imports that very file — is not among them. It was run by hand and was green; nothing in the derived list said to run it.Reproduce
Why it is not folded into #13000
#13000's class is a gate that opens another script's source at a path anchored to its own location; that landed as a
program text readkey restricted to program-text targets, at a measured cost of 5 new leads across 7288 tracked files. This class is an order of magnitude larger and needs its own pricing decision: 228 pairs is not automatically 228 good leads, and thescripts/**-declaring gates already reach many of these modules by another route, so the honest number is the novel half rather than the raw count. That measurement is the first deliverable for whoever takes this.Severity is honest and small: like #13000, CI catches these loudly today. The cost is an avoidable red round per occurrence, plus a dev running the wrong local list.
Generated by Claude Code