Skip to content

fix(providers/pi): install PI_PACKAGE_DIR shim so Pi workflows run in a compiled binary#1360

Merged
Wirasm merged 1 commit intodevfrom
fix/pi-runtime-shim
Apr 22, 2026
Merged

fix(providers/pi): install PI_PACKAGE_DIR shim so Pi workflows run in a compiled binary#1360
Wirasm merged 1 commit intodevfrom
fix/pi-runtime-shim

Conversation

@Wirasm
Copy link
Copy Markdown
Collaborator

@Wirasm Wirasm commented Apr 22, 2026

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

  • `bun run validate` green
  • Manual: `bun build --compile --minify --target=bun-darwin-arm64` + `archon workflow run ` returns a response
  • Before cutting next release: `/test-release` should run a Pi workflow end-to-end, not just Claude (separate follow-up)

Summary by CodeRabbit

  • Bug Fixes

    • Fixed Pi provider initialization by ensuring proper package directory configuration before dynamic imports in compiled environments.
  • Tests

    • Added regression test to verify Pi provider's package directory setup occurs before SDK initialization.

… 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.
@Wirasm Wirasm merged commit 0e9f1c8 into dev Apr 22, 2026
3 of 4 checks passed
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51ecb0e9-2644-4abc-9f8d-a1e8eb1e3d39

📥 Commits

Reviewing files that changed from the base of the PR and between 6f86402 and 00f416f.

📒 Files selected for processing (2)
  • packages/providers/src/community/pi/provider.test.ts
  • packages/providers/src/community/pi/provider.ts

📝 Walkthrough

Walkthrough

The changes introduce a shim directory mechanism that initializes process.env.PI_PACKAGE_DIR before Pi SDK dynamic imports occur, enabling proper package.json resolution in compiled environments. A regression test validates this initialization ordering.

Changes

Cohort / File(s) Summary
Pi Provider Implementation
packages/providers/src/community/pi/provider.ts
Added Node.js filesystem/os/path imports and ensurePiPackageDirShim() helper function that creates a temporary archon-pi-shim directory with a minimal package.json stub and sets PI_PACKAGE_DIR environment variable. Updated sendQuery() to invoke this helper before dynamic Pi SDK imports.
Pi Provider Tests
packages/providers/src/community/pi/provider.test.ts
Added regression test that verifies PI_PACKAGE_DIR environment variable is initialized and includes archon-pi-shim when sendQuery() is invoked, ensuring the shim setup occurs before Pi SDK dynamic imports.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 A shim appears, cozy and small,
In temp directories standing tall,
Package paths now have a home,
No more imports left to roam,
Pi SDK hops with glee! 🥕

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pi-runtime-shim

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant