- Clear ReviewFeedback only after a successful re-run so a failed/cancelled
run keeps it for a manual retry.
- Clear stale StartedAt/FinishedAt when rejecting a task back to the queue.
- Only non-planning standalone tasks gate on review (guard PlanningPhase).
- Hide "send to queue" for WaitingForReview tasks so review isn't bypassed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Planning subtasks are now "Draft" until their parent plan is finalized,
then "Planned" (queueable). Finalizing a plan no longer auto-queues the
child chain; the user sends the plan to the queue explicitly.
- TaskStateService rejects a child entering Queued/Running unless its parent
is Finalized; this single invariant covers UI, queue, RunNow and MCP paths
- WorkerHub.SetTaskStatus routes Queued through the gated EnqueueAsync
- Finalize call sites pass queueAgentTasks: false
- PlanningChainCoordinator.QueuePlanAsync guards the chain build on Finalized
- TaskRowViewModel derives Draft/Planned from ParentFinalized; gates
CanSendToQueue / CanQueuePlan; view shows a PLANNED badge
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds ForceSetStatusAsync on ITaskStateService (no transition guards)
plus SetTaskStatus / SetTaskTags / GetAllTags hub methods so the UI
can edit a task's status and tags directly. PlanningHubTests ctor
updated for the new ITaskStateService dependency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Slice 4 of the worker state consolidation refactor. Eliminates the
"queue never picks up planning tasks" bug structurally by routing both
the manager and MCP finalize paths through TaskStateService and
PlanningChainCoordinator.SetupChainAsync, where the auto-wake on enqueue
guarantees the queue picker claims the first child immediately.
- Delete TaskRepository.FinalizePlanningAsync; PlanningSessionManager
now orchestrates via _state.FinalizePlanningAsync + _chain.SetupChainAsync.
- Rename QueueSubtasksSequentiallyAsync to SetupChainAsync (internal);
layout is now Status=Queued + BlockedByTaskId, with auto-attached agent tag.
- OnChildFinishedAsync looks up the successor by BlockedByTaskId, drops
the legacy Waiting status lookup.
- PlanningMcpService.Finalize routes through state+chain; EditableStatuses
drops Waiting and adds Idle; gate uses PlanningPhase==Active.
- TaskStateService.FinalizePlanningAsync clears the planning session token.
- UI: TaskRowViewModel adds BlockedByTaskId; IsQueued/IsWaiting reflect
the new layout; TasksIslandViewModel.RemoveFromQueueAsync clears
BlockedByTaskId on dequeue.
- New regression test PlanningEndToEndTests.FinalizeAsync_FirstChildIs
ClaimedByPicker_WithinDeadline asserts the picker claims the first
child within 200ms with no manual WakeQueue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Slice 3 of the worker state and queue consolidation refactor.
- Add IQueueWaker / QueueWaker (singleton holding the wake semaphore).
- Add IQueuePicker / QueuePicker; raw SQL UPDATE...RETURNING moves out of
TaskRepository.GetNextQueuedAgentTaskAsync (deleted) and now also filters
on blocked_by_task_id IS NULL and writes started_at on claim.
- TaskStateService takes IQueueWaker directly; the Func<QueueService>
indirection is gone. State transitions to Queued auto-wake the dispatcher.
- QueueService waits via the shared waker and dispatches via the picker.
- Drop explicit _queue.WakeQueue() calls in WorkerHub.QueuePlanningSubtasksAsync
and ExternalMcpService.AddTask. The hub WakeQueue endpoint stays for
diagnostics, delegating to _waker.Wake().
- Migrate tests; pre-existing flaky AppSettings/ExternalMcp tests untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Slice 2 of the worker state consolidation refactor (spec sections 2 and 8).
Adds Worker/State/ITaskStateService + TaskStateService as the single component
that mutates Status, PlanningPhase, and BlockedByTaskId. Each transition is one
atomic ExecuteUpdate with a WHERE filter on the expected source status, so
parallel claims are TOCTOU-free. Side effects (queue wake on -> Queued, hub
TaskUpdated broadcast, chain advance + parent completion on terminal child)
are owned by the service so callers no longer need to remember them.
Migrated callers (mechanical, behavior preserved):
- TaskRunner: HandleSuccess/HandleFailure/MarkFailed/RunAsync/ContinueAsync
- StaleTaskRecovery: bulk recover stale Running tasks
- TaskResetService: status flip (worktree cleanup stays in service)
- PlanningSessionManager.StartAsync: status flip via state, token write via repo
- PlanningChainCoordinator.OnChildFinishedAsync: routes the next-sibling write
through state.UnblockAsync (Slice 4 finishes the rewrite)
- ExternalMcpService.UpdateTaskStatus: Queued case via state.EnqueueAsync
Repo Mark*Async helpers (MarkRunning/MarkDone/MarkFailed/FlipAllRunningToFailed)
are now internal; ClaudeDo.Data grants InternalsVisibleTo to ClaudeDo.Worker
and ClaudeDo.Worker.Tests for the existing repo-level tests.
DI: TaskStateService is registered as Singleton in both the main app and the
external-MCP app; the queue-wake delegate captures sp -> QueueService.WakeQueue
to break the TaskStateService -> QueueService -> TaskRunner -> TaskStateService
construction cycle. PlanningChainCoordinator takes Func<ITaskStateService> for
the same reason; Slice 3 will replace both with IQueueWaker.
Tests: TaskStateServiceTests covers happy + reject for every transition, the
parallel StartRunningAsync claim race, child-terminal chain advancement, and
stale recovery. Existing service/repo tests are updated to construct the new
state-service via a TaskStateServiceBuilder helper. Pre-existing constructor
drift in QueueService/ExternalMcp/PlanningHub tests is patched to keep the
test project building (the surrounding test logic is otherwise untouched).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>