fix(engine): recover already-merged tasks stuck in in-review

When a merge completes but auto-recovery moves the task back to
in-review, the retry gating (mergeRetries >= 3) blocked re-processing.
Now canMergeTask always accepts mergeConfirmed tasks and drainMergeQueue
fast-paths them directly to done without re-running the merge agent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-13 07:49:54 -07:00
parent a2d54c4a42
commit 4bbd42c88e

View File

@@ -373,7 +373,10 @@ export class ProjectEngine {
workflowStepResults?: Array<{ status: string }>;
log?: Array<{ action?: string }>;
updatedAt?: string | null;
mergeDetails?: { mergeConfirmed?: boolean } | null;
}): boolean {
// Already-confirmed merges always eligible — just need to move to done
if (task.mergeDetails?.mergeConfirmed) return true;
if (this.options.getTaskMergeBlocker?.(task as Task)) return false;
return (
(task.mergeRetries ?? 0) < ProjectEngine.MAX_AUTO_MERGE_RETRIES ||
@@ -422,6 +425,22 @@ export class ProjectEngine {
continue;
}
// Fast path: merge already confirmed (e.g. task was moved back to
// in-review by auto-recovery after a successful merge) — just
// complete the task without re-running the merge process.
if (task.mergeDetails?.mergeConfirmed) {
runtimeLog.log(
`Auto-merge: ${taskId} already has mergeConfirmed — moving to done`,
);
await store.logEntry(
taskId,
"Merge already confirmed; completing task (recovered from post-merge state inconsistency)",
);
await store.updateTask(taskId, { status: null });
await store.moveTask(taskId, "done");
continue;
}
// Auto-heal verification buffer failures by resetting retry counter
if (this.hasAutoHealableVerificationBufferFailure(task as any)) {
await store.logEntry(