fix(daemon): Kill zombie tmux sessions before recreating

The daemon was failing to restart agents when zombie tmux sessions existed
(session alive but Claude dead). Added EnsureSessionFresh() helper to
tmux package that:
- Checks if session exists
- If exists but Claude not running (zombie), kills the session
- Creates fresh session

Updated all daemon session creation points to use EnsureSessionFresh:
- ensureDeaconRunning()
- ensureWitnessRunning()
- restartPolecatSession()
- restartSession() in lifecycle.go

Added tests for the new helper function. (gt-j1i0r)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
dag
2026-01-02 18:52:06 -08:00
committed by Steve Yegge
parent 883997e044
commit 9ad826cd8c
4 changed files with 137 additions and 4 deletions

View File

@@ -76,6 +76,40 @@ func (t *Tmux) NewSession(name, workDir string) error {
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.
//
// A session is considered a zombie if:
// - The tmux session exists
// - But Claude (node process) is not running in it
//
// Returns nil if session was created successfully.
func (t *Tmux) EnsureSessionFresh(name, workDir string) error {
// Check if session already exists
exists, err := t.HasSession(name)
if err != nil {
return fmt.Errorf("checking session: %w", err)
}
if exists {
// Session exists - check if it's a zombie
if !t.IsClaudeRunning(name) {
// Zombie session: tmux alive but Claude dead
// Kill it so we can create a fresh one
if err := t.KillSession(name); err != nil {
return fmt.Errorf("killing zombie session: %w", err)
}
} else {
// Session is healthy (Claude running) - nothing to do
return nil
}
}
// Create fresh session
return t.NewSession(name, workDir)
}
// KillSession terminates a tmux session.
func (t *Tmux) KillSession(name string) error {
_, err := t.run("kill-session", "-t", name)