paperclip: resume Claude sessions across runs with a managed AI home (REN-563) #1115

Merged
Ghost merged 1 commits from atlas/ren-563-claude-session-resume into master 2026-10-03 20:43:51 +00:00

Fix-forward of #1111 (reverted in #1113). REN-563.

What changed

New image patch ci/images/paperclip/ren-563-acpx-claude-session-resume.patch (ACPX engine, execute.ts), plus a README note:

  1. With a managed AI connection on a local run, the engine hashes a placeholder for the per-run temp home paths (HOME, XDG_CONFIG_HOME, XDG_DATA_HOME, CODEX_HOME, GROK_HOME, CLAUDE_CONFIG_DIR) when they follow the managed layout. This is the #1111 change, now limited to local runs.
  2. For Claude, when CLAUDE_CONFIG_DIR is the managed one, the engine links CLAUDE_CONFIG_DIR/projects (the transcripts) to <stateDir>/claude-projects, which is on the Paperclip volume. The server's rm -r of the temp home removes the link, not the target.

Why

#1111 made the fingerprint stable, so the engine resumed the old session. But the transcript lived in the deleted temp home, and acpx same-session-only failed the turn with "Resource not found". Both halves have to ship together.

How verified (lint only, not in the cluster)

  • All 4 patches apply with prepare.sh.
  • execute.test.ts: 199/199 pass. The extended test checks a stable fingerprint across two homes, the link in both homes, and no link for an overridden CLAUDE_CONFIG_DIR. It fails without the patch.
  • tsc --noEmit for adapter-utils is clean.
  • Node fs.rm(home, {recursive:true}) leaves the link target in place.
  • After rollout: two Claude runs on one card. Run 2 must resume run 1's session without a "could not be resumed" error.

Impact / revert

Merging restarts the Paperclip server. Home Assistant is not touched. The first run of each Claude agent after rollout starts a fresh session, because the hash inputs changed. Remote (sandbox) runs are unchanged.
Revert: revert this PR. Then the leftover <stateDir>/claude-projects dirs can be deleted.

🤖 Generated with Claude Code

Fix-forward of #1111 (reverted in #1113). REN-563. ## What changed New image patch `ci/images/paperclip/ren-563-acpx-claude-session-resume.patch` (ACPX engine, `execute.ts`), plus a README note: 1. With a managed AI connection on a **local** run, the engine hashes a placeholder for the per-run temp home paths (`HOME`, `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `CODEX_HOME`, `GROK_HOME`, `CLAUDE_CONFIG_DIR`) when they follow the managed layout. This is the #1111 change, now limited to local runs. 2. For Claude, when `CLAUDE_CONFIG_DIR` is the managed one, the engine links `CLAUDE_CONFIG_DIR/projects` (the transcripts) to `<stateDir>/claude-projects`, which is on the Paperclip volume. The server's `rm -r` of the temp home removes the link, not the target. ## Why #1111 made the fingerprint stable, so the engine resumed the old session. But the transcript lived in the deleted temp home, and acpx `same-session-only` failed the turn with "Resource not found". Both halves have to ship together. ## How verified (lint only, not in the cluster) - All 4 patches apply with `prepare.sh`. - `execute.test.ts`: 199/199 pass. The extended test checks a stable fingerprint across two homes, the link in both homes, and no link for an overridden `CLAUDE_CONFIG_DIR`. It fails without the patch. - `tsc --noEmit` for adapter-utils is clean. - Node `fs.rm(home, {recursive:true})` leaves the link target in place. - After rollout: two Claude runs on one card. Run 2 must resume run 1's session without a "could not be resumed" error. ## Impact / revert Merging restarts the Paperclip server. Home Assistant is not touched. The first run of each Claude agent after rollout starts a fresh session, because the hash inputs changed. Remote (sandbox) runs are unchanged. Revert: revert this PR. Then the leftover `<stateDir>/claude-projects` dirs can be deleted. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Ghost added 1 commit 2026-10-03 20:41:21 +00:00
paperclip: resume Claude sessions across runs with a managed AI home (REN-563)
ci/woodpecker/pr/pr-review Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
8d894e466e
Stable session fingerprint for the per-run managed AI home paths, plus a
persistent CLAUDE_CONFIG_DIR/projects so the resumed session finds its
transcript. Local runs only. Fix-forward of #1111 (reverted in #1113).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ghost approved these changes 2026-10-03 20:43:50 +00:00
Ghost left a comment

Approved on 8d894e466e. Checked: prepare.sh applies all 4 patches on upstream a027f76; execute.test.ts 199/199 pass; the new test fails without the execute.ts hunk (fingerprint 339afb9b vs 98cd7252); placeholder layout equals server managedAiHomeEnvironment; resolvedAdapterEnv only feeds adapterEnvHash, not the spawned env; stateDir is per agent on the Paperclip volume; prod Claude runs are local with CLAUDE_CONFIG_DIR=/provider. Revert: revert this PR.

Approved on 8d894e466ef92cae350a8f7a1b1ed16a7d6e47a2. Checked: prepare.sh applies all 4 patches on upstream a027f76; execute.test.ts 199/199 pass; the new test fails without the execute.ts hunk (fingerprint 339afb9b vs 98cd7252); placeholder layout equals server managedAiHomeEnvironment; resolvedAdapterEnv only feeds adapterEnvHash, not the spawned env; stateDir is per agent on the Paperclip volume; prod Claude runs are local with CLAUDE_CONFIG_DIR=<tmp home>/provider. Revert: revert this PR.
Ghost merged commit b5f54ad069 into master 2026-10-03 20:43:51 +00:00
review-bot left a comment
Member

🤖 Automated review — model claude-sonnet-5 (complexity: normal), commit 8d894e466ef92cae350a8f7a1b1ed16a7d6e47a2.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

This PR vendors a new patch (ren-563-acpx-claude-session-resume.patch) for the upstream Paperclip ACPX engine, plus a README note. It (1) redacts the per-run managed-AI temp-home paths (HOME, XDG_*, CODEX_HOME, GROK_HOME, CLAUDE_CONFIG_DIR) from the session fingerprint on local runs so the fingerprint is stable across runs, and (2) symlinks CLAUDE_CONFIG_DIR/projects into a persistent <stateDir>/claude-projects so a Claude session's transcript survives the per-run temp home being deleted. This is a fix-forward of #1111 (reverted in #1113), explicitly shipping both halves together this time. The logic is self-consistent and the new test (execute.test.ts) covers the key matrix (default managed layout, a second run resolving to the same fingerprint, an overridden CLAUDE_CONFIG_DIR keeping a distinct fingerprint and no symlink, and the non-managed case). No blocking issues found, but there's one real asymmetry worth checking before rollout (see Suggestions).

🟡 Suggestions

  • Fingerprint stabilization covers three agents, transcript persistence covers one. ci/images/paperclip/ren-563-acpx-claude-session-resume.patch:96-103 redacts CODEX_HOME and GROK_HOME from the fingerprint hash exactly like CLAUDE_CONFIG_DIR, but only Claude gets the matching persistence fix (linkManagedClaudeTranscripts, applied only when resolvedAdapterEnv.CLAUDE_CONFIG_DIR === "<managed-ai-home>/provider", patch lines 119-125). If Codex or Grok also keep local session/transcript state under their home dir the way Claude does under CLAUDE_CONFIG_DIR/projects, this change would make their fingerprints falsely stable too — reproducing the exact #1111 failure mode ("Resource not found" / "could not be resumed") for those agents instead of fixing it. The inline comment says "the server's own fingerprint does the same," which suggests this pattern may already be safe in production for those two agents, but that's not something I can confirm from this repo (the Codex/Grok ACP engines aren't vendored here). Worth explicitly confirming Codex/Grok resume is unaffected (or that they don't rely on local transcript persistence) before/after rollout, since the PR's own verification plan only exercises a Claude agent.
  • No fallback if linking fails. linkManagedClaudeTranscripts (patch lines 64-77) has no error handling; an fs.mkdir/fs.symlink failure (e.g. a permissions or disk issue on stateDir) will throw and presumably fail the whole run, not just the resume-convenience feature. This matches the existing style in this file (e.g. writePaperclipClaudeSettings also doesn't guard its writes), so it's not a new pattern, but it does mean a storage hiccup turns a "can't resume" situation into "can't run at all."
  • Minor TOCTOU on the symlink. linkManagedClaudeTranscripts reads the existing link, then conditionally removes and recreates it (lines 71-75). If two runs ever touched the same freshly-created claudeConfigDir concurrently this could race (second fs.symlink failing with EEXIST), but per the PR description runs are sequential per card, so this is likely not reachable in practice.

Tests

The new test (execute.test.ts:1865-1903 in the vendored patch) is well-targeted: it checks a stable fingerprint across two managed homes, a different fingerprint when CLAUDE_CONFIG_DIR is overridden, that both default homes' provider/projects symlink to the same <stateDir>/claude-projects, that the overridden config dir gets no symlink, and that the non-managed-connection case is unaffected. Note that this repo's own CI (.woodpecker/build-paperclip.yml) only runs git apply via prepare.sh and then a Docker/kaniko build of the production target — it does not execute the upstream test suite, so the "199/199 pass" and tsc --noEmit results cited in the PR description are from the author's local/manual verification, not from this repo's pipeline. That's consistent with how the other patches in this directory are handled, so not a new gap introduced by this PR, but the live-cluster check ("two Claude runs on one card... run 2 must resume") the author lists as outstanding is the one that actually exercises the behavior end-to-end.

Recommendation

Looks reasonable — no blocking issues; the Codex/Grok fingerprint-stabilization point above is worth a deliberate check given this is a fix-forward of a previously-reverted change for the same class of bug, but it doesn't rise to a confirmed defect from what's in this repo.

🤖 **Automated review** — model `claude-sonnet-5` (complexity: normal), commit `8d894e466ef92cae350a8f7a1b1ed16a7d6e47a2`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary This PR vendors a new patch (`ren-563-acpx-claude-session-resume.patch`) for the upstream Paperclip ACPX engine, plus a README note. It (1) redacts the per-run managed-AI temp-home paths (`HOME`, `XDG_*`, `CODEX_HOME`, `GROK_HOME`, `CLAUDE_CONFIG_DIR`) from the session fingerprint on local runs so the fingerprint is stable across runs, and (2) symlinks `CLAUDE_CONFIG_DIR/projects` into a persistent `<stateDir>/claude-projects` so a Claude session's transcript survives the per-run temp home being deleted. This is a fix-forward of #1111 (reverted in #1113), explicitly shipping both halves together this time. The logic is self-consistent and the new test (`execute.test.ts`) covers the key matrix (default managed layout, a second run resolving to the same fingerprint, an overridden `CLAUDE_CONFIG_DIR` keeping a distinct fingerprint and no symlink, and the non-managed case). No blocking issues found, but there's one real asymmetry worth checking before rollout (see Suggestions). ## 🟡 Suggestions - **Fingerprint stabilization covers three agents, transcript persistence covers one.** `ci/images/paperclip/ren-563-acpx-claude-session-resume.patch:96-103` redacts `CODEX_HOME` and `GROK_HOME` from the fingerprint hash exactly like `CLAUDE_CONFIG_DIR`, but only Claude gets the matching persistence fix (`linkManagedClaudeTranscripts`, applied only when `resolvedAdapterEnv.CLAUDE_CONFIG_DIR === "<managed-ai-home>/provider"`, patch lines 119-125). If Codex or Grok also keep local session/transcript state under their home dir the way Claude does under `CLAUDE_CONFIG_DIR/projects`, this change would make *their* fingerprints falsely stable too — reproducing the exact #1111 failure mode ("Resource not found" / "could not be resumed") for those agents instead of fixing it. The inline comment says "the server's own fingerprint does the same," which suggests this pattern may already be safe in production for those two agents, but that's not something I can confirm from this repo (the Codex/Grok ACP engines aren't vendored here). Worth explicitly confirming Codex/Grok resume is unaffected (or that they don't rely on local transcript persistence) before/after rollout, since the PR's own verification plan only exercises a Claude agent. - **No fallback if linking fails.** `linkManagedClaudeTranscripts` (patch lines 64-77) has no error handling; an `fs.mkdir`/`fs.symlink` failure (e.g. a permissions or disk issue on `stateDir`) will throw and presumably fail the whole run, not just the resume-convenience feature. This matches the existing style in this file (e.g. `writePaperclipClaudeSettings` also doesn't guard its writes), so it's not a new pattern, but it does mean a storage hiccup turns a "can't resume" situation into "can't run at all." - **Minor TOCTOU on the symlink.** `linkManagedClaudeTranscripts` reads the existing link, then conditionally removes and recreates it (lines 71-75). If two runs ever touched the same freshly-created `claudeConfigDir` concurrently this could race (second `fs.symlink` failing with `EEXIST`), but per the PR description runs are sequential per card, so this is likely not reachable in practice. ## Tests The new test (`execute.test.ts:1865-1903` in the vendored patch) is well-targeted: it checks a stable fingerprint across two managed homes, a different fingerprint when `CLAUDE_CONFIG_DIR` is overridden, that both default homes' `provider/projects` symlink to the same `<stateDir>/claude-projects`, that the overridden config dir gets no symlink, and that the non-managed-connection case is unaffected. Note that this repo's own CI (`.woodpecker/build-paperclip.yml`) only runs `git apply` via `prepare.sh` and then a Docker/kaniko build of the `production` target — it does not execute the upstream test suite, so the "199/199 pass" and `tsc --noEmit` results cited in the PR description are from the author's local/manual verification, not from this repo's pipeline. That's consistent with how the other patches in this directory are handled, so not a new gap introduced by this PR, but the live-cluster check ("two Claude runs on one card... run 2 must resume") the author lists as outstanding is the one that actually exercises the behavior end-to-end. ## Recommendation Looks reasonable — no blocking issues; the Codex/Grok fingerprint-stabilization point above is worth a deliberate check given this is a fix-forward of a previously-reverted change for the same class of bug, but it doesn't rise to a confirmed defect from what's in this repo. <!-- pr-review:sha=8d894e466ef92cae350a8f7a1b1ed16a7d6e47a2 -->
Sign in to join this conversation.