feat(planning): consolidate finalize+chain via TaskStateService, fix queue pickup
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>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
064a903076
commit
4ab906ff0b
@@ -24,6 +24,7 @@ public sealed class PlanningSessionManager
|
||||
private readonly WorkerConfig _cfg;
|
||||
private readonly string _rootDirectory;
|
||||
private readonly ITaskStateService? _state;
|
||||
private readonly PlanningChainCoordinator? _chain;
|
||||
|
||||
// DI constructor.
|
||||
public PlanningSessionManager(
|
||||
@@ -31,12 +32,14 @@ public sealed class PlanningSessionManager
|
||||
GitService git,
|
||||
WorkerConfig cfg,
|
||||
ITaskStateService state,
|
||||
PlanningChainCoordinator chain,
|
||||
string rootDirectory)
|
||||
{
|
||||
_factory = factory;
|
||||
_git = git;
|
||||
_cfg = cfg;
|
||||
_state = state;
|
||||
_chain = chain;
|
||||
_rootDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
@@ -48,7 +51,8 @@ public sealed class PlanningSessionManager
|
||||
GitService git,
|
||||
WorkerConfig cfg,
|
||||
string rootDirectory,
|
||||
ITaskStateService? state = null)
|
||||
ITaskStateService? state = null,
|
||||
PlanningChainCoordinator? chain = null)
|
||||
{
|
||||
_tasksOverride = tasks;
|
||||
_listsOverride = lists;
|
||||
@@ -56,6 +60,7 @@ public sealed class PlanningSessionManager
|
||||
_git = git;
|
||||
_cfg = cfg;
|
||||
_state = state;
|
||||
_chain = chain;
|
||||
_rootDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
@@ -194,7 +199,21 @@ public sealed class PlanningSessionManager
|
||||
var (tasks, lists, settings, ctx) = CreateRepos();
|
||||
await using var __ = ctx;
|
||||
|
||||
var count = await tasks.FinalizePlanningAsync(taskId, queueAgentTasks, ct);
|
||||
if (_state is null || _chain is null)
|
||||
throw new InvalidOperationException(
|
||||
"PlanningSessionManager.FinalizeAsync requires ITaskStateService and PlanningChainCoordinator.");
|
||||
|
||||
var finalizeResult = await _state.FinalizePlanningAsync(taskId, ct);
|
||||
if (!finalizeResult.Ok)
|
||||
throw new InvalidOperationException(
|
||||
finalizeResult.Reason ?? $"Could not finalize planning for task {taskId}.");
|
||||
|
||||
int count = 0;
|
||||
var children = await tasks.GetChildrenAsync(taskId, ct);
|
||||
if (queueAgentTasks && children.Count > 0)
|
||||
count = await _chain.SetupChainAsync(taskId, ct);
|
||||
else
|
||||
count = children.Count;
|
||||
|
||||
// Best-effort cleanup — don't block finalization on git state.
|
||||
await TryCleanupWorktreeAsync(taskId, lists, settings, ct);
|
||||
|
||||
Reference in New Issue
Block a user