Skip to content

W-104: Fix package build and dev scripts

Type: chore · Size: M · Epic: E-setup · Target: packages/*, turbo.json and root package.json · Breaking: no · Dependency change: typescript 6 -> 7.0.2 (user approved); root engines.node >=20.19.0 -> >=26.0.0 (user approved) Depends: – Refs: AGENTS.md (Commands), turbo.json, package-rules

Goal

From a clean pnpm install, pnpm turbo run build typecheck lint test passes for the whole repo, and pnpm dev starts every package's tsdown --watch without errors. The six packages/*/tsdown.config.ts keep dts: { generator: "tsgo" } (user decision); the build is fixed by upgrading typescript from ~6.0.2 to exactly 7.0.2 (stable, ships the Go compiler tsgo) in the whole workspace. This is a dependency change; the user approved it ("go to ts7 if suitable") and research found TS 7 suitable with the conditions below. The root engines.node is also bumped from >=20.19.0 to >=26.0.0 (user decision: tsdown 0.23.4 / rolldown-plugin-dts 0.28.8 require ^22.18.0 || ^24.11.0 || >=26.0.0, and the user wants v26), in S7. S1 only confirms the TS version exists and the installed Node is >=26 (otherwise the bump would make pnpm install warn or fail with engine-strict). The root package.json is not covered by the Implementer file guards, so the main agent or user makes that edit. Aligning @types/node is out of scope (W-105).

Verified findings (from pnpm build at the root and the .turbo/turbo-build.log files):

  1. Package build (confirmed, root cause; fix = TypeScript 7.0.2). All six packages/*/tsdown.config.ts set dts: { generator: "tsgo" }. tsdown 0.23.4 / rolldown-plugin-dts 0.28.8 then needs typescript@7.0 or @typescript/native-preview, but the workspace has typescript@6.0.3 (~6.0.2) and neither is installed, so tsdown throws "TypeScript Go is not installed" (see packages/ecs-schedule/.turbo/turbo-build.log). Decision (user): keep the tsgo generator; go to TypeScript 7 if suitable. The tsdown.config.ts files stay unchanged. All package tsconfigs already have declaration and emitDeclarationOnly.
  2. Chosen: typescript exactly 7.0.2 (npm latest, stable, ships the Go compiler). tsdown 0.23.4 peer is typescript ^5||^6||^7; rolldown-plugin-dts 0.28.8 peer is ^5||^6||~7.0.0. The plugin's tsgo detection checks version.slice(0,3) === "7.0", so pin exactly 7.0.2 (not ~ or ^): a future 7.1 would break tsgo detection. With TS 7 installed the plugin's tsc generator throws, which is fine while tsgo stays.
  3. TS 7 compatibility (research): no repo code imports the typescript JS API (TS 7 does not export it). No removed or deprecated tsconfig options found (no baseUrl, node10 resolution and so on). Options in use: paths, moduleResolution: bundler, module: preserve, verbatimModuleSyntax, erasableSyntaxOnly, allowImportingTsExtensions, tsBuildInfoFile, declaration + emitDeclarationOnly. Unverified: tsc -b and tsBuildInfoFile behaviour on TS 7, and whether the apps' tsc -b failure (finding 2) changes; S3 and S5 verify.
  4. Scope: change "typescript": "~6.0.2" to "7.0.2" in the root package.json and in 12 more: packages/{ecs,ecs-devtools,ecs-prefab,ecs-relations,ecs-schedule,ecs-serialize,ui} and apps/{demo-anno,demo-ck3,demo-dnd,demo-totalwar,web}; pnpm install updates pnpm-lock.yaml. apps/web, packages/ui and the root package.json are not covered by the Implementer, DemoImplementer or Scaffolder file guards: the user or main agent makes those edits (S5).
  5. Environment: the node_modules store is out of sync with the lockfile (the lockfile has typescript@6.0.3 only; a stale typescript-eslint leftover is in node_modules but not in the lockfile). pnpm install resolves it.
  6. Fallback (not planned): @typescript/native-preview pinned to an exact dev snapshot as a devDependency of the six packages, with typescript staying at 6.0.x. Use only if S1 fails or TS 7 breaks something S3 cannot fix with a small change; it needs a new user approval.
  7. Rejected fallback: dts: true (the default tsc generator). It contradicts the user decision and throws on TS 7.
  8. W-105 later moves typescript into its build catalog; see its S1 (updated to 7.0.2).
  9. App build (silent tsc -b failure; cause not yet determined). apps/demo-dnd and apps/web fail at tsc -b and their logs contain only "Command failed", with no diagnostics. Comparison with the passing apps/demo-anno: tsconfig.json, tsconfig.app.json, tsconfig.node.json, package.json, vite.config.ts, src/App.tsx (except the title), src/main.tsx, src/ecs/world.ts and src/test/setup.ts are identical, so the difference is not config or source. demo-dnd has node_modules/.tmp/*.tsbuildinfo from an earlier success (typescript 6.0.3, no errors) and web has none, so web has never completed tsc -b (that tsbuildinfo came from TS 6.0.3; TS 7 may not read it). web additionally imports Button from @workspace/ui (packages/ui/src is type-checked through the paths alias), which demo-dnd and demo-anno do not. demo-anno, demo-ck3, demo-totalwar passed from the turbo cache, so they may fail the same way on a cache miss. The real error needs a run of tsc -b (S5).
  10. Missing lint script (confirmed). packages/ecs/package.json has no lint script; the other five packages have "lint": "oxlint", and the root .oxlintrc.json applies.
  11. Turbo config (verified sound, no change). build (^build, outputs dist/**, inputs with $TURBO_DEFAULT$), lint, typecheck, test and dev (cache: false, persistent: true) fit the packages' scripts (tsdown, tsdown --watch, oxlint, tsc --noEmit). Packages typecheck cleanly today on TypeScript 6.0.3 (tsc --noEmit with emitDeclarationOnly); on TS 7 this is unverified until S3. dev runs after the tsgo fix because tsdown --watch uses the same config.
  12. Side findings, not fixed here (see open questions). App typecheck is tsc --noEmit against a tsconfig.json with "files": [], so it checks nothing. @types/node is ^26.6.5 in packages/* and ^24 in apps/* and packages/ui (no failure observed; alignment stays in W-105). tsdown needs Node ^22.18 || ^24.11 || >=26 while the root engines says >=20.19.0: resolved here by bumping to >=26.0.0 (S7). Node-version pin check (grep of the repo outside reference/): the only pin is root package.json engines.node. There is no .nvmrc, .node-version, .tool-versions, mise.toml, volta entry or .github/workflows/ in the repo root (.npmrc is empty, so no engine-strict); packageManager is pnpm@11.25.0 (not a Node pin); README, AGENTS and docs/ do not mention a Node version; no workspace package.json has its own engines. Matches under reference/ belong to read-only sources and are ignored.

Steps

  • S1 (gate, no file changes): confirm TS 7 is usable and Node is new enough for the engines bump. Run pnpm view typescript@7.0.2 version peerDependencies (must print 7.0.2), and node -v (must be v26.0.0 or higher; this also satisfies tsdown's pnpm view tsdown@0.23.4 engines, ^22.18 || ^24.11 || >=26). Record both results in the Log. The user already approved TS 7 and the engines bump (see Log), so no further approval is needed; if the TS version is missing or node -v is below 26, stop and ask (do not run pnpm install or edit engines; the S7 bump would make pnpm install warn or fail with engine-strict). Fallback for TS: finding 1. Files: docs/work/W-104-fix-package-build-dev.md (Log only)
  • S2: set "typescript": "7.0.2" (exact, no ~ or ^) in the devDependencies of the six packages/ecs, packages/ecs-devtools, packages/ecs-prefab, packages/ecs-relations, packages/ecs-schedule and packages/ecs-serialize, then run pnpm install (never hand-edit pnpm-lock.yaml). Leave every tsdown.config.ts unchanged. Verify: pnpm --filter @workspace/ecs-schedule build exits 0 and writes dist/index.mjs and dist/index.d.mts, and pnpm --filter @workspace/ecs-schedule typecheck passes. If tsdown still reports "TypeScript Go is not installed" or pnpm install fails, record it in the Log and stop (no switch to dts: true or @typescript/native-preview without asking). tsdown's exports: true may rewrite package.json exports (for example add types): review git diff -- 'packages/*/package.json' and keep the change only if it is limited to devDependencies and exports and the ./dist/index.mjs entry is intact; files: packages/{ecs,ecs-devtools,ecs-prefab,ecs-relations,ecs-schedule,ecs-serialize}/package.json, pnpm-lock.yaml
  • S3: verify the six packages on TS 7: pnpm turbo run build typecheck --filter="./packages/*" exits 0 (this runs tsc --noEmit on TS 7 for every package), each package has dist/index.mjs and dist/index.d.mts, git diff --stat -- 'packages/*/tsdown.config.ts' is empty. If TS 7 rejects something, fix it with the smallest change in the named file only (no tsconfig option removals across packages, no test edits); if the fix needs more than that, stop and ask. Apply the exports diff review from S2; files: only the file named by the error (none if clean)
  • S4: add "lint": "oxlint" to the scripts of packages/ecs/package.json, placed after typecheck as in the other packages. Verify: pnpm --filter @workspace/ecs lint reports 0 errors (packages/ecs/src/index.ts and tests/ are linted); files: packages/ecs/package.json
  • S5: align the rest of the workspace on one TypeScript version, then diagnose the apps' tsc -b on TS 7. (1) Set "typescript": "7.0.2" in packages/ui, the root package.json, apps/demo-anno, apps/demo-ck3, apps/demo-dnd, apps/demo-totalwar and apps/web, then pnpm install; pnpm why typescript shows only 7.0.2. apps/web, packages/ui and the root package.json are outside the Implementer, DemoImplementer and Scaffolder file guards: the user or main agent makes those three edits. (2) Diagnosis, no further file changes: run pnpm --filter demo-dnd exec tsc -b --verbose, pnpm --filter web exec tsc -b --verbose, then pnpm --filter demo-anno exec tsc -b --force to see whether the passing demos fail on a real run. Record in the Log, per command: the exit code, the full error text, which project (tsconfig.app.json or tsconfig.node.json) fails, and the cause category: (a) a type error in app or packages/ui source, (b) a stale or corrupt node_modules/.tmp/*.tsbuildinfo, © a config or TypeScript 7 option problem shared by all apps, or (d) something else. If no error text appears even with --verbose, also run pnpm --filter demo-dnd exec tsc -b --listFilesOnly and record what it shows. If (1) and (2) are better as two commits, split into S5 (bump) and S5a (diagnosis); files: package.json, packages/ui/package.json, apps/{demo-anno,demo-ck3,demo-dnd,demo-totalwar,web}/package.json, pnpm-lock.yaml
  • S6: fix what the S5 diagnosis found, one small change that leaves the repo green. The fix is determined by the S5 result (if S5 found nothing failing, tick this step with a Log line and no change): (a) correct the type error in the file named by S5 (for a packages/ui error, in that file only; if the fix needs a component or dependency change, stop and ask); (b) for a stale .tsbuildinfo, delete only apps/<app>/node_modules/.tmp/*.tsbuildinfo with a non-recursive rm (the hook blocks rm -r) and rerun; this changes no tracked file, so record it in the Log and add no commit; © fix the shared TS 7 option in the affected tsconfig.*.json of every apps/* that has it (apps/web is not covered by the Implementer, DemoImplementer or Scaffolder file guards, so the user or main agent makes that edit); (d) write a new Log line with the cause and add steps S6a.. before continuing. Verify: pnpm turbo run build --filter=demo-dnd --filter=web exits 0 and pnpm --filter demo-anno exec tsc -b --force exits 0. If the S6 fix touches a dependency, this is a gate: stop for user approval; files: determined by the S5 result
  • S7: bump the root Node engine. In the root package.json change "engines": { "node": ">=20.19.0" } to ">=26.0.0"; this is the only Node pin in the repo (see finding 5; nothing else to keep consistent, and @types/node stays out of scope, W-105). The root package.json is outside the Implementer file guards: the user or main agent makes this edit. Do this only after the S1 gate passed (installed Node >=26). Verify: pnpm install finishes without an engines warning or error and leaves pnpm-lock.yaml unchanged by this step; node -v is still >=26; files: package.json
  • S8 (acceptance): run pnpm install, then pnpm turbo run build typecheck lint test --force over the whole repo (no --filter) so turbo cache hits cannot hide failures; all tasks pass. Then the dev smoke check: start pnpm --filter @workspace/ecs-schedule dev, confirm the log shows a successful first build and a watching state with no ERROR, then stop it (Ctrl+C). Confirm git status shows only the expected files: the 13 package.json files (root plus 12 workspaces: 7 packages and 5 apps; the root one carries both the typescript and engines changes), pnpm-lock.yaml, and any S3 or S6 fix (no stray dist/, since dist is git-ignored; tsdown.config.ts untouched). Changeset check: all six packages are private at 0.0.0 and the change is toolchain-only (chore), so per package-rules no changeset is added. If S2 or S3 changed published exports metadata in a package.json, add .changeset/w-104-fix-package-build-dev.md with a patch entry for the affected packages. Do not edit .changeset/config.json (the guard hooks allow only .changeset/w-* files for the Implementer); no change to it is needed

Log

  • 2026-11-02 created from user request under E-setup (Triage)
  • 2026-11-02 sized M, epic E-setup (Triage)
  • 2026-11-02 placed in Later, not prioritized; the user has not yet named the observed build/dev failures (Triage)
  • 2026-11-02 Goal replaced with verified findings; steps S1..S6 written (Planner). tsgo failure confirmed in packages/ecs-schedule/.turbo/turbo-build.log; app tsc -b cause still unknown, diagnosed in S4. No dependency change planned; @typescript/native-preview is only an alternative and needs user approval
  • 2026-11-02 moved Later -> Next, plan awaiting user approval (Triage)
  • 2026-11-02 open questions for user: app tsc -b cause unknown; typecheck of apps checks nothing
  • 2026-11-02 plan revised: keep tsgo, add @typescript/native-preview (or TS 7), dependency gate added (Planner)
  • 2026-11-02 open questions for user: confirm tsgo source and version (@typescript/native-preview recommended, S1); W-105's build catalog should include it (W-105 not edited here); app tsc -b cause unknown; typecheck of apps checks nothing; @types/node ^26 vs ^24
  • 2026-11-02 plan revised: upgrade typescript to 7.0.2, keep tsgo; user approved TS 7 if suitable, research found it suitable (Planner)
  • 2026-11-02 user approved the typescript major (6 -> 7.0.2) in this session; W-105's stop-and-ask for a typescript major is therefore satisfied for 7.0.2 (W-105 S1 and S2 updated); S1 only confirms the version exists and Node satisfies tsdown's engines
  • 2026-11-02 open questions for user: (1) engines mismatch: tsdown needs Node ^22.18 || ^24.11 || >=26, root engines says >=20.19.0; fix in a separate item? (2) apps' typecheck is tsc --noEmit on "files": [] and checks nothing; (3) @types/node ^26 in packages/* vs ^24 in apps/* and packages/ui (W-105 aligns it); (4) tsc -b failure cause unknown and may change on TS 7 (S5); (5) size: 7 steps exceeds M (4-6); keep as M or split S5..S6 into a follow-up item (Triage decides)
  • 2026-11-02 plan approved by user (Triage to set active)
  • 2026-11-02 user asked to bump root engines.node to >=26; step added (Planner)
  • 2026-11-02 plan approved, set active (Triage)
  • 2026-11-02 S1: pass. node v26.5.0, pnpm 11.25.0; typescript@7.0.2 exists (version 7.0.2, no peerDependencies, engines node >=16.20.0); tsdown@0.23.4 engines ^22.18.0 || ^24.11.0 || >=26.0.0 (satisfied by node v26.5.0), peer typescript ^5.0.0 || ^6.0.0 || ^7.0.0
  • 2026-11-02 S2: pass. typescript set to 7.0.2 in the six packages/ecs* package.json files; pnpm install updated pnpm-lock.yaml (installed typescript 7.0.2). pnpm --filter @workspace/ecs-schedule build exit 0 ("Emit types with typescript@7.0.2", dist/index.mjs and dist/index.d.mts written; WARN that TS 7 API is experimental); typecheck exit 0. git diff -- 'packages/*/package.json' shows only the typescript devDependency line; exports unchanged; tsdown.config.ts untouched
  • 2026-11-02 S3: pass. pnpm turbo run build typecheck --filter="./packages/*" exit 0 (13/13 tasks; typecheck re-run with --force: 7/7, exit 0, tsc --noEmit on TS 7.0.2). All six packages have dist/index.mjs and dist/index.d.mts; git diff --stat -- 'packages/*/tsdown.config.ts' empty; package.json diff limited to the typescript devDependency, exports unchanged. No TS 7 incompatibility found, no fix needed
  • 2026-11-02 S4: pass. Added "lint": "oxlint" after typecheck in packages/ecs/package.json. pnpm --filter @workspace/ecs lint exit 0 (oxlint prints no diagnostics; src/index.ts and tests/index.test.ts present)
  • 2026-11-02 S5: partial, not ticked. Done: typescript set to 7.0.2 in apps/demo-{anno,ck3,dnd,totalwar}/package.json; pnpm install exit 0 (needed unsandboxed store access). BLOCKED by .github/hooks/guard.mjs (outside agent scope): root package.json, packages/ui/package.json, apps/web/package.json still ~6.0.2; pnpm why typescript therefore shows 6.0.3 (zard-ecs, @workspace/ui, web) and 7.0.2 (everything else). The user or main agent must make those three edits and rerun pnpm install and the web diagnosis (the web run below used TS 6.0.3)
  • 2026-11-02 S5 diagnosis (no file changes): pnpm --filter demo-dnd exec tsc -b --verbose exit 0 on TS 7.0.2 (tsbuildinfo "generated with version 6.0.3 differs with 7.0.2", rebuilt both projects, no errors); pnpm --filter web exec tsc -b --verbose exit 0 but on TS 6.0.3 (no tsbuildinfo existed, built both projects, no errors; not valid for TS 7 until web is bumped); pnpm --filter demo-anno exec tsc -b --force exit 0, no output. No error text, failing project or category (a)-(d) reproduced: the earlier silent failure did not reproduce; --listFilesOnly not needed. pnpm --filter demo-dnd build exit 0 (vite built). Likely earlier cause: category (b), stale tsbuildinfo, unconfirmed
  • 2026-11-02 S5: pass. User edited root package.json, apps/web/package.json (7.0.2) and packages/ui/package.json (~7.0.2, tilde not exact: reported, not edited). pnpm install exit 0 ("Already up to date"); pnpm why typescript -r shows only typescript@7.0.2 ("Found 1 version of typescript"; the ui tilde range resolves to 7.0.2). pnpm --filter web exec tsc -b --verbose exit 0 on TS 7.0.2 (both projects rebuilt, tsbuildinfo 6.0.3 -> 7.0.2, no errors); pnpm --filter web build exit 0 (tsc -b && vite build, 55 modules, built in 176ms)
  • 2026-11-02 S6: nothing failing in S5, no change
  • 2026-11-02 S7: pass (verify only; user made the edit). Root engines.node is >=26.0.0; pnpm install exit 0 with no engines warning or error; pnpm-lock.yaml byte-identical before and after the install; node -v v26.5.0
  • 2026-11-02 S8: pass. pnpm install exit 0. pnpm turbo run build typecheck lint test --force exit 0 (47/47 tasks, 0 cached; first sandboxed run failed only with EROFS on /tmp mkdtemp in the tsgo dts step, passed unsandboxed). timeout 30 pnpm --filter @workspace/ecs-schedule dev: "Build start", "Emit types with typescript@7.0.2", dist/index.mjs and dist/index.d.mts written, "Rebuilt in 44ms", no ERROR (only the TS 7 experimental WARN); killed by timeout (exit 124). git status: only expected files (13 package.json incl. packages/ui now exact 7.0.2, pnpm-lock.yaml, docs, plus unrelated .gitignore and zensical.toml); tsdown.config.ts untouched; no exports change, so no changeset (.changeset/ has only config.json and README.md)
  • 2026-11-02 open question for user (resolved, packages/ui/package.json is now exact 7.0.2): packages/ui/package.json has typescript ~7.0.2 (tilde) while the item and rolldown-plugin-dts detection require an exact 7.0.2; change to 7.0.2?
  • 2026-11-02 count corrected in S8 step text and S8 Log line: 13 package.json files (root plus 12 workspaces: 7 packages, 5 apps) (Triage)
  • 2026-11-02 final Reviewer pass APPROVED; state active -> review (Triage)
  • 2026-11-02 no [manual] requirements, so no accept gate; review -> done, moved Now -> Done on board, archive trimmed (W-005 to archive) (Triage)