fix(engine): prevent two tasks showing "merging" status simultaneously

Root cause: if a merge crashed or the process restarted mid-merge, the
"merging" status was never cleared. On next startup the stale task kept
its "merging" status while the queue moved on to the next task, resulting
in two tasks appearing to merge at once.

Two fixes:
1. Add "merging"/"merging-pr" to BLOCKING_TASK_STATUSES so tasks with
   active merge status are not re-enqueued by the retry sweep.
2. Clear stale "merging" statuses during startup merge sweep — no merge
   is actually running at engine start, so any such status is a leftover.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-13 17:06:17 -07:00
parent 98bc2dfdae
commit 0d0e2e6dbe
2 changed files with 16 additions and 0 deletions

View File

@@ -4,6 +4,8 @@ const BLOCKING_TASK_STATUSES = new Set([
"failed",
"awaiting-inspection",
"awaiting-user-review",
"merging",
"merging-pr",
]);
const NON_TERMINAL_STEP_STATUSES = new Set([

View File

@@ -683,6 +683,20 @@ export class ProjectEngine {
if (!settings.autoMerge) return;
const tasks = await store.listTasks({ column: "in-review" });
// Clear stale "merging"/"merging-pr" statuses left by a prior crash.
// No merge is actually running at startup, so any task still marked
// as merging is a leftover from a previous engine lifecycle.
const staleStatuses = new Set(["merging", "merging-pr"]);
for (const t of tasks) {
if (t.status && staleStatuses.has(t.status)) {
runtimeLog.log(`Startup sweep: clearing stale '${t.status}' status on ${t.id}`);
await store.updateTask(t.id, { status: null });
// Update in-memory object so canMergeTask sees the cleared status
(t as any).status = null;
}
}
const eligible = tasks.filter((t) => this.canMergeTask(t as any));
if (eligible.length > 0) {
runtimeLog.log(`Auto-merge startup sweep: enqueueing ${eligible.length} task(s)`);