fix(scripts): the console spec-injection probes are chosen with the bundle in view (#20646) - #20743
Conversation
…undle in view The build-time assertion took the alphabetically first unique description from every exports-map entry, so both probes could come from entries the console never imports: after the migrations entry split the fresh witness was @objectstack/spec/migrations text and a working injection read as 'neither spec appears', and the stale detector was published ./cloud text, absent by construction. The witness is now the first injected-only description the bundle carries, and the stale leg fails on ANY published-only description in the bundle. The stamp keeps its shape. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #20646 (body and all 16 comments — the landing-with-a-red-check record ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #20646
Clause-②: no
Console Pin Gateis red onmainsincefbec216e2d(PR #20695, the@objectstack/spec/migrationsentry split). Its build step exits 2: "Neither spec appears in the built console — no @objectstack/spec content matched." The injection works. The check was reading text the console never bundles. This PR makes the build-time probe derivation choose with the bundle in view. The fresh leg stops reading entries the console never imports, and the stale leg becomes strictly stronger.Root cause, measured
scripts/console-spec-probes.mjsbuilds each spec's blob from every JS file the package'sexportsmap resolves to. The assertion then took the alphabetically first unique.describe()text on each side. A console bundles only the entries it imports, so either probe could come from an entry the bundle can never carry.The readings below compare the published
@objectstack/spec17.4.0 that objectui locks at pindd3f7e1be356with the framework spec.f927864ea0(main before the split).)./cloud)fbec216e2d(origin/main)./migrations)./cloud)./migrations, which the console never imports. Meanwhile 102 of the 142 injected-only descriptions ARE in that bundle; the first sorts from./ui../cloudentry, which objectui's shipped source never imports: 116/ui, 62/data, 17/kerneland so on, and no/cloud. A console built WITHOUT the injection carries 56 of the 160 published-only descriptions, and not that one.packages/specis untouched here.The fix (
scripts/console-spec-probes.mjs,chooseProbes)Fresh witness: the first injected-only description the bundle DOES carry. With none carried, "neither spec appears" is still exit 2.
Stale leg: judged over EVERY published-only description. It fails when ANY of them is in the bundle, and reports the first one found with the count. A bundle the old single pick flagged is still flagged, so this is strictly stronger.
Stamp: keeps its shape and version. With nothing published in the bundle, the stamped detector is the same one
pickProbechose, socheck:console-injection's cache-hit replay is unchanged.Assertion script:
scripts/assert-console-spec-injection.mjscallschooseProbesand prints the counts. The exit ladder is unchanged: 0 verified, 1 published spec bundled, 2 unverified.Self-test:
scripts/check-console-injection.mjsgains battery 13 (roster floor 10 to 11). It runs the real assertion script on fixtures whose first unique candidate is not in the bundle:I checked that the battery can fail: reverting
chooseProbesto first-pick semantics made it red with 5 failures, and restoring the fix turned it green again.Proof
All legs ran against real
vite builds of the console, with the old and the new assertion on the same bundle.fbec216e2ddd3f7e1be356./ui; 102/142 present; 0/160 published)dd3f7e1be356f927864ea0(dark)dd3f7e1be356fbec216e2ddb11afd49670(PR #20706's pin)db11afd49670On this branch's head, the Console Pin Gate's steps run locally all pass:
bash scripts/build-console.shwith the injected spec exits 0; the dist presence assert passes;pnpm check:console-shaexits 0; andpnpm check:console-injection --require-stampexits 0 and replays the new stamp.Verification record
dispatch-gates --commandsfor the 3 changed paths derives 31 families.--ranreconciles them as 30 run, all exit 0, and 1 NOT MEASURED:check:pm-dispatch-gates, whose self-test alone outruns the 590-second foreground cap here. It does not read these files' behaviour.node scripts/check-console-injection.mjs --self-testpasses: 44 assertions, battery floor met.eslint --no-inline-configon the 3 changed files reports 0 errors and 0 warnings. The config enables no type-aware linting, so untouched files' verdicts cannot move.scripts/, which ship in no published package (@objectstack/spec-monorepois private).Acceptance notes
./cloudtext no console bundles, so on a dist-cache HIT the replay cannot catch a published spec. The build-time leg now can. Fixing the replay needs a multi-detector stamp (astampVersionbump that invalidates every cached dist under the unchanged cache key), which is outside this claim. Carrier: thedomain:specseat. Noted, not filed.Console Pin Gate(job 109696257740) concluded success. The dist cache missed, so step 11 built the console and ran the changed assertion (105 of 145 injected-only descriptions present, all 160 published-only absent), and steps 12 to 14 passed on that fresh dist.Generated by Claude Code