fix(ui): live-update child outcomes + enable Review combined diff for improvement parents

This commit is contained in:
mika kuns
2026-06-04 16:53:43 +02:00
parent 469e68bbc8
commit a3f407b0e5

View File

@@ -358,6 +358,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
_worker.TaskStartedEvent += (slot, taskId, startedAt) =>
{
if (Task?.Id == taskId) AgentState = "running";
_ = RefreshChildOutcomeAsync(taskId);
};
_worker.TaskFinishedEvent += (slot, taskId, status, finishedAt) =>
{
@@ -371,18 +372,21 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
AgentState = FinishedStatusToStateKey(status);
// Re-query to pick up worktree created during the run.
_ = RefreshWorktreeAsync(taskId);
_ = RefreshChildOutcomeAsync(taskId);
};
_worker.WorktreeUpdatedEvent += taskId =>
{
if (Task?.Id == taskId) _ = RefreshWorktreeAsync(taskId);
if (Task?.IsPlanningParent == true) _ = RefreshPlanningChildAsync(taskId);
_ = RefreshChildOutcomeAsync(taskId);
};
_worker.TaskUpdatedEvent += taskId =>
{
if (Task?.Id == taskId) _ = RefreshStatusAsync(taskId);
if (Task?.IsPlanningParent == true) _ = RefreshPlanningChildAsync(taskId);
_ = RefreshChildOutcomeAsync(taskId);
};
Subtasks.CollectionChanged += (_, _) =>
@@ -391,6 +395,12 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
ReviewCombinedDiffCommand.NotifyCanExecuteChanged();
};
ChildOutcomes.CollectionChanged += (_, _) =>
{
RecomputeCanMergeAll();
ReviewCombinedDiffCommand.NotifyCanExecuteChanged();
};
PrepLog.CollectionChanged += (_, _) => OnPropertyChanged(nameof(ShowPrepEmptyState));
}
@@ -887,6 +897,30 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
catch { /* best-effort */ }
}
// Live-update a single improvement child's outcome row from a task event. No-op if the
// updated task isn't one of this parent's children.
private async System.Threading.Tasks.Task RefreshChildOutcomeAsync(string childTaskId)
{
var row = ChildOutcomes.FirstOrDefault(c => c.Id == childTaskId);
if (row is null) return;
try
{
await using var ctx = await _dbFactory.CreateDbContextAsync();
var child = await ctx.Tasks
.AsNoTracking()
.Include(t => t.Worktree)
.FirstOrDefaultAsync(t => t.Id == childTaskId);
if (child is null) return;
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
@@ -937,7 +971,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
await ShowPlanningDiffModal(vm);
}
private bool CanReviewDiff() => Task?.IsPlanningParent == true && Subtasks.Any();
private bool CanReviewDiff() => (Task?.IsPlanningParent == true && Subtasks.Any()) || HasChildOutcomes;
[RelayCommand(CanExecute = nameof(CanMergeAll))]
private async System.Threading.Tasks.Task MergeAllAsync()
@@ -1283,14 +1317,24 @@ public sealed partial class SubtaskRowViewModel : ViewModelBase
[ObservableProperty] private ClaudeDo.Data.Models.WorktreeState _worktreeState = ClaudeDo.Data.Models.WorktreeState.Active;
}
// Read-only row on an improvement parent's review card: a suggested child's outcome.
public sealed class ChildOutcomeRowViewModel
// A suggested child's outcome on an improvement parent's review card. Observable so the
// row reflects the child's live status (Idle → Running → Done/Failed) as it executes.
public sealed partial class ChildOutcomeRowViewModel : ViewModelBase
{
public required string Id { get; init; }
public required string Title { get; init; }
public required ClaudeDo.Data.Models.TaskStatus Status { get; init; }
public int RoadblockCount { get; init; }
public ClaudeDo.Data.Models.WorktreeState WorktreeState { get; init; }
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(StatusLabel))]
private ClaudeDo.Data.Models.TaskStatus _status;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasRoadblock))]
[NotifyPropertyChangedFor(nameof(RoadblockText))]
private int _roadblockCount;
[ObservableProperty]
private ClaudeDo.Data.Models.WorktreeState _worktreeState = ClaudeDo.Data.Models.WorktreeState.Active;
public string StatusLabel => Status switch
{