Skip to content

ci(ui): regenerate the SDUI manifest in Build & E2E UI and compare it with the committed bytes (#26137 PR A) - #26315

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-6qe56u
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-6qe56u

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Part of #26137 (PR A of two: the CI comparison; PR B retires the per-package records once this is green on main). Tier H (.github/workflows/**).
Part of objectstack#25677.

Clause-②: no
Metadata-protocol: none

维护者速读(草稿)

  • 改了什么: Build & E2E UI 多了一步:用 node scripts/gen-sdui-manifest-node.mjs 从本树重新生成 sdui.manifest.json,再和提交的文件逐字节比较,不一致就红,并打印差异和修复命令。ci.yml 的 ui 过滤器加了 turbo 的四个全局依赖文件,以及清单自己的四个文件。原来 lint 里按源码摘要判新鲜度的检查保留不动。
  • 为什么改: 摘要只读各 registry 包的 src/,看不见三类变化:解析器 @objectstack/sdui-parser 的改动、registry 运行时引用的 @objectstack 包的改动、两个排队 PR 合起来的效果。实测:在 main 上改 manifestFromConfigs 一行,现有检查仍是绿的,而重新生成的清单差了 87 行。现在 PR 和合并队列的合并树上都会重新生成并比较。
  • 哪些原来能合的现在会被拒: 只有清单确实过期的 PR。今天 main 重新生成与提交的字节完全一致。
  • 代价: 每次跑 ui 的 run 多约 3.6 分钟 runner 时间(27 个 @object-ui 包冷构建 3m25s,枚举 8s;36 个 @objectstack 任务从 Build UI Deps 回放)。今天 22 次绿 run 里 ui 尾部都比它长,所以墙钟时间增加 0。Build & E2E UI 超时按规则重算为 35 分钟。
  • 风险与回滚: 只改 CI 和文档,不发布任何包。回滚 = revert 本 PR。
  • 你要做的: Tier H,需要 GOVERNED_APPROVERS 审批。

席位核对

(left for the dispatch seat)

Why

check-sdui-manifest (lint, every PR) judges the committed sdui.manifest.json fresh when each registry package's src/ digest equals its one-line record under scripts/sdui-manifest.sources/ (#26051, #26120). That digest is blind to everything outside those src/ trees that moves the artefact:

Before: the gap on main, measured

On main 81bba6c, in packages/sdui-parser/src/index.ts manifestFromConfigs, isContainer: c.isContainer → isContainer: c.isContainer ?? false:

  • node scripts/check-sdui-manifest.mjs: exit 0, "present, intact … each package's digest current in its record";
  • node scripts/gen-sdui-manifest-node.mjs: sdui.manifest.json 1 file changed, 87 insertions(+).

So a stale manifest passes today. With this PR, the same edit reds Build & E2E UI on the PR, because packages/sdui-parser/** is in ui_consumers and the step serialises through the parser the job just built.

What changes

ci.yml, Build & E2E UI: the comparison

A new step, "The committed SDUI manifest is the one this tree generates", after "Build the @objectstack packages objectui consumes" and before the Console build:

node scripts/gen-sdui-manifest-node.mjs
if ! git diff --quiet -- sdui.manifest.json; then
  echo "::error file=sdui.manifest.json::…Run 'node scripts/gen-sdui-manifest-node.mjs' and commit what it writes."
  git diff --stat -- sdui.manifest.json
  git diff -- sdui.manifest.json | head -n 200
  exit 1
fi
  • What it reuses: the generator's turbo run is 63 build tasks. 36 are exactly the set Build UI Deps builds and hands over, and they replay from the downloaded ui-deps-turbo-cache. The other 27 are the @object-ui registry closure.
  • What it adds: on 4 cores, the 27 build in 3m25s cold and the enumeration takes 8s; with nothing moved the whole run takes 6s. The output on main is byte-identical to the committed file (sha256 1f6e1ccb925a…, 106 components), and two runs agree.
  • Why this job: it is required already, already replays the 36, and is the shortest ui job (1.9–2.9 min across 22 green runs on 2026-10-26: 14 pull_request, 6 merge_group, 2 push). The ui lane's tail after Build UI Deps was 8.4–14.2 min in the same runs, so the added wall time measured 0 in all of them. Lint UI builds the 27 too, but it runs on the ui output only, so a PR that changes only the parser or one of the five packages would skip it (devx(sdui): CI regenerates the sdui manifest and compares it to the committed artefact, so ui PRs conflict only on real contract changes and the merge queue catches combined effects #26137 comment 6424273287).
  • What it compares: only the artefact. The records the generator rewrites beside it stay with check-sdui-manifest until PR B.
  • The Console build is unaffected: ui/apps/console/vite.config.ts aliases every workspace package to its src/, so the dist this step builds does not reach it.

ci.yml, the ui filter

Added Why
tsconfig.json, tsconfig.base.json, tsconfig.test.base.json, tsup.config.ts turbo.json's globalDependencies. Every build task hashes them, so they move what the ui jobs build. The digest never saw them either.
sdui.manifest.json, scripts/sdui-manifest.record.json, scripts/gen-sdui-manifest-node.mjs, scripts/check-sdui-manifest.mjs The artefact, its record, its producer and its offline gate. A hand edit of the artefact, or a producer edit, now runs the comparison.

The registry's runtime closure, walked over dependencies and peerDependencies from the 17 seeds, is the 27 ui packages plus spec, client, core, formula and types. All of them were under ui/** or ui_consumers already. Cost of the additions: in the 30 days to 2026-10-26, 2 merged PRs moved one of the eight paths and no other ui path (#26120, #25833); each would have run the ui lane once more.

ci.yml, the ceiling

Build & E2E UI is re-derived by the rule in the comment above Lint UI. The slowest successful run was 457 s; adding 213 s gives 670 s (11.2 min), so max(33.5, 26.2) ⇒ 35, up from 25. The pin in ui/scripts/__tests__/turbo-cache-save-bound.test.ts moves with it.

Prose

  • lint.yml: the check-sdui-manifest step comment says where the regeneration now runs. The stale reference to the retired lockstep neighbour goes.
  • scripts/check-sdui-manifest.mjs header: "What this deliberately does NOT do" and check 3's "What is NOT forced" now name the CI step that does it.
  • docs/releases-maintenance.md: one paragraph on the CI comparison.

Widening or narrowing

This is a widening. It adds a check, and no case that the current check refuses passes now: the current check stays on, unchanged. The step goes red only when the committed manifest differs from this tree's regeneration. That is exactly a stale manifest, so nothing an author can write is refused that is not stale.

What PR B does, after this is green on main

It retires check 3 in-tree (the per-package digests) and scripts/sdui-manifest.sources/. It also updates their readers:

  • scripts/gen-sdui-manifest-node.mjs;
  • scripts/check-changeset-no-major.mjs (L1368–1371, L4879, L4966);
  • docs/releases-maintenance.md;
  • cut-rc.yml and release.yml prose.

After that, PRs conflict only when the manifest's bytes really change. Whichever of PR B and 2g PR 2 lands second rebases on scripts/sdui-manifest.sources/sdui-parser.json.

Verification

Head d36dc4280, on main ff918954e. 5 files, +79 / −23, one commit. Tier H: check-governed-merges --test exits 3 for .github/workflows/ci.yml and .github/workflows/lint.yml and 0 for the other three paths.

  • The regeneration on this head: main here already carries feat(sdui-parser)!: one parser body in @objectstack/sdui-parser (#25712 2g PR 1) #26211's parser. On d36dc4280, node scripts/gen-sdui-manifest-node.mjs wrote byte-identical bytes: sha256 1f6e1ccb925a…, 106 components, git status clean, no record rewritten. So the new step should be green on this PR. With main moved, that run rebuilt 62 of 63 tasks locally in 7m25s; in CI, Build UI Deps supplies 36 of them.
  • The gap, before: see "Before" above (main 81bba6c: gate exit 0, regeneration +87 lines).
  • check-sdui-manifest: --self-test 29 cases behave; the production run is green.
  • Tests that read the job: ui/scripts/__tests__/turbo-cache-save-bound.test.ts and ensure-chromium-ready.test.ts pass 52/52, with the 35-minute pin.
  • Workflows parse: ci.yml and lint.yml both parse (14 jobs each). build-e2e-ui reads timeout-minutes: 35, and the new step sits between "Build the @objectstack packages objectui consumes" and "Build the Console for E2E".
  • Gate sweep: node scripts/pm/dispatch-gates.mjs --commands derives 79 gates. They ran on 468450e19, this commit before its rebase from 4f7e0414a to ff918954e; nothing on main between those touched the five files. 78 exited 0. check:dual-build-cjs-loads exited 3 (prerequisite: no workspace dist), then exited 0 after pnpm build. --ran reconciles 79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN.
  • Push battery (GITHUB_ACTIONS=true GITHUB_EVENT_NAME=push node scripts/pm/check-dispatch-gates.mjs) on 468450e19: 2164 cases pass, 0 fail, EXIT 0 (1586 s).
  • CI: pending on d36dc4280.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS

… with the committed bytes (#26137)

check-sdui-manifest judges freshness from committed per-package source
digests, so it cannot see an edit to @objectstack/sdui-parser, to a package
the registry imports, or two queued PRs whose effect only shows together.
Build & E2E UI now runs scripts/gen-sdui-manifest-node.mjs after its
@objectstack build and fails when sdui.manifest.json differs from what this
tree generates. The 36 @objectstack tasks replay from Build UI Deps; the 27
@object-ui builds plus the enumeration cost 213 s cold on 4 cores.

- ci.yml `ui` filter: turbo.json's globalDependencies and the manifest's
  own four files, so a diff that moves only those still runs the step.
- Build & E2E UI ceiling re-derived 25 -> 35 (457 + 213 s), with the pin in
  ui/scripts/__tests__/turbo-cache-save-bound.test.ts.
- lint.yml and check-sdui-manifest.mjs prose, and releases-maintenance.md,
  point at the new step. The digest check stays until this is green on main.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
@github-actions github-actions Bot added size/m ci/cd documentation Improvements or additions to documentation tests labels Oct 26, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 26, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Check Changeset: the one red check on d36dc4280 is a stale run, not a defect in this diff.

  • The failing run is 40397030156, started by the opened event at 20:35:43Z. It is red because the PR had no changeset and no skip-changeset label yet.
  • This PR releases nothing: it changes two workflows, a gate script, a maintainer doc and a ui repo test. So the right input is the skip-changeset label, not a changeset. The label was applied through the relay at about 20:38Z.
  • The labeled run, 40397545390, is skipped by the label exemption. As pr-automation.yml notes (Check Changeset 从事件载荷读 skip-changeset 标签:开 PR 后 5 秒内加标签,首个 run 永久红(重跑复用载荷)—— 一日三例 #5580), a labeled run's verdict does not clear an opened run's red one.
  • A re-run of 40397030156 would go green: its first step re-reads the live labels and finds the exemption. I don't re-run jobs from a dev thread, and the relay's re-run op covers ci.yml only. That re-run is for the dispatch seat or a maintainer.
  • Every other check is green: 55 success and 10 skipped, mergeable clean. Build & E2E UI passed in 4.8 min, and its new step "The committed SDUI manifest is the one this tree generates" took 163 s and found the committed manifest byte-identical.

@os-zhuang
os-zhuang added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
#26278 widened the ci.yml `ui` filter (every root workflow, .github/prompts,
skills/objectui). Both sides kept: main's entries, then this PR's turbo
globalDependencies and the SDUI manifest's four files.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants