fix(providers/pi): install PI_PACKAGE_DIR shim so Pi workflows run in a compiled binary#1360
fix(providers/pi): install PI_PACKAGE_DIR shim so Pi workflows run in a compiled binary#1360
Conversation
… a compiled binary
v0.3.9 made Pi boot-safe: lazy-loading its imports meant `archon version`
no longer crashed on `@mariozechner/pi-coding-agent/dist/config.js`'s
module-init `readFileSync(getPackageJsonPath())`. That's what the
`provider-lazy-load.test.ts` regression test guards.
The fix was only half the problem though. When a Pi workflow actually
runs, sendQuery() triggers the dynamic import — and Pi's config.js
module-init fires then, hitting the exact same ENOENT on
`dirname(process.execPath)/package.json`. Discovered by running
`archon workflow run test-pi` against a locally-compiled 0.3.9 binary:
[main] Failed: ENOENT: no such file or directory,
open '/private/tmp/package.json'
at readFileSync (unknown)
at <anonymous> (/$bunfs/root/archon-providertest:184:7889)
at init_config
Boot-safe ≠ runtime-safe. The `/test-release` run for 0.3.9 passed
because it only exercised `archon-assist` (Claude); Pi was never
actually invoked on the released binary.
Fix: before the dynamic `import('@mariozechner/pi-coding-agent')` in
sendQuery, install a PI_PACKAGE_DIR shim. Pi's config.js checks
`process.env.PI_PACKAGE_DIR` first in its `getPackageDir()` and
short-circuits the `dirname(process.execPath)` walk. We write a
minimal `{name, version, piConfig:{}}` stub to
`tmpdir()/archon-pi-shim/package.json` (idempotent — existsSync check)
and set the env var. Pi only reads `piConfig.name`, `piConfig.configDir`,
and `version` from that file, all optional, so the stub surface is
genuinely minimal.
Localized to PiProvider: no global state, no mutation of any shared
config, no upstream fork. Claude and Codex providers are unaffected
(their SDKs don't have this class of module-init side effect).
Verified end-to-end: built a compiled archon binary with this patch,
ran `archon workflow run test-pi --no-worktree` (Pi workflow with
model `anthropic/claude-haiku-4-5`), got a clean response. Before the
patch, same binary crashed at `dag_node_started` with the ENOENT above.
Regression test added: asserts `PI_PACKAGE_DIR` is set after sendQuery
hits even its fast-fail "no model" path. Together with the existing
`provider-lazy-load.test.ts` (boot-safe) this covers both halves.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe changes introduce a shim directory mechanism that initializes Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
v0.3.9 made Pi boot-safe (lazy-load moved the crash out of archon startup) but not runtime-safe: the moment
PiProvider.sendQuery()triggers the dynamic import, Pi's `config.js` fires its own module-init `readFileSync(getPackageJsonPath())` and we hit the exact same ENOENT the lazy-load was supposed to prevent. Discovered today by running an actual Pi workflow against a locally-compiled 0.3.9 binary; `/test-release` missed this because it only exercised `archon-assist` (Claude).Boot-safe ≠ runtime-safe. The `provider-lazy-load.test.ts` regression test I added in #1355 covers only the former — this PR covers the latter.
Change
Before the dynamic `import('@mariozechner/pi-coding-agent')` in `sendQuery()`, install a `PI_PACKAGE_DIR` shim. Pi's `config.js` short-circuits the `dirname(process.execPath)` walk when that env var is set, so it reads from our stub instead. The stub is a minimal `{name, version, piConfig:{}}` JSON written once to `tmpdir()/archon-pi-shim/package.json` (idempotent existsSync check). Pi only reads `piConfig.name`, `piConfig.configDir`, and `version` from that file — all optional — so the surface is genuinely minimal.
Localized to `PiProvider`. No global state, no upstream fork, no mutation of shared config. Claude and Codex providers are unaffected (their SDKs don't have this class of init-time side effect).
E2E verification
Before patch, on a compiled 0.3.9 binary:
```
[main] Failed: ENOENT: no such file or directory,
open '/private/tmp/package.json'
at readFileSync (unknown)
at init_config
```
After patch, same compiled binary running `archon workflow run test-pi --no-worktree` against a workflow with `provider: pi` + `model: anthropic/claude-haiku-4-5`:
```
{"module":"provider.pi","piProvider":"anthropic","modelId":"claude-haiku-4-5","msg":"pi.session_started"}
hi
Workflow completed successfully.
```
Regression test
Added a unit test that deletes `process.env.PI_PACKAGE_DIR`, calls `sendQuery()` via the fast-fail "no model" path (returns before any Pi SDK work), and asserts the shim still ran and set the env var. Paired with the existing boot-safety test, both halves of the bug are now covered.
Test plan
Summary by CodeRabbit
Bug Fixes
Tests