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

@@ -144,10 +144,10 @@ public sealed class TaskStateService : ITaskStateService
{
await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
var affected = await ctx.Tasks
.Where(t => t.Id == parentId &&
(t.Status == TaskStatus.Manual || t.Status == TaskStatus.Idle))
.Where(t => t.Id == parentId
&& t.Status == TaskStatus.Idle
&& t.PlanningPhase == PlanningPhase.None)
.ExecuteUpdateAsync(s => s
.SetProperty(t => t.Status, TaskStatus.Planning)
.SetProperty(t => t.PlanningPhase, PlanningPhase.Active), ct);
if (affected == 0)
@@ -198,13 +198,6 @@ public sealed class TaskStateService : ITaskStateService
if (affected == 0)
return new TransitionResult(false, "Task not found.");
// Bridge to legacy chain layout: a Waiting predecessor-blocked sibling becomes Queued
// when its predecessor finishes. New layout (post-Slice 4) stores siblings as
// Status=Queued + BlockedByTaskId set, so this is a no-op for them.
await ctx.Tasks
.Where(t => t.Id == taskId && t.Status == TaskStatus.Waiting)
.ExecuteUpdateAsync(s => s.SetProperty(t => t.Status, TaskStatus.Queued), ct);
_waker.Wake();
await _broadcaster.TaskUpdated(taskId);
return new TransitionResult(true, null);