feat(ui): wire list-handler nextPhase through the handoff hub event
HandoffRequested now carries nextPhase end to end (IWorkerClient -> WorkerClient -> MissionControlViewModel -> GetMergeHelperHandoffLaunchSpecAsync), and the outgoing tile is left open on handoff instead of being closed -- lookups that need the active pane for a task now use LastOrDefault since a handler task's ConPtySessions can hold more than one pane.
This commit is contained in:
@@ -20,7 +20,7 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
private readonly Action<string, string, string, DateTime> _onTaskFinished;
|
||||
private readonly Action<string> _onTaskUpdated;
|
||||
private readonly Action _onConnectionRestored;
|
||||
private readonly Action<string, IReadOnlyList<string>> _onHandoffRequested;
|
||||
private readonly Action<string, IReadOnlyList<string>, string> _onHandoffRequested;
|
||||
|
||||
// Embedded ConPTY sessions (task-based only) — a manual cockpit detached from the
|
||||
// review/merge/status machinery.
|
||||
@@ -85,7 +85,7 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
_onConnectionRestored = () => { _ = RefreshQueueAsync(); };
|
||||
_worker.ConnectionRestoredEvent += _onConnectionRestored;
|
||||
|
||||
_onHandoffRequested = (taskId, survivingTaskIds) => { _ = OpenMergeHelperHandoffConPtySessionAsync(taskId, survivingTaskIds); };
|
||||
_onHandoffRequested = (taskId, survivingTaskIds, nextPhase) => { _ = OpenMergeHelperHandoffConPtySessionAsync(taskId, survivingTaskIds, nextPhase); };
|
||||
_worker.HandoffRequestedEvent += _onHandoffRequested;
|
||||
|
||||
_ = RefreshQueueAsync();
|
||||
@@ -153,7 +153,9 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
public async System.Threading.Tasks.Task OpenConPtySessionAsync(string taskId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(taskId)) return;
|
||||
if (ConPtySessions.FirstOrDefault(s => s.TaskId == taskId) is { } existing)
|
||||
// A merge-helper handoff can leave more than one pane for this TaskId (the outgoing phase's
|
||||
// frozen tile plus the active one) -- LastOrDefault resolves to the newest/active pane.
|
||||
if (ConPtySessions.LastOrDefault(s => s.TaskId == taskId) is { } existing)
|
||||
{
|
||||
FocusedPane = existing;
|
||||
return;
|
||||
@@ -281,19 +283,19 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
// List-handler handoff: the running session called handoff_list_handler at the end of Phase 2.
|
||||
// Replaces the Phase 1-2 tile with a fresh one for the SAME handler task id, which carries out
|
||||
// Phases 3-5. The old tile used to be left open so its last message could still be read, but
|
||||
// its process is gone by then and the terminal renders empty — it was only ever a dead
|
||||
// placeholder to close by hand. No new task is created here; see
|
||||
// InteractiveLaunchSpecService.BuildForMergeHelperHandoffAsync.
|
||||
public async System.Threading.Tasks.Task OpenMergeHelperHandoffConPtySessionAsync(string taskId, IReadOnlyList<string> survivingTaskIds)
|
||||
// List-handler handoff: the running session called handoff_list_handler at the end of a phase.
|
||||
// Opens a NEW tile for the SAME handler task id to carry out nextPhase, and deliberately leaves
|
||||
// the outgoing tile open -- it still shows that phase's finished output, which the wait/merge
|
||||
// chain can run through several times for one handler task. ConPtySessions can therefore hold
|
||||
// more than one pane per TaskId from here on; lookups that mean "the current/active session for
|
||||
// this task" (OpenConPtySessionAsync's dedupe, OnPaneSubmitForReview) use LastOrDefault so they
|
||||
// resolve to the newest tile rather than a frozen earlier phase. No new task is created here;
|
||||
// see InteractiveLaunchSpecService.BuildForMergeHelperHandoffAsync.
|
||||
public async System.Threading.Tasks.Task OpenMergeHelperHandoffConPtySessionAsync(
|
||||
string taskId, IReadOnlyList<string> survivingTaskIds, string nextPhase)
|
||||
{
|
||||
if (string.IsNullOrEmpty(taskId) || survivingTaskIds is not { Count: > 0 }) return;
|
||||
|
||||
foreach (var stale in ConPtySessions.Where(s => s.TaskId == taskId).ToList())
|
||||
CloseConPtySession(stale);
|
||||
|
||||
var baseTitle = Loc.T("missionControl.mergeHelperTitle");
|
||||
var title = baseTitle + Loc.T("missionControl.mergeHelperHandoffTitleSuffix");
|
||||
try
|
||||
@@ -310,7 +312,7 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
catch { /* best-effort title lookup */ }
|
||||
|
||||
AddConPtyPane(new ConPtyPaneViewModel(taskId, title,
|
||||
() => DescribeAsync(() => _worker.GetMergeHelperHandoffLaunchSpecAsync(taskId, survivingTaskIds))));
|
||||
() => DescribeAsync(() => _worker.GetMergeHelperHandoffLaunchSpecAsync(taskId, survivingTaskIds, nextPhase))));
|
||||
}
|
||||
|
||||
// Wires a freshly built pane and shows it immediately — the pane resolves its own launch spec,
|
||||
@@ -340,7 +342,9 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
// SubmitTaskForReviewAsync calls, with the loser flashing a spurious footer error.
|
||||
private async void OnPaneSubmitForReview(string taskId)
|
||||
{
|
||||
if (ConPtySessions.FirstOrDefault(s => s.TaskId == taskId) is not { } pane || pane.IsSubmitPending)
|
||||
// See OpenConPtySessionAsync -- LastOrDefault resolves to the newest/active pane when a
|
||||
// merge-helper handoff has left an earlier phase's frozen tile in place for this TaskId.
|
||||
if (ConPtySessions.LastOrDefault(s => s.TaskId == taskId) is not { } pane || pane.IsSubmitPending)
|
||||
return;
|
||||
|
||||
pane.IsSubmitPending = true;
|
||||
|
||||
Reference in New Issue
Block a user