Repository navigation
fix: auto-resume Claude session on genie spawn - #994
Conversation
When `genie spawn` is called for an agent that previously had a running
Claude Code session (now dead), it always created a new session, losing
all conversation context. This adds auto-resume detection in
handleWorkerSpawn before rejectDuplicateRole cleans up the dead worker
entry, preserving the claudeSessionId for resume.
Also fixes sessionExists() in team-lead-command.ts to check for the
team-prefixed session name format ({name}-{name}) that Claude Code
actually stores.
…xity Moves the dead-worker detection logic from inline in handleWorkerSpawn into a standalone findDeadResumable() helper, keeping the function under the lint complexity threshold.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 83fafb96fd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await resumeAgent(deadResumable); | ||
| return deadResumable.id; |
There was a problem hiding this comment.
Skip auto-resume when tmux context is unavailable
This branch always calls resumeAgent for a dead Claude worker, but resumeAgent hard-exits when process.env.TMUX is unset. As a result, running genie spawn from a non-tmux shell now fails whenever a stale dead worker exists, instead of falling back to normal spawn behavior (including cases where the caller intended a fresh spawn). Gate auto-resume on a valid tmux context and continue through the regular spawn path when resume cannot run.
Useful? React with 👍 / 👎.
| const deadResumable = await findDeadResumable(team, effectiveRole); | ||
| if (deadResumable) { |
There was a problem hiding this comment.
Respect explicit provider override before auto-resume
Auto-resume runs before provider resolution and does not consider options.provider, so a dead Claude worker with the same role/team will be resumed even when the user explicitly requested --provider codex. This returns early and bypasses the normal spawn flow, making provider selection ineffective in this scenario. Auto-resume should be conditional on the requested/resolved provider being Claude.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Code Review
This pull request introduces auto-resume capabilities for Claude sessions and improves session name matching to include team-prefixed formats. It adds a mechanism to find and resume dead workers with existing session IDs during the worker spawn process. Feedback suggests improving the selection logic for resumable workers by excluding those in a 'done' state and prioritizing the most recent session, as well as ensuring the logic respects the user's auto-resume preference.
| const candidate = existing.find( | ||
| (w) => w.role === role && w.team === team && w.claudeSessionId && w.provider === 'claude', | ||
| ); |
There was a problem hiding this comment.
If multiple dead workers exist for the same role and team, find() will return the first one encountered in the registry list, which may not be the most recent session. Additionally, we should avoid resuming workers that reached a done state. Sorting by startedAt descending ensures the most recent context is resumed.
| const candidate = existing.find( | |
| (w) => w.role === role && w.team === team && w.claudeSessionId && w.provider === 'claude', | |
| ); | |
| const candidate = existing | |
| .filter((w) => w.role === role && w.team === team && w.claudeSessionId && w.provider === 'claude' && w.state !== 'done') | |
| .sort((a, b) => b.startedAt.localeCompare(a.startedAt))[0]; |
| return process.exit(1) as never; | ||
| } | ||
| // Auto-resume: if a dead worker exists with a Claude session, resume instead of fresh spawn. | ||
| const deadResumable = await findDeadResumable(team, effectiveRole); |
There was a problem hiding this comment.
The auto-resume logic should respect the autoResume option. If a user explicitly passes --no-auto-resume (setting options.autoResume to false), they likely intend to start a fresh session rather than resuming a dead one.
| const deadResumable = await findDeadResumable(team, effectiveRole); | |
| const deadResumable = options.autoResume !== false ? await findDeadResumable(team, effectiveRole) : null; |
Summary
genie spawnis called for an agent that previously had a running Claude Code session (now dead), it now resumes the existing session instead of creating a new one, preserving all conversation context.sessionExists()inteam-lead-command.tsnow also checks for{name}-{name}format, matching how Claude Code stores team-prefixed session names.findDeadResumable()helper to keephandleWorkerSpawnunder the cognitive complexity threshold.Root cause
handleWorkerSpawn()always generated a newsessionIdviacrypto.randomUUID(), andrejectDuplicateRole()unregistered dead workers before theirclaudeSessionIdcould be used for resume. The new logic runs beforerejectDuplicateRoleto detect and resume dead workers that have a saved Claude session.Files changed
src/term-commands/agents.ts— auto-resume detection +findDeadResumablehelpersrc/lib/team-lead-command.ts—sessionExists()name format fixTest plan
bun run buildpassesbun test src/term-commands/agents.test.ts— 9 tests passbun test src/lib/team-lead-command.test.ts— 16 tests pass