refactor(data): retire legacy TaskStatus values and backfill existing rows

Slice 6 of the worker state and queue consolidation refactor.

* Drop Manual, Planning, Planned, Draft, Waiting from the TaskStatus enum
  and from the EF value converter; only the lifecycle values remain
  (Idle, Queued, Running, Done, Failed, Cancelled).
* Add migration RetireLegacyTaskStatus that rewrites existing rows:
  manual/draft -> idle, planning -> idle+planning_phase=active,
  planned -> idle+planning_phase=finalized, waiting -> queued+blocked_by
  derived from sort_order via a CTE with LAG().
* Reroute every call site that compared/set legacy values to the new
  three-field model (Status + PlanningPhase + BlockedByTaskId), including
  the planning repo helpers, MCP services, the planning chain coordinator,
  and the UI view-models. TaskRowViewModel now exposes PlanningPhase to
  drive the planning badge.
* Refresh Worker/CLAUDE.md and Data/CLAUDE.md, the docs/plan.md status
  section, and the planning verification notes in docs/open.md.
This commit is contained in:
Mika Kuns
2026-04-27 15:28:55 +02:00
parent ff7c239959
commit dc3fc443b4
37 changed files with 306 additions and 229 deletions

View File

@@ -15,6 +15,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
[ObservableProperty] private bool _isMyDay;
[ObservableProperty] private bool _isSelected;
[ObservableProperty] private TaskStatus _status;
[ObservableProperty] private PlanningPhase _planningPhase;
[ObservableProperty] private string? _branch;
[ObservableProperty] private string? _diffStat;
[ObservableProperty] private string? _liveTail;
@@ -37,19 +38,20 @@ public sealed partial class TaskRowViewModel : ViewModelBase
public int StepsCompleted { get; init; }
public bool IsChild => !string.IsNullOrEmpty(ParentTaskId);
public bool IsPlanningParent => Status == TaskStatus.Planning
|| Status == TaskStatus.Planned
public bool IsPlanningParent => PlanningPhase != PlanningPhase.None
|| HasPlanningChildren;
public bool IsDraft => Status == TaskStatus.Draft;
public bool IsDraft => IsChild && Status == TaskStatus.Idle;
public bool CanOpenPlanningSession => Status == TaskStatus.Manual && !IsChild;
public bool CanResumeOrDiscardPlanning => Status == TaskStatus.Planning;
public bool CanOpenPlanningSession => Status == TaskStatus.Idle
&& PlanningPhase == PlanningPhase.None
&& !IsChild;
public bool CanResumeOrDiscardPlanning => PlanningPhase == PlanningPhase.Active;
public string? PlanningBadge => Status switch
public string? PlanningBadge => PlanningPhase switch
{
TaskStatus.Planning => "PLANNING",
TaskStatus.Planned => "PLANNED",
_ => null,
PlanningPhase.Active => "PLANNING",
PlanningPhase.Finalized => "PLANNED",
_ => null,
};
public bool HasBranch => !string.IsNullOrWhiteSpace(Branch);
@@ -59,8 +61,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
public bool IsOverdue => ScheduledFor is { } d && d.Date < DateTime.Today && !Done;
public bool IsRunning => Status == TaskStatus.Running;
public bool IsQueued => Status == TaskStatus.Queued && string.IsNullOrEmpty(BlockedByTaskId);
public bool IsWaiting => (Status == TaskStatus.Queued && !string.IsNullOrEmpty(BlockedByTaskId))
|| Status == TaskStatus.Waiting;
public bool IsWaiting => Status == TaskStatus.Queued && !string.IsNullOrEmpty(BlockedByTaskId);
public bool CanRemoveFromQueue => IsQueued || HasQueuedSubtasks;
public bool HasSchedule => ScheduledFor.HasValue;
public bool HasLiveTail => IsRunning && !string.IsNullOrEmpty(LiveTail);
@@ -71,13 +72,12 @@ public sealed partial class TaskRowViewModel : ViewModelBase
public string StatusChipClass => (Status, IsBlocked: !string.IsNullOrEmpty(BlockedByTaskId)) switch
{
(TaskStatus.Running, _) => "running",
(TaskStatus.Failed, _) => "error",
(TaskStatus.Done, _) => "review",
(TaskStatus.Running, _) => "running",
(TaskStatus.Failed, _) => "error",
(TaskStatus.Done, _) => "review",
(TaskStatus.Queued, true) => "waiting",
(TaskStatus.Queued, false) => "queued",
(TaskStatus.Waiting, _) => "waiting",
_ => "idle",
_ => "idle",
};
partial void OnStatusChanged(TaskStatus value)
@@ -87,14 +87,19 @@ public sealed partial class TaskRowViewModel : ViewModelBase
OnPropertyChanged(nameof(IsQueued));
OnPropertyChanged(nameof(IsWaiting));
OnPropertyChanged(nameof(HasLiveTail));
OnPropertyChanged(nameof(IsPlanningParent));
OnPropertyChanged(nameof(PlanningBadge));
OnPropertyChanged(nameof(IsDraft));
OnPropertyChanged(nameof(CanOpenPlanningSession));
OnPropertyChanged(nameof(CanResumeOrDiscardPlanning));
OnPropertyChanged(nameof(CanRemoveFromQueue));
}
partial void OnPlanningPhaseChanged(PlanningPhase value)
{
OnPropertyChanged(nameof(IsPlanningParent));
OnPropertyChanged(nameof(PlanningBadge));
OnPropertyChanged(nameof(CanOpenPlanningSession));
OnPropertyChanged(nameof(CanResumeOrDiscardPlanning));
}
partial void OnHasQueuedSubtasksChanged(bool value)
=> OnPropertyChanged(nameof(CanRemoveFromQueue));
@@ -141,6 +146,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
IsStarred = t.IsStarred;
IsMyDay = t.IsMyDay;
Status = t.Status;
PlanningPhase = t.PlanningPhase;
Branch = t.Worktree?.BranchName;
DiffStat = t.Worktree?.DiffStat;
ScheduledFor = t.ScheduledFor;