fix: recover tasks stuck in review after pause

This commit is contained in:
gsxdsm
2026-04-09 22:14:44 -07:00
parent 59674c6037
commit b93c418356
5 changed files with 194 additions and 14 deletions

View File

@@ -325,6 +325,7 @@ export class SelfHealingManager {
this.checkpointWal();
await this.enforceWorktreeCap();
await this.recoverCompletedTasks();
await this.recoverMergedReviewTasks();
await this.recoverMisclassifiedFailures();
await this.recoverOrphanedExecutions();
await this.recoverApprovedTriageTasks();
@@ -387,6 +388,58 @@ export class SelfHealingManager {
// ── Misclassified failure recovery ───────────────────────────────
/**
* Recover tasks that already merged successfully but never reached `done`.
*
* This catches races where the merge completed and merge metadata was stored,
* but a later transition failed or another process moved the task before the
* final `in-review` → `done` update completed.
*
* @returns Number of tasks recovered
*/
async recoverMergedReviewTasks(): Promise<number> {
try {
const tasks = await this.store.listTasks();
const mergedButNotDone = tasks.filter((t) =>
t.column === "in-review" &&
t.mergeDetails?.mergeConfirmed === true,
);
if (mergedButNotDone.length === 0) return 0;
log.warn(`Found ${mergedButNotDone.length} merged task(s) stuck in in-review`);
let recovered = 0;
for (const task of mergedButNotDone) {
try {
await this.store.updateTask(task.id, {
status: null,
error: null,
mergeRetries: 0,
});
await this.store.moveTask(task.id, "done");
await this.store.logEntry(
task.id,
"Auto-recovered: merge already confirmed — moved from in-review to done",
);
log.log(`Recovered merged task ${task.id}: moved to done`);
recovered++;
} catch (err: any) {
log.error(`Failed to recover merged task ${task.id}: ${err.message}`);
}
}
if (recovered > 0) {
log.log(`Recovered ${recovered} merged task(s) → done`);
}
return recovered;
} catch (err: any) {
log.error(`Merged review recovery failed: ${err.message}`);
return 0;
}
}
/**
* Recover tasks in `in-review` marked as `failed` where all steps are
* actually done. This catches the case where an agent completed all work