Merge claudedo/973ea49ec0a642ea9b1ddcff9b71b6fe

This commit is contained in:
mika kuns
2026-08-06 13:47:35 +02:00
10 changed files with 205 additions and 5 deletions
@@ -16,6 +16,7 @@ public sealed class TaskStateService : ITaskStateService
private readonly IQueueWaker _waker;
private readonly PlanningChainCoordinator _chain;
private readonly RunCancellationRegistry _runCancels;
private readonly Func<IActiveMergeState> _mergeState;
private readonly ILogger<TaskStateService> _logger;
public TaskStateService(
@@ -24,6 +25,7 @@ public sealed class TaskStateService : ITaskStateService
IQueueWaker waker,
PlanningChainCoordinator chain,
RunCancellationRegistry runCancels,
Func<IActiveMergeState> mergeState,
ILogger<TaskStateService> logger)
{
_dbFactory = dbFactory;
@@ -31,6 +33,7 @@ public sealed class TaskStateService : ITaskStateService
_waker = waker;
_chain = chain;
_runCancels = runCancels;
_mergeState = mergeState;
_logger = logger;
}
@@ -255,6 +258,14 @@ public sealed class TaskStateService : ITaskStateService
public async Task<TransitionResult> CancelAsync(
string taskId, DateTime finishedAt, CancellationToken ct, bool allowFromIdle = false)
{
// A unit merge drains its children's worktrees onto the target branch while the parent
// sits in WaitingForReview for the whole (potentially minutes-long) drain. Cancelling out
// from under it would flip the parent to Cancelled while the orchestrator keeps merging —
// FinalizeParentDoneAsync then finds the parent no longer WaitingForReview and gives up,
// leaving the merged children's diffs stranded with no way to roll them back.
if (_mergeState().HasActiveMerge(taskId))
return new TransitionResult(false, "A merge is in progress for this task; wait for it to finish before cancelling.");
List<string> cancelledChildIds;
await using (var ctx = await _dbFactory.CreateDbContextAsync(ct))
{