Skip to content

Commit e2aaaf1

Browse files
fix(sdui): the lockstep and manifest gates judge the in-tree ui/ copy when .objectui-sha is absent (#25833)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoAqVfFyhsxKuFcEP9YTBj25)_ Part of #25712 (4b PR 2a: the two SDUI gates keep the required lane green when the pin goes). Part of objectstack#25677. Clause-②: no Metadata-protocol: none ## Why The sitting merges the cutover PR #25825 (adds `ui/`), and then 4a #25745 (deletes `.objectui-sha`). Two steps of `lint.yml` job `lint-repo-tooling` (the required **Lint & Repo Gates** lane) read that pin, and without it both exit 1. Measured on the sitting tree (cutover `c326d2fdf1` + 4a `50bd417d30` with its README apply steps 2–3 + `main` `b00fc55c7d`) without this PR: - `pnpm check:sdui-lockstep`: `REFUSED — .objectui-sha could not be read`, exit 1. - `node scripts/check-sdui-manifest.mjs`: `✗ .objectui-sha is MISSING — cannot judge freshness.`, exit 1. This PR gives each gate, and the manifest generator, a second oracle: the in-tree copy under `ui/`. It is green on `main` as it is, so it can merge right after the cutover and before 4a, and `main` is never red. It deletes nothing: `packages/sdui-parser/objectui-lockstep.json`, the pin and every check stay. Unifying the two parsers is a later PR. ## What changes The mode is decided by `.objectui-sha` alone. **With the pin (today's `main`): zero behaviour change.** The same record, checks, messages and output. Proven by golden harnesses, captured before and after and compared byte for byte: - lockstep: every `judge()` outcome over the existing fixtures; - manifest: `checkTree` over 14 fixtures × 2 option sets; - the real `main` output of both gates. The existing self-test rows are unchanged and pass. **With no pin:** - `scripts/check-sdui-lockstep.mjs` compares `packages/sdui-parser/src` with `ui/packages/sdui-parser/src` directly. It uses the same three readings the record holds today: the grammar region of `parse.ts`, the diagnostic code set, and the `not-a-container` containment predicate. The record is not consulted. - `scripts/check-sdui-manifest.mjs`: - Checks 1–2 (shape, tamper) are unchanged, held against the in-tree record fields. - Check 3 becomes a **registry source digest**. It is a sha256 over the `src/` files (tests excluded) of the `@object-ui/*` workspace closure of `@object-ui/core` and the generator's 16 registry modules. It is read from the git index (`git ls-files -s`: sorted paths, committed blob ids, no generated or untracked files), so the lint lane builds nothing. - A mismatch prints the one command that clears it: `node scripts/gen-sdui-manifest-node.mjs`. - There is no check 4. The in-tree `@object-ui/core` version moves with every release of the fixed group while the registry does not, so it is recorded and printed as provenance only. - `scripts/gen-sdui-manifest-node.mjs`, with no pin: - It builds the registry closure itself with turbo (a cache hit when nothing moved), then enumerates from `ui/apps/console` exactly as before. - It refuses registry sources that the index does not hold. - It writes the record's `inTree` block and an in-tree `//` note. - **Pin absent and `ui/` absent:** both gates exit 1, and the message names both `.objectui-sha` and the in-tree path. ## Record fields added, and what stays untouched The new block is `inTree: { modulesRoot, objectuiWorkspaceVersion, registryPackages, registrySources, registrySourcesSha256 }`. It is written only by an in-tree run. The pin-mode fields (`objectuiSha`, `source`, `modulesRoot`, `objectuiWorkspaceVersion`, `generator`, `generatedAt`, `sha256`, `components`) are untouched. An in-tree run carries the pin-keyed ones only while the artefact stays byte-identical to the one they describe. **This PR commits no regenerated artefact.** `sdui.manifest.json` and `scripts/sdui-manifest.record.json` are unchanged, per the dispatcher's ruling below. ## The committed manifest describes the pin, not the frozen registry (measured) On the sitting tree, the in-tree generator exits 0 and writes **106** components (`1f6e1ccb925a…`). The committed artefact has **107** (`d1fde278ffd6…`). The registry really moved between pin `b41ed7f689` and freeze `9e1fe03286`: - objectui 610d9ef (#13475, objectui#13449) retired the raw-HTML `html` node type. - objectui 46c56ee (#13483, objectui#13386) reworded plugin-detail's `sections` input description. One committed artefact cannot be true for both the pin and the in-tree registry. So the regeneration rides with the pin deletion, in 4a: a ready patch and its measurements are with the dispatcher. Until 4a applies it, 4a's lint lane reds with exactly this: ``` ✗ check-sdui-manifest: record is missing `inTree`. The record is keyed to the one producer, which reads the in-tree console build (ui/apps/console). Regenerate: node scripts/gen-sdui-manifest-node.mjs ``` The registry source digest is identical on cutover `c326d2fdf1` alone and on the sitting tree: 27 packages, 922 files, `35ef1abd88b0011a06bfc895b3091d1f8ecf319c3f78d2bfb0d9feb4e53f3bbe`. So the regenerated record holds through both states. Two generator runs on the sitting tree were byte-identical, artefact and record. ## Known cost After 4a, a UI PR that touches a registry source reds **Lint & Repo Gates** until its author runs `node scripts/gen-sdui-manifest-node.mjs` and commits the two files. Measured: **96 of objectui's last 200 non-merge commits** (2026-10-20..25) touched the digested set, so about half of UI PRs would pay that one command. The follow-up that removes the tax is a regenerate-and-compare step in **Build & E2E UI** (`ci.yml`), which has the console build in hand. It is the next 4b dispatch. That compare must ignore the record's `generatedAt` date (the artefact itself is byte-deterministic). ## Verification | Gate | `main` (this branch) | sitting tree + this PR | |---|---|---| | `pnpm check:sdui-lockstep` (self-test, then the gate) | 0 (pin mode) | 0 (in-tree: 214 grammar lines, blob `0131f27cf86d`, 25 codes, predicate `76c18fb95d1f`) | | `node scripts/check-sdui-manifest.mjs --self-test` | 0 (22 cases) | 0 (22 cases) | | `node scripts/check-sdui-manifest.mjs` | 0 (pin mode, 107 components) | 1 before 4a's regeneration, as above; 0 after it | - New self-test rows: - lockstep battery `The in-tree oracle (no pin: the merged layout)`, 18 cases; - manifest, 9 in-tree rows, including an in-tree producer run through a stub turbo. - Ablations: 8 of 9 targeted ablations red the rows. The ninth drops `inTree` from the required list; the row stays red because the field-level check catches the same thing. - Derived gates (`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`): 35 families, all exit 0 at head `a6ee32ec`; `--ran` reconciles 35 run, 0 NOT-MEASURED, 0 UNRUN. ESLint on the three changed files: 0. - Git children over self-test fixtures run with `gitFreeEnv()` (#16644). ## Deviations from the order 1. **No regenerated artefact in this PR** (dispatcher ruling, after the 106/107 measurement above). The order asked that the generator reproduce the committed `sdui.manifest.json` byte for byte on the sitting tree; it cannot, so the regeneration moves to 4a. 2. **No in-tree version judgment** (accepted): `objectuiWorkspaceVersion` is provenance, printed beside the live `ui/packages/core` version. 3. **The generator builds the registry itself in-tree** (accepted), so regeneration is one command with no prior step. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b00fc55 commit e2aaaf1

3 files changed

Lines changed: 993 additions & 136 deletions

File tree

0 commit comments

Comments
 (0)