You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] workspace-enumerator.selfTest() is folded into 7 gates with no assertion floor anywhere, and is invisible to the #13489 census (no --self-test dispatch) #15375
Found while flooring scripts/check-type-check-coverage.mjs for #13799 (batch 10e). Filed unassigned, out of that card's scope: workspace-enumerator.mjs is not in #13489's measured population, so no batch of #13799 will reach it.
The reading
scripts/workspace-enumerator.mjs exports a selfTest({ root }) with 22t(name, ok) assertion sites and a failure-list sink. It has no --self-test dispatch of its own — deliberately, per its own header: "This module is deliberately not a gate, so it has no CI invocation of its own: its coverage is that every consolidated caller runs --self-test in lint.yml and every one of them calls this."
Seven gates fold that selfTest() into their own:
scripts/check-type-check-coverage.mjs
scripts/check-dev-prereqs.mjs
scripts/check-published-files.mjs
scripts/check-published-readme-exports.mjs
scripts/check-test-source-alias.mjs
scripts/check-type-source-resolution.mjs
scripts/pnpm-filter-targets.mjs
Every one of them adopts its failures, and none of them can floor its cases, because the fold-in is failure-only in every spelling used: five push a spread of the returned list, and two (check-test-source-alias, check-type-source-resolution) route it through for (const failure of ...) expect(false, failure), which calls the local sink only when there is a failure. On a green run the fold-in contributes zero registrations to every caller's ledger. So the 22 cases can stop running — an early return above them, a guard that starts skipping, a deleted block — and all seven gates keep printing their verdict lines unchanged.
That is exactly hole 1 of #13489, one level down from the population #13489 measures.
Why the census cannot see it
measure-self-test-floor.mjs selects on a --self-testdispatch (DISPATCH = includes/has('--self-test') as an argv membership test). workspace-enumerator.mjs names --self-test only in prose, so it is not in the census at all: measured on c4d1354e3, the instrument reports 170 files and this one is not among them, under any classification. Its absence is therefore not a clearance — it is a file the criterion cannot reach.
⛔ Note the boundary: this is not the same as typecheck-configs.mjs, which is also a library folded into a gate but does carry its own dispatch and was floored in its own module by PR #15327. The fix shape there does not transfer unchanged — a floor evaluated at a verdict site works because there is a dispatch to hold it; here there is no dispatch to put one in.
Options, none of them chosen here
Give workspace-enumerator.mjs its own --self-test dispatch, roster and floor, and wire it in lint.yml. Cost: one more CI invocation for a module whose header argues it should not be a gate; benefit: the census can then see it and the existing recipe applies verbatim.
Export the roster/floor and have each folding caller evaluate it at its own verdict site. Cost: seven call sites to keep in step, and the floor then reds in seven places for one cause.
Sizing, for triage: 22 assertions, 478-line module, 7 folding callers.
Reproduce
node scripts/measure-self-test-floor.mjs --json # 170 files; workspace-enumerator is not one of them
grep -rn 'workspaceEnumeratorSelfTest(' scripts/ # the 7 folding call sites
Found while flooring
scripts/check-type-check-coverage.mjsfor #13799 (batch 10e). Filed unassigned, out of that card's scope:workspace-enumerator.mjsis not in #13489's measured population, so no batch of #13799 will reach it.The reading
scripts/workspace-enumerator.mjsexports aselfTest({ root })with 22t(name, ok)assertion sites and a failure-list sink. It has no--self-testdispatch of its own — deliberately, per its own header: "This module is deliberately not a gate, so it has no CI invocation of its own: its coverage is that every consolidated caller runs--self-testin lint.yml and every one of them calls this."Seven gates fold that
selfTest()into their own:scripts/check-type-check-coverage.mjsscripts/check-dev-prereqs.mjsscripts/check-published-files.mjsscripts/check-published-readme-exports.mjsscripts/check-test-source-alias.mjsscripts/check-type-source-resolution.mjsscripts/pnpm-filter-targets.mjsEvery one of them adopts its failures, and none of them can floor its cases, because the fold-in is failure-only in every spelling used: five push a spread of the returned list, and two (
check-test-source-alias,check-type-source-resolution) route it throughfor (const failure of ...) expect(false, failure), which calls the local sink only when there is a failure. On a green run the fold-in contributes zero registrations to every caller's ledger. So the 22 cases can stop running — an earlyreturnabove them, a guard that starts skipping, a deleted block — and all seven gates keep printing their verdict lines unchanged.That is exactly hole 1 of #13489, one level down from the population #13489 measures.
Why the census cannot see it
measure-self-test-floor.mjsselects on a--self-testdispatch (DISPATCH = includes/has('--self-test')as an argv membership test).workspace-enumerator.mjsnames--self-testonly in prose, so it is not in the census at all: measured onc4d1354e3, the instrument reports 170 files and this one is not among them, under any classification. Its absence is therefore not a clearance — it is a file the criterion cannot reach.⛔ Note the boundary: this is not the same as
typecheck-configs.mjs, which is also a library folded into a gate but does carry its own dispatch and was floored in its own module by PR #15327. The fix shape there does not transfer unchanged — a floor evaluated at a verdict site works because there is a dispatch to hold it; here there is no dispatch to put one in.Options, none of them chosen here
workspace-enumerator.mjsits own--self-testdispatch, roster and floor, and wire it in lint.yml. Cost: one more CI invocation for a module whose header argues it should not be a gate; benefit: the census can then see it and the existing recipe applies verbatim.selfTest()returns its case count alongside its failures, and each caller floors that one number. Cost: a count is weaker than a name set ([finding] Survey: which otherscripts/*--self-test modes decide success with no assertion floor, or exit 0 viareturn selfTest()before their verdict #13489's own ruling), and it moves with the module.Sizing, for triage: 22 assertions, 478-line module, 7 folding callers.
Reproduce
Generated by Claude Code