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:
@@ -474,8 +474,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
||||
foreach (var s in subs)
|
||||
Subtasks.Add(new SubtaskRowViewModel { Id = s.Id, Title = s.Title, Done = s.Completed });
|
||||
|
||||
if (entity.Status == ClaudeDo.Data.Models.TaskStatus.Planning ||
|
||||
entity.Status == ClaudeDo.Data.Models.TaskStatus.Planned)
|
||||
if (entity.PlanningPhase != ClaudeDo.Data.Models.PlanningPhase.None)
|
||||
{
|
||||
await LoadPlanningChildrenAsync(row.Id, ct);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -176,7 +176,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
static bool IsPlanningStatus(TaskStatus s) => s == TaskStatus.Planning || s == TaskStatus.Planned;
|
||||
static bool IsPlanningParent(TaskEntity t) => t.PlanningPhase != PlanningPhase.None;
|
||||
|
||||
IEnumerable<TaskEntity> filtered = list.Kind switch
|
||||
{
|
||||
@@ -185,10 +185,10 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
ListKind.Smart when list.Id == "smart:planned" => all.Where(t => t.ScheduledFor != null),
|
||||
ListKind.Virtual when list.Id == "virtual:queued" => all.Where(t =>
|
||||
(t.Status == TaskStatus.Queued && t.ParentTaskId == null) ||
|
||||
(IsPlanningStatus(t.Status) && all.Any(c => c.ParentTaskId == t.Id && (c.Status == TaskStatus.Queued || c.Status == TaskStatus.Waiting)))),
|
||||
(IsPlanningParent(t) && all.Any(c => c.ParentTaskId == t.Id && c.Status == TaskStatus.Queued))),
|
||||
ListKind.Virtual when list.Id == "virtual:running" => all.Where(t =>
|
||||
(t.Status == TaskStatus.Running && t.ParentTaskId == null) ||
|
||||
(IsPlanningStatus(t.Status) && all.Any(c => c.ParentTaskId == t.Id && c.Status == TaskStatus.Running))),
|
||||
(IsPlanningParent(t) && all.Any(c => c.ParentTaskId == t.Id && c.Status == TaskStatus.Running))),
|
||||
ListKind.Virtual when list.Id == "virtual:review" => all.Where(t => t.Status == TaskStatus.Done && t.Worktree?.State == WorktreeState.Active && t.ParentTaskId == null),
|
||||
ListKind.User => all.Where(t => $"user:{t.ListId}" == list.Id),
|
||||
_ => Enumerable.Empty<TaskEntity>(),
|
||||
@@ -437,7 +437,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity != null)
|
||||
{
|
||||
entity.Status = row.Done ? TaskStatus.Done : TaskStatus.Manual;
|
||||
entity.Status = row.Done ? TaskStatus.Done : TaskStatus.Idle;
|
||||
row.Status = entity.Status;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
@@ -493,18 +493,16 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity is null) return;
|
||||
|
||||
// For a planning parent the dequeue button targets queued/waiting children,
|
||||
// not the parent itself (whose Status is Planning/Planned).
|
||||
if (entity.Status == TaskStatus.Planning || entity.Status == TaskStatus.Planned
|
||||
|| entity.PlanningPhase != PlanningPhase.None)
|
||||
// For a planning parent the dequeue button targets queued children
|
||||
// (chain-blocked or not), not the parent itself.
|
||||
if (entity.PlanningPhase != PlanningPhase.None)
|
||||
{
|
||||
var children = await db.Tasks
|
||||
.Where(t => t.ParentTaskId == row.Id
|
||||
&& (t.Status == TaskStatus.Queued || t.Status == TaskStatus.Waiting))
|
||||
.Where(t => t.ParentTaskId == row.Id && t.Status == TaskStatus.Queued)
|
||||
.ToListAsync();
|
||||
foreach (var c in children)
|
||||
{
|
||||
c.Status = TaskStatus.Manual;
|
||||
c.Status = TaskStatus.Idle;
|
||||
c.BlockedByTaskId = null;
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
@@ -513,7 +511,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
var childRow = Items.FirstOrDefault(r => r.Id == c.Id);
|
||||
if (childRow is not null)
|
||||
{
|
||||
childRow.Status = TaskStatus.Manual;
|
||||
childRow.Status = TaskStatus.Idle;
|
||||
childRow.BlockedByTaskId = null;
|
||||
}
|
||||
}
|
||||
@@ -521,9 +519,9 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.Status = TaskStatus.Manual;
|
||||
entity.Status = TaskStatus.Idle;
|
||||
await db.SaveChangesAsync();
|
||||
row.Status = TaskStatus.Manual;
|
||||
row.Status = TaskStatus.Idle;
|
||||
}
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
@@ -565,7 +563,8 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
[RelayCommand]
|
||||
private async Task OpenPlanningSessionAsync(TaskRowViewModel? row)
|
||||
{
|
||||
if (row is null || row.Status != TaskStatus.Manual) return;
|
||||
if (row is null) return;
|
||||
if (row.Status != TaskStatus.Idle || row.PlanningPhase != PlanningPhase.None) return;
|
||||
ForegroundHelper.AllowAny();
|
||||
try { await _worker!.StartPlanningSessionAsync(row.Id); }
|
||||
catch { }
|
||||
|
||||
Reference in New Issue
Block a user