Repository navigation
feat(tooling): objectui consumes @objectstack/* by commit — .objectstack-sha, bump-objectstack.sh, dev and CI built from the pin (objectui#12957) - #12963
Conversation
…ectstack-sha pins .objectstack-sha pins objectstack main 2ee5861b0f (it contains 586705d85d, 1e001c7df and e15897c0f1). scripts/objectstack-pin.mjs builds the seven packages the workspace resolves (spec, core, types, client, formula, sdui-parser, lint) at that commit and packs each into node_modules/.cache/objectstack/<name>; the root manifest overrides every @objectstack/* package to that directory with file:, so the lockfile records directory resolutions and no @objectstack/* package resolves from npm. `pnpm run setup` builds then installs frozen; OBJECTSTACK_ROOT packs an existing objectstack tree into the same directories instead. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
…ad the pinned build - scripts/bump-objectstack.sh moves .objectstack-sha. It refuses a commit it cannot resolve or read before writing anything, and warns (three answers) when the commit is not on objectstack origin/main -- the refusal and warning contract of objectstack's bump-objectui.sh. - objectstack-pin.mjs refuses when the lockfile does not record the dependency names the build declares (a frozen install links a directory dependency without re-reading its manifest), and builds in a short scratch path (tsx's IPC socket path overflowed under a long one). - check-lockfile-integrity: rule 1 (an @objectstack/* version moving backward) is retired; the lockfile carries no @objectstack/* version now. Rule 2 still names a registry copy beside the pinned build. - check-lockfile-dedupe: the pinned build is a named precondition. - check-installed-pin-claims and the live-e2e backend pin follow the pinned build's file: resolution to the version its manifest declares. - turbo.json hashes .objectstack-sha and the build stamp, so a pin bump invalidates cached verdicts and widens the type-check scope. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
…ctstack build; AGENTS.md and the setup docs name the pin - Each of the 18 jobs that install (or, in lockfile-dedupe, resolve) restores node_modules/.cache/objectstack from a cache keyed by .objectstack-sha and the script that builds it, runs `node scripts/objectstack-pin.mjs build` (a no-op on a hit), and saves it as its last step only when it built it -- bounded and non-fatal, per objectui#6577/#7048. An unchanged pin never rebuilds. - performance-budget.yml triggers on .objectstack-sha (the bundle moves with the pin); its and lockfile-dedupe.yml's filters list the new script. - AGENTS.md: a dependency and lockfile section (the pin, the bump, the stand-in, the frozen-install caveat), `pnpm run setup` in the worktree rule and the local dev runbook. - README, CONTRIBUTING, QUICK_REFERENCE, scripts/setup.sh and two docs pages: a clean checkout starts with `pnpm run setup`. - Tests that assumed one cache per job, an exact action-ref list, or no continue-on-error in shadcn-check now read the pinned build's steps too; ci-cd-pipeline.md names the new command in every section that runs it. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
…uilt-in `pnpm setup` `pnpm setup` runs pnpm's own built-in command (it writes PNPM_HOME into the shell rc) and never reaches the workspace's `setup` script; `pnpm run setup` does. Measured on objectui#12957 in a throwaway workspace. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…commit; the objectstack install reads its own npm config Seat-directed scope change on objectui#12957 (item 6 amended): the first pin is 2c9bf9430a, the commit @objectstack/spec@18.0.0-next.3 was released from, so the pinned build reproduces what objectui resolved from npm and CI proves the mechanism with no contract drift. Moved with scripts/bump-objectstack.sh; `pnpm install` leaves pnpm-lock.yaml unchanged. Moving to a commit with 586705d85d, 1e001c7df and e15897c0f1 is a separate pin-bump card. objectstack-pin.mjs no longer passes this repository's npm_config_* settings to pnpm in the objectstack tree: `pnpm run` exports objectui's engine-strict=true, which refused the objectstack install through `pnpm objectstack:build` while the direct `node` call installed. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
…ack-pin.mjs Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
ℹ️ Console Performance Budget — not measuredThis run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget. This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.
See the workflow run for details. No package size report: it is only generated from a complete package build, so a partial one is never shown. |
…ation The fetch, install and build output of the objectstack tree goes to a log in the build's scratch directory; a failure carries that log's last lines, and under GitHub Actions the refusal is also printed as an `::error::` annotation. 8 of 25 jobs on 185772b failed the build step about 40 s in, and the job log -- the only place their cause was -- is not readable from the checks API. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
ℹ️ Console Performance Budget — not measuredThis run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget. This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.
See the workflow run for details. No package size report: it is only generated from a complete package build, so a partial one is never shown. |
…ver masks it On c659d23, 13 of 25 cold jobs failed the build step about 50 s in with no annotation, so the failure was not a refusal: an exception escaped. The CLI now writes any failure, refusal or crash with its stack, synchronously to stderr and as a GitHub annotation; removing the scratch tree is retried and only warns. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
…s build is a function of the pin objectstack's spec build anchors its schema ratchets on origin/main and fetches it when it can. With the remote in place, building the 18.0.0-next.3 commit was judged against wherever main was that minute: it built earlier and then failed in 13 of 25 CI jobs on db2b723 once main had added two schemas (the annotation named them). With no remote the spec build anchors on the commit's own committed baseline ("origin/main is not resolvable in this build environment"). Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs: card objectui#12957 (body; comments 6248917934 claim, 6250606447 item-6 amendment, 6250846209 triage pointer, 6251605870 os-dev-report), decision card objectui#12955 (body and ruling 6248859734, letter B), PR #12963 (body, 42-file list, net diff from merge-base ① Derived judgments
② Semver levelNo ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 — ACCEPT ·
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Fixes #12957
Clause-②: no
objectui now consumes
@objectstack/*by commit (ruling B on objectui#12955, comment6248859734)..objectstack-shapins objectstack2c9bf9430a1198bfaa5c0de9ff4a811838c6869c(chore: version packages (next) (#23825)), the commit@objectstack/spec@18.0.0-next.3was released from; every@objectstack/*package resolves to a build of that commit, and none resolves from npm. Seat-directed scope change (item 6): the seat moved the first pin from objectstackmain2ee5861b0fto this release commit, so the pinned build is equivalent to what npm serves today. Moving the pin to a commit that contains586705d85d,1e001c7dfande15897c0f1is a separate pin-bump card, and no parity, refinement or block-config pin is edited here to absorb specmain's drift. No@objectstack/*range in any manifest moves (that is objectui#12958). Dispatched bydomain:uiseat 1, sessionhttps://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks.⛔ Tier H (
AGENTS.mdand.github/workflows/**): this stays a draft untilos-zhuangorhotlongapproves it.How it resolves
package.jsonoverrides each@objectstack/*package tofile:node_modules/.cache/objectstack/NAME.scripts/objectstack-pin.mjs build(alsopnpm objectstack:build) fills those directories: it fetches the pinned commit, installs only the build closure, builds, and unpacks each package exactly aspnpm packpublishes it.pnpm run setup= that build, thenpnpm install --frozen-lockfile.OBJECTSTACK_ROOT=TREE pnpm objectstack:buildpacks an already installed and built objectstack tree into the same directories instead. This is the input tooling(build-console): when the console build installs objectui at.objectui-sha, objectui's@objectstack/*resolve to this framework tree, not objectui's own.objectstack-shabuild (ruling B on objectstack-ai/objectui#12955, step 3) objectstack#24306 consumes.scripts/bump-objectstack.sh [SHA_OR_REF]moves the pin. It refuses a commit it cannot resolve or read before writing anything, and warns (three answers) when the commit is not on objectstackorigin/main. That is the contract of objectstack'sbump-objectui.sh; its changeset and SDUI-manifest steps are not copied.@objectstack/*dependency stops instead of falling back to npm); a packed entry point is missing; or the lockfile does not record the dependency names a packed manifest declares.originremote. objectstack's spec build anchors its schema ratchets onorigin/mainand fetches it when it can, so with the remote in place the build of the pin was judged against wherevermainwas that minute: atdb2b7239it failed in 13 of 25 CI jobs, namingjson-schema/api/PackageInstallManifest.jsonandjson-schema/ui/PageRecordBinding.json, two schemasmainhad added after the pin. Without the remote, the spec build anchors on its committed baseline, so the build is a function of the pin. A test pins the removal; deleting the call turns it red.npm_config_*settings (proxy, registry, CA and store settings are kept).pnpm runexports the invoking workspace's.npmrcthat way, and objectui'sengine-strictmadepnpm objectstack:buildfail withERR_PNPM_UNSUPPORTED_ENGINEinside the objectstack tree.objectstack-pinerror annotation, because job logs are not always readable. Cleanup of the scratch tree only warns, so it never masks the failure.Measurements (all local, this container, pnpm 10.31.0, Node 22.22.0)
The packages (Zone 2 item 1, corrected). Seven, not six. At the pin,
@objectstack/coredepends on@objectstack/types. The full set is spec, core, types, client, formula, sdui-parser and lint. It was read from the pinned commit's manifests and frompnpm --filter-prodover the four roots objectui declares.The resolution form (item 3, Zone 2 item 2). All three forms were installed against the same build:
file:tarballintegrityandversionper tarballERR_PNPM_TARBALL_INTEGRITYERR_PNPM_TARBALL_INTEGRITYlink:directorylink:only, no dependenciesERR_MODULE_NOT_FOUNDforzod(pnpm installs nothing a linked package depends on)file:directory (chosen)resolution: {directory, type: directory}, with no integrity and no versionpnpm-lock.yamlbyte-identicalSo the PM's tarball expectation held, and was worse than expected: a clean frozen install failed even for the same tree.
link:keeps the lockfile stable but breaks at runtime, and linking a source tree instead would bring that tree's own secondzod(the objectstack#21108 split). With afile:directory, the dependencies resolve from objectui's lockfile, so there is onezod. A frozen install also re-links a directory whose content changed: a marker written into a rebuilt directory reachednode_modules.Item 3, the stand-in. At the pin, I built objectstack at
e5136ff10f(the18.0.0-next.2release commit) in a separate checkout and ranOBJECTSTACK_ROOT=that tree withobjectstack-pin.mjs build. The build exited 0. Thenpnpm install --frozen-lockfileexited 0, and the lockfile's sha256 was the same before and after (da56792a0bb44808…). The same held earlier against the first candidate pin2ee5861b0f, with a stand-in at93b4acb57ec2(the parent of586705d85d): build exit 0, frozen install exit 0, lockfile identical. The installeddist/ui/index.d.mtsheld 10emptyStateoccurrences at that pin, 4 with the stand-in, and 10 again after the restore. The lockfile does not change across pins or stand-ins, because it records directories, not versions.What a frozen install does not check. I gave a packed manifest a dependency the lockfile does not record.
pnpm install --frozen-lockfileexited 0 and did not install it. That is why the build compares dependency names and refuses on any difference. I proved that refusal on the real tree: it exits 1 and names the package; its control exits 0.Item 6, the first pin (seat-directed). I moved the pin with
scripts/bump-objectstack.sh 2c9bf9430a1198bfaa5c0de9ff4a811838c6869c;pnpm installafterwards leftpnpm-lock.yamlunchanged. Equivalence with npm: I rannpm packfor all seven packages at18.0.0-next.3and compared each withdiff -rqagainstnode_modules/.cache/objectstack. Everypackage.jsonversion is18.0.0-next.3.types,formulaandsdui-parserare identical.core,clientandlintdiffer only inpackage.json, by the order of dependency keys (same keys, same versions). Inspec(2094 files), every JS and JSON Schema file is identical. The npm tarball carries two release-time files that the build does not produce,spec-changes.jsonandprotocol-upgrade-guide.md; objectui reads neither (git grepexits 1, a control term exits 0). After the hermetic change, 10 declaration files (api,automation,contracts,marketplaceandsystem, as.d.tsand.d.mts) and 2 hashed DTS chunk pairs differ. The sampled chunk (automation-api.zod) differs only in the order of union members, at the same byte length (523364): DTS emit nondeterminism, not a content difference. The pin is a strict ancestor of586705d85d,1e001c7dfande15897c0f1(git merge-base --is-ancestor 2c9bf943 Cexits 0 for each), so none of the three is in this pin.Ablation: the type check reads the pinned build. I wrote a throwaway probe that imports
SelectOptionKindfrom@objectstack/spec/data, a symbol that18.0.0-next.3adds, and checked it withtsc --ignoreConfig --noEmit. At the pin it exits 0. With thee5136ff10f(18.0.0-next.2) stand-in it exits 2 withTS2724(no exported member namedSelectOptionKind). After the restore it exits 0. The same shape held at the first candidate pin with anemptyStateprobe: exit 0, thenTS2339and exit 2 with the93b4acb57ec2stand-in, then exit 0. The probe was never committed. Afterwardsgit diff HEADis empty andobjectstack:statusreads the pin. My first attempt measured nothing: tsc refused with TS5112 because a tsconfig is present. I re-ran it with--ignoreConfig.Item 2, type check and tests at the pin. At
4bd617b0:pnpm type-check --concurrency=2, 81 of 81 tasks successful, exit 0; the scripts suite, 188 test files passed (5652 tests);pnpm type-check:scripts, exit 0. At185772ba(the next.3 pin; later commits change onlyscripts/objectstack-pin.mjsand its test):packages/core,packages/types,packages/reactand the spec-drift test files, 711 test files passed. Thepackages/app-shellsuite ran in CI (all 16 shards green), not locally.Cold build (Zone 2 item 3). I ran
objectstack-pin.mjs buildunder the shared verify lock. These are shared-box seconds with a warm pnpm store, at the first candidate pin: 202 s in total, of which fetch 17.0 s, install 4.4 s, build 160.7 s and pack 13.9 s. A filtered install is enough: the root plus--filter-prodover the roots is 8 projects and 348 packages. Plain--filter 'X...'follows devDependencies into 36 projects; that build took 352 s. Disk: 203 MB stays innode_modules/.cache/objectstack, and about 0.9 GB of scratch is deleted after packing. A warm cache is a stamp read, and the build step returns immediately. At the next.3 pin the same build took 174 s. A fresh worktree's coldpnpm run setuptook 175.7 s and added 295 MB (201 MB of it the cache). A second fresh worktree right after took 167.5 s and another 295 MB. Copying the first worktree's cache into a third and running the same command took 6.5 s (+286 MB as a copy; about 85 MB if hardlinked, an estimate). That is the evidence for the shared pin cache card; no shared cache is built here. AnOBJECTSTACK_ROOTpack of an already built tree took 22.0 s. The CI numbers are in the next section.pnpm setup(item 7). It is pnpm's own built-in command. In a probe it wrotePNPM_HOMEinto the shell rc and never ran the package script.pnpm run setupran it. So everything here sayspnpm run setup. I also corrected the same trap in this repository's backend runbook line.CI (item 4)
All 17 jobs that install (
docs-deploy's throughvercel build), pluslockfile-dedupe, which resolves, restorenode_modules/.cache/objectstackwithactions/cache/restore@v6. The key isobjectstack-pin-RUNNER_OS-hashFiles(.objectstack-sha, scripts/objectstack-pin.mjs). Each then runsnode scripts/objectstack-pin.mjs build, which is a no-op on a hit. On a miss, the job saves the build as its last step, bounded at 2 minutes and non-fatal (objectui#6577, objectui#7048). An unchanged pin never rebuilds. A script edit counts as a recipe change and rebuilds once. Every job pays the cold build on the first run of a new pin.Measured at
4bd617b0: 23 jobs built cold in 71 to 125 s (median 111 s); at1fa4f1de, 25 jobs took 109 to 151 s (median 138 s). Two jobs restored a build an earlier run of this PR had saved: Build Docs (restore 3 s, build 0 s) and Test shard 15/16 (restore 2 s, build 0 s). The save step took 0 to 2 s, and the install after the build 4 to 8 s. Whethermainsaves the cache is NOT MEASURED:mainhas no.objectstack-shauntil this merges, so its first push run afterwards misses and saves undermain's scope, which PR runs can restore. The Actions cache API is not readable from here, so the cache entries were not listed.Because of
turbo.jsonglobalDependencies(.objectstack-shaand the build's stamp), a pin bump invalidates replayed turbo verdicts. It also widens the type-check fast path to the whole workspace, becauseselect-type-check-scope.mjsreads that list. Without this, a bump PR would type-check zero packages and replaymain's cached verdicts.performance-budget.ymlnow triggers on.objectstack-sha.The lockfile and pin gates (item 5)
check-lockfile-integrity: rule 1 (an@objectstack/*version moving backward) is retired. The lockfile carries no@objectstack/*version any more, so the rule could never fire again. The identity it guarded is.objectstack-sha, and the build refuses any package that is not overridden. Rule 2 (a package gaining a physical copy) is kept: a registry copy beside the pinned build is exactly what it names. The tests pin that sample and the peer fork in its pinned-build form.check-lockfile-dedupe: adapted. The pinned build is a named precondition, checked before pnpm runs. With the build present:VERDICT deduped, exit 0, lockfile unchanged.check-installed-pin-claims: adapted. Face A follows the lockfile'sfile:resolution to the version the directory's manifest declares. An absent build is a named conflict. Gate: exit 0.ci-cd-pipeline-doc.test.ts, finding(ci):e2e/live/ci/backend.envpinsOBJECTSTACK_VERSION=17.0.0-rc.2while the lockfile resolves 17.2.0 — the live-e2e lane has been smoke-testing an unmatched pair for two minor versions #7689): adapted the same way. The page now states the gap this leaves (see the acceptance notes).check-spec-range-floorsis unchanged. It fetches each declared floor from the registry and never reads the installed tree.check-spec-symbol-derivationreads the installed spec, which is now the pin: exit 0.Files beyond the claim's file surface (named, not silent)
turbo.json: the twoglobalDependenciesentries above.scripts/dependabot-merge-gate.mjs: one prose string that described the retired rule.README.md,CONTRIBUTING.md,QUICK_REFERENCE.md,scripts/setup.sh,content/docs/guide/console.md,content/docs/utilities/runner.mdx: a clean checkout starts withpnpm run setup. A barepnpm installwithout the build fails (ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND/ENOENT).content/docs/guide/ci-cd-pipeline.md: its tests require each section to name every command its jobs run, and the live-e2e sentence moved.continue-on-errorinshadcn-check.yml:turbo-cache-save-bound,report-turbo-cache,check-action-ref-convention,shadcn-check-report,render-budget-comment.Acceptance notes
pnpm install --lockfile-onlywithout the build exits 1 withERR_PNPM_LINKED_PKG_DIR_NOT_FOUND. Whether Dependabot's sandbox fails the same way is NOT MEASURED; its next npm run after the merge is the first reading.apps/console/vercel.jsoninstalls withpnpm install --frozen-lockfileon Vercel, which would now need the pin first. No Vercel status appears on recentmaincommits, so it was left alone.OBJECTSTACK_VERSION. The console is now built from the pinned commit, whose version string only moves at an objectstack release. The two match by number, not by commit. The page says so, and nothing here closes it.e2e/live/ci/backend.env's header still says the version is "resolved in pnpm-lock.yaml". The lockfile now resolves the pinned build, whose manifest carries that version.AGENTS.mdandexamples/README.mddocumentpnpm setupfor first-time setup. That runs pnpm's built-in command, not the script. This is reported on the card, not changed here.pnpm run setupfirst) as the default. Nothing changes in this PR.维护者速读(草稿)
@objectstack/*。仓根新增.objectstack-sha,钉 objectstack 的一个提交。首个 pin 是@objectstack/spec18.0.0-next.3 的发版提交2c9bf943(席位指定),构建结果与 npm 上的 next.3 等价;objectui#12932、Studio 变更 drawer: on publish, offer 补默认值 per new field that carries a default, through the protocol's opt-in backfill (ruling A on cloud#2924) #12929、approvals B1 closing card (ruling C on objectui#12438): the inbox opens a request on the served sys_approval_request page where the side sheet is, deletes the sheet, and lists each retired pin against its user report #12484 等需要的那三个后端提交都在它之后,不在这次的 pin 里。本地和 CI 都从这个提交的源码构建所需的 7 个包,构建结果按 pin 缓存。换 pin 用scripts/bump-objectstack.sh,新机器或新目录跑pnpm run setup。.objectui-sha在 tooling(build-console): when the console build installs objectui at.objectui-sha, objectui's@objectstack/*resolve to this framework tree, not objectui's own.objectstack-shabuild (ruling B on objectstack-ai/objectui#12955, step 3) objectstack#24306 落地前不能越过本 PR。AGENTS.md与 workflows)。下一步是席位另开的 pin-bump 卡:把 pin 挪到包含那三个后端提交的位置。Generated by Claude Code