Fix stranded recovery issue escalation

Align immediate terminal-run cleanup with stranded recovery reconciliation so recovery issues are blocked in place instead of receiving generic retry copy. Update the heartbeat recovery test expectation for the in-place guard notice.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-05-07 18:59:05 -05:00
1 parent 70f8b3325e
commit b7567d5471
3 files changed
+21 -6

No files matched your search

@@ -2151,12 +2151,10 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => {
const comments = await db.select().from(issueComments).where(eq(issueComments.issueId, issueId));
expect(comments).toHaveLength(1);
expect(comments[0]?.body).toContain("retried dispatch");
expect(comments[0]?.body).toContain("Nested recovery: suppressed");
expect(comments[0]?.body).toContain("already a `stranded_issue_recovery` issue");
expect(comments[0]?.body).toContain(`Original source issue: [${issuePrefix}-2]`);
expect(comments[0]?.body).toContain("stopped automatic stranded-work recovery");
expect(comments[0]?.body).toContain("recovery issues do not create nested `stranded_issue_recovery` issues");
expect(comments[0]?.body).toContain(`Recovery issue: [${recoveryIssues[0]?.identifier}]`);
expect(comments[0]?.body).toContain("Next action:");
expect(comments[0]?.body).not.toContain("Recovery issue: [");
});
it("assigns open unassigned blockers back to their creator agent", async () => {
+17 -1
View File
@@ -8363,8 +8363,15 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
return { kind: "released" as const };
}
if (issue.originKind === RECOVERY_ORIGIN_KINDS.strandedIssueRecovery) {
return {
kind: "blocked_recovery_in_place" as const,
issue,
previousStatus: issue.status,
};
}
const shouldBlockImmediately =
issue.originKind === RECOVERY_ORIGIN_KINDS.strandedIssueRecovery ||
!recoveryAgentInvokable ||
!recoveryAgent ||
didAutomaticRecoveryFail(run, issue.status === "todo" ? "assignment_recovery" : "issue_continuation_needed");
@@ -8464,6 +8471,15 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
return;
}
if (promotionResult?.kind === "blocked_recovery_in_place") {
await recovery.escalateStrandedRecoveryIssueInPlace({
issue: promotionResult.issue,
previousStatus: promotionResult.previousStatus as "todo" | "in_progress",
latestRun: run,
});
return;
}
const promotedRun = promotionResult?.run ?? null;
if (!promotedRun) return;
+1
View File
@@ -2797,6 +2797,7 @@ export function recoveryService(db: Db, deps: { enqueueWakeup: RecoveryWakeup })
return {
buildRunOutputSilence,
escalateStrandedRecoveryIssueInPlace,
escalateStrandedAssignedIssue,
recordWatchdogDecision,
scanSilentActiveRuns,