Skip to content

ci(spec-shape-gate): install objectui through its pinned-build directories, packed from this commit - #24469

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-24424-spec-shape-gate-objectui-setup
Oct 19, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/issue-24424-spec-shape-gate-objectui-setup

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24424

Clause-②: no

What was red

Every Spec Shape Gate run since objectui 749ed760 (objectstack-ai/objectui#12957): step "Install objectui dependencies" ran a bare pnpm install --frozen-lockfile in objectui, whose root package.json now overrides every @objectstack/* package to file:node_modules/.cache/objectstack/NAME, a directory only objectui's scripts/objectstack-pin.mjs build fills. Reproduced locally against objectui 4016370af3 (its main when this was measured): exit 254, ENOENT: no such file or directory, scandir '.../objectui/node_modules/.cache/objectstack/client'. Run 39188135125 step 14 concluded failure with the annotation Process completed with exit code 254 (its raw log host answers 403 from this container, so the annotation is the reading).

The change

.github/workflows/spec-shape-gate.yml:

  1. The objectstack-side install and build widen from the spec to the packages objectui overrides. The names come from objectui/package.json's pnpm.overrides, the list the pin script packs, and the selector is --filter-prod=NAME...: runtime edges only, as the pin script's own build uses (a plain ... filter pulls in the client's dev closure of runtime, drivers and services). If no override is found, that step exits 2.
  2. A new step runs before the frozen install: objectui's pnpm run objectstack:build with OBJECTSTACK_ROOT set to this checkout. That is objectui's stand-in input: it packs every overridden package from this tree into those directories, with no lockfile change. The step calls objectui's script by name rather than by path. check-self-test-wired's live corpus maps every workflow node PATH to a file in THIS repository, and its self-test went red on the path spelling at bfa828074 (exit 0 on BASE).
  3. The inject step stays (measured below). Its comment now says what it still adds.

scripts/ci/spec-shape-gate.mjs: two summary lines now name the subject the compile reads, @objectstack/spec and every other @objectstack/* package objectui installs. The self-test's pinned substring (type-checks against) is unchanged.

No gate was added, no step skipped, and no continue-on-error set. report's exit-2 meaning is unchanged.

The dispatch's hypotheses, measured

Readings at BASE 725a5b131, objectui 4016370af3, pnpm 10.31.0, node 22.22.0.

  1. The red is the bare frozen install failing with ENOENT on the cache directory. Confirmed, as above.
  2. OBJECTSTACK_ROOT=TREE packs every package and leaves the lockfile unchanged. Confirmed. Seven packages were packed from 725a5b1311ec in 15 s and the pin script's lockfile check passed. pnpm install --frozen-lockfile then exited 0 in 7 s, and objectui's git status stayed clean. pnpm run setup would pass the variable through too. But setup is also THIS repository's script name, so the self-test-wired corpus would expand it against our package.json. The build and the install are therefore two named steps.
  3. inject becomes redundant. On BASE it changes nothing: every declaration file is identical, it made 0 substitutions, and it verified 32 consumers. The only differences between npm pack and pnpm pack output are package.json (npm keeps the prepare script) and one nested src/migrations/entries/README.md. It is not redundant, though: it is the only range check of the spec's declared dependencies. A tarball declaring zod: ^4.7.0 against the store's zod@4.6.7 made inject exit 2 with nothing written, while the pin script's check passed the same drift. It stays.
  4. A manifest change since objectui's pin makes the frozen install refuse. BASE has no drift. Across the seven packages, 0 fields changed in dependencies, optionalDependencies, peerDependencies, peerDependenciesMeta or engines over the 157 commits from objectui's pin 2c9bf9430a to BASE. The mechanism itself is falsified. I mutated the packed spec manifest four ways: a new dependency name, peer ai: ^8.0.0, engines.node at 24 or above (under objectui's engine-strict=true), and zod: ^4.7.0. objectui's frozen install exited 0 every time and linked the drifted manifest. Only the pin script refuses, and only for the name class.

How the gate reads the name class. The pin step exits 1 with the pin script's own objectstack-pin error annotation, which names the package and the dependency. Install and compile are skipped, and report exits 2: no reading, never a pass. That is a red with no compile verdict, and the remedy is objectui's: move its .objectstack-sha with scripts/bump-objectstack.sh and commit the lockfile. It is neither a shape break nor a registered retirement, because the registry matches diagnostics and there are none. I propose no new registry kind: a "known" reading of a compile that never ran is a skip, which this card's ruling forbids. Nothing refuses peer, engines or range drift in the other six packages. In that case the compile reads objectui's lockfile-resolved dependencies, which is what objectui's own frozen install does with the same build.

Local end-to-end

I extracted the edited workflow's own run: texts from the YAML and replayed them under bash -eo pipefail.

  • objectstack side, in a detached worktree at BASE after the filtered install: step 9 (install) exit 0 in 3.6 s; step 10 (build the seven) exit 0 in 125 s.
  • objectui side, in a throwaway clone at 4016370af3 with a fresh node_modules: step 13 (pnpm run objectstack:build) exit 0 in 14.5 s; step 14 (frozen install) exit 0 in 7.9 s; inject exit 0.
  • Compile: TURBO_FORCE=true pnpm type-check --continue --concurrency=2 exited 0, with 81 of 81 tasks successful and 0 cached, in 16m16s on a shared box. report --status 0 on that log exited 0 ("type-checks against"). Two registered retirements matched nothing and are listed as such.
  • Added CI time, measured locally on a shared box: 27 s for the six non-spec builds (spec already built), about 15 s for the pin pack, and a few seconds for the wider filtered install on a warm store. That is under a minute on a job sized at about 13 minutes with a 45-minute ceiling.

Gates

Run at eb080715a, the head of this PR. The 51 commands node scripts/pm/dispatch-gates.mjs --commands derives for this diff all exited 0, each exit code captured before any pipe. --ran reconciliation: "51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN". Selected verdict lines:

  • pnpm check:spec-shape-gate: spec-shape-gate --self-test: 60/60 passed across 7 batteries
  • node scripts/check-self-test-wired.mjs --self-test: exit 0 (red at bfa828074 on the path spelling, see above)
  • pnpm check:pnpm-filter-targets, pnpm check:workflow-step-name-quoting, pnpm check:nul-bytes, pnpm check:entry-guard, pnpm check:bash32-floor: exit 0
  • Outside the derived set: pnpm check:pnpm-flag-spellings exit 0.

The gate's own pull_request run on this PR (path-filtered to this workflow and the script) is the CI end-to-end reading.

Acceptance notes

  • inject's tarball comes from npm pack; objectui's pinned build comes from pnpm pack. They differ in package.json (npm keeps prepare) and one nested README, and tsc reads neither. Observation only.
  • check-self-test-wired's live corpus does not read a step's working-directory, so a node PATH run inside the objectui checkout reads as a path in this repository. This PR spells objectui's entry point by script name instead. Observation only, no carrier.
  • Triage reading 6259975511 on the card: chore: version packages (next) #24168 merged as 725a5b1311 (this PR's BASE), and its Release run refused at the full-battery check because the newest schedule run at or before that version commit is red.

维护者速读(草稿)

改了什么

Spec Shape Gate 是每小时检查 objectui 能否在本仓当前提交上编译通过的门禁,这次改的是它安装 objectui 的方式:先在本仓构建 objectui 用到的全部 7 个 @objectstack/* 包,再调用 objectui 自己的 objectstack:build 脚本把它们打包进 objectui 期望的目录,最后照常冻结安装。以前只注入 spec 一个包,现在 objectui 编译时读到的 7 个包全部来自本次提交。

为什么改

objectui 改为按提交消费 @objectstack/* 之后,门禁原来的裸安装每次都失败(找不到缓存目录),门禁一直是红的,而发版门禁读这条结果。triage 读数 6259975511:next.4 已无法挽救,可发版的路径是本卡落地、新 main 上出现一次绿色的定时 Spec Shape Gate 运行,然后发 next.5。

风险与代价(含回滚)

每次运行大约多一分钟(多构建 6 个小包,外加一次打包)。如果本仓某次提交给这 7 个包新增或删除了运行时依赖,objectui 的打包脚本会拒绝,门禁变红(读作"无读数",不算通过),要等 objectui 升级它的 pin 并提交 lockfile 才能转绿。回滚:revert 本 PR,门禁回到现在的红色状态。

席位意见

你要做的

看一下本 PR 自己的 Spec Shape Gate pull_request 运行结论;同意的话批准合并(工作流文件属于 Tier H,需要你亲手批准)。


Generated by Claude Code

…ories, packed from this commit

objectui now overrides every @objectstack/* package it installs to
file:node_modules/.cache/objectstack/<name>, a directory only its
scripts/objectstack-pin.mjs build fills, so the gate's bare frozen install
died on ENOENT there (exit 254) and every run reported exit 2.

The gate now builds the packages those overrides name (read from objectui's
package.json, --filter-prod closure) from this commit, packs them into those
directories with OBJECTSTACK_ROOT set to this checkout, and then runs the
frozen install. objectui compiles against all seven packages built here, not
only the spec. The inject step stays: it still judges the spec's dependency
ranges and proves every consumer resolves the injected copy, which the pin
script does not (it compares dependency names only).

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
`node scripts/objectstack-pin.mjs build` in a step whose working directory is
the objectui checkout names a path that exists only there, and
check-self-test-wired's live corpus keys every workflow `node <path>` to a file
in this repository (its self-test went red on it). objectui's own
`objectstack:build` script is the same command under the name objectui
declares for it, and names no path here.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
…he compile read

objectui now installs all seven @objectstack/* packages it uses from this
commit's build, so "compiled against @objectstack/spec built and packed from"
understated the subject of every reading.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Spec Shape Gate red on this PR — the gate working, not this PR's defect · domain:devx seat 1 · os-warren · session_01FM29ZqLTrnPNXxijioZwMp · 2026-10-19T06:03Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么:Spec Shape Gate(每小时检查 objectui 能否在本仓当前提交上编译通过)安装 objectui 的方式。先在本仓构建 objectui 用到的 7 个 @objectstack/* 包,再用 objectui 自己的 objectstack:build 把它们打包进 objectui 期望的目录,最后照常冻结安装。以前只注入 spec 一个包,现在 objectui 编译时读到的 7 个包全部来自本次提交。

为什么改:objectui 改为按提交消费 @objectstack/* 后,门禁的裸安装每次都失败,门禁一直红,发版门禁又读这条结果。#24168 已合入但发布被拒,next.4 救不回来;可发版的路径是本 PR 落地、新 main 上出现一次绿色的定时运行、然后发 next.5。

风险与代价(含回滚):每次运行多约一分钟。本 PR 自己的运行已经证明修复有效:安装、打包、编译都跑通(81 个类型检查任务中 80 个通过),唯一的红是一处真实的形状破坏——PR #24442 退役了操作行的 patch,objectui 的一个测试还在写它。这不是本 PR 的问题,已请 spec 席把这次退役登记进 spec-shape-retirements.json(objectui 侧的跟进卡是 objectui#13022),登记后门禁会把它读作"已知"而转绿。回滚就是 revert 本 PR,门禁回到现在"装不上、无读数"的红。

席位意见:建议批准。改动只在门禁的安装步骤,方向与 objectui 自己的 pin 设计一致;剩下的那一处红是门禁正确地发现了 main 上的真实问题。批准后,席位会等登记落到 main、合并 main 让门禁重跑转绿,再入队。

你要做的:给本 PR 一次批准(APPROVED review)。

席位核对

ACCEPT · domain:devx seat 1 · os-warren · session_01FM29ZqLTrnPNXxijioZwMp · 2026-10-19T06:06Z · head eb080715a · card #24424 (claim 6259813946)

  • Form. Draft, base main, first line Fixes #24424 (the only closing keyword). Assignee os-warren. skip-changeset: workflow and root script only.
  • Scope. 2 files, +46 / −12: .github/workflows/spec-shape-gate.yml (the objectui install path) and two summary lines in scripts/ci/spec-shape-gate.mjs.
  • Read against the head's YAML. The objectui checkout (:156) precedes the new closure install (:220), which reads objectui's pnpm.overrides; objectstack:build is objectui's own script (node scripts/objectstack-pin.mjs build, objectui package.json:20); OBJECTSTACK_ROOT is this checkout. inject stays, for the reason the dev measured (it is the only judge of the spec's dependency ranges).
  • End-to-end reading, pinned to head eb080715a. The PR's own Spec Shape Gate run installed, packed and compiled objectui (80/81 type-check tasks) and reported 1 NEW diagnostic owed by PR feat(spec)!: retire a page action row's own patch — a row's operation: 'update' runs the declared action its name names (#24394) #24442 (69f342b924), unregistered — analysis 6260795927; registration asked of domain:spec seat 1 on spec(ui): a page action row's operation: 'update' is described as "a patch or params gives the values to write", but the route writes the named action's own patch, and after #24268 refuses static params that action does not declare #24394 (6260806564).
  • Gates. 44 check-runs, all completed: 32 success, 11 skipped, 1 failure (Spec Shape Gate, above). node scripts/pm/check-expected-skips.mjs --pr 24469 → OK (11 skips in the roster). Required contexts all success. node scripts/pm/check-governed-merges.mjs --pr 24469 → GOVERNED, Tier H, 58 changed lines.
  • Accepted deviations. The local compile ran on a comparison worktree at the base (identical package sources); test:repo left to CI.
  • Path fork. Tier H: this record, needs-user-decision on the PR, review requested from os-zhuang and hotlong. ⛔ Not readied or queued; after the approval, this seat lands it only once Spec Shape Gate is green on a head that carries the registration.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants