Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 30 additions & 14 deletions packages/spec/scripts/check-generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,18 @@ const NO_GENERATOR: ReadonlyArray<{ check: string; why: string }> = [
];

/**
* Source audits that CANNOT RUN from this repository at all, because the input
* they compare against does not exist here and cannot be produced here.
* Source audits THIS AGGREGATE cannot run, because each needs an input handed to
* it on the command line and this aggregate hands none over. That, and only that,
* is the claim — ⛔ NOT that the input is unavailable here: for the entry below it
* is TRACKED in this repo and CI feeds it to the gate on every PR (that entry's
* `why` carries the reading).
*
* A separate bucket from `NO_GENERATOR` because the two say different things to a
* reader, and #4690 is what conflating them cost. `NO_GENERATOR` means "runnable,
* deliberately not run in this aggregate — run it yourself and it will answer".
* This one means "you cannot run it here at all, and here is the input it wants
* and who produces it". Sitting in the first list, `check:react-declaration-parity`
* reader, and #4690 is what conflating them cost. `NO_GENERATOR` means "runnable
* as it stands, deliberately not run in this aggregate — run it yourself and it
* will answer". This one means "running it takes an input this aggregate does not
* pass, so here is that input and who does pass it" — still runnable by hand, and
* the `why` says with what. Sitting in the first list, `check:react-declaration-parity`
* read as the former for the entire time it was the latter: it was wired into no
* workflow, and a manual run without `MANIFEST` printed a `⚠` and exited 0, so no
* path existed on which the gate could go red. Whoever read "deliberately not run"
Expand All @@ -380,10 +384,20 @@ const EXTERNAL_INPUT_REQUIRED: ReadonlyArray<{
runBy: 'scripts/gen-sdui-manifest.sh',
why:
'compares the spec schema props against the registry-declared inputs (two declarations, no renderer: #4472). ' +
'The registry is a browser app, so its manifest exists only after objectui is built at .objectui-sha and ' +
'enumerated in a real browser — nothing in this repo (console dist is gitignored, the published console ships ' +
'no sdui.manifest.json) can hand it one. `pnpm sdui:manifest` produces it and runs the ratchet; without it the ' +
'gate now exits 1 rather than skipping (#4690)',
'It reads its manifest from MANIFEST=<path> and THIS AGGREGATE PASSES NONE — that, and only that, is what ' +
'"cannot run here" means for this entry. The input is NOT unavailable in the repo: since #13446 a dump is ' +
'TRACKED at the repo root as sdui.manifest.json, and lint.yml runs the gate --strict against it on every PR ' +
'(MANIFEST="$PWD/sdui.manifest.json"), so the gate is neither unrun nor unrunnable — it is unrun BY THIS ' +
'AGGREGATE. Nor does producing one require a browser: scripts/gen-sdui-manifest-node.mjs regenerates the ' +
'tracked artefact under plain Node from the PUBLISHED @object-ui/* packages (the browser-only claim was ' +
'measured false on 2026-08-29, re-measured 2026-08-30 against published 17.6.0, and reproduced ' +
'byte-identically in review of #18608), and scripts/check-sdui-manifest.mjs holds artefact, record and pin ' +
'together. `pnpm sdui:manifest` (runBy) is the other producer, and it does NOT build objectui: it REQUIRES ' +
'a checkout already vendored at .cache/objectui-<sha> by `pnpm objectui:build`, exits 1 telling you to run ' +
'that first (gen-sdui-manifest.sh 507-516), then serves that tree with a vite dev server and dumps the ' +
'registry from a real browser, running this ratchet against THAT. ⚠️ The two producers read two ' +
'different registries — published packages vs the pinned checkout\'s source — and do not agree today (#17735). ' +
'Without a MANIFEST the gate exits 1 rather than skipping (#4690)',
},
];

Expand Down Expand Up @@ -481,7 +495,7 @@ function reconcileLedger(scripts: Record<string, string>): void {
if (name.startsWith('check:') && !declaredChecks.has(name)) {
problems.push(` \`${name}\` exists in package.json but is in neither GATED nor NO_GENERATOR (nor EXTERNAL_INPUT_REQUIRED).\n` +
` Classify it: does it compare a checked-in artifact against a generator, audit source,\n` +
` or audit source against an input this repo cannot produce (name where it DOES run)?`);
` or audit source against an input THIS AGGREGATE does not pass (name who does pass it)?`);
}
if (name.startsWith('gen:') && !declaredGens.has(name)) {
problems.push(` \`${name}\` exists in package.json but no GATED entry names it and it is not in UNGATED_GENERATORS.\n` +
Expand Down Expand Up @@ -646,10 +660,12 @@ for (const entry of GATED) {
console.log(`\nNot run here (${NO_GENERATOR.length} source audits with no artifact to regenerate): ` +
NO_GENERATOR.map((n) => n.check).join(', '));
// Narrowing is never silent, part three — and this one is a different sentence:
// "deliberately not run" invites the reader to run it, which for these is not an
// option from this repo. Say what the missing input is and who supplies it.
// "deliberately not run" invites the reader to run it AS IT STANDS, which for
// these does not work: each needs an input on the command line that this
// aggregate does not pass. Say what that input is and who does pass it, so the
// reader can run it by hand — for the entry below the input is tracked here.
if (EXTERNAL_INPUT_REQUIRED.length) {
console.log(`Cannot run here (${EXTERNAL_INPUT_REQUIRED.length} source audit(s) whose input this repo cannot produce):`);
console.log(`Needs an input this aggregate does not pass (${EXTERNAL_INPUT_REQUIRED.length} source audit(s)):`);
for (const e of EXTERNAL_INPUT_REQUIRED) {
console.log(` ${e.check} — needs ${e.input}\n runs in ${e.runBy}; ${e.why}`);
}
Expand Down
41 changes: 34 additions & 7 deletions packages/spec/scripts/check-react-blocks-declaration-parity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,42 @@
//
// So the accepted set per block is node-level keys ∪ per-block props ∪ overlay.
//
// WHERE THE MANIFEST COMES FROM — AND WHY NOTHING HERE CAN PRODUCE ONE (#4690).
// WHERE THE MANIFEST COMES FROM — TWO PRODUCERS, TWO REGISTRIES (#4690, #17735).
//
// The right-hand side is objectui's registry-inputs manifest (sdui.manifest.json).
// Its only producer is objectui's `scripts/dump-public-manifest.mjs`, which drives a
// real browser (Playwright chromium) at the built console's `dev/manifest-dump.html`
// and reads `window.__MANIFEST`: the registry is a browser app (plugin-map / charts
// pull browser-only deps), so nothing enumerates it from Node. `pnpm sdui:manifest`
// (scripts/gen-sdui-manifest.sh) is the wrapper that builds objectui at
// `.objectui-sha`, dumps the manifest, and then runs THIS gate against it.
// It has TWO producers, and they do not read the same registry — which is the whole
// of #17735, so do not reason from either one alone:
//
// browser objectui's `scripts/dump-public-manifest.mjs` drives a real browser
// (Playwright chromium) at `dev/manifest-dump.html` and reads
// `window.__MANIFEST`. `pnpm sdui:manifest`
// (scripts/gen-sdui-manifest.sh) is the wrapper — and it does NOT
// build objectui. It REQUIRES a checkout already vendored at
// `.cache/objectui-<sha>` by `pnpm objectui:build`, and exits 1
// telling you to run that first (gen-sdui-manifest.sh 507-516). It
// then serves THAT tree with a vite dev server, dumps, and runs THIS
// gate on the result — so what it reads is the pinned SOURCE.
// node `scripts/gen-sdui-manifest-node.mjs` writes the TRACKED repo-root
// artefact under plain Node — no browser, no objectui build. It
// enumerates the PUBLISHED `@object-ui/*` packages at the version named
// in `scripts/sdui-manifest.record.json`.
//
// ⚠️ "The registry is a browser app so nothing enumerates it from Node" was true
// when this header was written and is now FALSE — measured 2026-08-29,
// re-measured 2026-08-30 against published `@object-ui/*` 17.6.0, and reproduced
// byte-identically in review of #18608; the node producer's header carries the
// readings. ⛔ Do not put an issue number back on this line: the objectui
// citation this used to carry resolves to nothing — 404 for both the issue and
// the PR at that number, with three neighbouring objectui numbers answering 200
// as controls — and the readings above stand without one.
//
// ⚠️ The two producers disagree TODAY, and the reason is the input, not the code:
// objectui bumps its `version` only at release, so the version read off the pinned
// commit's `packages/core/package.json` names the PREVIOUS release's tarball. The
// node route therefore describes the registry of the last PUBLISHED release, while
// the browser route describes the PINNED SOURCE — and `check-sdui-manifest.mjs`
// cannot see the gap, because it verifies the record's sha and pin, never that the
// version installed corresponds to the pinned commit (#17735).
//
// NOTHING HERE PRODUCES one — but since #13446 one is CHECKED IN. The production
// half is unchanged and still measured: `packages/console/dist/` is gitignored (the
Expand Down
Loading