Merge claudedo/ccd650a8d2b04a7092e81ed07c16dbe0

This commit is contained in:
mika kuns
2026-08-05 22:46:37 +02:00
18 changed files with 503 additions and 1 deletions
@@ -20,6 +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;
// Embedded ConPTY sessions (task-based only) — a manual cockpit detached from the
// review/merge/status machinery.
@@ -77,6 +78,9 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
_onConnectionRestored = () => { _ = RefreshQueueAsync(); };
_worker.ConnectionRestoredEvent += _onConnectionRestored;
_onHandoffRequested = (taskId, survivingTaskIds) => { _ = OpenMergeHelperHandoffConPtySessionAsync(taskId, survivingTaskIds); };
_worker.HandoffRequestedEvent += _onHandoffRequested;
_ = RefreshQueueAsync();
}
@@ -243,6 +247,34 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
() => DescribeAsync(() => _worker.GetMergeHelperLaunchSpecAsync(taskIds, listId))));
}
// List-handler handoff: the running session called handoff_list_handler at the end of Phase 2.
// Opens a SECOND ConPTY tile for the SAME handler task id to carry out Phases 3-5 — deliberately
// NOT deduped by TaskId like OpenMergeHelperConPtySessionAsync above, since the original tile is
// meant to stay open (Mika closes it by hand once he has seen its last message). No new task is
// created here; see InteractiveLaunchSpecService.BuildForMergeHelperHandoffAsync.
public async System.Threading.Tasks.Task OpenMergeHelperHandoffConPtySessionAsync(string taskId, IReadOnlyList<string> survivingTaskIds)
{
if (string.IsNullOrEmpty(taskId) || survivingTaskIds is not { Count: > 0 }) return;
var baseTitle = Loc.T("missionControl.mergeHelperTitle");
var title = baseTitle + Loc.T("missionControl.mergeHelperHandoffTitleSuffix");
try
{
await using var ctx = await _dbFactory.CreateDbContextAsync();
var task = await ctx.Tasks.AsNoTracking().FirstOrDefaultAsync(t => t.Id == taskId);
if (task is not null)
{
var list = await ctx.Lists.AsNoTracking().FirstOrDefaultAsync(l => l.Id == task.ListId);
if (list?.Name is { Length: > 0 } name)
title = $"{baseTitle} — {name}{Loc.T("missionControl.mergeHelperHandoffTitleSuffix")}";
}
}
catch { /* best-effort title lookup */ }
AddConPtyPane(new ConPtyPaneViewModel(taskId, title,
() => DescribeAsync(() => _worker.GetMergeHelperHandoffLaunchSpecAsync(taskId, survivingTaskIds))));
}
// Wires a freshly built pane and shows it immediately — the pane resolves its own launch spec,
// so the tile is on screen (spinner running) while the worker is still preparing the worktree.
private void AddConPtyPane(ConPtyPaneViewModel pane)
@@ -327,6 +359,7 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
_worker.TaskFinishedEvent -= _onTaskFinished;
_worker.TaskUpdatedEvent -= _onTaskUpdated;
_worker.ConnectionRestoredEvent -= _onConnectionRestored;
_worker.HandoffRequestedEvent -= _onHandoffRequested;
ConPtySessions.CollectionChanged -= OnConPtySessionsChanged;
Panes.CollectionChanged -= OnPanesChanged;
foreach (var c in ConPtySessions.ToList())