Skip to content

[finding] dispatch-gates.mjs is a followable non-gate module, so its join bases and tier globs are inheritable — 2553 fabricated pairs for the next gate that imports it #11556

Description

@os-steve

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions