Skip to content

fix(pi): isolate the test suite from the host config directory - #219

Merged
ualtinok merged 1 commit into
cortexkit:mainfrom
iceteaSA:fix/pi-test-config-isolation
Sep 16, 2026
Merged

ualtinok merged 1 commit into
cortexkit:mainfrom
iceteaSA:fix/pi-test-config-isolation

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

packages/pi has no test preload, so its suite inherits the host's config directories. One test reads the operator's live ~/.config/opencode/anthropic-auth.json and passes because of what happens to be in it — on a machine without that file it fails.

That is CI, and anyone who clones the repo.

Evidence

packages/pi/src/tests/stream.test.tssticky-balanced preserves the strict API fallback gate after OAuth exhaustion, varying one variable at a time on a developer machine:

baseline                            1 pass 0 fail
HOME=<empty tmpdir>                 0 pass 1 fail
XDG_CONFIG_HOME=<empty tmpdir>      0 pass 1 fail
OPENCODE_CONFIG_DIR=<empty tmpdir>  0 pass 1 fail
PI_AGENT_DIR=<empty tmpdir>         1 pass 0 fail

Pi's own agent dir is not the dependency. The OpenCode config dir is: getConfigDir() in packages/core/src/accounts.ts resolves $OPENCODE_CONFIG_DIR, else $XDG_CONFIG_HOME/opencode, else ~/.config/opencode, and Pi tests reach core code that falls back to it.

Pre-existing, and not caused by any recent change — a clean detached worktree at main fails identically under an empty HOME. packages/core and packages/opencode each received this preload on 2026-09-07; packages/pi was missed.

Change

packages/pi/bunfig.toml + packages/pi/src/tests/setup.ts, mirroring the existing two setups: a per-test mkdtemp, host path variables deleted and redirected into it, temp dir removed.

Seven variables, chosen by reading the call graph rather than by guessing — OPENCODE_CONFIG_DIR, OPENCODE_ANTHROPIC_AUTH_FILE, OPENCODE_ANTHROPIC_AUTH_STATE_FILE (accounts.ts:514-528), PI_AGENT_DIR (paths.ts:7), and the three PI_ANTHROPIC_AUTH_* state vars (stream.ts:138,157) which are reset rather than pinned since tests set them individually.

The preload deliberately does not restore the host's original values. The first version did, in afterEach — but Bun runs afterEach inner-file-first and the preload's hook last, so a per-file afterAll saw host values again, and commands.test.ts:82 asserts OPENCODE_ANTHROPIC_AUTH_STATE_FILE is unset. Any host exporting that variable got a red suite from the isolation meant to make it deterministic:

with OPENCODE_ANTHROPIC_AUTH_STATE_FILE set   114 pass  1 fail
after the fix                                 114 pass  0 fail

Restoring is unnecessary anyway: this is in-process mutation that cannot reach the operator's shell, and every beforeEach re-isolates. Dropping the snapshot removed a second hazard with it — its entries were only cleared in afterEach, so a test that threw could leave a stale value to restore over a later test's isolation.

Verification

before (main, empty env)   0 pass  1 fail
after  (empty env)       114 pass  0 fail
after  (normal env)      114 pass  0 fail

Empty env means HOME, XDG_CONFIG_HOME, OPENCODE_CONFIG_DIR and PI_AGENT_DIR all pointed at a fresh temp dir. The before/after pair is what shows the preload is load-bearing rather than incidental.

core 188/188 · opencode 1869/1869 · pi 114/114 · typecheck, format, biome clean.

No production file and no test assertion was modified.

Note

OPENCODE_CONFIG_DIR alone was not sufficient in practice — the storage and state file variables had to be pinned too. The isolation is proven by the suite passing in a clean environment; the precise call site that still reached the host path was not pinned down by reading, and is recorded here rather than left implied.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Isolates the packages/pi test suite from the host's config directories so tests pass in CI and on machines without a live OpenCode config.

  • Adds a preload that redirects the OpenCode and Pi config path variables into a per-test temp dir without restoring host values afterward — the restore re-exposed host config between tests, and the env mutation is in-process only, so it can't reach the operator's shell.
  • Mirrors the existing isolation in packages/core and packages/opencode; packages/pi was missed when they received theirs.

Written for commit 9f59e05. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files

Architecture diagram
sequenceDiagram
    participant Bun as Bun Test Runner
    participant Preload as Pi Test Preload (setup.ts)
    participant TestSuite as Pi Test Suite
    participant Core as Core Modules (accounts.ts, paths.ts)
    participant Env as Process Env Vars
    participant FS as Temp Filesystem

    Note over Bun,FS: Current State: Isolated Pi Test Runtime

    Bun->>Preload: Load preload (bunfig.toml)
    Preload->>FS: mkdtemp() per-test temp dir
    Preload->>Env: Capture host OPENCODE_CONFIG_DIR, OPENCODE_ANTHROPIC_AUTH_FILE,<br/>OPENCODE_ANTHROPIC_AUTH_STATE_FILE, PI_AGENT_DIR,<br/>PI_ANTHROPIC_AUTH_* (store originals)
    
    loop Each Test Case
        Preload->>Env: Redirect all captured vars into test temp dir<br/>(pin OPENCODE_* + PI_AGENT_DIR, reset PI_ANTHROPIC_AUTH_*)
        Env-->>TestSuite: Clean isolated config paths
        TestSuite->>TestSuite: Run test (e.g. stream.test.ts)
        
        alt Test reads config paths
            TestSuite->>Core: getConfigDir() / auth file resolution
            Core->>Env: Read OPENCODE_CONFIG_DIR (pinned to temp)
            Env-->>Core: Temp path (not host)
            Core-->>TestSuite: Isolated config data (or missing file → controlled failure)
            
            alt PI_ANTHROPIC_AUTH_* state vars used
                TestSuite->>Env: Set individual state vars per test
                Env-->>TestSuite: Test-specific state
            end
        end
        
        TestSuite-->>Preload: Test complete
        Preload->>Env: Restore original host env vars<br/>(delete if absent, else restore saved value)
        Preload->>FS: rm temp dir (cleanup)
    end
    
    Note over Preload: Restoring originals ensures host env untouched<br/>after suite exits (no residual pollution).
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/pi/src/tests/setup.ts Outdated
`packages/pi` had no test preload, so its suite inherited the host's
config directories. One test read the operator's live
`~/.config/opencode/anthropic-auth.json` and passed because of what
happened to be in it — on a machine without that file it failed, which
is CI and anyone who clones the repo.

Varying one variable at a time against the failing test on a developer
machine: unsetting `HOME`, `XDG_CONFIG_HOME`, or `OPENCODE_CONFIG_DIR`
each turned it red, while `PI_AGENT_DIR` did not. Pi's own agent dir is
not the dependency; `getConfigDir()` in `packages/core/src/accounts.ts`
is, and Pi tests reach core code that falls back to it.

Pre-existing rather than caused by a recent change — a clean detached
worktree at `main` fails identically under an empty `HOME`.
`packages/core` and `packages/opencode` each received this preload on
2026-09-07; `packages/pi` was missed.

The preload deliberately does not restore the host's original values.
Restoring them in `afterEach` re-exposed host config mid-run: Bun runs
`afterEach` inner-file-first and the preload's hook last, so a per-file
`afterAll` saw them again — and `commands.test.ts` asserts
`OPENCODE_ANTHROPIC_AUTH_STATE_FILE` is unset. Restoring is also
unnecessary, since this is in-process mutation that cannot reach the
operator's shell and every `beforeEach` re-isolates.
@iceteaSA

Copy link
Copy Markdown
Contributor Author

Stack, for merge order. Four PRs came out of chasing three separate CI failures to root cause; each fix is in its own PR rather than folded into the custody change.

#219  fix(pi): isolate the test suite from the host config   <- base
 ├── #228  test(custody): lock tests independent of wall clock
 ├── #229  fix(recovery): deliver the restoration notice
 └── #218  fix(custody): carry the vault credential id        <- stacked on #228

Suggested order: #219#228#229#218. Each rebases cleanly on the previous.

This PR: base of the stack. Its own check is red on bridges back to a stale Opus cache…, which is a pre-existing failure on main, not something this PR introduces — it is fixed in #229. Merging this first does not regress anything.

The three CI failures were unrelated to each other: a Pi suite reading the operator's live config (#219), manifest-lock tests racing a wall-clock TTL (#228), and a genuine product bug where the model-restored notice never reaches a session with no TUI attached (#229).

@ualtinok
ualtinok merged commit f725a34 into cortexkit:main Sep 16, 2026
4 of 5 checks passed
@iceteaSA
iceteaSA deleted the fix/pi-test-config-isolation branch September 16, 2026 22:25
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.

2 participants