Repository navigation
Description
Filing gate ③: a task the maintainer ordered directly. In the devx seat thread on 2026-10-26 he asked 「还有更好的方案吗」 / 「后续还要立卡优化吗」, the seat proposed this card, and at 13:29Z he answered verbatim 「立卡」. Filed by devx seat 1 (#6023). ⛔ Not a claim. Follow-up to p1 #26074 (merge-queue backlog) and #26051 / PR #26120.
Problem
Even after #26120, check-sdui-manifest judges freshness from committed source digests: one record per ui registry package under scripts/sdui-manifest.sources/. A digest is committed only because regenerating the manifest needs the registry built (8m43s cold in a card thread), which a PR author cannot afford on every push. Two costs are left:
- Conflicts on records, not on the contract. Any two PRs that touch the same registry package's
src/conflict on that package's record, even when neither changessdui.manifest.json.i18nis the hot one: 5 of 12 open ui PRs touch it. - The combined-effect residual (accepted by the maintainer for devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120 on 2026-10-26 13:28Z): two PRs on different packages that each leave the artefact unchanged, but would change it together, land with no run seeing the combination. The next regeneration finds it.
Proposal
Let CI compute the manifest instead of trusting a committed digest. After the registry packages are built, CI regenerates sdui.manifest.json and compares it with the committed file:
- PRs conflict only when the component contract really changes, which is a real conflict.
- The merge queue builds the combined tree, so the combined effect is caught at enqueue time and the residual disappears.
- The per-package source records (and probably the source half of
sdui-manifest.record.json) can be retired.
Measure first, then decide (report numbers on this card before any PR)
- Can the regeneration reuse what CI already builds?
ci.ymlBuild UI Deps(L3163) builds and uploads the ui dependency closure thatTest UIreplays. Find out whethergen-sdui-manifest-node.mjscan run on that output, and what it adds per run: the wall time, and which job carries it. - Which events need the check: pull_request, merge_group, or both. Today
lint.ymlL1028 runsnode scripts/check-sdui-manifest.mjs --self-test && node scripts/check-sdui-manifest.mjson every lint run, with no build. - Who reads the source records today (
scripts/check-changeset-no-major.mjs,docs/releases-maintenance.md,scripts/ui-import/README.md), and what each needs instead. - Is this a narrowing or a widening of the gate? The expectation is a widening, since the artefact is checked directly and the combination is checked in the queue. If any case would let a stale manifest pass, it is a narrowing and needs contract review.
Constraints
- Tier H: it edits
.github/workflows/**, so it needs a GOVERNED_APPROVERS approval (hotlong / os-zhuang). - Lands after devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120. Don't start the code while devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120 is open.
- ⛔ No gate is skipped or loosened along the way. The old check stays until the new one is green on main.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 26, 2026
PM input for this card from #25712 item 3 (2g), 2026-10-26T16:04Z.
Once 2g lands, the manifest digest check stops covering parser edits, and this card is what closes that gap. Details:
ui/packages/sdui-parser(2g PR 2). After that, the registry closure thatscripts/check-sdui-manifest.mjsdigests no longer reaches the parser, andscripts/sdui-manifest.sources/sdui-parser.jsondrops out.packages/sdui-parser/src, for example to the manifest serialiser, moves no digest, socheck-sdui-manifestcan stay green over a stalesdui.manifest.json.objectui#,ui/AGENTS.mdnests, 18 governance workflows merged or retired (parent #25677, 4b/6) #25712), 2g does not widen the closure. This card's regenerate-and-compare check closes the gap by construction, because it serialises with the parser as built. If the design keeps any digest step, includepackages/sdui-parser/srcin it.objectstack-fleet commentedon Oct 26, 2026
Evidence from domain:spec seat 2 (session_01RyMY3prh9GRva1Tt37Di3i): the per-package split (#26120) still conflicts within one package. PR #26011 was dequeued for MERGE_CONFLICT at 2026-10-26T16:32Z on
scripts/sdui-manifest.sources/core.jsonalone, because #26033 (4f48d2b) landed anotherui/packages/corechange and re-recorded the samesha256line.git check-attr mergereadsunspecifiedfor everyscripts/sdui-manifest.sources/*.json, so neither the os-regen driver nor the seat's Regen-provenance carry applies. Every such hop costs a manual merge-and-regenerate round plus a fresh contract review. That was #26011's fifth queue ejection today: four on the sdui digest and one on a real liveness conflict.objectstack-fleet commentedon Oct 26, 2026
Claim: epic #25677 (D1) — this card is an 18.2 blocker by maintainer ruling (#25712 comment 6418729849: 18.2 waits for 2g and 2i); taken into the D1 epic with the coordinator's agreement, no other seat owns it 2026-10-26T17:21Z
Session:
session_01RSDL3SFtJaKzcymvCD8RsUAccount:
os-teslaBranch:
claude/project-thread-6qe56u(the #25712 4b card thread's designated branch in objectstack-ai/objectstack — the thread can push only this branch, a measured platform exception toclaude/issue-<n>-<slug>; the PR opens after #26211 leaves it)Worktree:
objectstack-issue-26137Domain:
domain:devxSeat:
epic:#25677Scope: the whole card — measure first and report the four numbers the card asks for on this card (reuse of
Build UI Depsoutput and added wall time per job; which events need the check; who reads the source records and what each needs instead; widening vs narrowing), then the PR: CI regeneratessdui.manifest.jsonafter the registry build and compares it with the committed file, on pull_request and merge_group as measured; the per-package source records retire only after the new check is green; plus the parser-digest gap named in comment 6420796812 (an edit topackages/sdui-parser/srcmust move the verdict)Why now: 18.2 is held for it; and once 2g removes the ui parser copy, a parser edit moves no committed digest, so a stale manifest can pass until this lands
Container & model: M,
mode:thread(thread sessionsession_01A6SEitNngAMugGbuJHWsDS; order: this card's body + comment 6420796812, FRAME.md applies),model: opus,effort: xhighClause-②: no
Metadata-protocol: none
Responsibility: n/a — epic step
Thread-read: none
Serial constraints cleared: #26120 (the card's predecessor) is checked before any code. The old check stays until the new one is green on
main(card ⛔). 2g PR 2 (#25712, 2g-b seat,claude/project-thread-hrsgde) dropsscripts/sdui-manifest.sources/sdui-parser.json; whichever lands second rebases. Tier H (.github/workflows/**): the PR stops at draft; derived gates and the push-environment battery (GITHUB_ACTIONS=true GITHUB_EVENT_NAME=push node scripts/pm/check-dispatch-gates.mjs, 2182/0) are reported before the PM flips readyobjectstack-fleet commentedon Oct 26, 2026
Measure first: the four numbers
Measured on
mainat 81bba6c, on a 4-core, 16 GB container (the size of a GitHububuntu-latestrunner), with no remote cache. There is no code change yet.1. Reuse of
Build UI Deps, and what the regeneration adds@objectstackhalf. The generator's turbo run (node scripts/gen-sdui-manifest-node.mjs, from a--dry=jsonof its filters) schedules 63 build tasks. 36 of them are exactly the 36 thatBuild UI Depsbuilds and hands over asui-deps-turbo-cache; none is missing from that set. The other 27 are the@object-uiregistry closure (core,components, the 16 registry modules and their runtime dependencies). Some ui jobs build parts of that set inside their own run, but none hands it on.Build UI Deps' cold build), the generator replayed all 36 from cache. It built the 27 in 3m25s and enumerated in 8s, so a cold run adds 3m33s. A second run with nothing moved took 6s: turbo replayed all 63 in 0.16s, and the enumeration took the rest. The output onmainwas byte-identical to the committedsdui.manifest.json(sha2561f6e1ccb925a…, 106 components), and the second run reproduced the same bytes.Build & E2E UI, as one more step after its@objectstackbuild step. It already downloads the artifact and replays the 36, it is already a required check, and it is the shortest ui job. In 22 green CI runs today (14pull_request, 6merge_group, 2push), it took 1.9 to 2.9 min. The ui lane's tail (the last ui job to finish, counted from the end ofBuild UI Deps) was 8.4 to 14.2 min. Adding about 3.6 min keeps it at about 6.5 min or less, under the shortest tail, so the measured added wall time is 0 in all 22 runs. The cost is about 3.6 runner-minutes per ui run. A new job would cost the same plus a setup and install of about 1.5 min, and a repo admin would have to add it to the required checks.2. Which events need the check
pull_requestandmerge_group. Onpull_request, the author learns before enqueueing.merge_groupis the only run that sees a combination: the ui outputs there come from the queue diff,merge_group.base_shaagainst the group head (ci(ui-merge): every merge-queue entry runs all four ui lanes because theuifilter output falls back to 'true' on merge_group — decide the ui lanes from the diff againstmerge_group.base_sha, as Test Core already does (parent #25677) #25867), so an entry is judged on the tree with every entry ahead of it. That closes the residual accepted for devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120.pushtomainruns the ui jobs too, which gives the "green onmain" signal the card requires before the old check retires.dependenciesandpeerDependencies, walked from the 17 seeds) is the 27 ui packages plusspec,client,core,formulaandtypes, and the generator serialises withsdui-parser. All of them sit underui/**orui_consumersalready. The artefact and its producer do not:sdui.manifest.json,scripts/sdui-manifest.record.json,scripts/gen-sdui-manifest-node.mjsandscripts/check-sdui-manifest.mjs. Without them, a hand edit of the artefact would skip the comparison. They go into theuilist inci.yml.3. Who reads the per-package source records, and what each needs instead
scripts/check-sdui-manifest.mjssdui-manifest.record.json) stay unfiltered in lint, as does the self-test.scripts/gen-sdui-manifest-node.mjsregistrySourceDigestsfor its "the index holds the bytes" refusalsdui-manifest.record.jsonlosesinTree.registryRecordsand the note lines about the records.scripts/check-changeset-no-major.mjs(L1368–1371, L4879, L4966)sources/clause and the fixture line. Therecord.jsonhalf stays.docs/releases-maintenance.md(L58–84, L227)pull_requestandmerge_group.lint.yml(L1010–1025),cut-rc.yml(L279),release.yml(L1846)scripts/ui-import/README.md(L139–140),packages/console/CHANGELOG.mdrecord.jsonNothing else reads
scripts/sdui-manifest.sources/: no workflow,.gitattributesrow, CODEOWNERS entry or package.4. Widening or narrowing
A widening: no case lets a stale manifest pass that the current check refuses.
mainnow, not only after 2g PR 2. I changedisContainer: c.isContainertoc.isContainer ?? falseinmanifestFromConfigs(packages/sdui-parser/src/index.ts).node scripts/check-sdui-manifest.mjsstayed green, exit 0, while regeneration produced a manifest 87 lines different.packages/sdui-parser/**is inui_consumers, so the comparison runs on such a PR and goes red.@objectstackpackages the registry imports at runtime, anypackage.jsonor lockfile change, and the combination of two PRs. The digest reads only each registry package'ssrc/.src/edit that leaves the artefact byte-identical (a comment, a test undersrc/, a refactor). The manifest it ships is the regenerated one, so nothing stale passes. This is the change that removes the record conflicts: thei18nhot spot and feat(spec,console): atype: 'flow'action may declareonSuccess, and the console navigates after a run that completes #26011's ejection oncore.jsonboth came from it.globalDependencies(tsconfig.json,tsconfig.base.json,tsup.config.ts) are not in theuilist, and the current digest does not see them either. I propose adding them to theuilist in the same change, which costs a ui run on edits to those three files.Proposed PRs (Tier H, draft until you flip it)
Build & E2E UIstep: run the generator, thengit diff --exit-code -- sdui.manifest.json, printing the diff and the one command that clears it. Add the four artefact paths and the three root files to theuifilter. The current check stays on.main. Retire check 3 in-tree and the per-package records, and update the readers in the table. Whichever of PR B and 2g PR 2 lands second rebases onscripts/sdui-manifest.sources/sdui-parser.json.objectstack-fleet commentedon Oct 26, 2026
Correction to point 1 of 6424208318
I wrote that "some ui jobs build parts of that set inside their own run". That understates Lint UI. Its
ui-gate-buildstep runsturbo run build --filter='./ui/packages/*', so it builds all 27 into dist in its own run. A regeneration step there would add only the enumeration, about 8s.It still cannot carry the check. Lint UI runs only when the
uioutput is set, notui_consumers. A PR that changes onlyspec,client,core,formula,typesorpackages/sdui-parserwould skip it, and that is the parser gap again.Build & E2E UIruns on either output, so the choice in point 1 stands.Possible later saving, not part of PR A:
Build & E2E UIcould restore Lint UI's main turbo seed. On a PR that leaves the ui packages alone, the 27 builds would then replay instead of running cold.objectstack-fleet commentedon Oct 26, 2026
Measured sighting for this anchor · 2026-10-26T19:55Z ·
session_01FMuBZmssbfQkVrKRtxNvngPR #26215 (card #26136; touches
ui/packages/i18nin ten locales, so it re-recordsscripts/sdui-manifest.sources/i18n.json) has needed threeorigin/mainmerge rounds in one afternoon on that single file, each a textual conflict of the one-line digest against another i18n-touching PR that landed first: round 1 against #26158 (2026-10-26T18:03Z, the platform's update-branch refused), round 2 against934810cc(2026-10-26T18:38Z,mergeable_state: dirtywhile its CI was green), round 3 after the merge queue itself dequeued the entry withMERGE_CONFLICTat 2026-10-26T19:54Z against #26202, which merged at 2026-10-26T19:02Z while #26215 was queued. Each round costs the generator's fullturbobuild (6–8 min cold) plus a full CI cycle; the path is not routedmerge=os-regen, soos-regen-merge.shstops on it as a non-generated conflict and the dev takes main's side by hand before regenerating. Evidence for this card's premise, no new card.Generated by Claude Code
Merge origin/main into the 2i PR A branch (#26137)
Merge origin/main into the 2i PR B branch (#26137)
Merge origin/main into the 2i PR B branch (#26137)