Skip to content

[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

Description

@claude

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 22 t(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-test dispatch (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

  1. 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.
  2. 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.
  3. Change the fold-in contract so 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 other scripts/* --self-test modes decide success with no assertion floor, or exit 0 via return 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

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

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

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions