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
20 changes: 20 additions & 0 deletions .changeset/sdui-manifest-one-producer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'@objectstack/console': patch
---

The prebuilt Console dist now ships `dist/sdui.manifest.json`: the ADR-0080 public-tier
component manifest of the objectui registry at the pinned commit.

It is the same file the framework repository tracks at its root and gates on every pull
request. `scripts/build-console.sh` copies it in, and one producer writes it:
`scripts/gen-sdui-manifest-node.mjs`, which reads objectui's built tree at the pin. No
earlier published `@objectstack/console` carried this file. The RC cut used to write a
browser-dumped copy into `dist/`, but the release build replaced `dist/` before packing, so
none reached a tarball (17.0.0, 17.3.0 and 17.4.0 each list 0 matches). That browser dump is
retired. It was byte-identical to the tracked file over the same built tree.

For now the file is only present in the tarball. This package's `exports` map exposes
`./package.json` and nothing else, so resolving `@objectstack/console/dist/sdui.manifest.json`
through `exports` fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`. Anything that resolves through
`exports` cannot read the file yet. That includes the CLI's JSX-page manifest fallback, which
catches the error and keeps parse-level validation, as before.
4 changes: 2 additions & 2 deletions .claude/hooks/guard-process-kill.selftest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ echo "== the teardown AGENTS.md itself prescribes: the port is one YOU picked ==
expect allow 'kill $(lsof -ti tcp:38421)'
expect allow 'kill $(lsof -ti tcp:3000) 2>/dev/null'

echo "== pgrep -P / -s select by a handle the caller owns — live in two tracked scripts =="
# scripts/publish-smoke.sh kill_tree() and scripts/gen-sdui-manifest.sh sdui_live_pids().
echo "== pgrep -P / -s select by a handle the caller owns — legitimate teardown shapes =="
# scripts/publish-smoke.sh kill_tree() uses -P; -s was the retired gen-sdui-manifest.sh's.
# A rule that reddened these would be routed around within the hour.
expect allow 'for child in $(pgrep -P "$pid"); do kill "$child"; done'
expect allow 'pgrep -s "$leader" | xargs kill'
Expand Down
6 changes: 3 additions & 3 deletions .claude/hooks/guard-process-kill.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@
# you picked, not a name
# pgrep -P "$pid" / pgrep -s "$leader" — even piped into a kill: the selector is a parent
# or session handle the caller owns, and both are
# live in scripts/publish-smoke.sh and
# scripts/gen-sdui-manifest.sh
# legitimate teardown shapes (scripts/publish-smoke.sh
# uses the first)
# pkill -P "$pid" / pkill -s "$sid" — the same selectors on pkill, with NO pattern
# operand. Add a pattern and it is blocked again.
# pgrep -f foo / ps aux | grep node — a READ. Nothing dies; look all you like. But a
Expand Down Expand Up @@ -243,7 +243,7 @@ check_killall() {
# --- does this segment select processes by NAME? (pgrep with a bare pattern operand) -----
# Returns 0 = yes, this is a name-pattern selector. `pgrep -s "$leader"` and
# `pgrep -P "$pid"` consume their argument and leave no operand behind, so they answer 1 —
# which is what keeps scripts/publish-smoke.sh and scripts/gen-sdui-manifest.sh legal.
# which is what keeps scripts/publish-smoke.sh's `pgrep -P` teardown legal.
is_name_selector() {
[ "$HEAD" = "pgrep" ] || return 1
local n=${#W[@]} i=$((HEAD_I + 1))
Expand Down
41 changes: 5 additions & 36 deletions .github/workflows/cut-rc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -405,42 +405,11 @@ jobs:
- name: Verify the Console dist stamp matches the pin
run: pnpm check:console-sha

# ────────────────────────────────────────────────────────────────────────
# ADR-0082 D4 declaration-parity ratchet — the mandatory SECOND HALF of
# every pin move (#5960), run here against the pin as COMMITTED.
#
# ⚠️ Since #10134 this lane no longer moves the pin, so this is no longer
# "the second half of a move this run performed" — the move, and the
# ratchet that belongs to it, happen on the manual bump PR. Kept anyway,
# and deliberately: the ratchet is an on-demand gate by decision (ADR-0082
# addendum), which means "unrun" is its live failure mode, and a bump PR
# that skipped it leaves a divergence that reaches a release unnoticed.
# This is the last place before publish that can still say so. It writes to
# a gitignored path and asserts against the committed baseline, so it adds
# no file to the version commit; on a pin that already ratcheted it simply
# passes.
#
# Installing a Playwright browser is fine HERE. The #5960 ruling that keeps
# the ratchet off CI is about not putting an objectui build plus a browser
# download on every matching PR; this workflow runs a handful of times a
# month, on purpose, and has already built the console two steps above.
# ────────────────────────────────────────────────────────────────────────
- name: Install a Playwright browser for the manifest dump
run: |
# The dump runs INSIDE objectui's build tree (gen-sdui-manifest.sh does
# `pushd $BUILD_ROOT` and drives objectui's own playwright), so the
# browser must be installed against THAT workspace. `pnpm exec
# playwright` from the framework root resolves nothing — playwright is
# not a framework dependency.
BUILD_ROOT=".cache/objectui-${OBJECTUI_SHA:0:12}"
if [ ! -d "$BUILD_ROOT" ]; then
echo "::error::expected objectui build tree at ${BUILD_ROOT} (created by 'pnpm objectui:build'). Cannot install the browser the ratchet needs."
exit 1
fi
pnpm --dir "$BUILD_ROOT" exec playwright install chromium-headless-shell

- name: 'Declaration-parity ratchet at the committed pin (ADR-0082 D4)'
run: pnpm sdui:manifest
# No SDUI manifest step follows. The console dist built above already
# carries the TRACKED repo-root sdui.manifest.json (build-console.sh copies
# it), whose freshness against the pin the required lint job holds on every
# PR; the ADR-0082 D4 declaration-parity ratchet that used to run here, and
# the Playwright browser it needed, are retired.

# ────────────────────────────────────────────────────────────────────────
# PRE-VERSION GATES. Every one of these reads `.changeset/*`, so all of them
Expand Down
15 changes: 2 additions & 13 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,8 @@ jobs:
# is the thing that notices. Offline record checks only (existence, shape,
# sha256 vs the provenance record, record pin vs `.objectui-sha`): the
# regenerate-and-compare half runs where a regeneration can (the Node
# generator needs the published @object-ui set — see
# generator reads objectui's BUILT tree at the pin, which only
# `pnpm objectui:build` materialises — see
# scripts/gen-sdui-manifest-node.mjs), and a pin bump goes red HERE until
# it has been re-run — the same moment the lockstep step above forces the
# parser parity re-verification, with an objectui checkout guaranteed in
Expand Down Expand Up @@ -6712,18 +6713,6 @@ jobs:
- name: Check docs YAML examples parse against live spec schemas
run: pnpm --filter @objectstack/spec run check:yaml-examples

# ADR-0082 D4's spec↔registry react-block declaration-parity ratchet, fed
# by the checked-in repo-root manifest (#12924 execution point 1: the same
# artefact resolves this gate's EXTERNAL_INPUT_REQUIRED standing — the
# input it always lacked now lives in the tree, held fresh by the
# check-sdui-manifest step in the lint job). Baseline-ratcheted: only a
# divergence NEW since packages/spec/react-declaration-parity.baseline.json
# reds, so wiring it per-PR promotes the cut-rc-only run without
# manufacturing a standing red. Reads spec src/ through tsx like its
# neighbours — no dist. ~5 s.
- name: Ratchet spec-vs-registry react declaration parity against the checked-in manifest
run: MANIFEST="$PWD/sdui.manifest.json" pnpm --filter @objectstack/spec check:react-declaration-parity --baseline react-declaration-parity.baseline.json --strict

# Same anti-drift class as the gates above, for the generated translation
# bundles in packages/platform-objects/src/apps/translations/. Nothing
# regenerated them either, so they rotted three ways at once (#3670):
Expand Down
24 changes: 6 additions & 18 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,11 @@ pull its build into `packages/console/`.
Other scripts: `objectui:bump` (pull only), `objectui:build`, `objectui:clean`. ⛔ Never hand-edit
`packages/console/dist/` or `.cache/objectui-*/` — regenerated.

**Moving the pin has a second half: regenerate the committed manifest** — `node scripts/gen-sdui-manifest-node.mjs
--objectui-version {the @object-ui version the new pin ships}`; `scripts/check-sdui-manifest.mjs` reds until you do.
ADR-0082 D4's spec↔registry declaration-parity ratchet reads that tracked artefact, so it gates **every PR**, not a
pin bump alone. `pnpm sdui:manifest` is the separate browser dump of objectui's own registry (needs Playwright
chromium); `objectui:bump` and `objectui:refresh` print it. Full procedure: `docs/releases-maintenance.md`.
**Moving the pin has a second half: regenerate the committed manifest** — `pnpm objectui:build`, then
`node scripts/gen-sdui-manifest-node.mjs`, which reads objectui's BUILT tree at the pin (never an npm install, whose
version string names an older commit) and takes no arguments; `scripts/check-sdui-manifest.mjs` reds until you do.
`objectui:bump` and `objectui:refresh` print the step, and the console build ships that same tracked file in
`packages/console/dist/`. Full procedure: `docs/releases-maintenance.md`.

**Fast iteration on `../objectui` src (no commit/refresh loop):** run objectui's own console dev server —
`cd ../objectui && pnpm --filter @object-ui/console dev` (Vite on **:5180**, HMR). Its `/api` proxy targets
Expand Down Expand Up @@ -755,18 +755,6 @@ Principles the wrapper encodes (its own output is the authority on detail):
a name, with accepted cases in the shrink-only, hand-edited
`dual-source-exports.baseline.json`.

**`check:react-declaration-parity` compares two DECLARATIONS, not a declaration against an
implementation** — the props the spec zod schema declares vs the inputs the objectui
registry config declares. Its `spec-only` / `registry-only` / `missing` signals are real;
just don't read it as proof anything renders. Its right-hand side is the **tracked
repo-root `sdui.manifest.json`** and its record `scripts/sdui-manifest.record.json`, which
the required lint job's `scripts/check-sdui-manifest.mjs` checks OFFLINE only — existence,
shape, sha256 vs the record, record pin vs `.objectui-sha`; its version-vs-pin leg runs
only where an objectui checkout is in hand, so lint prints `NOT CHECKED` by design.
`lint.yml` runs THIS gate `--strict` against it on every PR; it still **exits 1** with no
usable manifest and `check:generated` files it `EXTERNAL_INPUT_REQUIRED` because that
aggregate hands it none. ⛔ Do not "fix" a red by re-adding a skip.

Two generators have **no** gate at all — `gen:openapi` and `gen:sbom`. Nothing verifies
their output is current; the wrapper reports that each run rather than staying silent.

Expand Down Expand Up @@ -1100,7 +1088,7 @@ Both non-handshake shapes, and how to classify and probe your own:
so a removal or rename the pinned sibling still imports turns `main` red for every PR in the repo the moment it
merges — "retire the surface" and "leave the sibling untouched" cannot both hold. A ruling that authorizes such a
removal therefore implicitly authorizes the objectui-side fix and the pin bump as part of the same landing (the
bump's `sdui:manifest` second half included — see the Frontend section). Pre-merge check for any removal or rename
bump's manifest regeneration included — see the Frontend section). Pre-merge check for any removal or rename
of an exported surface: does the pinned sibling import what you are removing? `git grep` it in `../objectui` at the
pinned SHA before merging.
5. **Touched `packages/spec`? Regenerate and commit its artifacts before pushing** — § *Touched `packages/spec`*
Expand Down
2 changes: 2 additions & 0 deletions docs/adr/0082-react-component-contract-governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ These are declared in the `react-blocks.ts` overlay with a `kind` of `binding`/`

### 4. Declaration parity is a build-time baseline ratchet, not a per-PR gate

> **Status: retired 2026-09-18 by maintainer ruling on #17735; the artefact stays for the CLI witness and the freshness gate.**

> **Corrected by #4472 — see addendum 1.** This decision was written and implemented as "conformance": the check was named `check:react-conformance`, and its script header claimed it confirmed the components "ACTUALLY implement" the spec props. It does not and never did — it compares **two declarations**, and it was **warn-only** besides. The mechanism below is real and kept; the words for it are now `check:react-declaration-parity`, and the gate now runs `--strict`.
>
> **Corrected by #5960 — see addendum 2.** This section shipped saying the ratchet runs *inside `build-console.sh`*. That was true when it was written and stopped being true at #4472, which moved it into `scripts/gen-sdui-manifest.sh`; the file names below are corrected in place, because a reader who followed this ADR opened the wrong file. #5960 also answers the question this decision left open — "not every PR" never said *when*, and the answer is **at the objectui pin bump**.
Expand Down
5 changes: 5 additions & 0 deletions docs/audits/2026-06-react-blocks-conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
> "frontend-only" as "the registry declared an input the spec did not", not as
> "the component accepts it". Evidence about the render path comes from
> objectui's `public-block-binding-reach.test.tsx`; see the ADR-0082 addendum.
>
> **Retired (2026-09-18, maintainer ruling on #17735).** `check:react-declaration-parity`,
> its script, test and baseline, and the `pnpm sdui:manifest` browser dump no longer
> exist; the commands below are a record of how this audit ran, not something to
> run. The status line on ADR-0082 decision 4 carries the ruling.

**Question** (raised in review): we can't guarantee the frontend (objectui)
components actually implement the props the backend spec protocol declares —
Expand Down
Loading
Loading