fix(ui): treat an immediate ConPTY exit as a start failure

InteractiveSessionId is persisted before the ConPTY process spawns (1a988ff).
If `claude --session-id <guid>` exits immediately after launch (auth/network
hiccup, crash before the TUI starts), OnSessionProcessExited set HasExited
but never StartError, so ConPtyPaneViewModel.CanRetry (which requires
StartError) never offered Retry — every reopen just resumed the same dead
session id, permanently poisoning that task's interactive sessions.

Now a nonzero exit within 5s of the session becoming "running" is treated
as a died-at-startup failure and routed through the same StartError path
as a launch-time exception, so the pane shows the error banner + Retry.

Retry re-fetches the LaunchSpec via BuildForTaskAsync, which reuses the
same persisted InteractiveSessionId — correct for a transient failure
(fresh process, same id), but does not help a genuinely dead session id.
Clearing a dead session id server-side is a separate design question,
left out of scope here.
This commit is contained in:
mika kuns
2026-08-06 14:33:24 +02:00
parent bac8387069
commit 4a28bfe82e
3 changed files with 98 additions and 3 deletions
@@ -134,5 +134,21 @@ public class ConPtyPaneViewModelTests
Assert.True(pane.RetryCommand.CanExecute(null));
}
// ── Died-at-startup (fix: an immediate ConPTY exit used to leave StartError null, so Retry
// never became reachable and the pane was stuck reusing the same persisted session id) ──
[Fact]
public void RetryCommand_Enabled_AfterProcessExitsNonzeroRightAfterLaunch()
{
using var pane = NewTaskPane(NeverCompletes);
SetRunning(pane);
Assert.False(pane.RetryCommand.CanExecute(null));
pane.Terminal.OnSessionProcessExited(null, 1);
Assert.NotNull(pane.Terminal.StartError);
Assert.True(pane.RetryCommand.CanExecute(null));
}
private static void SetRunning(ConPtyPaneViewModel pane) => pane.Terminal.IsRunning = true;
}