From 2fe23b7be5b98b29eff42b61e38674e7591b4516 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 20 Jan 2026 14:09:51 -0800 Subject: [PATCH] fix(done): terminate polecat session for all exit types (#800) Previously, gt done only killed the polecat session when exitType was COMPLETED. For DEFERRED, ESCALATED, and PHASE_COMPLETE, it would call os.Exit(0) which only exited the gt process, leaving Claude running. Now all exit types terminate the polecat session ("done means gone"). Only COMPLETED also nukes the worktree - other statuses preserve the work in case it needs to be resumed. Co-authored-by: julianknutsen Co-authored-by: Claude Opus 4.5 --- internal/cmd/done.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/internal/cmd/done.go b/internal/cmd/done.go index 792aab17..13ab1ce3 100644 --- a/internal/cmd/done.go +++ b/internal/cmd/done.go @@ -462,27 +462,28 @@ func runDone(cmd *cobra.Command, args []string) error { // This is the self-cleaning model - polecats clean up after themselves // "done means gone" - both worktree and session are terminated selfCleanAttempted := false - if exitType == ExitCompleted { - if roleInfo, err := GetRoleWithContext(cwd, townRoot); err == nil && roleInfo.Role == RolePolecat { - selfCleanAttempted = true + if roleInfo, err := GetRoleWithContext(cwd, townRoot); err == nil && roleInfo.Role == RolePolecat { + selfCleanAttempted = true - // Step 1: Nuke the worktree + // Step 1: Nuke the worktree (only for COMPLETED - other statuses preserve work) + if exitType == ExitCompleted { if err := selfNukePolecat(roleInfo, townRoot); err != nil { // Non-fatal: Witness will clean up if we fail style.PrintWarning("worktree nuke failed: %v (Witness will clean up)", err) } else { fmt.Printf("%s Worktree nuked\n", style.Bold.Render("✓")) } - - // Step 2: Kill our own session (this terminates Claude and the shell) - // This is the last thing we do - the process will be killed when tmux session dies - fmt.Printf("%s Terminating session (done means gone)\n", style.Bold.Render("→")) - if err := selfKillSession(townRoot, roleInfo); err != nil { - // If session kill fails, fall through to os.Exit - style.PrintWarning("session kill failed: %v", err) - } - // If selfKillSession succeeds, we won't reach here (process killed by tmux) } + + // Step 2: Kill our own session (this terminates Claude and the shell) + // This is the last thing we do - the process will be killed when tmux session dies + // All exit types kill the session - "done means gone" + fmt.Printf("%s Terminating session (done means gone)\n", style.Bold.Render("→")) + if err := selfKillSession(townRoot, roleInfo); err != nil { + // If session kill fails, fall through to os.Exit + style.PrintWarning("session kill failed: %v", err) + } + // If selfKillSession succeeds, we won't reach here (process killed by tmux) } // Fallback exit for non-polecats or if self-clean failed