Skip to content

fix(spawn): bootstrap tmux session when cold-starting with --new-window - #1168

Merged
namastex888 merged 1 commit into
devfrom
fix/tui-cold-start-session-bootstrap
Apr 17, 2026
Merged

namastex888 merged 1 commit into
devfrom
fix/tui-cold-start-session-bootstrap

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Summary

  • genie spawn <agent> --session <name> --new-window failed silently when the target tmux session didn't exist yet — tmux rejects new-window -t <session>: with can't find session.
  • This is the cold-start path used by the TUI (Nav.tsx → spawnAgent → genie spawn … --new-window) to launch an offline agent. The TUI invokes the child with stdio: 'ignore', so the error was swallowed and the user saw nothing happen when clicking "start" on an offline agent.
  • Patch checks has-session first; if the session doesn't exist, bootstraps with new-session -d -s <name> (creates session + first pane in one call). Existing behaviour preserved when the session is already present.

Repro (before fix)

$ tmux -L genie new-window -d -t 'khal-os:' -P -F '#{pane_id}' 'echo'
can't find session: khal-os
exit=1

After the fix, the same cold-start path takes the new-session branch and the pane is created correctly.

Test plan

  • `bun run typecheck` — clean
  • `bun run lint` — no new warnings in `createTmuxPane` (12 pre-existing complexity warnings unchanged)
  • `bun test` — 2501 pass / 0 fail
  • Manual: launch TUI, click start on an offline agent whose tmux session doesn't exist, confirm a new window/session appears

`genie spawn <agent> --session <name> --new-window` failed silently when
the target tmux session did not yet exist. `createTmuxPane` ran
`tmux new-window -t <session>:` which tmux rejects with "can't find
session" when the session has not been created.

This is the cold-start path the TUI uses to launch an offline agent
(Nav.tsx spawnAgent → `genie spawn … --new-window`). Because the TUI
fires the child with `stdio: 'ignore'`, the error was swallowed and the
user saw nothing happen.

Check `has-session` first; if the session does not exist, bootstrap with
`new-session -d -s <name>` which creates the session and its first pane
in one call. Existing behaviour is preserved when the session is
already present.

Reproduced before fix:
  $ tmux -L genie new-window -d -t 'khal-os:' -P -F '#{pane_id}' 'echo'
  can't find session: khal-os
  exit=1
@coderabbitai

coderabbitai Bot commented Apr 17, 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: 58068843-23c2-45d0-bcc3-556e1d787f98

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/tui-cold-start-session-bootstrap

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6682fc1797

ℹ️ 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".

const cmd = `${tmuxPrefix}new-window -a -d -t ${shellQuote(`${session}:`)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`;
let sessionExists = false;
try {
execSync(`${tmuxPrefix}has-session -t ${shellQuote(`=${session}`)}`, { stdio: 'ignore' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use real session name for cold-start existence check

The new has-session probe treats session as a tmux session name, but in this code path session can come from teamWindow.windowId (from ensureTeamWindow), which is a window id (@...), not a session identifier. That makes has-session -t '=…' mis-targeted and pushes --new-window spawns into the new-session -s ... fallback with an invalid/wrong name, causing failures or spawning into the wrong session when --session is not provided. Resolve the actual session name (not window id) before the existence check.

Useful? React with 👍 / 👎.

Comment on lines +795 to +797
const cmd = sessionExists
? `${tmuxPrefix}new-window -a -d -t ${shellQuote(`${session}:`)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`
: `${tmuxPrefix}new-session -d -s ${shellQuote(session)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make session bootstrap atomic under concurrent spawns

This introduces a check-then-create sequence (has-session followed by new-session) without handling duplicate session on create. If two genie spawn ... --new-window commands cold-start the same missing session concurrently, both can observe "missing" and one will fail on new-session, aborting that spawn. The tmux helpers elsewhere already avoid this TOCTOU by treating duplicate session as success; this branch should do the same.

Useful? React with 👍 / 👎.

@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 pane creation logic to handle cold-start scenarios by checking for session existence and using new-session when necessary. The review identifies several issues, including incorrect session naming using window IDs, redundant session checks when a window is already present, and the omission of standard UTF-8 environment variables, providing a comprehensive refactoring suggestion to address these concerns.

Comment on lines 786 to +797
const session = ctx.sessionOverride ?? teamWindow?.windowId?.split(':')[0] ?? ctx.validated.team;
const cwdFlag = ctx.cwd ? ` -c ${shellQuote(ctx.cwd)}` : '';
const cmd = `${tmuxPrefix}new-window -a -d -t ${shellQuote(`${session}:`)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`;
let sessionExists = false;
try {
execSync(`${tmuxPrefix}has-session -t ${shellQuote(`=${session}`)}`, { stdio: 'ignore' });
sessionExists = true;
} catch {
sessionExists = false;
}
const cmd = sessionExists
? `${tmuxPrefix}new-window -a -d -t ${shellQuote(`${session}:`)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`
: `${tmuxPrefix}new-session -d -s ${shellQuote(session)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`;

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.

high

The logic for deriving the session name and checking its existence has a few issues:

  1. Window ID vs Session Name: teamWindow.windowId is a tmux window ID (e.g., @0). The expression teamWindow?.windowId?.split(':')[0] results in @0. Using this as a session name in has-session -t '=@0' will fail (as it looks for a session literally named @0), and new-session -s '@0' will then attempt to create a session with that ID as a name, which is likely unintended and will fail if the session already exists under its real name.
  2. Redundant Check: If teamWindow is present, the session is already guaranteed to exist because resolveSpawnTeamWindow (which calls ensureTeamWindow) was successful. The has-session check can be skipped in this case.
  3. Redundant Assignment: The sessionExists = false in the catch block is redundant as it's already initialized to false.
  4. Consistency: Other session creation logic in src/lib/tmux.ts sets UTF-8 environment variables (-e LC_ALL=C.UTF-8 -e LANG=C.UTF-8). It's recommended to include them here as well for a consistent environment in cold-started sessions.

Suggested refactoring to use the team name as a fallback and leverage teamWindow presence:

Suggested change
const session = ctx.sessionOverride ?? teamWindow?.windowId?.split(':')[0] ?? ctx.validated.team;
const cwdFlag = ctx.cwd ? ` -c ${shellQuote(ctx.cwd)}` : '';
const cmd = `${tmuxPrefix}new-window -a -d -t ${shellQuote(`${session}:`)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`;
let sessionExists = false;
try {
execSync(`${tmuxPrefix}has-session -t ${shellQuote(`=${session}`)}`, { stdio: 'ignore' });
sessionExists = true;
} catch {
sessionExists = false;
}
const cmd = sessionExists
? `${tmuxPrefix}new-window -a -d -t ${shellQuote(`${session}:`)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`
: `${tmuxPrefix}new-session -d -s ${shellQuote(session)}${cwdFlag} -P -F '#{pane_id}' ${tmuxCommand}`;
const sessionName = ctx.sessionOverride ?? ctx.validated.team;
const cwdFlag = ctx.cwd ? " -c " + shellQuote(ctx.cwd) : "";
let sessionExists = !!teamWindow;
if (!sessionExists) {
try {
execSync(tmuxPrefix + "has-session -t " + shellQuote("=" + sessionName), { stdio: "ignore" });
sessionExists = true;
} catch {
// sessionExists remains false
}
}
const target = teamWindow ? teamWindow.windowId : sessionName + ":";
const cmd = sessionExists
? tmuxPrefix + "new-window -a -d -t " + shellQuote(target) + cwdFlag + " -P -F '#{pane_id}' " + tmuxCommand
: tmuxPrefix + "new-session -d -s " + shellQuote(sessionName) + " -e LC_ALL=C.UTF-8 -e LANG=C.UTF-8" + cwdFlag + " -P -F '#{pane_id}' " + tmuxCommand;

@namastex888
namastex888 merged commit ef90931 into dev Apr 17, 2026
6 checks passed
@automagik-genie
automagik-genie deleted the fix/tui-cold-start-session-bootstrap branch September 25, 2026 04:51
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