From f68ac16137f75c6a9c5b57ba5380b1bfcfa335a3 Mon Sep 17 00:00:00 2001 From: atlas-bot Date: Sat, 3 Oct 2026 20:08:28 +0000 Subject: [PATCH] Revert "paperclip: keep per-run managed AI home out of ACPX session fingerprint (REN-563)" This reverts #1111 (3f3305cf). With the fingerprint stable, the ACPX engine now resumes the previous Claude session, but the Claude transcript lives in the per-run CLAUDE_CONFIG_DIR, which the server deletes after each run. acpx uses resumePolicy same-session-only for persistent sessions, so the turn fails ("Persistent ACP session ... could not be resumed: Resource not found") instead of falling back to a fresh session (run 12a2a911). Co-Authored-By: Claude Opus 5.5 --- ci/images/paperclip/README.md | 9 --- ...en-563-acpx-managed-home-fingerprint.patch | 74 ------------------- 2 files changed, 83 deletions(-) delete mode 100644 ci/images/paperclip/ren-563-acpx-managed-home-fingerprint.patch diff --git a/ci/images/paperclip/README.md b/ci/images/paperclip/README.md index 3131c9bf..a6bcb8c1 100644 --- a/ci/images/paperclip/README.md +++ b/ci/images/paperclip/README.md @@ -29,15 +29,6 @@ every run, local and in k8s pods. The value is not part of the session fingerprint, so resume is unchanged. No upstream PR yet; rename the patch to `pr--…` once there is one. -Claude sessions never resume with a managed AI connection (REN-563): the -server gives every run a fresh temp home (`HOME`, `XDG_*`, -`CLAUDE_CONFIG_DIR`, …), and the ACPX engine hashed those paths into the -session fingerprint (`adapterEnvHash`), so a resume never matched. -`ren-563-acpx-managed-home-fingerprint.patch` hashes a fixed placeholder for -those paths when they follow the managed layout, as the server's own -fingerprint already does (`managedAiSessionFingerprintConfig`). No upstream -PR yet. - ## Files - `upstream.env` — pinned commit and the version string stamped into the diff --git a/ci/images/paperclip/ren-563-acpx-managed-home-fingerprint.patch b/ci/images/paperclip/ren-563-acpx-managed-home-fingerprint.patch deleted file mode 100644 index acad1062..00000000 --- a/ci/images/paperclip/ren-563-acpx-managed-home-fingerprint.patch +++ /dev/null @@ -1,74 +0,0 @@ -diff --git a/packages/adapter-utils/src/acpx-engine/execute.test.ts b/packages/adapter-utils/src/acpx-engine/execute.test.ts -index 112e2eb..67ea043 100644 ---- a/packages/adapter-utils/src/acpx-engine/execute.test.ts -+++ b/packages/adapter-utils/src/acpx-engine/execute.test.ts -@@ -1857,6 +1857,37 @@ describe("shared ACPX engine runtime behavior", () => { - expect(changed.result.sessionParams?.configFingerprint).not.toBe(first.result.sessionParams?.configFingerprint); - }); - -+ it("keeps the per-run managed AI home out of session identity", async () => { -+ const root = await makeTempRoot(); -+ const config = { agentCommand: "node ./fake-acp.js", stateDir: path.join(root, "state") }; -+ async function withManagedHome(home: string, claudeConfigDir = path.join(home, "provider")) { -+ return runExecutor({ -+ ...config, -+ managedAiConnection: { identity: "grant-1:shared:abc" }, -+ env: { -+ HOME: home, -+ XDG_CONFIG_HOME: path.join(home, "config"), -+ XDG_DATA_HOME: path.join(home, "data"), -+ CODEX_HOME: path.join(home, "provider"), -+ GROK_HOME: path.join(home, "provider"), -+ CLAUDE_CONFIG_DIR: claudeConfigDir, -+ }, -+ }, { context: { taskId: "issue-1" } }); -+ } -+ const fp = (run: Awaited>) => run.result.sessionParams?.configFingerprint; -+ const first = await withManagedHome(path.join(root, "paperclip-ai-a")); -+ const second = await withManagedHome(path.join(root, "paperclip-ai-b")); -+ const overridden = await withManagedHome(path.join(root, "paperclip-ai-c"), "/custom/claude"); -+ expect(fp(first)).toBeTypeOf("string"); -+ expect(fp(second)).toBe(fp(first)); -+ expect(fp(overridden)).not.toBe(fp(first)); -+ -+ // Without a managed AI connection the same paths are user config and still count. -+ const plainA = await runExecutor({ ...config, env: { HOME: path.join(root, "home-a") } }, { context: { taskId: "issue-1" } }); -+ const plainB = await runExecutor({ ...config, env: { HOME: path.join(root, "home-b") } }, { context: { taskId: "issue-1" } }); -+ expect(fp(plainB)).not.toBe(fp(plainA)); -+ }); -+ - it("busts the session fingerprint when a stable configured PAPERCLIP_* value rotates", async () => { - const root = await makeTempRoot(); - const stateDir = path.join(root, "state"); -diff --git a/packages/adapter-utils/src/acpx-engine/execute.ts b/packages/adapter-utils/src/acpx-engine/execute.ts -index e5681b8..b1d0207 100644 ---- a/packages/adapter-utils/src/acpx-engine/execute.ts -+++ b/packages/adapter-utils/src/acpx-engine/execute.ts -@@ -2027,6 +2027,27 @@ async function buildRuntime(input: { - // are absent from tempKeysApplied and keep their compatibility protection. - if (!scratchKeys.has(key) || value !== scratch.dir) resolvedAdapterEnv[key] = value; - } -+ // A managed AI connection gets a fresh server-made temp home on every run -+ // (HOME, XDG_*, and the provider home under it). Hash a fixed placeholder for -+ // those paths so the session can resume; the server's own fingerprint does -+ // the same (managedAiSessionFingerprintConfig). Values that differ from the -+ // managed layout are real overrides and stay in the hash. -+ const managedHome = config.managedAiConnection ? resolvedAdapterEnv.HOME : undefined; -+ if (managedHome) { -+ const managedLayout: Record = { -+ HOME: "", -+ XDG_CONFIG_HOME: "config", -+ XDG_DATA_HOME: "data", -+ CODEX_HOME: "provider", -+ GROK_HOME: "provider", -+ CLAUDE_CONFIG_DIR: "provider", -+ }; -+ for (const [key, sub] of Object.entries(managedLayout)) { -+ if (resolvedAdapterEnv[key] === (sub ? path.join(managedHome, sub) : managedHome)) { -+ resolvedAdapterEnv[key] = sub ? `/${sub}` : ""; -+ } -+ } -+ } - if (authToken) env.PAPERCLIP_API_KEY = authToken; - // For the claude agent, set model via ANTHROPIC_MODEL at startup rather than - // via session/set_config_option — the ACP server's set_config_option handler -- 2.54.0