Found while working #10703, which adds scripts/pr-labels.mjs — a new file directly under scripts/. node scripts/pm/dispatch-gates.mjs did not name check:entry-guard, the gate whose population is exactly that directory. The gate then failed in CI on Lint & Repo Gates, after the dev had reported.
This is not a one-off miss. Re-derived after the file was committed and tracked, at e0accbffc7:
$ node scripts/pm/dispatch-gates.mjs
$ grep -c "entry-guard" <output>
0
It is absent from the matched list, absent from the Unreachable list, and absent from the model-tier lines. --residue places it in Silent — "source names paths, none of which cover yours":
- pnpm check:entry-guard [lint.yml] names: scripts/check-changeset-no-major.mjs,
scripts/check-empty-changeset.mjs, scripts/check-error-status-conformance.mjs, …
The mechanism
Those names are KNOWN_IMPORT_UNSAFE in scripts/check-entry-guard.mjs:437-448 — the allowlist of the ten files that already violate the import-safety half. The derivation reads that literal list as the gate's declared population.
So the population is an enumeration of the files that already exist. A newly added scripts/*.mjs can never appear in it, by construction — which inverts the verdict exactly where it matters most. At runtime the gate scans all 127 scripts/ files and judges new ones; the derivation says "silent" precisely for the case most likely to fire. A gate that is silent for edits to existing scripts and silent for new ones is silent always.
dispatch-gates already warns that silent is its weakest claim and names two ways to earn it that have nothing to do with the caller's paths. This is a third: a gate whose declared population is a roster of current members rather than the surface it scans.
The sibling, failing the other way
check:parse-guard covers the same directory and is reported Unreachable — dead: 'scripts' — the tree HAS it; the covering rule refuses the literal as too generic (no path separator).
So both scripts/** gates are invisible to the derivation, by two different routes: one declares a population too generic to match, the other declares one too specific. Anyone adding a script gets neither named.
Directions, not a prescription
- Let a gate declare a scan surface distinct from its baseline/allowlist artifact, so
check-entry-guard can say scripts/** while still carrying KNOWN_IMPORT_UNSAFE as data. This is the same distinction the docblock already draws for gates that "compute their own population and name only their baseline artifact". - Failing that, treat an allowlist-shaped literal as a hint that the real population is its common prefix (
scripts/), and report it as matched rather than silent. - At minimum, make
silent distinguishable from silent because the gate enumerates its current members in the residue output, so a reader can tell the weak verdict from the inverted one.
Not filed against check-entry-guard itself — that gate worked correctly and caught the defect. This is about the derivation that failed to name it.
Filed unassigned, PM triage.
Generated by Claude Code
Generated by Claude Code
Found while working #10703, which adds
scripts/pr-labels.mjs— a new file directly underscripts/.node scripts/pm/dispatch-gates.mjsdid not namecheck:entry-guard, the gate whose population is exactly that directory. The gate then failed in CI onLint & Repo Gates, after the dev had reported.This is not a one-off miss. Re-derived after the file was committed and tracked, at
e0accbffc7:It is absent from the matched list, absent from the
Unreachablelist, and absent from the model-tier lines.--residueplaces it in Silent — "source names paths, none of which cover yours":The mechanism
Those names are
KNOWN_IMPORT_UNSAFEinscripts/check-entry-guard.mjs:437-448— the allowlist of the ten files that already violate the import-safety half. The derivation reads that literal list as the gate's declared population.So the population is an enumeration of the files that already exist. A newly added
scripts/*.mjscan never appear in it, by construction — which inverts the verdict exactly where it matters most. At runtime the gate scans all 127scripts/files and judges new ones; the derivation says "silent" precisely for the case most likely to fire. A gate that is silent for edits to existing scripts and silent for new ones is silent always.dispatch-gatesalready warns thatsilentis its weakest claim and names two ways to earn it that have nothing to do with the caller's paths. This is a third: a gate whose declared population is a roster of current members rather than the surface it scans.The sibling, failing the other way
check:parse-guardcovers the same directory and is reportedUnreachable — dead: 'scripts' — the tree HAS it; the covering rule refuses the literal as too generic (no path separator).So both
scripts/**gates are invisible to the derivation, by two different routes: one declares a population too generic to match, the other declares one too specific. Anyone adding a script gets neither named.Directions, not a prescription
check-entry-guardcan sayscripts/**while still carryingKNOWN_IMPORT_UNSAFEas data. This is the same distinction the docblock already draws for gates that "compute their own population and name only their baseline artifact".scripts/), and report it as matched rather than silent.silentdistinguishable fromsilent because the gate enumerates its current membersin the residue output, so a reader can tell the weak verdict from the inverted one.Not filed against
check-entry-guarditself — that gate worked correctly and caught the defect. This is about the derivation that failed to name it.Filed unassigned, PM triage.
Generated by Claude Code
Generated by Claude Code