Skip to content

[finding] dispatch-gates never discovers a gate whose script lacks a check- prefix - five required-lane steps, including the one that shipped a red on PR #11397, are absent from the family universe entirely #11404

Description

@claude

Measured on PR #11397 (fixing #10907), which shipped a red nobody could have predicted locally.

What happened

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths, merge-base derived) reported 7 local gate families for the diff. Every one was run and every one was green. Lint & Repo Gates then failed on a gate that appeared in none of them:

node scripts/pm/bare-root-worklist.mjs --self-test
  x self-test: no gate has NEWLY joined the invisible bare-root species -
    FRESH: check:i18n-coverage PACKAGES_DIR packages.

The red was real and the fix was one recorded verdict. What is worth a card is that no local derivation could have named it.

Root cause - the INVOCATION matcher, not the population matcher

This is a different failure from #11199 and #11190, and the difference decides the remedy.

Both of those concern gates that are discovered as families and whose populations are not path-expressible (#11199) or would be lost through a shared import (#11190). Under --residue they at least appear, in the silent / undetermined bucket.

This gate is not a family at all. Measured:

discoverFamilies().byCheck.size                     -> 139
[...keys].filter(k => k.includes('bare-root'))      -> []
[...keys].filter(k => k.includes('--self-test'))    -> []

It is absent from the matched list, the convention-triggered list, the unreachable-by-construction list, and from all three residue buckets - because it never enters the universe those buckets partition. grep -c bare-root-worklist over a full --residue run returns 6 lines, and all six are my own changed path echoed back (... bare-root-worklist.mjs -> gate source 'scripts/**'). The file is visible to the derivation only as an INPUT, never as a GATE.

The mechanism is collectInvocations in scripts/pm/dispatch-gates.mjs, which recognises exactly two spellings:

/pnpm\s+(?:--filter\s+(\S+)\s+)?(?:run\s+)?(check:[\w:-]+)/g
/node\s+(scripts\/[\w./-]*check-[\w.-]+\.mjs)/g

So a workflow step qualifies only if it is a check:* npm script or its script basename contains check-. scripts/pm/bare-root-worklist.mjs is neither: it has no package.json script, and its filename does not carry the prefix. Verified directly:

"node scripts/pm/bare-root-worklist.mjs --self-test"  => []
"node scripts/pm/check-half-states.mjs"               => ["scripts/pm/check-half-states.mjs"]

The discovery is keyed on a naming convention, and a real gate that reds the required lane simply does not follow it.

Blast radius, measured

Five steps in lint.yml - the required Lint & Repo Gates job - invoke a scripts/** script whose basename lacks check-, and are therefore invisible to every dispatch brief:

lint.yml:400   node scripts/pm/bare-root-worklist.mjs --self-test
lint.yml:556   node scripts/pm/release-rehearsal-clone.mjs --self-test
lint.yml:785   node scripts/pm/ci-failure.mjs --self-test
lint.yml:1601  node scripts/pr-labels.mjs --self-test
lint.yml:1902  node scripts/partition-test-shards.mjs --self-test

Across all workflows the same sweep finds 13 distinct such scripts. Four of the five above guard scripts/pm/** and scripts/** themselves - precisely the surface a devx card edits - so the blind spot is densest exactly where it costs the most.

bare-root-worklist is also the sharpest specimen for a second reason: its own self-test asserts that it declares no population of its own (this tool declares no population of its own). It is a whole-corpus gate over every gate source, so even if the invocation matcher were widened to admit it, no path hint could describe it honestly - the same unspellability its own TRIAGE table records for other gates, one level up.

Direction (not a decision)

Widening the regex to any scripts/**/*.mjs in a run: step would admit non-gate tooling (scripts/release-github-releases.mjs, scripts/run-with-stall-guard.mjs) and is the fabrication direction hintCovers prices. Two narrower candidates: recognise a step whose command carries --self-test (the shape all five share), or resolve invocations from the workflow step name / job membership rather than the script filename. Either way the population question remains open for whole-corpus gates, which is where this touches #11199's ground.

Refs

#10907 / PR #11397 (where this was measured) - #11199 (a discovered family whose population is not path-expressible) - #11190 (hints lost through a shared import)


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions