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] check:published-readme-exports has a FOURTH no-population state one level below targets — every import resolves, no name binds, both halves check nothing, exit 0 #10417
Observation only — no gate is red. Found while implementing #9911 (which adds the refusal for targets.size === 0). This is a different stage of the same axis, one level deeper, and #9911's refusal correctly does not cover it — so it is recorded here rather than folded in.
#9911 makes the population axis three staged refusals:
published documents → import statements → workspace type entries
targets.size === 0 is now refused. But targets.size >= 1 only says a type entry was reached — it does not say a name was bound. The two halves consume bindings, not entries, so there is a fourth state below the third:
every import resolves to a workspace member, no import binds a name, so symbolChecks === 0 and callChecks === 0 — both halves check nothing, and the run exits 0.
Measured
Fixture: one published package, one published document, one import that resolves to a real workspace member with a built type entry, binding no names.
Run against the gate with #9911's refusal already in place:
EXIT=0
✓ check:published-readme-exports — 1 published document(s) across 1 workspace package(s); 1 import statement(s), 1 workspace type entr(ies).
Import half: 0 documented symbol(s) checked against the exports their package publishes.
Call-site half: 0 documented `X.y(…)` call(s) checked, on 0 import-bound name(s) and 0 name(s) built from one.
NOT read: 2 documented `X.y(…)` call(s) on 1 receiver(s) with no type this gate can reach
1 workspace type entr(ies) — a real population by the stage-3 test — with both halves at zero. Stage 3 is behaving correctly here: there genuinely is a resolved target. The vacuity has simply moved one level down, from entries reached to names bound.
Why it matters, and why it is filed rather than fixed
Same argument as #9911 and #4690: a green over a population of nothing is worthless, and this is that shape with one more step. The plausible causes are the ones a gate should catch loudly — parseImportClause losing a clause shape, or a change to extractImports' statement matcher that keeps finding statements while dropping their bindings. Note that importStatements and targets both stay non-zero through such a regression, so neither existing refusal sees it.
⚠️It is not obviously safe to refuse, which is exactly why this is an observation and not a build:
The honest condition is a whole-run one (measured.symbolChecks === 0 && measured.callChecks === 0), and unlike the three stages above it, it can only be evaluated afteranalyzeDocument has run over every document — so it does not slot into the staged populationRefusal the way the first three do.
A tree whose published READMEs legitimately document only side-effect or type-only imports would newly go red, and this gate is merge-blocking with a baseline its own file refuses as an author remedy. On the real tree today there is enormous headroom (313 documented symbol(s) checked), but the headroom is not the argument — the false-red cost is.
So it wants a judgement, not an automatic build — same reasoning that kept #9911 out of #9767.
Related: #9911 (stage 3, the refusal this sits below), #10367 (a third distinct defect in the same file), #4690 (the class), #10057 and #9089 (the same vacuous-pass family elsewhere).
Observation only — no gate is red. Found while implementing #9911 (which adds the refusal for
targets.size === 0). This is a different stage of the same axis, one level deeper, and #9911's refusal correctly does not cover it — so it is recorded here rather than folded in.The axis, after #9911
#9911 makes the population axis three staged refusals:
targets.size === 0is now refused. Buttargets.size >= 1only says a type entry was reached — it does not say a name was bound. The two halves consume bindings, not entries, so there is a fourth state below the third:Measured
Fixture: one published package, one published document, one import that resolves to a real workspace member with a built type entry, binding no names.
Run against the gate with #9911's refusal already in place:
1 workspace type entr(ies)— a real population by the stage-3 test — with both halves at zero. Stage 3 is behaving correctly here: there genuinely is a resolved target. The vacuity has simply moved one level down, from entries reached to names bound.Why it matters, and why it is filed rather than fixed
Same argument as #9911 and #4690: a green over a population of nothing is worthless, and this is that shape with one more step. The plausible causes are the ones a gate should catch loudly —
parseImportClauselosing a clause shape, or a change toextractImports' statement matcher that keeps finding statements while dropping their bindings. Note thatimportStatementsandtargetsboth stay non-zero through such a regression, so neither existing refusal sees it.measured.symbolChecks === 0 && measured.callChecks === 0), and unlike the three stages above it, it can only be evaluated afteranalyzeDocumenthas run over every document — so it does not slot into the stagedpopulationRefusalthe way the first three do.313 documented symbol(s) checked), but the headroom is not the argument — the false-red cost is.NOT readpair is not usable as the test: it is the complement of what was checked, so it goes up in a vacuous run, not to zero (measured in finding: check:published-readme-exports greens when documents and imports exist but NOTHING resolves to a workspace member — the one no-population shape it does not refuse #9911:NOT read: 3against0 … checked).So it wants a judgement, not an automatic build — same reasoning that kept #9911 out of #9767.
Related: #9911 (stage 3, the refusal this sits below), #10367 (a third distinct defect in the same file), #4690 (the class), #10057 and #9089 (the same vacuous-pass family elsewhere).
Generated by Claude Code