mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-01 15:35:12 +01:00
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:
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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -2797,6 +2797,7 @@ export function recoveryService(db: Db, deps: { enqueueWakeup: RecoveryWakeup })
|
||||
|
||||
return {
|
||||
buildRunOutputSilence,
|
||||
escalateStrandedRecoveryIssueInPlace,
|
||||
escalateStrandedAssignedIssue,
|
||||
recordWatchdogDecision,
|
||||
scanSilentActiveRuns,
|
||||
|
||||
Reference in new issue
Block a user