feat(mission-control): give the list handler its own review task
"Let Claude handle it" now creates one ClaudeDo task per run to host the ConPTY session (Idle/IsManual, never queued) instead of an untracked ad-hoc tile, so the run has a real title, diff, and review outcome. Since the handler merges its own changes straight into the list's working dir, the task never gets a WorktreeEntity; its review range lives as new HandlerBaseCommit/HandlerHeadCommit columns on TaskEntity instead, reusing the existing commit-range diff machinery and keeping it out of the worktrees overview entirely.
This commit is contained in:
@@ -571,8 +571,11 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
Model = entity.Model;
|
||||
_listWorkingDir = entity.List?.WorkingDir;
|
||||
WorktreePath = entity.Worktree?.Path;
|
||||
WorktreeBaseCommit = entity.Worktree?.BaseCommit;
|
||||
WorktreeHeadCommit = entity.Worktree?.HeadCommit;
|
||||
// A worktree-less list-handler host task (Mission Control's "Let Claude handle it")
|
||||
// has no WorktreeEntity, so its review range falls back to HandlerBaseCommit/
|
||||
// HandlerHeadCommit -- see TaskEntity for why.
|
||||
WorktreeBaseCommit = entity.Worktree?.BaseCommit ?? entity.HandlerBaseCommit;
|
||||
WorktreeHeadCommit = entity.Worktree?.HeadCommit ?? entity.HandlerHeadCommit;
|
||||
WorktreeStateLabel = entity.Worktree?.State.ToString();
|
||||
BranchLine = entity.Worktree is { } w ? $"{w.BranchName} ← main" : null;
|
||||
var (add, del) = ParseDiffStat(entity.Worktree?.DiffStat);
|
||||
@@ -778,8 +781,11 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
|
||||
_listWorkingDir = entity.List?.WorkingDir;
|
||||
WorktreePath = entity.Worktree?.Path;
|
||||
WorktreeBaseCommit = entity.Worktree?.BaseCommit;
|
||||
WorktreeHeadCommit = entity.Worktree?.HeadCommit;
|
||||
// A worktree-less list-handler host task (Mission Control's "Let Claude handle it")
|
||||
// has no WorktreeEntity, so its review range falls back to HandlerBaseCommit/
|
||||
// HandlerHeadCommit -- see TaskEntity for why.
|
||||
WorktreeBaseCommit = entity.Worktree?.BaseCommit ?? entity.HandlerBaseCommit;
|
||||
WorktreeHeadCommit = entity.Worktree?.HeadCommit ?? entity.HandlerHeadCommit;
|
||||
WorktreeStateLabel = entity.Worktree?.State.ToString();
|
||||
BranchLine = entity.Worktree is { } w ? $"{w.BranchName} ← main" : null;
|
||||
if (Task is { } row && entity.Worktree?.DiffStat is { } stat)
|
||||
|
||||
@@ -42,8 +42,11 @@ public sealed partial class MergeSectionViewModel : ViewModelBase
|
||||
public bool ShowMergePreviewMuted =>
|
||||
!MergeIsClean && !MergeIsConflict && !string.IsNullOrEmpty(MergePreviewText);
|
||||
|
||||
// CanDiffMergedRange covers a worktree-less list-handler host task (no live worktree, but
|
||||
// a HandlerBaseCommit/HandlerHeadCommit review range over the list's working dir) so its
|
||||
// merge/diff card still renders even though _worktreePath stays null for it.
|
||||
public bool ShowMergeSection =>
|
||||
_worktreePath != null || _isPlanningParent || _hasChildOutcomes;
|
||||
_worktreePath != null || _isPlanningParent || _hasChildOutcomes || CanDiffMergedRange;
|
||||
|
||||
public Func<DiffViewerViewModel, System.Threading.Tasks.Task>? ShowDiffViewer { get; set; }
|
||||
public Func<MergeModalViewModel, System.Threading.Tasks.Task>? ShowMergeModal { get; set; }
|
||||
|
||||
@@ -294,22 +294,44 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
return System.Threading.Tasks.Task.CompletedTask;
|
||||
}
|
||||
|
||||
// List-handler session over a hand-picked set of tasks ("Let Claude handle it").
|
||||
// Ad-hoc style: no owning task, never deduped — every run opens a fresh pane.
|
||||
// List-handler session over a hand-picked set of tasks ("Let Claude handle it"). Task-based:
|
||||
// creates one new ClaudeDo task per run to own the session (title/diff/result), deduped by
|
||||
// TaskId like OpenConPtySessionAsync. The handler still merges the tasks it handles itself
|
||||
// (no worktree of its own) — see TaskEntity.HandlerBaseCommit/HandlerHeadCommit.
|
||||
public async System.Threading.Tasks.Task OpenMergeHelperConPtySessionAsync(string listId, IReadOnlyList<string> taskIds)
|
||||
{
|
||||
if (taskIds is not { Count: > 0 }) return;
|
||||
|
||||
var title = Loc.T("missionControl.mergeHelperTitle");
|
||||
var listName = listId;
|
||||
try
|
||||
{
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
||||
var list = await ctx.Lists.AsNoTracking().FirstOrDefaultAsync(l => l.Id == listId);
|
||||
if (list?.Name is { Length: > 0 } name) title = $"{title} — {name}";
|
||||
if (list?.Name is { Length: > 0 } name) { listName = name; title = $"{title} — {name}"; }
|
||||
}
|
||||
catch { /* best-effort title lookup */ }
|
||||
|
||||
AddConPtyPane(ConPtyPaneViewModel.CreateAdHoc(title,
|
||||
string taskId;
|
||||
try
|
||||
{
|
||||
taskId = await _worker.CreateMergeHelperTaskAsync(taskIds, listId,
|
||||
Loc.T("missionControl.mergeHelperTaskTitle", listName),
|
||||
Loc.T("missionControl.mergeHelperTaskDescriptionHeader"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorReported?.Invoke(Loc.T("missionControl.conptyLaunchFailed", ex.Message));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConPtySessions.FirstOrDefault(s => s.TaskId == taskId) is { } existing)
|
||||
{
|
||||
FocusedPane = existing;
|
||||
return;
|
||||
}
|
||||
|
||||
AddConPtyPane(new ConPtyPaneViewModel(taskId, title,
|
||||
() => DescribeAsync(() => _worker.GetMergeHelperLaunchSpecAsync(taskIds, listId))));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user