Measured while landing #11404 (PR #11554, the --self-test invocation matcher). Filed unassigned; nothing is red today — the one live instance is closed inside that PR, and this card is about the latent half it does not close.
What was measured
scripts/pm/dispatch-gates.mjs is not a discovered gate FILE. check:pm-dispatch-gates resolves to scripts/pm/check-dispatch-gates.mjs, which reaches the tool by spawnSync, never by import — so discoverFamilies' "never follow a module that is itself a gate file" rule does not protect it.
Its module body yields 9 watch hints under today's extractor:
scripts, .claude/skills/pm-dispatch/SKILL.md, .claude/agents/os-dev.md,
skills/objectstack-pm-dispatch/SKILL.md, packages/spec/src/**,
.github/workflows, packages/plugins, packages/drivers, packages/services
Against 6465 tracked files those cover 2553 of them. Only .github/workflows is something the tool reads as a population; packages/plugins, packages/drivers and packages/services are the bases its package resolver joins, and packages/spec/src/** is a tier glob the file's own self-test pins as inert for gate matching.
So any gate script that statically imports ./dispatch-gates.mjs inherits 2553 (gate, file) pairs of population it never opens. Measured live: scripts/pm/bare-root-worklist.mjs does exactly that, and admitting it as a family handed it all 2553 — 96% of that PR's entire price.
Why the spawn is not a general guard
check-dispatch-gates.mjs's header already records this verdict at length: it exists as a separate file, spawning rather than importing, because "the derivation resolves a check family to its script file and then scans THAT FILE's source for the path literals it operates on", and a directly-wired gate "would print MATCHED for every card under them — a smaller fabrication than the fixture one, of the same kind."
That guard protects exactly one caller — the one that remembered. It is a convention held in one file's prose, not a property of the module, so the next gate that reaches for discoverFamilies or hintCovers as a library gets the fabrication with no warning and every gate green.
PR #11554 closes the live instance narrowly and by measurement, not generally: a --self-test family follows no import, because that invocation runs the script's self-test rather than its work. A check- gate importing the same module is untouched by that rule.
Not asserted
No claim about which remedy is right. Three shapes, in increasing width:
The second is the one that composes with #11511 and #11510, which want the same module shape for the same reason.
Related
#11404 / PR #11554 (where this was measured, and which closes the live instance) · #11511 (the mirror case: a module that IS a gate file, so the follow refuses it and the honest half is lost) · #11510 · #8162 (check-dispatch-gates.mjs's spawn verdict) · #11190 / PR #11512 (the follow this rides on)
Generated by Claude Code
Measured while landing #11404 (PR #11554, the
--self-testinvocation matcher). Filed unassigned; nothing is red today — the one live instance is closed inside that PR, and this card is about the latent half it does not close.What was measured
scripts/pm/dispatch-gates.mjsis not a discovered gate FILE.check:pm-dispatch-gatesresolves toscripts/pm/check-dispatch-gates.mjs, which reaches the tool byspawnSync, never by import — sodiscoverFamilies' "never follow a module that is itself a gate file" rule does not protect it.Its module body yields 9 watch hints under today's extractor:
Against 6465 tracked files those cover 2553 of them. Only
.github/workflowsis something the tool reads as a population;packages/plugins,packages/driversandpackages/servicesare the bases its package resolver joins, andpackages/spec/src/**is a tier glob the file's own self-test pins as inert for gate matching.So any gate script that statically imports
./dispatch-gates.mjsinherits 2553 (gate, file) pairs of population it never opens. Measured live:scripts/pm/bare-root-worklist.mjsdoes exactly that, and admitting it as a family handed it all 2553 — 96% of that PR's entire price.Why the spawn is not a general guard
check-dispatch-gates.mjs's header already records this verdict at length: it exists as a separate file, spawning rather than importing, because "the derivation resolves a check family to its script file and then scans THAT FILE's source for the path literals it operates on", and a directly-wired gate "would print MATCHED for every card under them — a smaller fabrication than the fixture one, of the same kind."That guard protects exactly one caller — the one that remembered. It is a convention held in one file's prose, not a property of the module, so the next gate that reaches for
discoverFamiliesorhintCoversas a library gets the fabrication with no warning and every gate green.PR #11554 closes the live instance narrowly and by measurement, not generally: a
--self-testfamily follows no import, because that invocation runs the script's self-test rather than its work. Acheck-gate importing the same module is untouched by that rule.Not asserted
No claim about which remedy is right. Three shapes, in increasing width:
DEFAULT_BASE_REFidiom already in that file, which avoids exactly this for its own constant;scripts/i18n-bundle-surface.mjsshape, and the same move [finding] Two gates import their shared glob helper and their shared declaration table OUT OF a gate script, which costs check:ci-filter-parity a 595-pair derivation lead #11511 proposes forcheck-cross-package-test-inputs.mjs;The second is the one that composes with #11511 and #11510, which want the same module shape for the same reason.
Related
#11404 / PR #11554 (where this was measured, and which closes the live instance) · #11511 (the mirror case: a module that IS a gate file, so the follow refuses it and the honest half is lost) · #11510 · #8162 (
check-dispatch-gates.mjs's spawn verdict) · #11190 / PR #11512 (the follow this rides on)Generated by Claude Code