Repository navigation
fix(spawn): add home keeper window so isolated sessions survive agent death - #1190
Conversation
… death When `--new-window --session <name>` bootstraps a fresh tmux session for a team-of-one (or any isolated spawn), the session previously contained only the agent's `claude` window. When that window closed — via `genie kill`, agent crash, or natural exit — tmux's default behavior tore the session down with it. Any subsequent resume inside another tmux context then fell through `resolveRepoSession` tier 1 (exact match — the session was gone) to tier 2 (current TMUX session), so the agent respawned as a window in the caller's session instead of returning to its dedicated home. Observed: after killing `khal-os` canonical, pressing Enter in the TUI (attached to `genie-configure`) placed the respawned khal-os as window 2 of `genie-configure` rather than a standalone `khal-os` session — losing the topology guarantee that PR #1175 restored for fresh spawns. Fix: when creating a new session in the isolated path, create a `home` window (bash shell) first, then add the `claude` agent window. The home window is cheap (single shell, no work) and acts as a session keeper: closing the agent leaves `home` behind so the session stays alive. Future resume/respawn calls find the exact-match session via tier 1 and return the agent to its intended home. For existing sessions the branch is unchanged — we just add the claude window. Verified tmux semantics directly: tmux new-session -d -s probe -n home tmux new-window -a -d -t probe: -n claude 'sleep' tmux kill-window -t probe:claude tmux list-windows -t probe # => just home, session still alive Test plan: - `bun run typecheck` clean - `bun test` 2634/2634 pass - Live tmux probe confirmed keeper behavior
|
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.
Code Review
This pull request updates the tmux session creation logic to ensure session persistence by introducing a 'home' keeper window. This prevents the session from being terminated when the agent window is closed. A review comment highlighted a potential TOCTOU race condition during session bootstrapping and recommended wrapping the command in a try-catch block and explicitly setting UTF-8 environment variables for better robustness.
| if (!sessionExists) { | ||
| // Bootstrap session with a `home` keeper window (bash shell). `-d` keeps | ||
| // the new session detached. No command → default shell. stdio:'ignore' | ||
| // because we don't need the session name back (we already have it). | ||
| execSync(`${tmuxPrefix}new-session -d -s ${shellQuote(session)} -n home${cwdFlag}`, { stdio: 'ignore' }); | ||
| } |
There was a problem hiding this comment.
There is a potential TOCTOU (time-of-check to time-of-use) race condition here. If another process creates the tmux session between the 'has-session' check and the 'new-session' call, 'execSync' will throw a 'duplicate session' error and crash the spawn process. Wrapping the bootstrap call in a 'try-catch' block makes this more robust. Additionally, for consistency with other session creations in the codebase (see src/lib/tmux.ts), it is recommended to set the UTF-8 environment variables.
if (!sessionExists) {
// Bootstrap session with a home keeper window (bash shell). -d keeps
// the new session detached. No command -> default shell. stdio: ignore
// because we don't need the session name back (we already have it).
try {
execSync(tmuxPrefix + "new-session -d -s " + shellQuote(session) + " -n home" + cwdFlag + " -e LC_ALL=C.UTF-8 -e LANG=C.UTF-8", { stdio: "ignore" });
} catch (e) {
/* best-effort: handles TOCTOU race if session was created concurrently */
}
}
Symptom (Felipe's report)
Root cause
PR #1175 correctly made
--new-window --session <name>always spawn into its own tmux session for team-of-one / TUI per-agent spawns. But the bootstrap path created the session with only theclaudewindow:When that
claudewindow closed —genie kill, crash, or natural exit — tmux tore the session down. The next resume then fell throughresolveRepoSessiontier 1 (session gone) → tier 2 (caller's current TMUX), placing the respawned agent ingenie-configureinstead of returning to its dedicated home.Fix
Bootstrap the session with a
homekeeper window (bash shell) before theclaudeagent window. The keeper survives the agent's lifecycle — closing claude leaveshomebehind so the session stays alive. Future resumes find the exact-match session via tier 1 and return the agent to its home.For existing sessions the branch is unchanged — we still just add the
claudewindow.Topology
home(bash) +claude(agent)kill/ agent exithome(session alive)home+claude(in original session)Tmux semantics verified
Test plan
bun run typecheckcleanbun run lintclean on changed filebun test— 2634/2634 passkhal-ossession (not genie-configure)Context
Complements PR #1174 (auto-team-of-one), PR #1175 (topology fix), PR #1177 (otel flake). Fourth PR in the team-of-one-auto-create chain — closes the gap where the isolation guarantee didn't survive the agent's first death.