fix(tmux): use NewSessionWithCommand to avoid send-keys race condition

Agent sessions would fail on startup because send-keys arrived before the
shell was ready, causing 'bad pattern' and 'command not found' errors.

Fix: Create sessions with the command directly using tmux new-session's
command argument. This runs the agent as the pane's initial process,
avoiding shell readiness timing issues entirely.

Updated all agent managers: mayor, deacon, witness, refinery, polecat, crew.

Also fixes pre-existing build error in polecat/manager.go (polecatPath →
clonePath/newClonePath).

Closes #280

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-01-08 23:35:31 -08:00
committed by Steve Yegge
parent a91e6cd643
commit afff85cdff
8 changed files with 99 additions and 138 deletions

View File

@@ -78,6 +78,22 @@ func (t *Tmux) NewSession(name, workDir string) error {
return err
}
// NewSessionWithCommand creates a new detached tmux session that immediately runs a command.
// Unlike NewSession + SendKeys, this avoids race conditions where the shell isn't ready
// or the command arrives before the shell prompt. The command runs directly as the
// initial process of the pane.
// See: https://github.com/anthropics/gastown/issues/280
func (t *Tmux) NewSessionWithCommand(name, workDir, command string) error {
args := []string{"new-session", "-d", "-s", name}
if workDir != "" {
args = append(args, "-c", workDir)
}
// Add the command as the last argument - tmux runs it as the pane's initial process
args = append(args, command)
_, err := t.run(args...)
return err
}
// EnsureSessionFresh ensures a session is available and healthy.
// If the session exists but is a zombie (Claude not running), it kills the session first.
// This prevents "session already exists" errors when trying to restart dead agents.