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
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4148,12 +4148,12 @@ jobs:
# 81bba6c3f), then it enumerates the registry under plain Node (~8 s; ~6 s
# for the whole run when nothing moved). It serialises through the
# @objectstack/sdui-parser the step above built, so a parser edit moves
# this verdict too, which no committed digest does. On merge_group it runs
# on the queue's combined tree, the one run that sees two entries' effect
# on the manifest together. Only the artefact is compared: the records the
# generator rewrites beside it are held by `check-sdui-manifest` in
# lint.yml. The Console build below reads the registry through its src/
# aliases, so the dist this step builds does not reach it.
# this verdict too. On merge_group it runs on the queue's combined tree,
# the one run that sees two entries' effect on the manifest together.
# Only the artefact is compared: the record the generator rewrites beside
# it is held by `check-sdui-manifest` in lint.yml. The Console build below
# reads the registry through its src/ aliases, so the dist this step
# builds does not reach it.
- name: The committed SDUI manifest is the one this tree generates
run: |
node scripts/gen-sdui-manifest-node.mjs
Expand Down
12 changes: 5 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -966,13 +966,11 @@ jobs:
# repo-root `sdui.manifest.json` is absent or unreadable, so nothing in
# the production path ever says "the gate lost its whitelist" — this step
# is the thing that notices. Offline record checks only (existence, shape,
# sha256 vs the provenance record, and with no pin each registry
# package's source digest vs its record): the regenerate-and-compare half
# runs where the registry is built, as ci.yml's Build & E2E UI step "The
# committed SDUI manifest is the one this tree generates" (#26137), on
# pull_request and on the merge queue's combined tree. Unfiltered:
# filtering it to the artefact would go dormant on exactly the PR that
# moves the artefact's inputs and not the artefact.
# sha256 vs the provenance record): freshness is decided where the
# registry is built, by ci.yml's Build & E2E UI step "The committed SDUI
# manifest is the one this tree generates" (#26137), on pull_request and
# on the merge queue's combined tree. Unfiltered: it builds nothing and
# runs in seconds, and an absent artefact is the failure it exists for.
# Direct invocation per the GATE INVOCATION IDIOM at the top of this file.
- name: SDUI manifest is present, intact and fresh at the objectui pin
id: sdui-manifest-is-present-intact-and-fresh
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,11 @@ The Studio/Console UI lives in this workspace under `ui/` (`@object-ui/*`; the C
and `ui/AGENTS.md` adds that tree's own rules to this file's. `pnpm console:build` builds the Console and copies its
dist into `packages/console/` (`packages/console/build-dist.sh`). ⛔ Never hand-edit `packages/console/dist/`.

**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`.
**A registry change has a second half: regenerate the committed manifest** with
`node scripts/gen-sdui-manifest-node.mjs`, which builds the registry under `ui/`, enumerates it from `ui/apps/console`
(never an npm install) and takes no arguments; commit what it writes. CI's `Build & E2E UI` runs it too and fails
when the committed `sdui.manifest.json` differs, the merge queue's combined tree included, and the console build
ships that same tracked file in `packages/console/dist/`. Full procedure: `docs/releases-maintenance.md`.

**Fast iteration on `ui/` src (no rebuild loop):** run the Console dev server —
`pnpm --filter @object-ui/console dev` (Vite on **:5180**, HMR). Its `/api` proxy targets
Expand Down
50 changes: 22 additions & 28 deletions docs/releases-maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ ships the Console from the same commit as the spec it builds against.
> root `.changeset/` before the first joint Version PR is #25831. New frontend
> changesets go in the root `.changeset/`.

#### After a registry change: regenerate the committed manifest (#12924, #17735, #25712, #26051)
#### After a registry change: regenerate the committed manifest (#12924, #17735, #25712, #26137)

The repo-root `sdui.manifest.json` (ADR-0080, the Console's public-tier component
manifest), its provenance record `scripts/sdui-manifest.record.json` and one record
per registry package under `scripts/sdui-manifest.sources/` are generated, and one
command writes them:
manifest) and its provenance record `scripts/sdui-manifest.record.json` are
generated, and one command writes them:

```bash
node scripts/gen-sdui-manifest-node.mjs # no arguments
Expand All @@ -69,31 +68,26 @@ node scripts/gen-sdui-manifest-node.mjs # no arguments
It builds the registry packages under `ui/packages/`, enumerates them from
`ui/apps/console` under plain Node, and serializes them through
`@objectstack/sdui-parser` (it needs `packages/sdui-parser/dist`:
`pnpm --filter @objectstack/sdui-parser build`). Each package record is one line:
a digest of that package's own `src/` and the `sha256` of the artefact it was
verified against. A run rewrites only the records whose line changed, so a PR that
edits one registry package re-records that package alone; the shared record moves
only when the artefact does, and then every package record names the new `sha256`.

A required lint step reads the tracked files on **every PR**:
`node scripts/check-sdui-manifest.mjs` checks the shape, the `sha256` against the
record, and recomputes each package's source digest from the index. A PR that edits
a registry package's source without regenerating goes red there, naming the package,
and the failure text prints the regeneration call. Two PRs that edit different
registry packages touch different record files and do not conflict; two that edit
the same package, or a PR that changes the artefact, still conflict once, and the
later one merges `main` and re-runs the command. ⛔ There is no npm install route to the manifest; that was
deleted by ruling 丙 on #17735.

CI also regenerates the manifest and compares it with the committed bytes (#26137):
`pnpm --filter @objectstack/sdui-parser build`). The record moves only when the
artefact does, so a PR that does not change the manifest touches neither file.
⛔ There is no npm install route to the manifest; that was deleted by ruling 丙 on
#17735.

CI regenerates the manifest and compares it with the committed bytes (#26137):
the `Build & E2E UI` step "The committed SDUI manifest is the one this tree
generates" runs the same command after the registry is built, on every PR that
touches `ui/`, one of the seven packages objectui consumes (the parser among them)
or the manifest's own files, and again on the merge queue's combined tree. A
difference fails the step with the diff and the command to run. That covers what
the source digests cannot see: an edit to `@objectstack/sdui-parser`, to a package
the registry imports, and two queued PRs whose effect on the manifest only shows
together.
difference fails the step with the diff and the command to run. Two PRs conflict
on the manifest only when both change it, and the later one merges `main` and
re-runs the command; two queued PRs whose effect on the manifest only shows
together fail in the queue.

A required lint step, `node scripts/check-sdui-manifest.mjs`, reads the tracked
files on **every PR**: the artefact is present and is a component manifest, and
its `sha256` and component count are the record's. It builds nothing, so it does
not judge freshness. (The per-package source records under
`scripts/sdui-manifest.sources/` that it used to recompute, #26051, are retired.)

**One producer, one artefact.** `packages/console/build-dist.sh` copies the tracked
`sdui.manifest.json` into `packages/console/dist/`, so the published console carries the
Expand Down Expand Up @@ -234,9 +228,9 @@ What it does, in order:
re-reads `main`, so **`main` may keep moving for the whole run**.
2. Builds the workspace, both scopes, then the Console (`pnpm run build`, then
`pnpm run console:build`) from that snapshot. The dist carries the tracked
`sdui.manifest.json`, whose freshness the required lint job already held on the
PR that changed it. A Console that cannot build stops the cut before anything is
written.
`sdui.manifest.json`, whose freshness the required `Build & E2E UI` job already
held on the PR that changed it and in the merge queue. A Console that cannot build
stops the cut before anything is written.
3. Runs the gates that read `.changeset/*`, before versioning consumes it.
4. Runs `pnpm run version` — the repo script, never a bare `changeset version` —
and **fails unless the computed version equals the one you typed**.
Expand Down
15 changes: 6 additions & 9 deletions scripts/check-changeset-no-major.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1365,9 +1365,8 @@ export function publishedSourceOwners(path, manifestOf) {
// packed output — one path — and never which files it copies: the day the
// console build ships a second tracked file, this leg sees it with no edit
// here, and the day it stops copying the manifest, the leg stops claiming it.
// `scripts/sdui-manifest.record.json` and the per-package records under
// `scripts/sdui-manifest.sources/` are measured OUT for that reason rather than
// by choice: the build does not copy them, so they are not part of the shipped
// `scripts/sdui-manifest.record.json` is measured OUT for that reason rather
// than by choice: the build does not copy it, so it is not part of the shipped
// artefact.
//
// ⚠️ Residual, stated rather than implied: the reader understands the plain
Expand Down Expand Up @@ -4876,8 +4875,8 @@ function selfTest() {
`this repository's ${PACKED_COPY_BUILD_SCRIPTS[0]} copies the repo-root sdui.manifest.json into packages/console/dist/ as read by this leg — got ${JSON.stringify(realPairs)}`,
);
assert(
!realPairs.some((p) => p.source === 'scripts/sdui-manifest.record.json' || p.source.startsWith('scripts/sdui-manifest.sources/')),
`measured, not chosen: the real build ships neither the manifest record nor a package record — got ${JSON.stringify(realPairs)}`,
!realPairs.some((p) => p.source === 'scripts/sdui-manifest.record.json'),
`measured, not chosen: the real build does not ship the manifest record — got ${JSON.stringify(realPairs)}`,
);

// ── The owner of a destination ────────────────────────────────────────
Expand Down Expand Up @@ -4957,21 +4956,19 @@ function selfTest() {
const no = levelOf(copyRepo({ head: { ...PR22812, '.changeset/console-bbbb.md': changeset([[CONSOLE, 'patch']]) } }), declaredNo);
assert(no.result.verdict === 'none-clean' && no.exitCode === 0, `the manifest moved under \`Clause-②: no\` + \`none\` is unaffected — got ${no.result.verdict} / ${no.exitCode}`);

// The records move with regeneration but ship nothing: the shared one, and
// the one-line record of the registry package a ui PR re-recorded.
// The record moves with regeneration but ships nothing.
const recordOnly = levelOf(
copyRepo({
head: {
'scripts/sdui-manifest.record.json': '{"objectuiSha":"bbbb"}\n',
'scripts/sdui-manifest.sources/plugin-grid.json': '{"package":"ui/packages/plugin-grid"}\n',
'.changeset/console-bbbb.md': changeset([[CONSOLE, 'patch']]),
},
}),
yesWidening,
);
assert(
recordOnly.touched.packages.length === 0 && recordOnly.touched.unreadable.length === 0 && recordOnly.result.verdict === 'clean',
`control: the records alone move no published file — got ${JSON.stringify(recordOnly.touched)} / ${recordOnly.result.verdict}`,
`control: the record alone moves no published file — got ${JSON.stringify(recordOnly.touched)} / ${recordOnly.result.verdict}`,
);

// #4690 on this leg: a build script that is THERE but yields no copy is
Expand Down
Loading
Loading