pool: pinned Node 22.11.0 runtime is stale — new pi crashes on missing zstd API, and no path re-realizes the runtime #38
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom: after upgrading the pi harness through wraptool, pi crashes on start with a missing zlib zstd function, while npx @earendil-works/pi-coding-agent works fine. Reinstalling via wraptool abandons after confirming the latest version is already installed, and there is no --force. Root cause: internal/pool/orchestrator.go line 83 pins defaultNodeVersion to 22.11.0, a pool-wide constant every npm-sourced harness resolves against, and the generation bundles that exact nodejs.org tarball as its runtime. Node's zlib gained the zstd API only in v22.15.0 backport / v23.8.0 mainline, so a recent pi release that calls it crashes on the bundled 22.11.0. Three compounding facts, confirmed in code: 1. upgrading pi cannot help, since UpdateHarness re-resolves the package but passes the same constant at orchestrator.go line 851, so a new generation ships new pi plus the same old node, and the constant's own doc comment already records per-descriptor runtime requirements and latest-patch-on-the-line resolution as known Phase-2 follow-ups. 2. reinstall short-circuits by design, since Decide sees lock version equal to latest and recipe_revision matching, and the node version is not folded into npmRecipeRevision, so nothing ever marks the lock stale. 3. there is no user-side workaround, since deleting the lock or rolling back still resolves against the same constant. Proposed immediate fix: bump defaultNodeVersion to 22.23.2 and bump recipeFormatVersion from 3 to 4, the designed self-heal path, so every existing lock mismatches and the next harness update or sync re-resolves and realizes with the new node. Proposed Phase 2: descriptor-level node runtime requirement, resolve the latest patch on the line at refresh time, and fold the resolved node version into recipe_revision so future runtime bumps self-heal without a format bump. Optional UX: harness install --force to rebuild at an unchanged version. Diagnosed with Claude Code. https://claude.ai/code/session_01NA8scMaA7UJSmBzWsPpPYc
Design for the permanent fix is now in the repo: design/harness/node-runtime-tracking.md on branch fix/node-runtime-bump, PR #39. Summary: node versions become observed, quarantined, locked inputs — the same lifecycle package versions already have. 1. Pin the line, not the patch: Descriptor gains NodeLine, the default line is a pool-wide var, and moving majors stays a deliberate code change. 2. Observe at refresh: fetch the nodejs.org dist index, record the line's newest patch as a node-line observation with write-once first_seen_at, so node releases get the same minimum_release_age and minimum_observation_age quarantine as package versions. 3. Resolve the eligible patch at update and lock it — the lock shape is unchanged, Runtime already records version and artifacts. 4. Fold the resolved node version into recipe_revision, so a runtime bump self-heals through the standard Decide path with no more format bumps. 5. Check the packument's engines.node against the resolved node at update time and fail actionably on mismatch — the check that would have caught this bug at update instead of at pi startup. Result: routine node bumps flow through refresh, update and sync with zero code changes — a patch is only needed to change lines, like 22 to 24. Immediate fix meanwhile: PR #39 bumps defaultNodeVersion to 22.23.2 and recipeFormatVersion to 4 so every existing lock self-heals on the next harness update. Work packages WP1 observe+resolve and WP2 lock+recipe+engines are defined in the design note — WP1 is in progress.