From a0e5831f693dacb8e8faf4bf2a53da457b8a7a8e Mon Sep 17 00:00:00 2001 From: glory Date: Sun, 4 Jan 2026 12:32:53 -0800 Subject: [PATCH] fix(witness): explicitly kill tmux session in NukePolecat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Witness lifecycle was not reliably killing polecat tmux sessions after POLECAT_DONE. Sessions were piling up because gt polecat nuke may fail to kill the session due to rig loading issues or race conditions with IsRunning checks. Fix: Kill the tmux session FIRST and unconditionally in NukePolecat, before calling gt polecat nuke. This ensures the session is always killed regardless of what happens in the downstream nuke command. The session name pattern is deterministic (gt--), so we can construct it directly from the rigName and polecatName parameters. Fixes: gt-g9ft5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/witness/handlers.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/internal/witness/handlers.go b/internal/witness/handlers.go index 77a65849..da908882 100644 --- a/internal/witness/handlers.go +++ b/internal/witness/handlers.go @@ -12,6 +12,7 @@ import ( "github.com/steveyegge/gastown/internal/beads" "github.com/steveyegge/gastown/internal/git" "github.com/steveyegge/gastown/internal/mail" + "github.com/steveyegge/gastown/internal/tmux" "github.com/steveyegge/gastown/internal/workspace" ) @@ -645,6 +646,28 @@ func UpdateCleanupWispState(workDir, wispID, newState string) error { // This kills the tmux session, removes the worktree, and cleans up beads. // Should only be called after all safety checks pass. func NukePolecat(workDir, rigName, polecatName string) error { + // CRITICAL: Kill the tmux session FIRST and unconditionally. + // The session name follows the pattern gt--. + // We do this explicitly here because gt polecat nuke may fail to kill the + // session due to rig loading issues or race conditions with IsRunning checks. + // See: gt-g9ft5 - sessions were piling up because nuke wasn't killing them. + sessionName := fmt.Sprintf("gt-%s-%s", rigName, polecatName) + t := tmux.NewTmux() + + // Check if session exists and kill it + if running, _ := t.HasSession(sessionName); running { + // Try graceful shutdown first (Ctrl-C), then force kill + _ = t.SendKeysRaw(sessionName, "C-c") + // Brief delay for graceful handling + time.Sleep(100 * time.Millisecond) + // Force kill the session + if err := t.KillSession(sessionName); err != nil { + // Log but continue - session might already be dead + // The important thing is we tried + } + } + + // Now run gt polecat nuke to clean up worktree, branch, and beads address := fmt.Sprintf("%s/%s", rigName, polecatName) cmd := exec.Command("gt", "polecat", "nuke", address) //nolint:gosec // G204: address is constructed from validated internal data