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:
@@ -174,12 +174,17 @@ func (m *Manager) Start(foreground bool) error {
|
||||
return fmt.Errorf("ensuring runtime settings: %w", err)
|
||||
}
|
||||
|
||||
if err := t.NewSession(sessionID, refineryRigDir); err != nil {
|
||||
// Build startup command first
|
||||
bdActor := fmt.Sprintf("%s/refinery", m.rig.Name)
|
||||
command := config.BuildAgentStartupCommand("refinery", bdActor, m.rig.Path, "")
|
||||
|
||||
// Create session with command directly to avoid send-keys race condition.
|
||||
// See: https://github.com/anthropics/gastown/issues/280
|
||||
if err := t.NewSessionWithCommand(sessionID, refineryRigDir, command); err != nil {
|
||||
return fmt.Errorf("creating tmux session: %w", err)
|
||||
}
|
||||
|
||||
// Set environment variables (non-fatal: session works without these)
|
||||
bdActor := fmt.Sprintf("%s/refinery", m.rig.Name)
|
||||
_ = t.SetEnvironment(sessionID, "GT_RIG", m.rig.Name)
|
||||
_ = t.SetEnvironment(sessionID, "GT_REFINERY", "1")
|
||||
_ = t.SetEnvironment(sessionID, "GT_ROLE", "refinery")
|
||||
@@ -206,22 +211,6 @@ func (m *Manager) Start(foreground bool) error {
|
||||
return fmt.Errorf("saving state: %w", err)
|
||||
}
|
||||
|
||||
// Start Claude agent with full permissions (like polecats)
|
||||
// NOTE: No gt prime injection needed - SessionStart hook handles it automatically
|
||||
// Restarts are handled by daemon via LIFECYCLE mail, not shell loops
|
||||
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
|
||||
command := config.BuildAgentStartupCommand("refinery", bdActor, m.rig.Path, "")
|
||||
// Wait for shell to be ready before sending keys (prevents "can't find pane" under load)
|
||||
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
||||
_ = t.KillSession(sessionID)
|
||||
return fmt.Errorf("waiting for shell: %w", err)
|
||||
}
|
||||
if err := t.SendKeys(sessionID, command); err != nil {
|
||||
// Clean up the session on failure (best-effort cleanup)
|
||||
_ = t.KillSession(sessionID)
|
||||
return fmt.Errorf("starting Claude agent: %w", err)
|
||||
}
|
||||
|
||||
// Wait for Claude to start and show its prompt (non-fatal)
|
||||
// WaitForRuntimeReady waits for the runtime to be ready
|
||||
if err := t.WaitForRuntimeReady(sessionID, runtimeConfig, constants.ClaudeStartTimeout); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user