fix(mission-control): show post-merge verify as Verifying instead of In review

Derived from the VerifyMerge entry in RunningOperations (no new TaskStatus). The Running now row stays, reworded to 'Verifying after merge'.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-10-01 14:50:55 +02:00
co-authored by Claude Sonnet 5.5
parent 5dae5ff679
commit 46e9303da6
4 changed files with 51 additions and 6 deletions
+2 -1
View File
@@ -384,7 +384,7 @@
"settings": "Einstellungen",
"activity": "Aktivität",
"runningNow": "Läuft gerade",
"opVerifyMerge": "Verify (Merge)",
"opVerifyMerge": "Prüfung nach Merge",
"opVerifyBatch": "Verify (Batch)",
"opMerging": "Merge",
"opRebasing": "Rebase",
@@ -393,6 +393,7 @@
"blocked": "Blockiert",
"running": "Läuft",
"review": "Im Review",
"verifying": "Wird geprüft",
"failed": "Fehlgeschlagen",
"roadblock": "Blockade gemeldet",
"openSession": "Sitzung öffnen",
+2 -1
View File
@@ -384,7 +384,7 @@
"settings": "Settings",
"activity": "Activity",
"runningNow": "Running now",
"opVerifyMerge": "Verify (merge)",
"opVerifyMerge": "Verifying after merge",
"opVerifyBatch": "Verify (batch)",
"opMerging": "Merging",
"opRebasing": "Rebasing",
@@ -393,6 +393,7 @@
"blocked": "Blocked",
"running": "Running",
"review": "In review",
"verifying": "Verifying",
"failed": "Failed",
"roadblock": "Roadblock reported",
"openSession": "Open session",
@@ -157,10 +157,12 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
try
{
var snapshot = await _worker.GetActiveOperationsAsync();
var hadVerify = RunningOperations.Any(IsVerifyMerge);
RunningOperations.Clear();
foreach (var op in snapshot.Where(o => !_endedOperationIds.Contains(o.Id)).OrderBy(o => o.StartedAt))
RunningOperations.Add(new RunningOperationViewModel(op));
NotifyOperationsChanged();
if (hadVerify || RunningOperations.Any(IsVerifyMerge)) _ = RefreshQueueAsync();
}
catch (Exception ex) { ErrorReported?.Invoke(Loc.T("missionControl.operationsRefreshFailed", ex.Message)); }
}
@@ -171,8 +173,10 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
private void AddOperation(RunningOperationDto op)
{
if (_endedOperationIds.Contains(op.Id) || RunningOperations.Any(r => r.Id == op.Id)) return;
RunningOperations.Add(new RunningOperationViewModel(op));
var row = new RunningOperationViewModel(op);
RunningOperations.Add(row);
NotifyOperationsChanged();
if (IsVerifyMerge(row)) _ = RefreshQueueAsync();
}
private void RemoveOperation(string id)
@@ -182,8 +186,12 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
if (row is null) return;
RunningOperations.Remove(row);
NotifyOperationsChanged();
if (IsVerifyMerge(row)) _ = RefreshQueueAsync();
}
/// <summary>A post-merge verify keeps its task in WaitingForReview, so only this op marks the activity row "Verifying".</summary>
private static bool IsVerifyMerge(RunningOperationViewModel r) => r.Kind == OperationKinds.VerifyMerge;
private void NotifyOperationsChanged()
{
OnPropertyChanged(nameof(HasRunningOperations));
@@ -220,7 +228,8 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
{
var id = r.Id;
var running = r.Status == ClaudeDo.Data.Models.TaskStatus.Running;
var (tint, colorKey, labelKey) = Decorate(r.Status, r.RoadblockCount, r.BlockedByTaskId != null);
var (tint, colorKey, labelKey) = Decorate(r.Status, r.RoadblockCount, r.BlockedByTaskId != null,
RunningOperations.Any(o => o.Kind == OperationKinds.VerifyMerge && o.TaskId == id));
Queued.Add(new QueuedTaskViewModel
{
Id = id,
@@ -243,12 +252,14 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
// Row tint + status label for the activity strip. Keys are resolved to brushes by
// DotBrushConverter ("<key>Brush"), so they must match tokens in Tokens.axaml.
private static (string? Tint, string? ColorKey, string? LabelKey) Decorate(
ClaudeDo.Data.Models.TaskStatus status, int roadblockCount, bool blocked)
ClaudeDo.Data.Models.TaskStatus status, int roadblockCount, bool blocked, bool verifying)
=> status switch
{
ClaudeDo.Data.Models.TaskStatus.Failed => ("ErrorTint", "StatusError", "missionControl.failed"),
ClaudeDo.Data.Models.TaskStatus.WaitingForReview when roadblockCount > 0
=> ("RoadblockTint", "Amber", "missionControl.roadblock"),
ClaudeDo.Data.Models.TaskStatus.WaitingForReview when verifying
=> ("RunningTint", "StatusRunning", "missionControl.verifying"),
ClaudeDo.Data.Models.TaskStatus.WaitingForReview => ("DoneTint", "StatusReview", "missionControl.review"),
ClaudeDo.Data.Models.TaskStatus.Running => ("RunningTint", "StatusRunning", "missionControl.running"),
_ when blocked => (null, "Amber", "missionControl.blocked"),
@@ -890,7 +901,11 @@ public sealed partial class RunningOperationViewModel : ObservableObject
private readonly DateTime _startedAtUtc;
public string Id { get; }
/// <summary>Localized operation kind ("Verify (merge)", "Rebasing", ...).</summary>
/// <summary>Wire kind token (<see cref="OperationKinds"/>).</summary>
public string Kind { get; }
/// <summary>The task the operation runs for; null for a batch verify.</summary>
public string? TaskId { get; }
/// <summary>Localized operation kind ("Verifying after merge", "Rebasing", ...).</summary>
public string KindLabel { get; }
/// <summary><c>#Number Title</c> for a task operation, the list name for a batch verify.</summary>
public string Subject { get; }
@@ -900,6 +915,8 @@ public sealed partial class RunningOperationViewModel : ObservableObject
public RunningOperationViewModel(RunningOperationDto op)
{
Id = op.Id;
Kind = op.Kind;
TaskId = op.TaskId;
_startedAtUtc = op.StartedAt;
KindLabel = op.Kind switch
{
@@ -1,4 +1,6 @@
using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels;
using Microsoft.EntityFrameworkCore;
@@ -126,6 +128,30 @@ public class MissionControlRunningOperationsTests : IDisposable
Assert.Equal("c", Assert.Single(vm.RunningOperations).Id);
}
[Fact]
public async Task VerifyMergeOperation_ShowsReviewTaskAsVerifying_UntilItEnds()
{
await using (var db = NewContext())
{
db.Lists.Add(new ListEntity { Id = "L1", Name = "Work", CreatedAt = DateTime.UtcNow });
db.Tasks.Add(new TaskEntity { Number = TestTaskNumbers.Next(), Id = "rev1", ListId = "L1", Title = "review", Status = ClaudeDo.Data.Models.TaskStatus.WaitingForReview, CreatedAt = DateTime.UtcNow });
await db.SaveChangesAsync();
}
var worker = new FakeWorker();
using var vm = BuildVm(worker);
await vm.RefreshQueueAsync();
Assert.Equal(Loc.T("missionControl.review"), vm.Queued.Single().StatusLabel);
worker.RaiseOperationStarted(new RunningOperationDto("v1", OperationKinds.VerifyMerge, "rev1", 1, "L1", "review", DateTime.UtcNow));
await vm.RefreshQueueAsync();
Assert.Equal(Loc.T("missionControl.verifying"), vm.Queued.Single().StatusLabel);
Assert.Equal("StatusRunning", vm.Queued.Single().StatusColorKey);
worker.RaiseOperationEnded("v1");
await vm.RefreshQueueAsync();
Assert.Equal(Loc.T("missionControl.review"), vm.Queued.Single().StatusLabel);
}
[Fact]
public async Task TickOperations_UpdatesElapsed()
{