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
+6 -1
View File
@@ -19,6 +19,7 @@ public sealed class QueueService : BackgroundService
private readonly IQueuePicker _picker;
private readonly OverrideSlotService _override;
private readonly ITaskStateService _state;
private readonly RunCancellationRegistry _runCancels;
private readonly object _lock = new();
private readonly Dictionary<string, QueueSlotState> _queueSlots = new();
@@ -31,7 +32,8 @@ public sealed class QueueService : BackgroundService
QueueWaker waker,
IQueuePicker picker,
OverrideSlotService overrideSlot,
ITaskStateService state)
ITaskStateService state,
RunCancellationRegistry runCancels)
{
_dbFactory = dbFactory;
_runner = runner;
@@ -41,6 +43,7 @@ public sealed class QueueService : BackgroundService
_picker = picker;
_override = overrideSlot;
_state = state;
_runCancels = runCancels;
}
public IReadOnlyList<(string slot, string taskId, DateTime startedAt)> GetActive()
@@ -125,12 +128,14 @@ public sealed class QueueService : BackgroundService
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
_queueSlots[task.Id] = new QueueSlotState { TaskId = task.Id, StartedAt = DateTime.UtcNow, Cts = cts };
_runCancels.Register(task.Id, cts);
_ = RunInSlotAsync(task.Id, cts.Token).ContinueWith(t =>
{
if (t.IsFaulted)
_logger.LogError(t.Exception, "RunInSlotAsync failed for task {TaskId} in queue slot", task.Id);
lock (_lock) { _queueSlots.Remove(task.Id); }
_runCancels.Unregister(task.Id, cts);
cts.Dispose();
_waker.Wake(); // Check for next task immediately.
}, TaskScheduler.Default);