Filed by the PM seat (domain:devx) from PR #9660's open question. The dev measured it while wiring CI for #8134 and deliberately did not act on it — correctly, since it changes CI cost characteristics.
The residue
PR #9660 makes scripts/build-console.sh inject OBJECTSTACK_SPEC_DIST so the console bundles this tree's@objectstack/spec instead of the published tarball, and adds scripts/assert-console-spec-injection.mjs as a post-build assertion.
But the console dist cache key does not include packages/spec, and it is spelled identically in three places (verified on origin/main):
.github/workflows/ci.yml:1439 key: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}
.github/workflows/ci.yml:1485 (identical)
.github/workflows/release.yml:491 (identical)
So a console dist built while spec was at state X is restored and reused after spec moves to X+1 — and because the new assertion runs insidebuild-console.sh, a cache hit skips it entirely.
⚠️The injection fixes resolution; the cache can still serve a console whose bundled spec lags this tree. PR #9660 busts the key once (it edits build-console.sh), so the gap reopens on the next spec-only change, silently — which is the same shape as the defect #8134 fixed, one layer out.
Ruled shape: B — lift the assertion into a standalone check:* gate
The four options the dev laid out, with the PM ruling:
| Option | Verdict |
|---|
| A | Add a packages/spec content hash to the cache key | ⛔ Rejected — closes the class fully, but every spec change busts the cache and forces a full cold console rebuild (~20 min, measured) on a repo doing ~18 merges/day |
| B | Keep the key; run the assertion against the restored dist on every PR | ✅ Ruled |
| C | Accept it, scope the concern to release.yml | ⛔ Rejected — leaves PR-time verification blind, and a defect first seen at release time is the expensive kind |
| D | Do nothing | ⛔ Rejected |
Why B: ci.yml's comments show the split restore/save was deliberately engineered so a failed build never poisons the cache. B preserves those economics and removes only the silent half — the assertion already exists and is content-derived, so running it against a restored dist costs one node process.
⚠️ Two constraints, 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. Decide deliberately whether the right response is to fail or to invalidate and rebuild. If it fails, the message must name the remedy, not just report the mismatch — a red nobody knows how to clear is how a gate becomes noise.
- Do not let it become a second
--re-measure. Whatever it does must be cheap enough to run on every PR, or it will be skipped and we are back here.
Worth checking while you are in there
Refs: #8134 · PR #9660 · #9659 · #8893 (the .objectui-sha pin lag)
Generated by Claude Code
Filed by the PM seat (
domain:devx) from PR #9660's open question. The dev measured it while wiring CI for #8134 and deliberately did not act on it — correctly, since it changes CI cost characteristics.The residue
PR #9660 makes
scripts/build-console.shinjectOBJECTSTACK_SPEC_DISTso the console bundles this tree's@objectstack/specinstead of the published tarball, and addsscripts/assert-console-spec-injection.mjsas a post-build assertion.But the console dist cache key does not include
packages/spec, and it is spelled identically in three places (verified onorigin/main):So a console dist built while spec was at state X is restored and reused after spec moves to X+1 — and because the new assertion runs inside
build-console.sh, a cache hit skips it entirely.build-console.sh), so the gap reopens on the next spec-only change, silently — which is the same shape as the defect #8134 fixed, one layer out.Ruled shape: B — lift the assertion into a standalone
check:*gateThe four options the dev laid out, with the PM ruling:
packages/speccontent hash to the cache keyrelease.ymlWhy B:
ci.yml's comments show the split restore/save was deliberately engineered so a failed build never poisons the cache. B preserves those economics and removes only the silent half — the assertion already exists and is content-derived, so running it against a restored dist costs onenodeprocess.--re-measure. Whatever it does must be cheap enough to run on every PR, or it will be skipped and we are back here.Worth checking while you are in there
.objectui-sha+build-console.shfully determine the dist? The three call sites are identical strings — a fourth may exist by copy.@objectstack/*packages still reach the Console bundle from objectui's lockfile — the same publish-ordering trap #8134 closes forspec#9659 records that 4 of 6@objectstack/*packages in the console build tree still resolve from objectui's lockfile. If any of them later gains an injection, its content belongs in the same staleness question — so prefer a shape that generalises over one hard-coded tospec.Refs: #8134 · PR #9660 · #9659 · #8893 (the
.objectui-shapin lag)Generated by Claude Code