Found while wiring the new check:exported-any-returns gate for #11927 — the new gate landed
invisible, and checking why turned up 22 existing families in the same state.
Measured at 0e0bf8049 plus that card's branch.
The rule
scripts/pm/dispatch-gates.mjs:
exportfunctionresolveCheckToFiles(checkName,scriptsMap){constcmd=scriptsMap[checkName];if(!cmd)return[];return[...newSet([...cmd.matchAll(/(scripts\/[\w./-]+\.(?:mjs|cjs|js|sh))/g)].map((m)=>m[1]))];}A gate whose npm script names a .ts / .mts / .cts file matches nothing. entry.files stays
empty, so discoverFamilies never opens the source — and everything downstream of opening it is
therefore also empty: no watch hints, no first-party import following, no no-population
marker read, and no entry in the gateFiles set.
The consequence, in the derivation's own terms
Such a family can never be matched for any card. It scores undetermined ("its sources name no
path at all") for every PR in the tree — but the reason is not that its author declined to declare
a population. It is that the declaration was never read. The two are indistinguishable in the
output, which is the same conflation scripts/pm/bare-root-worklist.mjs was built to report on one
level down.
It also silently voids the bare-root sweep for these files: that sweep walks the gate sources
discoverFamilies resolves, so a TypeScript gate is never swept, and --self-test reporting
"none missing" says nothing about it either way.
The 23
Verified by resolving each zero-file family's command back to its script:
| gate | source it names |
|---|
check:generated | scripts/check-generated.ts |
check:api-surface | scripts/build-api-surface.ts |
check:exported-any | packages/spec/scripts/check-exported-any.ts |
check:dual-source-exports | packages/spec/scripts/check-dual-source-exports.ts |
check:browser-reachable-entries | packages/spec/scripts/check-browser-reachable-entries.ts |
check:skill-examples | packages/spec/scripts/check-skill-examples.ts |
check:authorable-surface | scripts/build-schemas.ts |
check:docs | scripts/build-docs.ts |
check:skill-docs | scripts/build-skill-docs.ts |
check:skill-refs | scripts/build-skill-references.ts |
check:meta-url-spelling | scripts/build-meta-url-spelling.ts |
check:spec-changes | scripts/build-spec-changes.ts |
check:upgrade-guide | scripts/build-upgrade-guide.ts |
check:export-origins | scripts/build-export-origins.ts |
check:react-blocks | scripts/build-react-blocks-contract.ts |
check:template-manifests | scripts/check-template-manifests.ts |
check:objectui-pin-citations | scripts/check-objectui-pin-citations.ts |
check:llms-txt | scripts/check-llms-txt.ts |
check:liveness | scripts/liveness/check-liveness.mts |
check:empty-state | scripts/liveness/check-empty-state.mts |
check:variant-docs | scripts/check-variant-docs.mts |
check:strictness-ledger | scripts/check-strictness-ledger.mts |
check:exported-any-returns | scripts/check-exported-any-returns.mts (#11927, this card's trigger) |
Reproducer:
import{discoverFamilies}from'./scripts/pm/dispatch-gates.mjs';const{ byCheck }=discoverFamilies();console.log([...byCheck].filter(([,e])=>!e.files.length).length,'of',byCheck.size);// → 24 of 166 (23 above + one family whose script is a `pnpm -r` composite)Two further wrinkles the fix has to decide, not just the extension list
- The path is relative to the PACKAGE, not the repo.
packages/client's alias reads
tsx ../../scripts/check-exported-any-returns.mts …, and the package-filter branch of
discoverFamilies prepends the package's own directory prefix to whatever
resolveCheckToFiles returns — which would produce packages/client/../../scripts/…. That
resolves on disk but is not a tracked-path spelling, so it would match nothing in hintCovers
without a normalisation step. The existing .mjs callers never hit this because they all live
in the package they serve. - It is a widening, and this repo prices widenings.
firstPartyImportTargets' docblock
measures its own at +893 pairs and states the zero-loss property explicitly. Admitting 23 gate
sources adds hints AND adds them to gateFiles (which suppresses import-following into those
files) — a subtraction direction that has to be measured, not argued, the way that docblock
does it.
⛔ Deliberately not fixed inside #11927: the hint sets are fleet-wide, so re-attributing 23
families' matched lists is a change every open dispatch brief reads, not a rider on a card about
one package's return types.
Generated by Claude Code
Found while wiring the new
check:exported-any-returnsgate for #11927 — the new gate landedinvisible, and checking why turned up 22 existing families in the same state.
Measured at
0e0bf8049plus that card's branch.The rule
scripts/pm/dispatch-gates.mjs:A gate whose npm script names a
.ts/.mts/.ctsfile matches nothing.entry.filesstaysempty, so
discoverFamiliesnever opens the source — and everything downstream of opening it istherefore also empty: no watch hints, no first-party import following, no
no-populationmarker read, and no entry in the
gateFilesset.The consequence, in the derivation's own terms
Such a family can never be
matchedfor any card. It scoresundetermined("its sources name nopath at all") for every PR in the tree — but the reason is not that its author declined to declare
a population. It is that the declaration was never read. The two are indistinguishable in the
output, which is the same conflation
scripts/pm/bare-root-worklist.mjswas built to report on onelevel down.
It also silently voids the bare-root sweep for these files: that sweep walks the gate sources
discoverFamiliesresolves, so a TypeScript gate is never swept, and--self-testreporting"none missing" says nothing about it either way.
The 23
Verified by resolving each zero-file family's command back to its script:
check:generatedscripts/check-generated.tscheck:api-surfacescripts/build-api-surface.tscheck:exported-anypackages/spec/scripts/check-exported-any.tscheck:dual-source-exportspackages/spec/scripts/check-dual-source-exports.tscheck:browser-reachable-entriespackages/spec/scripts/check-browser-reachable-entries.tscheck:skill-examplespackages/spec/scripts/check-skill-examples.tscheck:authorable-surfacescripts/build-schemas.tscheck:docsscripts/build-docs.tscheck:skill-docsscripts/build-skill-docs.tscheck:skill-refsscripts/build-skill-references.tscheck:meta-url-spellingscripts/build-meta-url-spelling.tscheck:spec-changesscripts/build-spec-changes.tscheck:upgrade-guidescripts/build-upgrade-guide.tscheck:export-originsscripts/build-export-origins.tscheck:react-blocksscripts/build-react-blocks-contract.tscheck:template-manifestsscripts/check-template-manifests.tscheck:objectui-pin-citationsscripts/check-objectui-pin-citations.tscheck:llms-txtscripts/check-llms-txt.tscheck:livenessscripts/liveness/check-liveness.mtscheck:empty-statescripts/liveness/check-empty-state.mtscheck:variant-docsscripts/check-variant-docs.mtscheck:strictness-ledgerscripts/check-strictness-ledger.mtscheck:exported-any-returnsscripts/check-exported-any-returns.mts(#11927, this card's trigger)Reproducer:
Two further wrinkles the fix has to decide, not just the extension list
packages/client's alias readstsx ../../scripts/check-exported-any-returns.mts …, and the package-filter branch ofdiscoverFamiliesprepends the package's own directory prefix to whateverresolveCheckToFilesreturns — which would producepackages/client/../../scripts/…. Thatresolves on disk but is not a tracked-path spelling, so it would match nothing in
hintCoverswithout a normalisation step. The existing
.mjscallers never hit this because they all livein the package they serve.
firstPartyImportTargets' docblockmeasures its own at +893 pairs and states the zero-loss property explicitly. Admitting 23 gate
sources adds hints AND adds them to
gateFiles(which suppresses import-following into thosefiles) — a subtraction direction that has to be measured, not argued, the way that docblock
does it.
⛔ Deliberately not fixed inside #11927: the hint sets are fleet-wide, so re-attributing 23
families' matched lists is a change every open dispatch brief reads, not a rider on a card about
one package's return types.
Generated by Claude Code