diff --git a/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs b/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs index ac31e35..28c99b6 100644 --- a/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs +++ b/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs @@ -59,7 +59,6 @@ public interface IWorkerClient : INotifyPropertyChanged Task GetMergeTargetsAsync(string taskId); Task> GetPlanningAggregateAsync(string planningTaskId); Task BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch); - Task MergeAllPlanningAsync(string planningTaskId, string targetBranch); Task ContinuePlanningMergeAsync(string planningTaskId); Task AbortPlanningMergeAsync(string planningTaskId); Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default); diff --git a/src/ClaudeDo.Ui/Services/WorkerClient.cs b/src/ClaudeDo.Ui/Services/WorkerClient.cs index 3d21695..c04998d 100644 --- a/src/ClaudeDo.Ui/Services/WorkerClient.cs +++ b/src/ClaudeDo.Ui/Services/WorkerClient.cs @@ -66,7 +66,7 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC public event Action? PrimeFired; - public string? LastMergeAllTarget { get; private set; } + public string? LastApproveTarget { get; private set; } public WorkerClient(string signalRUrl) { @@ -412,7 +412,10 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC } public Task ApproveReviewAsync(string taskId, string targetBranch) - => TryInvokeAsync("ApproveReview", taskId, targetBranch); + { + LastApproveTarget = targetBranch; + return TryInvokeAsync("ApproveReview", taskId, targetBranch); + } public Task PreviewMergeAsync(string taskId, string targetBranch) => TryInvokeAsync("PreviewMerge", taskId, targetBranch); @@ -486,12 +489,6 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC public Task BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch) => TryInvokeAsync("BuildPlanningIntegrationBranch", planningTaskId, targetBranch); - public async Task MergeAllPlanningAsync(string planningTaskId, string targetBranch) - { - LastMergeAllTarget = targetBranch; - await _hub.InvokeAsync("MergeAllPlanning", planningTaskId, targetBranch); - } - public async Task ContinuePlanningMergeAsync(string planningTaskId) { await _hub.InvokeAsync("ContinuePlanningMerge", planningTaskId); diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs index 53e0758..fdefcc9 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs @@ -393,11 +393,6 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable // Planning merge controls [ObservableProperty] private ObservableCollection _mergeTargetBranches = new(); [ObservableProperty] private string? _selectedMergeTarget; - [ObservableProperty] - [NotifyCanExecuteChangedFor(nameof(MergeAllCommand))] - private bool _canMergeAll; - [ObservableProperty] private string? _mergeAllDisabledReason; - [ObservableProperty] private string? _mergeAllError; [ObservableProperty] [NotifyPropertyChangedFor(nameof(ShowMergePreviewMuted))] @@ -580,13 +575,11 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable Subtasks.CollectionChanged += (_, _) => { - RecomputeCanMergeAll(); ReviewCombinedDiffCommand.NotifyCanExecuteChanged(); }; ChildOutcomes.CollectionChanged += (_, _) => { - RecomputeCanMergeAll(); ReviewCombinedDiffCommand.NotifyCanExecuteChanged(); NotifySessionSections(); }; @@ -836,9 +829,6 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable OnPropertyChanged(nameof(HasChildOutcomes)); MergeTargetBranches.Clear(); SelectedMergeTarget = null; - CanMergeAll = false; - MergeAllDisabledReason = null; - MergeAllError = null; SessionOutcome = null; Roadblocks = null; _claudeBuf.Clear(); @@ -993,7 +983,6 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable } } - RecomputeCanMergeAll(); } catch (OperationCanceledException) { } catch { /* best-effort */ } @@ -1090,7 +1079,6 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable } } - RecomputeCanMergeAll(); } catch (OperationCanceledException) { } catch { /* best-effort */ } @@ -1115,7 +1103,6 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable existing.WorktreeState = child.Worktree?.State ?? ClaudeDo.Data.Models.WorktreeState.Active; } - RecomputeCanMergeAll(); } catch { /* best-effort */ } } @@ -1137,54 +1124,11 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable row.Status = child.Status; row.RoadblockCount = child.RoadblockCount; row.WorktreeState = child.Worktree?.State ?? ClaudeDo.Data.Models.WorktreeState.Active; - RecomputeCanMergeAll(); - MergeAllCommand.NotifyCanExecuteChanged(); ReviewCombinedDiffCommand.NotifyCanExecuteChanged(); } catch { /* best-effort */ } } - internal void RecomputeCanMergeAll() - { - // Improvement parent: merge is allowed once every child is terminal. The - // orchestrator folds the parent's own branch and skips failed/cancelled children. - if (ChildOutcomes.Count > 0) - { - var unfinished = ChildOutcomes.Count(c => - c.Status != ClaudeDo.Data.Models.TaskStatus.Done - && c.Status != ClaudeDo.Data.Models.TaskStatus.Failed - && c.Status != ClaudeDo.Data.Models.TaskStatus.Cancelled); - if (unfinished > 0) - { - CanMergeAll = false; - MergeAllDisabledReason = $"{unfinished} improvement(s) not finished"; - return; - } - CanMergeAll = true; - MergeAllDisabledReason = null; - return; - } - - var notDone = Subtasks.Count(c => c.Status != ClaudeDo.Data.Models.TaskStatus.Done); - if (notDone > 0) - { - CanMergeAll = false; - MergeAllDisabledReason = $"{notDone} subtask(s) not done"; - return; - } - var badWt = Subtasks.FirstOrDefault(c => - c.WorktreeState == ClaudeDo.Data.Models.WorktreeState.Discarded || - c.WorktreeState == ClaudeDo.Data.Models.WorktreeState.Kept); - if (badWt is not null) - { - CanMergeAll = false; - MergeAllDisabledReason = "at least one worktree was discarded/kept"; - return; - } - CanMergeAll = true; - MergeAllDisabledReason = null; - } - [RelayCommand(CanExecute = nameof(CanReviewDiff))] private async System.Threading.Tasks.Task ReviewCombinedDiffAsync() { @@ -1196,20 +1140,6 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable private bool CanReviewDiff() => (Task?.IsPlanningParent == true && Subtasks.Any()) || HasChildOutcomes; - [RelayCommand(CanExecute = nameof(CanMergeAll))] - private async System.Threading.Tasks.Task MergeAllAsync() - { - MergeAllError = null; - try - { - await _worker.MergeAllPlanningAsync(Task!.Id, SelectedMergeTarget ?? "main"); - } - catch (Exception ex) - { - MergeAllError = ex.Message; - } - } - private async System.Threading.Tasks.Task RefreshWorktreeAsync(string taskId) { try @@ -1526,8 +1456,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable if (Task is null || !_worker.IsConnected) return; try { + var hasChildren = Subtasks.Count > 0 || ChildOutcomes.Count > 0; var result = await _worker.ApproveReviewAsync(Task.Id, SelectedMergeTarget ?? ""); - if (result?.Status == "conflict") + if (!hasChildren && result?.Status == "conflict") { if (RequestConflictResolution is not null) { @@ -1540,6 +1471,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable MergePreviewText = text; MergeIsClean = false; MergeIsConflict = true; } } + // hasChildren: conflicts arrive via PlanningMergeConflictEvent → conflict dialog } catch { /* stale review action; broadcast reconciles */ } } diff --git a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs index e1fa8ac..31e78af 100644 --- a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs @@ -155,7 +155,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable string subtaskTitle = subtaskId; string worktreePath = System.Environment.CurrentDirectory; - string targetBranch = Worker?.LastMergeAllTarget ?? "main"; + string targetBranch = Worker?.LastApproveTarget ?? "main"; try { diff --git a/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml b/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml index fb5cc35..0405baf 100644 --- a/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml @@ -305,17 +305,7 @@