fix(worker): kill cancelled runs' processes and make MCP approve actually merge

- CancelAsync now signals the running Claude process of the cancelled task and
  its cascaded children via the new RunCancellationRegistry (queue + override
  slots register their CTS there) instead of only flipping DB state.
- external MCP review_task 'approve' now mirrors the hub's ApproveReview:
  unit merge for parents, ApproveAndMergeAsync for childless tasks, optional
  targetBranch; ReviewTaskResult carries mergeStatus/conflicts.
This commit is contained in:
mika kuns
2026-07-23 20:24:36 +02:00
parent 451afc80f8
commit fee69998f8
15 changed files with 330 additions and 46 deletions
@@ -15,6 +15,7 @@ public sealed class TaskStateService : ITaskStateService
private readonly HubBroadcaster _broadcaster;
private readonly IQueueWaker _waker;
private readonly PlanningChainCoordinator _chain;
private readonly RunCancellationRegistry _runCancels;
private readonly ILogger<TaskStateService> _logger;
public TaskStateService(
@@ -22,12 +23,14 @@ public sealed class TaskStateService : ITaskStateService
HubBroadcaster broadcaster,
IQueueWaker waker,
PlanningChainCoordinator chain,
RunCancellationRegistry runCancels,
ILogger<TaskStateService> logger)
{
_dbFactory = dbFactory;
_broadcaster = broadcaster;
_waker = waker;
_chain = chain;
_runCancels = runCancels;
_logger = logger;
}
@@ -255,6 +258,13 @@ public sealed class TaskStateService : ITaskStateService
.SetProperty(t => t.BlockedByTaskId, (string?)null), ct);
}
// Also stop the actual Claude processes: the DB flip above doesn't reach a run
// already executing in a queue/override slot — without this it would keep going
// and only fail its final (now-invalid) transition.
_runCancels.TryCancel(taskId);
foreach (var childId in cancelledChildIds)
_runCancels.TryCancel(childId);
await OnChildTerminalAsync(taskId, TaskStatus.Cancelled);
await _broadcaster.TaskUpdated(taskId);
foreach (var childId in cancelledChildIds)