From 9d04d1d9f6df47ace8818a0284ecf692f226a436 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Fri, 24 Apr 2026 16:24:24 +0200 Subject: [PATCH] fix(worker): reorder PlanningAggregator checkout/delete and kill git on cancel Also stub new IWorkerClient planning members in FakeWorkerClient to restore build. Co-Authored-By: Claude Sonnet 4.6 --- .../Planning/PlanningAggregator.cs | 16 +++++++++++++--- .../UiVm/TasksIslandViewModelPlanningTests.cs | 13 +++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/ClaudeDo.Worker/Planning/PlanningAggregator.cs b/src/ClaudeDo.Worker/Planning/PlanningAggregator.cs index 9866378..3938cdc 100644 --- a/src/ClaudeDo.Worker/Planning/PlanningAggregator.cs +++ b/src/ClaudeDo.Worker/Planning/PlanningAggregator.cs @@ -78,11 +78,13 @@ public sealed class PlanningAggregator var integrationBranch = BuildIntegrationBranchName(planning); - // Reset: delete if exists, then recreate off the target branch. + // Reset: checkout target first (so we're never ON the integration branch when deleting it), + // then delete if exists, then recreate off the target branch. + await _git.CheckoutBranchAsync(repoDir, targetBranch, ct); + try { await _git.BranchDeleteAsync(repoDir, integrationBranch, force: true, ct); } catch { /* didn't exist */ } - await _git.CheckoutBranchAsync(repoDir, targetBranch, ct); await GitRawAsync(repoDir, ct, "checkout", "-b", integrationBranch); foreach (var child in childSubtasks) @@ -150,7 +152,15 @@ public sealed class PlanningAggregator using var p = System.Diagnostics.Process.Start(psi)!; var stdoutTask = p.StandardOutput.ReadToEndAsync(); var stderrTask = p.StandardError.ReadToEndAsync(); - await p.WaitForExitAsync(ct); + try + { + await p.WaitForExitAsync(ct); + } + catch (OperationCanceledException) + { + try { if (!p.HasExited) p.Kill(entireProcessTree: true); } catch { } + throw; + } var stdout = await stdoutTask; var stderr = await stderrTask; if (p.ExitCode != 0) throw new InvalidOperationException($"git {string.Join(' ', args)} failed: {stderr}"); diff --git a/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs b/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs index d74a141..51d2bee 100644 --- a/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs +++ b/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs @@ -32,6 +32,19 @@ sealed class FakeWorkerClient : IWorkerClient public Task DiscardPlanningSessionAsync(string taskId, CancellationToken ct = default) { DiscardPlanningCalls++; return Task.CompletedTask; } public Task FinalizePlanningSessionAsync(string taskId, bool queueAgentTasks = true, CancellationToken ct = default) { FinalizePlanningCalls++; return Task.CompletedTask; } public Task GetPendingDraftCountAsync(string taskId, CancellationToken ct = default) => Task.FromResult(0); + + public event Action? PlanningMergeStartedEvent; + public event Action? PlanningSubtaskMergedEvent; + public event Action>? PlanningMergeConflictEvent; + public event Action? PlanningMergeAbortedEvent; + public event Action? PlanningCompletedEvent; + + public Task GetMergeTargetsAsync(string taskId) => Task.FromResult(null); + public Task> GetPlanningAggregateAsync(string planningTaskId) => Task.FromResult>(Array.Empty()); + public Task BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch) => Task.FromResult(null); + public Task MergeAllPlanningAsync(string planningTaskId, string targetBranch) => Task.CompletedTask; + public Task ContinuePlanningMergeAsync(string planningTaskId) => Task.CompletedTask; + public Task AbortPlanningMergeAsync(string planningTaskId) => Task.CompletedTask; } // ── Helper to build VM with pre-seeded Items ──────────────────────────────────