Skip to content

feat: reconcile lost backend session ids from the tool's on-disk store - #100

Merged
TraderSamwise merged 3 commits into
masterfrom
feat/reconcile-backend-ids-from-disk
Jun 7, 2026
Merged

feat: reconcile lost backend session ids from the tool's on-disk store#100
TraderSamwise merged 3 commits into
masterfrom
feat/reconcile-backend-ids-from-disk

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem (epic W2)

W0 reconnected go-forward backend-id capture, but it can't reach ids that were already lost before the fix, or lost while the project service was down. A crash that kills the tmux pane before capture leaves an offline agent permanently unresumable:

Cannot restore session "claude-…" without an exact resumable backend session id for "claude"

This is the residual memory↔disk divergence: the agent's real UUID still exists on disk (~/.claude/projects/<encoded-cwd>/<uuid>.jsonl), but aimux's durable state lost it.

Change

src/backend-session-discovery.tsdiscoverBackendSessionId(tool, cwd) recovers a claude session's UUID from its own transcript store, scoped to the session's exact worktree so it can never bind an agent from another project. UUID-validated; picks the most recently active transcript (the one live at the crash). Codex carries its id in launch args, so only claude needs disk recovery.

resumeOfflineSession — when the durable backend id is missing and the session isn't being relaunched fresh, discover it from disk, use it for an exact resume, and persist it via the new running session's saveState. If nothing is found, the existing refuse-to-guess behavior is preserved unchanged — discovery never weakens the "exact id only" guarantee (an unusable discovered id still throws).

Safety

The original code deliberately refused to guess. This keeps that: discovery is cwd-scoped (never cross-project), uuid-validated, and gated behind canResumeWithBackendSessionId. The only judgment-call trade-off — documented in the code — is that multiple sessions sharing one worktree resolve by latest-activity; worst case resumes a sibling in that same worktree (read-only history, recoverable), never an unrelated agent. That's strictly better than permanently stranding the session.

Verification

  • yarn verify green: typecheck + lint clean, 1022/1022 tests.
  • New backend-session-discovery.test.ts (single/multiple/non-uuid/missing/tool-filter) + a resumeOfflineSession locking test proving a missing claude id is reconciled from disk instead of refused.
  • Independent implementation audit: PASS on safety invariant, encoding, wiring, hot-path, UUID validation.

Scope

Builds on W0 (merged). Recovers crash-stranded claude sessions (incl. the one that started this investigation). Does not touch the write layer (W1) or the CLI surface (W3).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved offline session recovery by automatically discovering missing backend session IDs from saved transcript history; resumes reliably after crashes or lost state, preferring the most recent valid transcript.
  • Tests
    • Added test coverage validating discovery behavior, file-filtering rules, selection of the newest transcript, and integration with offline session resumption.

test and others added 2 commits June 7, 2026 10:42
A crash that kills the tmux pane before the backend id is captured leaves
an offline session unresumable ("without an exact resumable backend
session id"). Recover it from the tool's own session store at resume time
instead of stranding the agent:

- backend-session-discovery: discoverBackendSessionId(tool, cwd) finds the
  most recently active claude transcript under ~/.claude/projects for the
  session's exact worktree (cwd-scoped so it cannot bind an unrelated
  agent; uuid-validated). Codex carries its id in launch args, so only
  claude needs disk recovery.
- resumeOfflineSession: when the durable backend id is missing and the
  session is not being relaunched fresh, discover it from disk, use it for
  an exact resume, and persist it via the new running session's saveState.
  Falls back to the existing refuse-to-guess behavior if nothing is found.

This closes the residual memory<->disk divergence the W0 capture fix could
not reach (ids lost before this change, or while the service was down) and
makes crash-stranded claude sessions restorable again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document that the latest-activity tie-break only ever resolves within the
same worktree (worst case a sibling session, never an unrelated project),
and that CLAUDE_CONFIG_DIR mirrors Claude Code's own config location.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jun 7, 2026 2:56am

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: db15ece9-412f-4191-a854-08b350842f04

📥 Commits

Reviewing files that changed from the base of the PR and between 2e0d1d9 and 5506ca6.

📒 Files selected for processing (2)
  • src/backend-session-discovery.ts
  • src/multiplexer/runtime-state.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/backend-session-discovery.ts
  • src/multiplexer/runtime-state.test.ts

📝 Walkthrough

Walkthrough

Adds a claude-specific discovery module that scans encoded worktree transcript .jsonl files to find the most-recent UUID filename and integrates that discovery into offline session resumption to recover missing Claude backendSessionIds.

Changes

Claude backend session discovery and recovery

Layer / File(s) Summary
Backend session discovery module and tests
src/backend-session-discovery.ts, src/backend-session-discovery.test.ts
New discovery functions: discoverClaudeBackendSessionId scans an encoded worktree under CLAUDE_CONFIG_DIR/~/.claude/projects, filters UUID-named .jsonl transcripts, and returns the newest file's UUID; discoverBackendSessionId dispatches only for claude. Tests validate UUID extraction, mtime selection, file filtering, absent directories, and dispatcher behavior.
Integration with offline session resumption
src/multiplexer/runtime-state.ts, src/multiplexer/runtime-state.test.ts
resumeOfflineSession imports discoverBackendSessionId and, when session.backendSessionId is missing (and not a fresh relaunch), attempts discovery and patches the in-memory session id; integration test writes a transcript file to a temp CLAUDE_CONFIG_DIR and asserts createSession is called with the discovered UUID and useBackendResume=true.

Possibly related PRs

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through projects, seeking a clue,
A UUID hidden in .jsonl dew,
I sniffed the newest file with glee,
And patched the session — now back to tea! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: reconcile lost backend session ids from the tool's on-disk store' directly and clearly summarizes the main change: adding functionality to recover lost backend session UUIDs from on-disk transcript storage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/reconcile-backend-ids-from-disk

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend-session-discovery.ts`:
- Around line 32-35: The directory enumeration can throw even after
existsSync(dir); wrap the readdirSync(dir) call in a try/catch and return null
on error so the routine preserves best-effort recovery. Concretely, replace the
direct for (const entry of readdirSync(dir)) loop with: try { const entries =
readdirSync(dir); for (const entry of entries) { ... } } catch (err) { return
null; } — keep the existing checks for entry.endsWith(".jsonl") and the best
variable ({ id, mtimeMs }) logic unchanged.

In `@src/multiplexer/runtime-state.test.ts`:
- Around line 334-335: The temp directory cleanup (rmSync(claudeHome, {
recursive: true, force: true })) is currently inside the try block so it won't
run if an assertion throws; move that rmSync call into the finally block so
cleanup always runs. Locate the test in runtime-state.test.ts where claudeHome
is created and ensure rmSync(claudeHome, { recursive: true, force: true }) is
executed inside the finally clause that pairs with the try surrounding the test
logic (preserve any existing error handling inside the try/catch).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aad6432f-2e91-4f2c-87ec-459e88680073

📥 Commits

Reviewing files that changed from the base of the PR and between 7180be5 and 2e0d1d9.

📒 Files selected for processing (4)
  • src/backend-session-discovery.test.ts
  • src/backend-session-discovery.ts
  • src/multiplexer/runtime-state.test.ts
  • src/multiplexer/runtime-state.ts

Comment thread src/backend-session-discovery.ts
Comment thread src/multiplexer/runtime-state.test.ts Outdated
- discoverClaudeBackendSessionId: readdirSync can throw (EACCES/ENOTDIR)
  even after existsSync; catch it and return null so a recovery miss stays
  best-effort instead of becoming a hard failure.
- runtime-state test: move the temp-dir rmSync into finally so cleanup
  runs even if an assertion throws. Addresses CodeRabbit feedback on #100.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TraderSamwise
TraderSamwise merged commit 43ace06 into master Jun 7, 2026
3 checks passed
@TraderSamwise
TraderSamwise deleted the feat/reconcile-backend-ids-from-disk branch June 7, 2026 02:57
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