Skip to content

build-console: objectui's @objectstack/* install from this framework tree when the pin carries .objectstack-sha (ruling B on objectstack-ai/objectui#12955) - #24440

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-24306-build-console-objectstack-root
Oct 19, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-24306-build-console-objectstack-root

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24306
Clause-②: no

Ruling B on objectstack-ai/objectui#12955 (comment 6248859734), step 3. objectui consumes @objectstack/* by commit since objectstack-ai/objectui#12957 landed as 749ed760: its root package.json overrides seven packages (spec, core, types, client, formula, sdui-parser, lint) to file:node_modules/.cache/objectstack/NAME, and its scripts/objectstack-pin.mjs build fills those directories. With OBJECTSTACK_ROOT=TREE set, that script packs an installed and built objectstack tree instead of building its own pin, with no lockfile change.

scripts/build-console.sh now uses that input. Until this lands, .objectui-sha cannot move past 749ed760: the console build's frozen install of objectui dies there.

What changed

One file of behaviour, scripts/build-console.sh, plus one test.

  • Detection by the file. When the pinned objectui carries .objectstack-sha, a new block runs before objectui's frozen install. A pin without the file never enters it (Done-when item 2).
  • Refusal without the hook. A pin with .objectstack-sha whose scripts/objectstack-pin.mjs is missing or has no OBJECTSTACK_ROOT input exits 1, naming the hook, like the existing OBJECTSTACK_SPEC_DIST / OBJECTSTACK_CLIENT_DIST guards (item 3).
  • The closure build. It builds the packages objectui overrides, read from objectui's root package.json at the pin, through turbo (^build brings their dependencies). In every workflow that runs this script this is a cache hit (measured below), and --output-logs=new-only keeps a hit from replaying about 3,600 cached log lines.
  • Declarations whatever OS_SKIP_DTS says. That build runs with OS_SKIP_DTS unset and says so when the caller had it set. See the consumer pointer section below.
  • The stand-in. It runs OBJECTSTACK_ROOT=FRAMEWORK_ROOT node scripts/objectstack-pin.mjs build in the objectui checkout, then the unchanged frozen install. Every @objectstack/* package objectui installs is then this tree's (item 1).
  • The refusal text. On a pin-script refusal the build stops before the install. The text names what an operator does when the reason is a dependency mismatch (this tree added or dropped a runtime dependency of one of the seven after objectui's pin): the fix is objectui's. Move objectui's .objectstack-sha, commit the lockfile (and any override) that needs, land it, then move .objectui-sha here. ⛔ Not by editing the scratch lockfile or making the install non-frozen.
  • OBJECTSTACK_SPEC_DIST / OBJECTSTACK_CLIENT_DIST stay: older pins still install from npm and need them.

Not changed: .objectui-sha (item 5: the bump that follows is this card's verification), any workflow, anything in objectui.

The consumer pointer on this card (6257693480, repo:cloud#2)

It measured, on objectstack-ai/cloud#3023, that objectui's pin script refuses an OS_SKIP_DTS=1 (typeless) tree, and that the rebuild its refusal prints inherits the same OS_SKIP_DTS.

  • Who sets it on the way to this script, measured here: no workflow, composite action, package.json script, shell script or Dockerfile in this repository (git grep OS_SKIP_DTS: readers only). It is a developer's or agent's local speed flag (AGENTS.md mentions it), and cloud's lane.
  • Reproduced on this branch before the fix, at f106564740, OS_SKIP_DTS=1, all seven built typeless: exit 1, @objectstack/client is not built in TREE: its manifest points at dist/index.d.ts, which is not there.
  • Choice: build with declarations, not refuse up front.
    • Business need: nobody in this repo sets it before this script, so the case is a local caller; building serves them in one run, refusing makes them re-run.
    • Long-term fit: the script already owns this closure build, and the pin contract needs complete packages; the contract is not loosened, the script produces the input the contract asks for.
    • AI error-proofing: an agent that exported OS_SKIP_DTS can no longer reach a refusal whose printed remedy loops; the override is announced, not silent.
    • Scope: three lines, no new gate.
  • turbo.json lists OS_SKIP_DTS in globalEnv, so the unset build hashes apart from a typeless one and cannot replay it. The proof is on disk, not turbo's word: see the measurements.

Measurements (each command with its exit code, captured before any pipe)

Shared 4-core box; wall-clock figures are shared-box readings. The objectui trees were scratch clones under the session scratchpad (OBJECTUI_ROOT). ⛔ The shared ../objectui checkout was not touched. .objectui-sha was set locally only, and restored by a trap that compared blob hashes with HEAD. Every run ended with git diff HEAD empty and a clean git status.

Item 4, new branch, objectui 749ed760, the CI way, at f106564740:

step command exit
A pnpm exec turbo run build --filter=@objectstack/client... --concurrency=2 0
B OBJECTUI_ROOT=SCRATCH bash scripts/bump-objectui.sh --no-commit --no-changeset 749ed760… (refresh, first half) 0
C OBJECTUI_ROOT=SCRATCH bash scripts/build-console.sh (refresh, second half; the CI step) 0
D node scripts/gen-sdui-manifest-node.mjs 0
E pnpm check:console-sha 0
F pnpm check:console-injection --require-stamp (the dist stamp check) 0
  • In C, the new block's turbo build was 36 of 36 tasks cached.
  • pack 7 package(s): 16 s. The pin script's lockfile dependency-name check passed.
  • The frozen install finished in 5.9 s, and the console built.
  • A first run at a532a18745 was also all exit 0.

Item 4, with OS_SKIP_DTS=1 exported and the seven typeless, at 10e5579ba9 (the final script; the later commit adds only the test):

  • Before the run: dist/index.d.ts was ABSENT for all seven, with 0 declaration files under each dist.
  • build-console.sh exited 0 and printed the OS_SKIP_DTS notice.
  • After the run: PRESENT for all seven (spec 134 declaration files, lint 4, types 4, the rest 2).
  • gen-sdui-manifest-node.mjs, check:console-sha and check:console-injection --require-stamp each exited 0.

Item 1, read off the objectui tree after the install:

  • node_modules/.cache/objectstack/.stamp.json: source: external, root = this worktree, sha = its HEAD.
  • node_modules/.pnpm: 7 @objectstack entries, each @objectstack+NAME@file+node_modules+.cache+objectstack+NAME….
  • pnpm-lock.yaml: 0 registry-resolved @objectstack/* keys (14 file: keys).
  • Each installed package's dist/ against packages/NAME/dist/ here: diff -rq exit 0 for all seven (control, spec against core: exit 1).
  • The bundled spec is this tree's. In the build, the injection check reports no skew: the vendored spec is now this tree's, packed. Two-sided, run on a copy of the dist against the npm @objectstack/spec@18.0.0-next.3 tarball (the spec objectui's own pin 2c9bf9430a was released as), assert-console-spec-injection.mjs exited 0: 10 of 10 injected-only descriptions present, the 1 next.3-only description absent.

Item 2, the committed pin 0707b4ae37 (git cat-file -e 0707b4ae37:.objectstack-sha exit 128; control 749ed760 exit 0), at a532a18745:

  • This branch's script exited 0, and so did origin/main 97cb5c5a82's script run over the same tree.
  • The two dists' assets/ sha256 manifests are identical (3,450 files), and so are the injection stamps. check:console-sha and check:console-injection --require-stamp exited 0 after both.
  • The new block's log lines: 0 (control: 3 in the item 4 log).
  • The later commits touch only lines inside the .objectstack-sha block, and the test's first case pins that a pin without the file reaches the install with no closure build and no pin script.

Item 3, two scratch commits on 749ed760, both carrying .objectstack-sha:

  • fac93cc890: OBJECTSTACK_ROOT renamed out of the pin script, 6 occurrences to 0.
  • 11ad951921: the pin script deleted.
  • Each exited 1 and printed has no OBJECTSTACK_ROOT hook. The install never ran, the pin script never ran, and the build tree has no node_modules.

Before state (the dispatch's ablation; not committed). The branch was disabled through scripts/ablation-replace.mjs (anchor 1 to 0, blob ec24facf to 33c9e4d0, restore proved), and objectui 749ed760 built from a fresh tree. The frozen install died with ENOENT: no such file or directory, scandir '…/node_modules/.cache/objectstack/client', exit 254. ⚠️ That is not the ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND the card's unlock note names; that code was measured on objectui's side, not in this script.

The test: packages/spec/scripts/build-console-objectstack-root.test.ts, in spec's repo project; scripts/** is already spec's declared cross-package input.

  • It copies the real script into a temp framework root beside a temp objectui git repo. pnpm and the pin script are recording shims, and pnpm install exits 42, so reaching the install is the exit code.
  • Five cases: no .objectstack-sha; no hook; hook (exact order: closure build, then the pin script with OBJECTSTACK_ROOT = the framework root and cwd = the build tree, then the install); inherited OS_SKIP_DTS=1 (the closure build sees it unset, the install still sees 1); a pin-script refusal.
  • vitest run --project repo on it: 5 passed, 839 ms.
  • Ablations, each through ablation-replace.mjs with the restore proved:
    • closure build inheriting OS_SKIP_DTS: 1 failed, 4 passed;
    • branch disabled: 4 failed, 1 passed;
    • restored: 5 passed, git diff HEAD empty.
  • tsc --noEmit -p tsconfig.scripts.json --listFiles: exit 0, and the file is in the program.

The dispatch's mechanism assumptions, measured

  1. Build closure. Turbo dry runs at this branch show that every workflow filter builds all seven: ci.yml --filter=@objectstack/client... (37 tasks), cut-rc.yml client... + spec... (37), showcase-smoke.yml @objectstack/example-showcase^... (65), release.yml pnpm run build (83). Control: --filter=@objectstack/types builds spec and types only. So the script builds what it needs itself and no workflow changes. The PR is not on the governed register: check-governed-merges.mjs --test: NOT governed.

  2. Dependency names. Confirmed: the pin script's lockfile check passed for objectui 749ed760 (pin 2c9bf9430a) against this tree. Operator remedy: the refusal text above. Design alternative: in the report.

  3. The injections on the new branch. They are redundant for "which spec and client ship". A rebuild of the console in the same objectui tree without them passes:

    • the two-sided probe against next.3 (3 of 10 injected-only present, the 1 next.3-only absent);
    • the client canary;
    • the single-zod canary.

    They are not neutral: with them the JS is 907 KB larger (34,623,558 against 33,716,662 bytes; vendor-objectstack 6,128,118 against 5,222,138), and the bundle carries more spec text. Kept, as ordered; a follow-up decision is in the report.

  4. The console-dist cache key (ci.yml) under-covers the new branch. Core, types, formula, sdui-parser and lint now come from this tree, while the key carries only packages/spec/package.json and tsup.config.ts of them. For CONTENT that is the same accepted class as the client and the spec's content (the ruled cost model).

    • New failure class: the dependency-name refusal is a build FAILURE driven by the other six manifests. While the key hits, the Console Pin Gate never runs the pin script, so the refusal first fires where the console is built unconditionally: cut-rc.yml's pnpm objectui:build, or pnpm run release in the publish step.
    • Not changed here (Tier H). A finding in the report: adding the six manifests to the key and the console filter costs no extra miss on Version Packages merges, because all seven are in one changesets fixed group with spec.

Gates

  • Local, at e497bf7535: the 80 families node scripts/pm/dispatch-gates.mjs --commands derives. 79 exited 0.
    • pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: it needs a whole-tree pnpm build), so it is NOT MEASURED locally and CI's reading stands.
    • pnpm check:console-sha first read exit 1, against the dist my item 4 run left stamped 749ed760 after the pin was restored: the gate working. It re-read exit 0 with no dist, the CI lint state. Its substantive readings are the item 2 and item 4 rows above.
    • --ran reconciliation: 80 derived, 79 run, 1 NOT MEASURED (derived from the recorded exit 3), 0 unrun.
  • Also run: vitest run src/utils/console.not-built.test.ts test/console-resolve.test.ts in @objectstack/cli (2 files, 30 tests passed); spec's repo project in full (pnpm --filter @objectstack/spec run test:repo: 69 files, 1,130 tests passed); the scripts tsc above.
  • CI: the Console Pin Gate does not run on a PR or in the queue (full battery only). Its first run of this change is main's push battery after the merge, at today's pin, which is item 2's path.

Changeset

None. Nothing in any package's files[] changes. At today's pin the console dist is byte-identical (item 2), and the bump that moves the pin writes its own @objectstack/console changeset. skip-changeset applies. ⛔ No label was written here: the dispatch forbade label writes, so the seat applies it.

Acceptance notes (observations, not filed)

  • check-console-injection.mjs says "between the injected and published spec" for a no-skew stamp. On this branch the vendored side is this tree's packed spec, not a published one.
  • Item 3's message reads "no OBJECTSTACK_ROOT hook in scripts/objectstack-pin.mjs" also when that file is absent. It names the missing hook, as required.
  • packages/console/README.md step 2 says opening the PR runs the Console Pin Gate. That has been false since the gate became full-battery only, and predates this change.
  • The run that this dispatch resumed left its scratch objectui clone, a 1.6 GB build tree and its logs on disk, though the resume note said they were gone. This run removed them and re-measured everything.

Generated by Claude Code

…tree

A pinned objectui that carries .objectstack-sha (objectui 749ed760 on,
ruling B on objectui#12955) resolves every @objectstack/* package from
file:node_modules/.cache/objectstack/NAME, which its
scripts/objectstack-pin.mjs fills. Nothing filled it here, so the frozen
install of such a pin died with ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND.

When the file is present, build the packages objectui overrides in this
tree through turbo and run the pin script with OBJECTSTACK_ROOT set to
this tree before the install, so objectui's install resolves to this
framework with no lockfile change. A pin without the file builds as
before; a pin with it but without the OBJECTSTACK_ROOT hook refuses,
naming the hook. A pin-script refusal prints the operator's remedy for
dependency drift (move objectui's pin first, then .objectui-sha).

Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks
Co-authored-by: Claude <noreply@anthropic.com>
… the pinned-build directory)

Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks
Co-authored-by: Claude <noreply@anthropic.com>
…remedy; say why the spec assert reports no skew on an OBJECTSTACK_ROOT pin

Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks
Co-authored-by: Claude <noreply@anthropic.com>
…S_SKIP_DTS the caller exported

objectui's pin script packs whole packages and refuses one whose types entry
is missing; with OS_SKIP_DTS=1 and a typeless tree this branch exited 1 at
@objectstack/client's dist/index.d.ts. The closure build now runs with
OS_SKIP_DTS unset (turbo hashes it, so a typeless cache entry cannot replay),
says so when the caller had it set, and the refusal names it for hand rebuilds.

Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks
Co-authored-by: Claude <noreply@anthropic.com>
…fake objectui

The real script, copied into a temp framework root beside a temp objectui
repo, with pnpm and the pin script as recording shims: no .objectstack-sha
builds as before; one without the OBJECTSTACK_ROOT hook refuses before any
install; one with it builds the overridden packages, passes this tree as
OBJECTSTACK_ROOT, then installs, in that order; an inherited OS_SKIP_DTS
does not reach the closure build; a pin-script refusal stops the install.

Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/vitest.repo-tests.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/vitest.repo-tests.json) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 143 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json db1f1fd20fba543408b209746e566b48373a4622 → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · domain:ui seat 1 · session_0188TsgPV8kQXfuRuKmkj2ks · 2026-10-19T03:17Z

Reviewed at head e497bf7535, against claim 6257400721 on #24306 and the dev's report on that card. It executes ruling B (6248859734) on objectstack-ai/objectui#12955, step 3.

  1. Shape. Draft PR against main, opening Fixes #24306, assigned sunhaolin. 3 files, +308: scripts/build-console.sh (+99), a new test packages/spec/scripts/build-console-objectstack-root.test.ts, and its row in packages/spec/vitest.repo-tests.json. check-governed-merges --test: not governed. No workflow changed, so this is the lane seat's review at TIER_DEFAULT.
  2. Done-when, read in the diff.
    • Item 1: when ${BUILD_ROOT}/.objectstack-sha exists, the script builds the packages objectui's root pnpm.overrides names (read from the pin's own package.json, the same list the pin script reads) through turbo, then runs OBJECTSTACK_ROOT=$FRAMEWORK_ROOT node scripts/objectstack-pin.mjs build in the objectui checkout, before the unchanged frozen install. set -euo pipefail is in force (:34), so a failed closure build stops the script.
    • Item 2: no .objectstack-sha ⇒ the block is skipped entirely. The dev measured the old pin 0707b4ae37 byte-identical in assets (3450 files) and in the injection stamp, against main's script.
    • Item 3: .objectstack-sha without scripts/objectstack-pin.mjs, or without OBJECTSTACK_ROOT in it ⇒ exit 1 naming the hook, before any install.
    • Item 4: the dev's end-to-end at objectui 749ed760 exited 0 throughout: build-console, gen-sdui-manifest-node.mjs, check:console-sha, and check:console-injection --require-stamp. All seven installed dists were diff -rq-identical to this tree's packages/NAME/dist, and there were 0 registry-resolved @objectstack/* lockfile keys.
    • Item 5: .objectui-sha is untouched.
  3. The cloud seat's pointer 6257693480 is handled. The closure builds with OS_SKIP_DTS unset, in a subshell. turbo.json lists OS_SKIP_DTS in globalEnv, so a typeless cached build cannot replay. Measured: with OS_SKIP_DTS=1 and a typeless tree, the declarations go from absent to present and the build exits 0. Before the fix it exited 1 with the pin script's missing-.d.ts refusal.
  4. The test. It drives the tracked script byte for byte in a fake world, and asserts the order of closure build, pin script and install through one log. It has 5 cases, and both ablations turn it red. Reach: scripts/build-console.sh is only in the console path filter, so a PR that changes nothing but the script does not run Test Core on its pull_request leg. merge_group sets every filter true (ci.yml:122), so the test runs before any such change lands. Accepted as is.
  5. Clause-② and changeset. Clause-②: no. Nothing in any released package's files changes: spec's files has no scripts/**, the root is private, and the console dist on today's pin is byte-identical. So skip-changeset is applied, per AGENTS.md: "that label is for a diff that publishes nothing from any released package".
  6. Corrections the dev measured, recorded here. In build-console.sh, the before-state failure is ENOENT ... scandir node_modules/.cache/objectstack/client, exit 254. It is not the ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND that this seat's unlock note 6257153350 and the objectui#12957 landing note quoted from a pnpm install run outside this script.
  7. Open questions, the seat's answers.
    • Q1 (dependency drift caught late): A. The seat files a Tier H follow-up: the six non-spec manifests join ci.yml's console-dist key and the console filter. The refusal itself lands as is.
    • Q2 (drop the two injections on the new branch, saving about 907 KB of JS): not now. The extra spec text's role is unmeasured, and the startup-phase rule holds an optimisation without a pull. Revisit after the first .objectui-sha bump past objectui 749ed760 is green on main. No card.
  8. Out of scope. The seat files the class-a finding in objectstack-ai/objectui: objectstack-pin.mjs's printed remedy rebuilds typeless again under an inherited OS_SKIP_DTS. The other notes stay in the PR's acceptance notes.
  9. Landing. This is not Tier H. Once every required check is green, the seat marks the PR ready and arms auto-merge. Then the merge queue lands it.

VERDICT: ACCEPT


Generated by Claude Code

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 19, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 19, 2026 03:47
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 19, 2026 03:47
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit 2be3d7a Oct 19, 2026
45 of 46 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24306-build-console-objectstack-root branch October 19, 2026 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests tooling

Projects

None yet

2 participants