fix(handoff): preserve tmux session by setting remain-on-exit before kill
When gt handoff killed pane processes before respawning, the pane would be destroyed (since remain-on-exit defaults to off), causing respawn-pane to fail with "can't find pane" error. Fix: Set remain-on-exit=on before killing processes, so the pane survives process death and can be respawned. This restores tmux session reuse on handoffs. Changes: - Add SetRemainOnExit method to tmux package - Call SetRemainOnExit(true) before KillPaneProcesses in: - Local handoff (runHandoff) - Remote handoff (handoffRemoteSession) - Mayor attach respawn (runMayorAttach) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -204,6 +204,13 @@ func runHandoff(cmd *cobra.Command, args []string) error {
|
||||
_ = os.WriteFile(markerPath, []byte(currentSession), 0644)
|
||||
}
|
||||
|
||||
// Set remain-on-exit so the pane survives process death during handoff.
|
||||
// Without this, killing processes causes tmux to destroy the pane before
|
||||
// we can respawn it. This is essential for tmux session reuse.
|
||||
if err := t.SetRemainOnExit(pane, true); err != nil {
|
||||
style.PrintWarning("could not set remain-on-exit: %v", err)
|
||||
}
|
||||
|
||||
// Kill all processes in the pane before respawning to prevent orphan leaks
|
||||
// RespawnPane's -k flag only sends SIGHUP which Claude/Node may ignore
|
||||
if err := t.KillPaneProcesses(pane); err != nil {
|
||||
@@ -212,6 +219,7 @@ func runHandoff(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Use exec to respawn the pane - this kills us and restarts
|
||||
// Note: respawn-pane automatically resets remain-on-exit to off
|
||||
return t.RespawnPane(pane, restartCmd)
|
||||
}
|
||||
|
||||
@@ -567,6 +575,13 @@ func handoffRemoteSession(t *tmux.Tmux, targetSession, restartCmd string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set remain-on-exit so the pane survives process death during handoff.
|
||||
// Without this, killing processes causes tmux to destroy the pane before
|
||||
// we can respawn it. This is essential for tmux session reuse.
|
||||
if err := t.SetRemainOnExit(targetPane, true); err != nil {
|
||||
style.PrintWarning("could not set remain-on-exit: %v", err)
|
||||
}
|
||||
|
||||
// Kill all processes in the pane before respawning to prevent orphan leaks
|
||||
// RespawnPane's -k flag only sends SIGHUP which Claude/Node may ignore
|
||||
if err := t.KillPaneProcesses(targetPane); err != nil {
|
||||
@@ -581,6 +596,7 @@ func handoffRemoteSession(t *tmux.Tmux, targetSession, restartCmd string) error
|
||||
}
|
||||
|
||||
// Respawn the remote session's pane
|
||||
// Note: respawn-pane automatically resets remain-on-exit to off
|
||||
if err := t.RespawnPane(targetPane, restartCmd); err != nil {
|
||||
return fmt.Errorf("respawning pane: %w", err)
|
||||
}
|
||||
|
||||
@@ -200,6 +200,12 @@ func runMayorAttach(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("building startup command: %w", err)
|
||||
}
|
||||
|
||||
// Set remain-on-exit so the pane survives process death during respawn.
|
||||
// Without this, killing processes causes tmux to destroy the pane.
|
||||
if err := t.SetRemainOnExit(paneID, true); err != nil {
|
||||
style.PrintWarning("could not set remain-on-exit: %v", err)
|
||||
}
|
||||
|
||||
// Kill all processes in the pane before respawning to prevent orphan leaks
|
||||
// RespawnPane's -k flag only sends SIGHUP which Claude/Node may ignore
|
||||
if err := t.KillPaneProcesses(paneID); err != nil {
|
||||
@@ -207,6 +213,7 @@ func runMayorAttach(cmd *cobra.Command, args []string) error {
|
||||
style.PrintWarning("could not kill pane processes: %v", err)
|
||||
}
|
||||
|
||||
// Note: respawn-pane automatically resets remain-on-exit to off
|
||||
if err := t.RespawnPane(paneID, startupCmd); err != nil {
|
||||
return fmt.Errorf("restarting runtime: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user