Skip to content

fix(spawn): add home keeper window so isolated sessions survive agent death - #1190

Merged
automagik-genie merged 1 commit into
devfrom
fix/isolated-session-keeper
Apr 18, 2026
Merged

automagik-genie merged 1 commit into
devfrom
fix/isolated-session-keeper

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Symptom (Felipe's report)

Pressed Enter on khal-os in TUI (attached to genie-configure session) and it spawned the agent inside genie-configure:2 as a window. The original dedicated khal-os session was gone. Empty sessions should stay alive so registered agents have a home to respawn into.

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 the claude window:

// Old — src/term-commands/agents.ts:797-799
const cmd = sessionExists
  ? `new-window -a -d -t ${session}: -n claude ${cwdFlag} -P ...`
  : `new-session -d -s ${session} -n claude ${cwdFlag} -P ...`;

When that claude window closed — genie kill, crash, or natural exit — tmux tore the session down. The next resume then fell through resolveRepoSession tier 1 (session gone) → tier 2 (caller's current TMUX), placing the respawned agent in genie-configure instead of returning to its dedicated home.

Fix

Bootstrap the session with a home keeper window (bash shell) before the claude agent window. The keeper survives the agent's lifecycle — closing claude leaves home behind 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 claude window.

Topology

State Windows
After first spawn home (bash) + claude (agent)
After kill / agent exit home (session alive)
After next respawn home + claude (in original session)

Tmux semantics verified

$ tmux new-session -d -s probe -n home
$ tmux new-window -a -d -t probe: -n claude 'sleep 120'
$ tmux list-windows -t probe
0: home*  (1 panes)
1: claude (1 panes)
$ tmux kill-window -t probe:claude
$ tmux list-windows -t probe
0: home*  (1 panes)   # session survives ✓

Test plan

  • bun run typecheck clean
  • bun run lint clean on changed file
  • bun test — 2634/2634 pass
  • Tmux semantics probe confirmed keeper behavior
  • Live verification: after merge + publish, kill khal-os, press Enter in TUI, confirm it respawns into the standalone khal-os session (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.

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

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9080209b-7cb2-4c34-88f0-fb113e3916bd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/isolated-session-keeper

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.

❤️ Share

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

@automagik-genie
automagik-genie merged commit d6813d7 into dev Apr 18, 2026
5 checks passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +804 to +809
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' });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

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 */
      }
    }

@automagik-genie
automagik-genie deleted the fix/isolated-session-keeper branch September 25, 2026 04:50
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.

2 participants