Skip to content

pnpm objectui:build fails on a cold tree: build-console.sh pre-builds @objectstack/client outside turbo, so spec/core dists are missing and the DTS build dies #9307

Description

@os-zhuang

Found while running the machine-lane baseline for the QA wave (#9296, round #9298). Filed as a finding, not fixed — R0 does not land code.

Summary

On a cold checkout (fresh git worktree + pnpm install, nothing built yet), pnpm objectui:build fails within ~20 s, long before it reaches the objectui console build. The console SPA never gets built, so packages/console/dist is never produced and /_console/ stays 404.

scripts/build-console.sh L136-139 pre-builds @objectstack/client by shelling straight into the package:

if [[ !-f"${CLIENT_PKG}/dist/index.mjs" ]];thenecho"→ @objectstack/client dist missing — building it first..."
(cd "$CLIENT_PKG"&& pnpm build)
fi

packages/client's own build is tsup --config ../../tsup.config.ts, which emits declarations. Its dependencies include @objectstack/core and @objectstack/spec, and it imports their subpath exports (@objectstack/spec/data, /api, /contracts, /automation, /identity, @objectstack/core/logger) — all of which resolve to ./dist/** via each package's exports map.

Invoking pnpm build in the package directory bypasses turbo, so turbo's ordering is lost. turbo.json declares:

"build": { "dependsOn": ["^build"], "outputs": ["dist/**", ...] }

^build is exactly the guarantee being skipped. On a cold tree packages/spec/dist and packages/core/dist do not exist, the subpaths resolve to nothing, and the DTS pass fails.

Environment

  • framework sha: e4e5c6e3c608b1b807c83a0d5b734f213eb1a1dd (tip of main at the time of the run)
  • .objectui-sha: 665661ab093263f39f2e660a295ea615dbcee35a
  • node v22.22.2, pnpm 10.31.0, linux x64
  • tree: fresh git worktree add --detach + a single pnpm install (exit 0, 35 s); no pnpm build had been run

Reproduction

git worktree add --detach ../objectstack-cold e4e5c6e3c608b1b807c83a0d5b734f213eb1a1dd
cd ../objectstack-cold && pnpm install
pnpm objectui:build # exit 1 in ~18s

Reproduced twice, both exit 1, both failing at the same step (@objectstack/client DTS build).

Run 1 — fully cold, canonical.packages/spec/dist and packages/core/dist both absent:

→ Using objectui source at /home/user/objectui
→ Pinned commit 665661ab0932 not present locally — fetching from origin...
Preparing worktree (detached HEAD 665661ab0)
HEAD is now at 665661ab0 fix(examples): catalog entries author the keys their renderers read (#4624) (#4630)
→ Building @object-ui/console at 665661ab0932...
→ @objectstack/client dist missing — building it first...
> @objectstack/client@17.0.0 build
> tsup --config ../../tsup.config.ts
CJS ⚡️ Build success in 90ms
ESM ⚡️ Build success in 91ms
DTS Build start
src/index.ts(3,91): error TS2307: Cannot find module '@objectstack/spec/data' or its corresponding type declarations.
src/index.ts(79,8): error TS2307: Cannot find module '@objectstack/spec/api' or its corresponding type declarations.
src/index.ts(85,8): error TS2307: Cannot find module '@objectstack/spec/contracts' or its corresponding type declarations.
src/index.ts(86,38): error TS2307: Cannot find module '@objectstack/spec/automation' or its corresponding type declarations.
src/index.ts(87,39): error TS2307: Cannot find module '@objectstack/spec/identity' or its corresponding type declarations.
src/index.ts(88,38): error TS2307: Cannot find module '@objectstack/core/logger' or its corresponding type declarations.
src/index.ts(4973,41): error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
src/index.ts(5466,8): error TS2307: Cannot find module '@objectstack/spec/api' or its corresponding type declarations.
src/index.ts(5476,8): error TS2307: Cannot find module '@objectstack/spec/contracts' or its corresponding type declarations.
Error: error occurred in dts build
DTS Build error
ELIFECYCLE Command failed with exit code 1.

Run 2 — after rm -rf packages/client/dist, exit 1 in 9 s. This one happened to run alongside a pnpm typecheck that had already emitted packages/spec/dist/**/*.js but not the .d.ts, so the same defect surfaced as TS7016 instead of TS2307 (Could not find a declaration file for module '@objectstack/spec/data'. '…/packages/spec/dist/data/index.js' implicitly has an 'any' type.). @objectstack/core/logger was TS2307 in both runs, and the DTS build failed in both. Same root cause, different observable depending on how far a concurrent job had populated the dep dists.

Second-order problem: the failure self-heals into a silent pass

The guard is [[ ! -f "${CLIENT_PKG}/dist/index.mjs" ]], but tsup writes the CJS/ESM bundles before the DTS pass. So the failed run still leaves packages/client/dist/index.mjs on disk. A plain re-run of pnpm objectui:build therefore skips the client build entirely and proceeds — shipping a client dist whose declarations were never generated.

The console only consumes the runtime JS via OBJECTSTACK_CLIENT_DIST, so the resulting bundle is probably fine at runtime — but the net effect is that the first invocation fails and the second appears to succeed, which reads as flakiness rather than a deterministic ordering bug. Anyone who hits this and just retries will conclude it was transient.

Pre-existing, not a fresh regression

git log -L 136,141:scripts/build-console.sh returns exactly one commit: b990bc258 (2026-07-02, #2513 — the change that introduced injecting this repo's client into the console bundle). That block has been unchanged for 3549 commits. This is latent, not new.

It only bites on a cold tree, which is why it has stayed invisible: any tree where pnpm build, pnpm typecheck, or a prior console build has already populated packages/spec/dist and packages/core/dist sails past the guard. That makes it a fresh-machine / fresh-CI-runner / fresh-worktree defect specifically.

Note this determines the failure mode by static reading of the script plus the one-commit -L history rather than an executed bisect — the QA wave is under a fixed container disk allowance and a second worktree + install was not available to run b990bc258 directly.

Workaround used by the wave

Build the client's dependency closure through turbo first, which honours ^build:

pnpm exec turbo run build --filter=@objectstack/client...
pnpm objectui:build

Suggested direction (not implemented here)

Replace the bare pnpm build with the turbo-mediated equivalent, so the ordering comes from the task graph that already declares it:

pnpm exec turbo run build --filter=@objectstack/client

and consider making the staleness guard test for a declaration artifact (e.g. dist/index.d.mts) rather than dist/index.mjs, so a half-built client is not mistaken for a complete one.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions