Merge claudedo/973ea49ec0a642ea9b1ddcff9b71b6fe

This commit is contained in:
mika kuns
2026-08-06 13:47:35 +02:00
10 changed files with 205 additions and 5 deletions
@@ -35,6 +35,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
private readonly Action<string, string, string, DateTime> _workerTaskFinishedHandler;
private readonly Action<string> _workerWorktreeUpdatedHandler;
private readonly Action<string> _workerTaskUpdatedHandler;
private readonly Action<string, string> _workerPlanningMergeStartedHandler;
private readonly Action<string> _workerPlanningMergeAbortedHandler;
private readonly Action<string> _workerPlanningCompletedHandler;
[ObservableProperty] private bool _isNotesMode;
[ObservableProperty] private bool _isPrepMode;
@@ -351,6 +354,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
ResetAndRetryCommand.NotifyCanExecuteChanged();
ContinueCommand.NotifyCanExecuteChanged();
SendRoadblockReplyCommand.NotifyCanExecuteChanged();
CancelReviewCommand.NotifyCanExecuteChanged();
}
};
_worker.PropertyChanged += _workerPropertyChangedHandler;
@@ -392,6 +396,24 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
};
_worker.TaskUpdatedEvent += _workerTaskUpdatedHandler;
_workerPlanningMergeStartedHandler = (planningTaskId, _) =>
{
if (Task?.Id == planningTaskId) IsMergeDraining = true;
};
_worker.PlanningMergeStartedEvent += _workerPlanningMergeStartedHandler;
_workerPlanningMergeAbortedHandler = planningTaskId =>
{
if (Task?.Id == planningTaskId) IsMergeDraining = false;
};
_worker.PlanningMergeAbortedEvent += _workerPlanningMergeAbortedHandler;
_workerPlanningCompletedHandler = planningTaskId =>
{
if (Task?.Id == planningTaskId) IsMergeDraining = false;
};
_worker.PlanningCompletedEvent += _workerPlanningCompletedHandler;
ChildOutcomes.CollectionChanged += (_, _) =>
{
Merge.SyncChildOutcomes(HasChildOutcomes, Subtasks.Count);
@@ -409,6 +431,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
_worker.TaskFinishedEvent -= _workerTaskFinishedHandler;
_worker.WorktreeUpdatedEvent -= _workerWorktreeUpdatedHandler;
_worker.TaskUpdatedEvent -= _workerTaskUpdatedHandler;
_worker.PlanningMergeStartedEvent -= _workerPlanningMergeStartedHandler;
_worker.PlanningMergeAbortedEvent -= _workerPlanningMergeAbortedHandler;
_worker.PlanningCompletedEvent -= _workerPlanningCompletedHandler;
AgentSettings.Dispose();
Prep.Dispose();
}
@@ -438,6 +463,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
DequeueCommand.NotifyCanExecuteChanged();
ResetAndRetryCommand.NotifyCanExecuteChanged();
ContinueCommand.NotifyCanExecuteChanged();
CancelReviewCommand.NotifyCanExecuteChanged();
// A state change means a new run/review cycle: the diff must be
// re-inspected before merge can be approved again.
ReviewDiffViewed = false;
@@ -547,6 +573,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
OnPropertyChanged(nameof(TaskIdBadge));
Monitor.Reset();
RoadblockReplyDraft = string.Empty;
IsMergeDraining = false;
Subtasks.Clear();
ChildOutcomes.Clear();
Attachments.Clear();
@@ -1200,14 +1227,28 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
catch { /* stale review action; broadcast reconciles */ }
}
[RelayCommand]
// True while a unit merge is actively draining this task's subtasks onto the target
// branch — set from PlanningMergeStarted, cleared on PlanningMergeAborted/Completed.
// Mirrors the worker-side guard in TaskStateService.CancelAsync (the real correctness
// fix); this just keeps the button from inviting a click the worker will reject anyway.
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(CancelReviewCommand))]
private bool _isMergeDraining;
[RelayCommand(CanExecute = nameof(CanCancelReview))]
private async System.Threading.Tasks.Task CancelReviewAsync()
{
if (Task is null || !_worker.IsConnected) return;
try { await _worker.CancelReviewAsync(Task.Id); }
catch { /* stale review action; broadcast reconciles */ }
catch (Exception ex)
{
ErrorReported?.Invoke(ex.Message);
}
}
private bool CanCancelReview() =>
Task != null && _worker.IsConnected && !IsMergeDraining;
private async System.Threading.Tasks.Task ReloadAttachmentsAsync()
{
if (Task is null) return;