From aa4384c39aaab73fcb58eb7ab1c25db24ec780e2 Mon Sep 17 00:00:00 2001 From: chhhee10 Date: Mon, 13 Jul 2026 00:40:23 +0530 Subject: [PATCH 1/3] =?UTF-8?q?fix(pi):=20use=20`pi=20list=20--approve`=20?= =?UTF-8?q?in=20e2e=20roundtrip=20for=20pi=20=E2=89=A50.80=20project=20tru?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pi-coding-agent ≥0.80 no longer trusts project-local .pi/settings.json by default; `pi list` needs --approve to include project packages. The live roundtrip tests installed --scope project then ran bare `pi list`, so pi reported "No packages installed." — failing the install roundtrip and making the uninstall roundtrip pass vacuously. Pass --approve in both. Only runs where pi is installed (piIsAvailable gate), which is why GitHub CI stayed green while pi-equipped reviewers saw the failure. Docs: note the pi ≥0.80 project-trust model in CLAUDE.md. Changelog entry added under a new 0.0.13-beta.3 section (PR number TBD — set on PR open). Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 5 +++++ CLAUDE.md | 9 +++++++++ __tests__/e2e/hooks/pi-integration.e2e.test.ts | 12 +++++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 064f58d6..77562aa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.0.13-beta.3 — 2026-07-13 + +### Fixes +- Fix the Pi live `pi list` roundtrip e2e tests against pi-coding-agent ≥0.80: newer Pi no longer trusts project-local `.pi/settings.json` by default, so the tests now pass `pi list --approve` to include the project-scope package failproofai's installer writes (previously `pi list` printed "No packages installed." — failing the install roundtrip and making the uninstall roundtrip pass vacuously). Gated behind `piIsAvailable()`, so it only runs where `pi` is installed. (#TBD) + ## 0.0.13-beta.2 — 2026-07-10 ### Fixes diff --git a/CLAUDE.md b/CLAUDE.md index 37b7df1c..c4d4d8ab 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -195,6 +195,15 @@ v0.72.1): Note: `~/.pi/settings.json` does NOT exist on a fresh install; user-scope settings live one level deeper under `~/.pi/agent/`. +**Project-local trust (pi ≥0.80).** Newer Pi (verified against pi-coding-agent +v0.80.3) no longer trusts a **project-local** `.pi/settings.json` by default: +`pi list` gained a `--approve`/`-a` flag ("Trust project-local files for this +command"), and without it project packages are silently ignored (`pi list` +prints `No packages installed.`). User-scope packages +(`~/.pi/agent/settings.json`) are always trusted. The live `pi list` roundtrip +tests in `__tests__/e2e/hooks/pi-integration.e2e.test.ts` pass `--approve` so +they see the project-scope package failproofai's installer writes. + **Schema** is a flat string array — `{"packages": ["./relative/path", ...]}`. Each entry is a path Pi resolves relative to the directory containing `settings.json` (so `/.pi/` for project scope). For dogfood we write diff --git a/__tests__/e2e/hooks/pi-integration.e2e.test.ts b/__tests__/e2e/hooks/pi-integration.e2e.test.ts index b0c64de9..6ecf1d77 100644 --- a/__tests__/e2e/hooks/pi-integration.e2e.test.ts +++ b/__tests__/e2e/hooks/pi-integration.e2e.test.ts @@ -444,8 +444,10 @@ describePi("E2E: Pi integration — live `pi list` roundtrip (real binary)", () { cwd: env.cwd, env: { ...process.env, HOME: env.home, FAILPROOFAI_TELEMETRY_DISABLED: "1", FAILPROOFAI_BINARY_OVERRIDE: BINARY_PATH } }, ); // Run `pi list` under the fixture HOME so it doesn't pick up the - // developer's real ~/.pi/agent/settings.json state. - const result = spawnSync("pi", ["list"], { + // developer's real ~/.pi/agent/settings.json state. pi ≥0.80 no longer + // trusts project-local .pi/settings.json by default — `--approve` + // opts in per-command so `pi list` includes the project package we wrote. + const result = spawnSync("pi", ["list", "--approve"], { cwd: env.cwd, encoding: "utf8", env: { ...process.env, HOME: env.home }, @@ -469,7 +471,11 @@ describePi("E2E: Pi integration — live `pi list` roundtrip (real binary)", () `bun ${BINARY_PATH} policies --uninstall --cli pi --scope project`, { cwd: env.cwd, env: baseEnv }, ); - const result = spawnSync("pi", ["list"], { + // --approve: pi ≥0.80 needs explicit per-command trust to read the + // project-local .pi/settings.json (see the install roundtrip above). + // Without it `pi list` prints "No packages installed." unconditionally, + // making this negative assertion pass vacuously. + const result = spawnSync("pi", ["list", "--approve"], { cwd: env.cwd, encoding: "utf8", env: { ...process.env, HOME: env.home }, From ebf681f272a164f3d370f6980f870773c50024bf Mon Sep 17 00:00:00 2001 From: "Nivedit (Hermes)" Date: Mon, 13 Jul 2026 11:51:19 +0530 Subject: [PATCH 2/3] docs(changelog): set PR number for 0.0.13-beta.3 pi --approve fix (#491) Replace the placeholder (#TBD) with the actual PR number in the 0.0.13-beta.3 changelog entry, per the repo changelog convention and the CodeRabbit review comment on this PR. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77562aa9..41ff82ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 0.0.13-beta.3 — 2026-07-13 ### Fixes -- Fix the Pi live `pi list` roundtrip e2e tests against pi-coding-agent ≥0.80: newer Pi no longer trusts project-local `.pi/settings.json` by default, so the tests now pass `pi list --approve` to include the project-scope package failproofai's installer writes (previously `pi list` printed "No packages installed." — failing the install roundtrip and making the uninstall roundtrip pass vacuously). Gated behind `piIsAvailable()`, so it only runs where `pi` is installed. (#TBD) +- Fix the Pi live `pi list` roundtrip e2e tests against pi-coding-agent ≥0.80: newer Pi no longer trusts project-local `.pi/settings.json` by default, so the tests now pass `pi list --approve` to include the project-scope package failproofai's installer writes (previously `pi list` printed "No packages installed." — failing the install roundtrip and making the uninstall roundtrip pass vacuously). Gated behind `piIsAvailable()`, so it only runs where `pi` is installed. (#491) ## 0.0.13-beta.2 — 2026-07-10 From 9c6a9aa46cf6a1c8fd1c118d73d2a988be91fe21 Mon Sep 17 00:00:00 2001 From: chhhee10 Date: Mon, 13 Jul 2026 19:56:24 +0530 Subject: [PATCH 3/3] Gate pi list --approve on detected Pi version (>=0.80) Review feedback on #491: pass --approve only when the installed Pi supports it. Older Pi (<0.80) trusts project-local .pi/settings.json unconditionally and rejects the unknown flag, so the roundtrip now detects the version via `pi --version` and builds the args accordingly. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 2 +- CLAUDE.md | 6 ++-- .../e2e/hooks/pi-integration.e2e.test.ts | 34 ++++++++++++------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41ff82ea..5f36efff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 0.0.13-beta.3 — 2026-07-13 ### Fixes -- Fix the Pi live `pi list` roundtrip e2e tests against pi-coding-agent ≥0.80: newer Pi no longer trusts project-local `.pi/settings.json` by default, so the tests now pass `pi list --approve` to include the project-scope package failproofai's installer writes (previously `pi list` printed "No packages installed." — failing the install roundtrip and making the uninstall roundtrip pass vacuously). Gated behind `piIsAvailable()`, so it only runs where `pi` is installed. (#491) +- Fix the Pi live `pi list` roundtrip e2e tests against pi-coding-agent ≥0.80: newer Pi no longer trusts project-local `.pi/settings.json` by default, so the tests now detect the installed Pi version and pass `pi list --approve` on ≥0.80 (older Pi trusts project settings without the flag and would reject it) to include the project-scope package failproofai's installer writes (previously `pi list` printed "No packages installed." — failing the install roundtrip and making the uninstall roundtrip pass vacuously). Gated behind `detectPiVersion()`, so it only runs where `pi` is installed. (#491) ## 0.0.13-beta.2 — 2026-07-10 diff --git a/CLAUDE.md b/CLAUDE.md index c4d4d8ab..b5e98fd5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -201,8 +201,10 @@ v0.80.3) no longer trusts a **project-local** `.pi/settings.json` by default: command"), and without it project packages are silently ignored (`pi list` prints `No packages installed.`). User-scope packages (`~/.pi/agent/settings.json`) are always trusted. The live `pi list` roundtrip -tests in `__tests__/e2e/hooks/pi-integration.e2e.test.ts` pass `--approve` so -they see the project-scope package failproofai's installer writes. +tests in `__tests__/e2e/hooks/pi-integration.e2e.test.ts` detect the installed +Pi version and pass `--approve` only on ≥0.80 (older Pi trusts project files +unconditionally and rejects the unknown flag), so they see the project-scope +package failproofai's installer writes on either side of the cutoff. **Schema** is a flat string array — `{"packages": ["./relative/path", ...]}`. Each entry is a path Pi resolves relative to the directory containing diff --git a/__tests__/e2e/hooks/pi-integration.e2e.test.ts b/__tests__/e2e/hooks/pi-integration.e2e.test.ts index 6ecf1d77..fa630a80 100644 --- a/__tests__/e2e/hooks/pi-integration.e2e.test.ts +++ b/__tests__/e2e/hooks/pi-integration.e2e.test.ts @@ -52,15 +52,24 @@ function writeConfig(cwd: string, enabledPolicies: string[]): void { writeFileSync(configPath, JSON.stringify({ enabledPolicies }, null, 2)); } -function piIsAvailable(): boolean { +function detectPiVersion(): string | null { try { const probe = spawnSync("pi", ["--version"], { encoding: "utf8", timeout: 5_000 }); - return probe.status === 0; + if (probe.status !== 0) return null; + return probe.stdout.match(/\d+\.\d+\.\d+/)?.[0] ?? null; } catch { - return false; + return null; } } +// pi ≥0.80 no longer trusts project-local .pi/settings.json by default — +// `--approve` opts in per command. Older pi trusts it unconditionally and +// rejects the (unknown) flag, so only pass it where it exists. +function piListArgs(version: string): string[] { + const [major = 0, minor = 0] = version.split(".").map(Number); + return major > 0 || minor >= 80 ? ["list", "--approve"] : ["list"]; +} + describe("E2E: Pi integration — hook protocol (handler-only)", () => { it("tool_call → PreToolUse: block-sudo emits {permission:'deny', reason}", () => { const env = createPiEnv(); @@ -432,8 +441,9 @@ describe("E2E: Pi integration — install/uninstall", () => { // Pi actually parses the settings.json failproofai writes. // ────────────────────────────────────────────────────────────────────────── -const piPresent = piIsAvailable(); -const describePi = piPresent ? describe : describe.skip; +const piVersion = detectPiVersion(); +const describePi = piVersion ? describe : describe.skip; +const PI_LIST_ARGS = piVersion ? piListArgs(piVersion) : ["list"]; describePi("E2E: Pi integration — live `pi list` roundtrip (real binary)", () => { it("after policies --install, `pi list` shows the failproofai entry", () => { @@ -444,10 +454,10 @@ describePi("E2E: Pi integration — live `pi list` roundtrip (real binary)", () { cwd: env.cwd, env: { ...process.env, HOME: env.home, FAILPROOFAI_TELEMETRY_DISABLED: "1", FAILPROOFAI_BINARY_OVERRIDE: BINARY_PATH } }, ); // Run `pi list` under the fixture HOME so it doesn't pick up the - // developer's real ~/.pi/agent/settings.json state. pi ≥0.80 no longer - // trusts project-local .pi/settings.json by default — `--approve` - // opts in per-command so `pi list` includes the project package we wrote. - const result = spawnSync("pi", ["list", "--approve"], { + // developer's real ~/.pi/agent/settings.json state. PI_LIST_ARGS adds + // `--approve` on pi ≥0.80 so `pi list` includes the project package + // we wrote (older pi trusts project settings without it). + const result = spawnSync("pi", PI_LIST_ARGS, { cwd: env.cwd, encoding: "utf8", env: { ...process.env, HOME: env.home }, @@ -471,11 +481,11 @@ describePi("E2E: Pi integration — live `pi list` roundtrip (real binary)", () `bun ${BINARY_PATH} policies --uninstall --cli pi --scope project`, { cwd: env.cwd, env: baseEnv }, ); - // --approve: pi ≥0.80 needs explicit per-command trust to read the + // PI_LIST_ARGS: on pi ≥0.80 `pi list` needs `--approve` to read the // project-local .pi/settings.json (see the install roundtrip above). - // Without it `pi list` prints "No packages installed." unconditionally, + // Without it, it prints "No packages installed." unconditionally, // making this negative assertion pass vacuously. - const result = spawnSync("pi", ["list", "--approve"], { + const result = spawnSync("pi", PI_LIST_ARGS, { cwd: env.cwd, encoding: "utf8", env: { ...process.env, HOME: env.home },