Uh oh!
There was an error while loading. Please reload this page.
fix(devx): bundle THIS tree's @objectstack/spec into the vendored Console SPA - #9660
Conversation
…sole SPA (#8134) build-console.sh injected only OBJECTSTACK_CLIENT_DIST, so the console's @objectstack/spec always came from objectui's lockfile under --frozen-lockfile — the last published spec, never this workspace. An authorable key added to packages/spec after that publish was accepted and round-tripped by the server while the Studio designer rejected it as unrecognized, with the framework-side card closing green throughout. Mirrors the client injection including its preflight: a hook-presence guard naming the pin, a build guard keyed on both generators that produce the spec's export targets, and a bundle assertion. The assertion derives its probes each run instead of freezing a literal, and tests both directions — the bundle also carries a transitive copy of this tree's spec via the injected client, which makes a one-sided probe pass with no injection at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
✅ PM ACCEPT — #8134 / PR #9660 · Q1 ruled B (as a separate card), Q2 no actionVerified independently: 3 files ⭐ The most important thing in this report is that you falsified the card's own probeI sent you H1 with the card's measurement to reproduce, and warned about the If you had run the card's probe as written, you would have measured "the key is present" before your change and concluded the defect was already fixed. Re-deriving the probe instead of executing the card's is what makes the rest of this report worth reading. And the class premise survives, which is the right outcome: 102 schema description strings declared in this tree's The before/after is a real measurement, twiceTwo full
H3's leak sweep makes it a collapse rather than a partial fix: 8 substring-safe vendored-only descriptions across all 18 export entries, 0 still present after. Not "the one string I checked is gone." ⭐ And the instrument was shown able to both hit and miss: self-test red against the vendored spec, green against this tree's; positive control — the card's original probe matches 24 files in the published tarball while the re-derived probe matches 0. Plus you caught a false-positive class mid-build: 3 candidate stale-detectors were prefixes of reworded workspace strings, so probes are now validated against the entire other spec's built output rather than a hand-picked string set. That is the difference between a canary and a coincidence. The two-copies finding, folded in correctlyThe bundle carried two copies of this tree's spec before the change (one via the injected client landing in ⭐ Ruling on Q1 (the cache-key residual): B — and it ships as its own card, not here.I verified your finding myself:
B for the reason you gave: the split restore/save in Two constraints for whoever takes it, because B has a trap of its own: a gate that reddens a PR because a cached artifact is stale will red on PRs that did nothing wrong. So (i) its failure message must name the remedy (bust/rebuild) rather than just reporting the mismatch, and (ii) whoever builds it should consider whether the right response is to rebuild rather than to fail. Filing it now. C rejected: scoping the concern to Ruling on Q2: no action, recorded exactly as you asked.The two injections read as one mechanism at the That last detail is precisely the kind of thing a future reader would "harmonize" away. It is now on the record with its reason. On the pre-existing red
#96594 of 6 Verdict: ACCEPT. Arming once the three running gates converge. Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#8134
Gives
scripts/build-console.shanOBJECTSTACK_SPEC_DISTinjection mirroring the existingOBJECTSTACK_CLIENT_DISTone, so the vendored Console SPA bundles this tree's@objectstack/specinstead of the last published tarball. This is the direction ruled by the maintainer (comment5307570335), and it closes the class, not the #7804 instance — that instance belongs to #8893, which is untouched here and stays open.The ordering constraint the earlier
blockedround measured is discharged:objectstack-ai/objectui#4854merged, and the pinned SHA82a94170c405carries the hook.Verification: before / after, on real console builds
Two full
scripts/build-console.shruns in this container at pin82a94170c405, the second differing only by this PR's change.The card's own probe no longer discriminates, and that had to be re-derived.#8134 measured the defect with #7804's
describe()text, which was absent from the vendored spec at filing time. Since the v17 GA cut, the pinned objectui lockfile resolves@objectstack/spec@17.0.0— which carries that key. Grepping for it now returns a hit in both builds and proves nothing.So the probe was re-derived against the two specs actually on disk: 102 schema description strings declared in this tree's
packages/specare absent from the vendored17.0.0. The class is live today, not hypothetical.@objectstack/client, landing inframework-*.js, a different chunk from the console's own@objectstack/specimports invendor-objectstack-*.js. The designer runs on the latter. A one-sided canary reads the wrong copy and reports success.The instrument is therefore two-sided: a fresh witness (injected-only text, must be present) and a stale detector (vendored-only text, must be absent). The stale detector is the half that actually catches this defect.
"Additional custom metadata types"(vendored-only)"Field providing the marker longitude ..."framework-*.jsonlyvendor-objectstack-*.jsassert-console-spec-injection.mjsThe probe migrating from
framework-*.jsintovendor-objectstack-*.jsis the load-bearing row: it is the console's own spec imports changing resolution. The zero-leak sweep across all 18 export entries answers whether the collapse is partial — it is not; no entry point still resolves to the published spec."Explicit columns for the inline grid (derived from the child object when omitted)", still matches after the change. It is not residual stale content: the workspace reworded that description by appending a clause, so the old text is a strict prefix of the new one, and the match sits in the same chunk as the workspace-only probe. Three of the first candidate detectors were exactly this shape, which is why probes are validated against the entire other spec's built output rather than against a string set.What the hook actually consumes (it is not symmetric with the client)
Read at the pinned SHA,
apps/console/vite.config.tsplusscripts/vite-objectstack-spec-dist.ts:dist/, or an entry file inside it, walking up to the nearestpackage.jsonand requiring it to be@objectstack/spec;@objectstack/spec/uito a path that does not exist;That last point drives the build guard here, which is keyed on two sentinels rather than the client's one.
dist/index.mjsis tsup's output;json-schema/openapi.jsonisgen:openapi's — the single export entry that does not live underdist/, is not committed, and is wiped by a latergen:schemarun. A guard keyed ondist/alone sails past a tree where that happened and the hook then throws mid-build. Conversely the guard deliberately does not key on a declaration file the way the client's does: the hook resolves theimportcondition only, so a spec whose DTS pass never ran is still complete for the injection.turbo.jsonat the pinned SHA already allowlistsOBJECTSTACK_SPEC_DIST, so it survives the turbo deps build as well as the direct console build.The assertion is derived, not frozen
scripts/assert-console-spec-injection.mjsis new. It is deliberately not a fixed literal like the client'sBUNDLE_CANARY: "does the bundle carry the surface the framework declares now" is a moving target, and any string pinned today is carried by the published spec within one release — after which it passes forever while proving nothing, which is the same silent pass this PR removes. Both probes are re-derived on every run from the two package trees on disk, and the check degrades honestly (reports "no skew" and exits 0) in the state right after a spec publish, where there is nothing to detect.It is self-tested in both directions: pointed at the vendored spec as haystack it exits 1, pointed at this tree's spec it exits 0.
Preflight, per the ruling
A hook-presence guard mirroring
build-console.sh's client one refuses the build and names the pin when the pinned objectui predates the hook. An unguarded injection would silently produce a console built against the published tarball again — the exact silent-wrong-answer this card is about.Scope
GlobalFilterSchemaneeds an optionalobject— a dashboard global filter's field label (and its option labels) have no bundle key to resolve against #7804 instance is not touched here; v17 GA follow-through: objectui's lockfile still resolves@objectstack/spec@17.0.0-rc.6, so the console keeps vendoring a pre-GA spec and #7804's key stays unreachable #8893 owns the pin chain and remains open.ci.yml,release.ymlandshowcase-smoke.ymlall invokebash scripts/build-console.sh, and referenceOBJECTSTACK_CLIENT_DISTonly in comments.content/docs/releases/**untouched; a changeset ships instead.Out-of-scope finding
Filed as #9659 (unassigned,
finding): 4 of 6@objectstack/*packages in the console build tree remain resolved from objectui's lockfile after this PR —formula,lint,core,sdui-parser.lintis confirmed to reach the bundle. No user-visible break is measured for any of them, so the card asks for a per-package judgement rather than a reflexive fourfold injection.Gates
Union re-run after the final commit, at
b841493ed:check:changeset-gate-self-tests,check:objectui-changeset,check:adr-0087-registration,check:empty-changeset,check:changeset-no-major,check:nul-bytes,check:console-sha— all green. The five changeset-family gates are whatnode scripts/pm/dispatch-gates.mjsderives for the actual changed paths.check:objectui-pin-freshreports STALE, and it is pre-existing:git diff origin/main...HEAD -- .objectui-shais empty, so the gate's input is byte-identical toorigin/main. That lag is #8893's pin bump, which remains open.Generated by Claude Code