diff --git a/docs/releases-maintenance.md b/docs/releases-maintenance.md index 644a7992c3..59980f23b9 100644 --- a/docs/releases-maintenance.md +++ b/docs/releases-maintenance.md @@ -108,33 +108,55 @@ job) pins this: it asserts the file's **bytes** across a refused run, because th broken and the fixed script both exit non-zero here and only the file tells them apart. -#### After the pin moves: run the declaration-parity ratchet (#5960) +#### After the pin moves: regenerate the committed manifest (#5960, #12924) The bump has a second half, and it is not optional: ```bash -pnpm sdui:manifest # rebuild objectui at the new pin, dump sdui.manifest.json, run the ratchet +node scripts/gen-sdui-manifest-node.mjs \ + --objectui-version {the @object-ui version the new pin ships} ``` -`sdui.manifest.json` is objectui's registry-inputs dump — the right-hand side of -ADR-0082 D4's spec↔registry **declaration-parity ratchet** (`scripts/gen-sdui-manifest.sh` -produces it and runs the ratchet against it; note it is *not* `scripts/build-console.sh`, -which the ADR named until #5960 corrected it). That file changes when — and only when — -this pin moves, so **the pin bump is the ratchet's trigger, and its only one.** - -This is an **on-demand gate by decision** (maintainer ruling, 2026-08-07). No workflow -produces the manifest and none should: the only producer drives a Playwright chromium -over objectui's built console and reads `window.__MANIFEST`, so wiring it into this -repo's CI would put a full objectui build plus a browser download on every matching PR. -The trade is deliberate — honest on-demand coverage over expensive full coverage — -and it is safe because the gate has no unearned green: since #4690 a missing, unreadable, -malformed or empty manifest **exits 1** rather than skipping. The one failure mode left is -that nobody runs it, which is what this step exists to close. `scripts/bump-objectui.sh` -and `scripts/build-console.sh` both print the reminder when they finish. - -Needs a Playwright browser (`pnpm exec playwright install chromium-headless-shell`). When -the ratchet fires, the fix is a spec/overlay edit or an explicit `--update` to re-accept -the baseline — see ADR-0082 D4 and its addendum 2. +Read that version from the objectui checkout's `packages/core/package.json` — the bump +already required that checkout. **That command writes the two tracked files, and nothing +else writes them:** the repo-root `sdui.manifest.json` and its provenance record +`scripts/sdui-manifest.record.json`. It installs the published `@object-ui/*` packages +into a temp dir and serializes the registry through `@objectstack/sdui-parser`, so it +needs neither a browser nor an objectui build (it does need +`packages/sdui-parser/dist` — `pnpm --filter @objectstack/sdui-parser build`). + +Two required lint steps read that tracked pair on **every PR**, so skipping this step +reds the bump PR rather than leaving it quietly uncovered: + +- `node scripts/check-sdui-manifest.mjs` — shape, `sha256` against the record, and the + record's `objectuiSha` against the live `.objectui-sha`. Moving the pin is what makes + the last one fail, and its failure text prints the exact regeneration call. +- ADR-0082 D4's spec↔registry **declaration-parity ratchet**, which `lint.yml` runs + `--strict` with `MANIFEST="$PWD/sdui.manifest.json"`. It is baseline-ratcheted, so only + divergence new since `packages/spec/react-declaration-parity.baseline.json` reds. + +**`pnpm sdui:manifest` is a different command and does not do this.** It +(`scripts/gen-sdui-manifest.sh`) builds objectui at the pin, drives a Playwright chromium +over the built console to read `window.__MANIFEST`, writes +`packages/console/dist/sdui.manifest.json` — **gitignored** — and runs the same parity +ratchet against that untracked copy. It is an independent read of the registry straight +from a browser, and `cut-rc.yml` runs it once per release cut; what it never does is +touch the tracked pair, so it cannot clear either gate above. Note the browser dump lives +in `scripts/gen-sdui-manifest.sh`, *not* `scripts/build-console.sh`, which the ADR named +until #5960 corrected it. + +Keeping that browser dump **out of per-PR CI** is a maintainer ruling (2026-08-07): it +would put a full objectui build plus a browser download on every matching PR. #12924 +changed the ratchet's input, not that ruling — the manifest is checked in now, so the +ratchet runs per-PR off the tracked artefact with no browser anywhere. Neither gate has +an unearned green: since #4690 a missing, unreadable, malformed or empty manifest +**exits 1** rather than skipping. + +`scripts/bump-objectui.sh` and `scripts/build-console.sh` both print the reminder when +they finish. When the ratchet fires, the fix is a spec/overlay edit or an explicit +`--update` to re-accept the baseline — see ADR-0082 D4 and its addendum 2. The browser +dump additionally needs a Playwright browser +(`pnpm exec playwright install chromium-headless-shell`). #### If the dispatch container's Playwright browser doesn't match the revision @@ -253,8 +275,13 @@ So the process is: `@objectstack/console` changeset for `OLD_PIN..NEW_PIN` from objectui's own declared changesets (`scripts/objectui-changeset-digest.mjs`). This is the mechanism that keeps the release record honest, and it is the whole of it. -4. **Then the declaration-parity ratchet** — see "After the pin moves" above; the - bump is that ratchet's only trigger. +4. **Then the committed manifest is regenerated in that same PR.** + `node scripts/gen-sdui-manifest-node.mjs --objectui-version {the @object-ui version + the new pin ships}` rewrites the tracked `sdui.manifest.json` and + `scripts/sdui-manifest.record.json`; `scripts/check-sdui-manifest.mjs` reds the bump + PR until it does, and ADR-0082 D4's declaration-parity ratchet reads that same tracked + artefact. `pnpm sdui:manifest` writes neither file — it dumps to the gitignored + `packages/console/dist/`. See "After the pin moves" above. 5. **Releases build against the pin as committed.** Both `cut-rc.yml` and `release.yml` read `.objectui-sha` and build the Console SPA at it. Neither resolves objectui `main`; neither moves the pin. diff --git a/scripts/build-console.sh b/scripts/build-console.sh index efc8d24e2d..896c2f08c8 100755 --- a/scripts/build-console.sh +++ b/scripts/build-console.sh @@ -274,17 +274,26 @@ node "${FRAMEWORK_ROOT}/scripts/assert-console-spec-injection.mjs" \ BYTES="$(du -sk "$TARGET" 2>/dev/null | awk '{print $1}')" echo "✓ @objectstack/console dist ready (${BYTES} KB) from objectui@${PINNED_SHA:0:12}" -# ADR-0080/0081: the public-tier SDUI manifest and the spec↔registry react-block -# declaration-parity ratchet are intentionally NOT generated here — they require a -# real browser (Playwright) to enumerate the console registry, and the console -# build must not drag in a browser dependency. Regenerate them on demand instead: -# pnpm sdui:manifest (see scripts/gen-sdui-manifest.sh) +# ADR-0080/0081: neither SDUI manifest is generated here, and the spec↔registry +# react-block declaration-parity ratchet is not run here either. Two different files +# wear that name, and the difference is what the reminder below exists to carry: +# +# sdui.manifest.json (repo root) + scripts/sdui-manifest.record.json — TRACKED, +# written only by `node scripts/gen-sdui-manifest-node.mjs`, which installs the +# published @object-ui packages (no browser). `scripts/check-sdui-manifest.mjs` +# in the required lint job reds once the pin moves and they have not followed, +# and ADR-0082 D4's ratchet reads the tracked manifest on every PR (#12924). +# packages/console/dist/sdui.manifest.json — GITIGNORED, written by +# `pnpm sdui:manifest` (scripts/gen-sdui-manifest.sh), which needs a real browser +# to enumerate the console registry. Nothing gates it; the console build must not +# drag in a browser dependency, so it stays on demand (#5960). # # The reminder names the TRIGGER, not just the command (#5960): `pnpm objectui:refresh` # runs bump-objectui.sh and then this script, so this is the last output an operator -# sees while moving the pin — and the pin bump is the ratchet's only trigger, by -# decision. bump-objectui.sh prints the same step; this repeats it because that one -# has scrolled past a whole console build by now. +# sees while moving the pin. bump-objectui.sh prints the same step; this repeats it +# because that one has scrolled past a whole console build by now. echo "ℹ SDUI manifest + declaration-parity ratchet are decoupled from the console build." -echo " Run 'pnpm sdui:manifest' whenever you move the objectui pin — that is the" -echo " ratchet's only trigger, on demand by decision (#5960). Requires Playwright." +echo " Moved the objectui pin? Regenerate the TRACKED manifest — the required lint gate:" +echo " node scripts/gen-sdui-manifest-node.mjs --objectui-version {the version the new pin ships}" +echo " It writes sdui.manifest.json + scripts/sdui-manifest.record.json. 'pnpm sdui:manifest'" +echo " writes neither: it is the browser dump to the gitignored packages/console/dist/." diff --git a/scripts/bump-objectui.sh b/scripts/bump-objectui.sh index ac91a8af8a..002fe71e61 100755 --- a/scripts/bump-objectui.sh +++ b/scripts/bump-objectui.sh @@ -7,12 +7,24 @@ # scripts/bump-objectui.sh --no-commit # update files only, don't commit # scripts/bump-objectui.sh --no-changeset # skip the @objectstack/console changeset # -# After the bump — the second half of the pin-update procedure (#5960): -# pnpm sdui:manifest # dump objectui's sdui.manifest.json and run the -# # spec↔registry declaration-parity ratchet (ADR-0082 D4). -# # The pin bump is that ratchet's ONLY trigger; it is an -# # on-demand gate by decision, never a CI job. Needs -# # Playwright chromium. This script prints the reminder. +# After the bump — the second half of the pin-update procedure (#5960, #12924): +# node scripts/gen-sdui-manifest-node.mjs \ +# --objectui-version {the @object-ui version the new pin ships} +# +# That call rewrites the two TRACKED files, and nothing else writes them: the +# repo-root `sdui.manifest.json` and its provenance record +# `scripts/sdui-manifest.record.json`. The required lint job reds the bump PR until +# it has run — `scripts/check-sdui-manifest.mjs` asserts the record's pin equals +# `.objectui-sha` — and ADR-0082 D4's spec↔registry declaration-parity ratchet reads +# that same tracked artefact on every PR. No browser and no objectui build; it +# installs the published @object-ui packages into a temp dir. This script prints the +# reminder on its way out. +# +# ⛔ `pnpm sdui:manifest` is a DIFFERENT command and does not clear that gate: it +# dumps objectui's registry from a real browser to the gitignored +# `packages/console/dist/sdui.manifest.json` and ratchets against that untracked +# copy. An independent read of the registry, worth running when you hold an objectui +# checkout and a Playwright chromium — but it writes neither tracked file. # # The pin must name a commit that is on objectui MAIN. This script WARNS — it # does not refuse — when the revision being pinned is not reachable from the @@ -686,40 +698,52 @@ if [[ -n "$CS_FILE" ]]; then echo "→ wrote changeset $(basename "$CS_FILE") (@objectstack/console: ${BUMP})" fi -# --- The other half of the pin-update procedure (#5960) ---------------------- -# ADR-0082 D4's spec↔registry declaration-parity ratchet reads objectui's -# `sdui.manifest.json`, and that file changes when — and only when — this pin -# moves. So the pin bump is the ratchet's trigger, and the ratchet has no -# AUTOMATIC one at all: `packages/console/dist/` is gitignored and the published -# @objectstack/console tarball ships no manifest, so the file exists only where -# somebody dumped it. Producing it in this repo's CI was considered and REJECTED -# (#5960) — it would put a full objectui build plus a chromium download on every -# matching PR. +# --- The other half of the pin-update procedure (#5960, #12924) -------------- +# The pin bump is what makes the committed manifest stale, and regenerating it is +# what this prints. Which command writes what is the whole of it: +# +# node scripts/gen-sdui-manifest-node.mjs -> sdui.manifest.json (repo root) and +# scripts/sdui-manifest.record.json +# — both TRACKED, and gated per PR +# pnpm sdui:manifest (gen-sdui-manifest.sh) -> packages/console/dist/sdui.manifest.json +# — GITIGNORED, and gated nowhere +# +# So only the first one can turn the bump PR green. `scripts/check-sdui-manifest.mjs` +# in the required lint job reds while the record's pin trails `.objectui-sha`, and +# ADR-0082 D4's declaration-parity ratchet runs `--strict` against the tracked +# manifest on every PR (#12924 checked it in; before that the ratchet had no +# automatic run at all, which is the world the reminder here used to describe). +# +# Producing the BROWSER dump in this repo's per-PR CI was considered and REJECTED +# (#5960) — a full objectui build plus a chromium download on every matching PR — +# and that ruling stands; what changed is the ratchet's input, not the ruling. # # ⚠️ Corrected 2026-08-30 (#13091): this comment used to read "measured on # origin/main, no workflow runs `pnpm sdui:manifest`, no workflow installs # Playwright for it". Both halves stopped being true on 2026-08-10, when # `.github/workflows/cut-rc.yml` landed doing both, one step apart, as the last -# check before publish. The trigger claim above is unaffected — cut-rc is a -# `workflow_dispatch`-only lane a human starts by typing the version — but "no -# workflow" was an absolute about the whole workflow set, and that is the shape -# that rotted. Read a workflow's `on:` block rather than a file count. ADR-0082 -# addendum 2 carries the full correction. -# -# Deliberately a REMINDER, not a hard gate: a machine without Playwright must -# still be able to move the pin, and hard-failing here would be the rejected -# CI cost wearing a local disguise. The gate itself cannot go falsely green — -# since #4690 a missing or unusable manifest exits 1 instead of skipping — so -# the only failure mode left is "nobody ran it", which is what this prints to -# prevent. Printed on BOTH exits below: --no-commit still moved the pin. +# check before publish — a `workflow_dispatch`-only lane a human starts by typing +# the version. "No workflow" was an absolute about the whole workflow set, and that +# is the shape that rotted. Read a workflow's `on:` block rather than a file count. +# ADR-0082 addendum 2 carries the full correction. +# +# Deliberately a REMINDER, not a hard gate: a machine that cannot reach npm must +# still be able to move the pin, and hard-failing here would be CI cost wearing a +# local disguise. Nothing is lost by that, because the gate cannot go falsely green +# — since #4690 a missing or unusable manifest exits 1 instead of skipping, and +# since #12924 the bump PR itself reds until the regeneration lands. Printed on +# BOTH exits below: --no-commit still moved the pin. print_sdui_next_step() { echo - echo "→ NEXT STEP — run the declaration-parity ratchet (ADR-0082 D4):" - echo " pnpm sdui:manifest" - echo " It rebuilds objectui at the new pin, dumps packages/console/dist/sdui.manifest.json" - echo " and ratchets spec↔registry declaration parity. A pin bump is its only trigger:" - echo " it is an on-demand gate by decision (#5960), never a CI job." - echo " Needs a Playwright browser — 'pnpm exec playwright install chromium-headless-shell'." + echo "→ NEXT STEP — regenerate the committed SDUI manifest (required lint gate):" + echo " node scripts/gen-sdui-manifest-node.mjs \\" + echo " --objectui-version {the @object-ui version the new pin ships}" + echo " Read that version from the objectui checkout's packages/core/package.json." + echo " It rewrites the tracked sdui.manifest.json and scripts/sdui-manifest.record.json;" + echo " scripts/check-sdui-manifest.mjs reds this bump PR until it does, and ADR-0082 D4's" + echo " declaration-parity ratchet reads the same tracked artefact. No browser needed." + echo " ⛔ 'pnpm sdui:manifest' does NOT write those files — it is the separate browser" + echo " dump to the gitignored packages/console/dist/ (needs Playwright chromium)." echo " Procedure: docs/releases-maintenance.md → 'After the pin moves'." }