fix: Make WaitForCommand/WaitForRuntimeReady fatal in manager Start() (#529)

Fixes #525: gt up reports deacon success but session doesn't actually start

Previously, WaitForCommand failures were marked as "non-fatal" in the
manager Start() methods used by gt up. This caused gt up to report
success even when Claude failed to start, because the error was silently
ignored.

Now when WaitForCommand or WaitForRuntimeReady times out:
1. The zombie tmux session is killed
2. An error is returned to the caller
3. gt up properly reports the failure

This aligns the manager Start() behavior with the cmd start functions
(e.g., gt deacon start) which already had fatal WaitForCommand behavior.

Changed files:
- internal/deacon/manager.go
- internal/mayor/manager.go
- internal/witness/manager.go
- internal/refinery/manager.go

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
aleiby
2026-01-17 00:00:53 -08:00
committed by GitHub
parent 11b38294d4
commit 15d1dc8fa8
4 changed files with 16 additions and 8 deletions

View File

@@ -106,9 +106,11 @@ func (m *Manager) Start(agentOverride string) error {
theme := tmux.DeaconTheme()
_ = t.ConfigureGasTownSession(sessionID, theme, "", "Deacon", "health-check")
// Wait for Claude to start (non-fatal)
// Wait for Claude to start - fatal if Claude fails to launch
if err := t.WaitForCommand(sessionID, constants.SupportedShells, constants.ClaudeStartTimeout); err != nil {
// Non-fatal - try to continue anyway
// Kill the zombie session before returning error
_ = t.KillSessionWithProcesses(sessionID)
return fmt.Errorf("waiting for deacon to start: %w", err)
}
// Accept bypass permissions warning dialog if it appears.

View File

@@ -114,9 +114,11 @@ func (m *Manager) Start(agentOverride string) error {
theme := tmux.MayorTheme()
_ = t.ConfigureGasTownSession(sessionID, theme, "", "Mayor", "coordinator")
// Wait for Claude to start (non-fatal)
// Wait for Claude to start - fatal if Claude fails to launch
if err := t.WaitForCommand(sessionID, constants.SupportedShells, constants.ClaudeStartTimeout); err != nil {
// Non-fatal - try to continue anyway
// Kill the zombie session before returning error
_ = t.KillSessionWithProcesses(sessionID)
return fmt.Errorf("waiting for mayor to start: %w", err)
}
// Accept bypass permissions warning dialog if it appears.

View File

@@ -223,10 +223,12 @@ func (m *Manager) Start(foreground bool, agentOverride string) error {
return fmt.Errorf("saving state: %w", err)
}
// Wait for Claude to start and show its prompt (non-fatal)
// Wait for Claude to start and show its prompt - fatal if Claude fails to launch
// WaitForRuntimeReady waits for the runtime to be ready
if err := t.WaitForRuntimeReady(sessionID, runtimeConfig, constants.ClaudeStartTimeout); err != nil {
// Non-fatal - try to continue anyway
// Kill the zombie session before returning error
_ = t.KillSessionWithProcesses(sessionID)
return fmt.Errorf("waiting for refinery to start: %w", err)
}
// Accept bypass permissions warning dialog if it appears.

View File

@@ -211,9 +211,11 @@ func (m *Manager) Start(foreground bool, agentOverride string, envOverrides []st
return fmt.Errorf("saving state: %w", err)
}
// Wait for Claude to start (non-fatal).
// Wait for Claude to start - fatal if Claude fails to launch
if err := t.WaitForCommand(sessionID, constants.SupportedShells, constants.ClaudeStartTimeout); err != nil {
// Non-fatal - try to continue anyway
// Kill the zombie session before returning error
_ = t.KillSessionWithProcesses(sessionID)
return fmt.Errorf("waiting for witness to start: %w", err)
}
// Accept bypass permissions warning dialog if it appears.