Repository navigation
fix(gate): judge an emitted template against its reader's own manifest - #9313
Conversation
The emitted-code census refuses a template that imports a bare specifier this
repository's ROOT manifest declares and no `paths` entry covers. The rule is
right in general — such a specifier reaches the snippet only through this
workspace's own installation. For a GENERATOR its premise is false: the same
generator also writes the `package.json` the reader installs, and the census
was not reading it.
`packages/create-plugin`'s `buildPackageJson` declares `@testing-library/react`,
`@testing-library/jest-dom`, `vitest`, `vite`, `vite-plugin-dts` and the rest;
three of its eight recognised templates were refused or mis-judged for exactly
those specifiers. The third was the whole of the census's `code` class — the one
diagnostic the summary calls "the only ones that say anything about the emitted
code" was an artefact of reading the wrong manifest.
The reader is static: `buildPackageJson` returns an object rather than a
template literal, which the filing card left open as a possible blocker, but the
census already parses these files with `ts.createSourceFile` and the object
literal is read out of that same tree. Nothing is executed or imported.
Three properties keep it a narrowing of the bound's PREMISE and not a hole in
the bound:
- per EMITTING PACKAGE, never merged — one generator's manifest says nothing
about what another generator's reader installs;
- mapped only at an IDENTICAL declared range, root or workspace package, so a
template is never judged against a version its reader will not install, and
a drift removes the mapping instead of lying;
- the ROOT-DECLARED control now runs in the census's own program too, so the
bound is proved still on in the very run where an emitting package's block
legitimately resolves the same specifier.
Nothing is excluded from compilation: walked, recognised and the blind side are
identical, and the count that moves is `judged`, upward.
before Judged 17 of 20 ... 2 refused by the root bound; of which 1 `code`
after Judged 19 of 20 ... 0 refused by the root bound; of which 0 `code`
The documentation verdict is byte-identical: its blocks live under
`content/docs/**`, match no emitting-package key, and take the unchanged map.
Refs objectui#8397
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
ACCEPT — the premise fork was measured FIRST and came back CAN. Flipped ready; ⛔ not armed. Probe below, posted BEFORE any merge.
⭐ The fork, answered by measurement rather than by argumentTriage made the deliverable premise-conditioned on a question nobody had answered: the census substitutes holes before compiling, and Measured before any edit: it can. The census already parses every censused source with ⭐ The standing fence —「must NOT read clean by removing the subject from compilation」 — verified structurally, ⛔ not from the reportThis is the failure mode the card exists to name, so I checked the diff rather than the summary. All 9 deleted lines are signature widenings, each replaced by a strict superset: ⇒ nothing was removed from the walk or from recognition. Confirmed by the run's own invariants: Walked 1444 / excluded 2780, Recognised 20 of 4056, Blind side 0 marker + 5 module-shaped — all IDENTICAL before and after. The only count that moved is ⭐ The bound is still live, and it is proved in the same runThe new summary line runs the ROOT-DECLARED control inside the census's own program:
⇒ and Also verified: the merge direction is safe. 「The global map WINS every collision … an emitted manifest may only ever ADD a specifier no existing mapping already backs.」 ⇒ an emitted manifest can never weaken an existing mapping, and manifests are read per emitting package, never merged, at an identical declared range only. ⛔ Deliverable (a) was NOT re-implemented — checked, because this card's first cycle died on exactly that
⭐ Pre-merge probe — counted on base
|
| leg | base | head | role |
|---|---|---|---|
extraPathsByPackage |
0 | 3 | must ARRIVE |
d.range === range |
0 | 1 | must ARRIVE — the identical-range anchor |
⭐ resolvesOnlyThroughRootManifest |
2 | 2 | must STAY — the root bound rule itself survives |
⭐ Neither of those is a pass |
1 | 1 | must STAY — (a) not re-implemented |
⭐ boundFailures |
11 | 11 | must STAY — a bound refusal still FAILS the run |
⭐ resolvesOnlyThroughRootManifest staying at 2 is the load-bearing leg: the cheap wrong fix for this card is to weaken or delete the root bound so the templates stop being refused. This pins that the rule is still there and still wired; what changed is only which manifest a generator's template is judged against.
⭐ The ablation confirms the anchor is what does the work: making d.range === range unsatisfiable returns the reading to exactly the pre-change state — 0 specifier(s) mapped, 21 left unresolvable, Judged 17 of 20 … 2 refused. Mutation proved on disk by counting both texts and by blob hash; restore proved by blob-hash equality and an empty git diff HEAD, ⛔ never by an exit code.
⚠️ Two things declared rather than buried — both correct calls
packages/cli's emitted manifest reaches its ranges through a helper call (platformPackageRange()), so 2 entries stay unreadable. ⭐ Printed as unresolvable rather than silently dropped ⇒ a declared edge of what landed, not a hidden gap. Widening the reader to follow a function return is a proposal for this seat, ⛔ not a defect in this PR. Noted; ⛔ not filed as a card today.check-node-esm-loadexits 1 in the dev's container — turbo's shared cache replayed 10 packages built in sibling worktrees, and the gate refuses to grade another tree's artifacts.⚠️ That is an environment artefact the gate names itself, and ⛔ I am not waving it away: CI runs on a clean checkout and is authoritative. If it reds there, it is real and this PR does not land until it is understood.
State
Flipped draft → ready. ⛔ Not armed — CI is re-read on 9d00369570 after the flip and every check must be green, ⛔ not the required subset. 9ae871d004; ⛔ re-confirm against the ACTUAL merge parent before reading the probe as passed.
Generated by Claude Code
ARMED — in the merge queue. ⛔ Legs frozen as posted before arming.
⭐ The
|
Fixes #8397
The premise fork, measured first
The card made the deliverable conditional on a feasibility question nobody had answered: "the census substitutes holes before compiling, and
buildPackageJsonreturns an object, not a template literal".It CAN. Measured before any edit, on
324b1d0684, by parsingpackages/create-plugin/src/templates.tswith the veryts.createSourceFilecall the census already makes on that file:An object literal is read by the same AST walk that finds the template literals beside it. Nothing has to be executed, transpiled or imported. ⇒ the fork takes the CAN branch.
My own first reading of the three sites (on today's tip, not the card's table)
The card's coordinates are from 2026-09-08 and had drifted. Re-located by content on
324b1d0684:buildVitestSetup:314:331[bound]refuses@testing-library/jest-dom/vitestbuildTestFile:491:528[bound]refuses@testing-library/react,vitestbuildViteConfig:270:287[semantic]TS2307 onvite-plugin-dts⭐ One thing the card did not record: the third was the whole of the census's
codeclass that run. The single diagnostic the summary calls "the only ones that say anything about the emitted code" was an artefact of reading the wrong manifest.What lands
The bound's sentence is "this specifier reaches the snippet only through this workspace's own installation, not through anything the emitted file's reader installs". For a document that is total. For a generator its premise can be false, because the same generator also writes the manifest the reader installs. The census now reads that manifest.
Three properties keep it a narrowing of the bound's premise rather than a hole in the bound:
scanEmittedManifestsreads a manifest-shaped object literal (name+version+ a dependency field) out of the AST, following spreads of constants the emitting package declares exactly once. Narrow in the safe direction: a literal it misses costs a refusal (the status quo); a literal it wrongly admitted would widen the bound.create-plugin's manifest says nothing about what apackages/clitemplate's reader installs. One merged map would have silenced a TS2307 in another generator's template — the same failure being fixed, one level up. The map is keyed bypackages/NAMEand applied per block, at the bound and at the resolver.The census also now runs the ROOT-DECLARED control in its own program, because the emitted-manifest map is the one thing that could widen the bound out from under it.
The before/after pair
Proof that nothing was removed from what is compiled
⛔ The card's standing fence: a change that improves the number by shrinking what is measured is the defect, not the fix. Every measured population is identical before and after; the only count that moved is
judged, and it moved up:The two previously-refused blocks are now compiled and judged; one of them promptly produced a new diagnostic (
buildTestFile's./__hole__Impl, classifiedinterpolated), which is what more measurement looks like.The bound is proved still on in the same program:
That line is not decoration:
vitestis exactly one of the specifiers acreate-pluginblock legitimately resolves in that run through its reader's own manifest. Both facts hold at once, or the map would be an exemption wearing a rule's clothes.What the census now says it cannot type
Reported line by line rather than assumed, every run:
packages/cli's emitted manifest reaches the reader through a helper call (platformPackageRange()), so two of its entries are unreadable and reported instead of guessed.Reverse verification
The range-equality anchor was ablated on the committed tree (
filter((d) => d.range === range)made unsatisfiable), the mutation proved on disk by counting both the removed and the injected text, and the restore proved by blob hash againstHEAD:The reading returns exactly to the pre-change one, so the new mapping — not an exclusion — is what moved the numbers.
Gates
node scripts/check-doc-snippet-types.mjs(documentation verdict)node scripts/check-doc-snippet-types.mjs --emit-censusvitest run scripts/__tests__/check-doc-snippet-emitted-census.test.tsvitest run scripts/__tests__/check-doc-snippet-types.test.tspnpm lint:rooteslinton the two changed files,--format jsoncheck-changeset-presence/check-changeset-no-majorcheck-control-bytes,check-changeset-claims,check-test-path-roots,check-unreferenced-sources,check-readme-exportscheck-new-cross-file-line-citationsThe documentation run was compared by running the base copy of the script and diffing the output: identical apart from the lock wrapper's own wall-clock line.
check-node-esm-loadexits 1 in this container for a reason outside this diff — turbo's shared cache replayed ten packages built in sibling worktrees (objectui-issue-9124,objectui-issue-7848), and the gate refuses to grade another tree's artifacts. This PR changes no package source; CI installs and builds fresh.Scope
scripts/only, plus an empty-frontmatter changeset (nothing published moves). ⛔ No test skipped, disabled or quarantined; ⛔ no threshold, floor or ratchet lowered; ⛔ no exemption widened. Draft, not enqueued.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code