fix(recovery): exempt routine-parent issues from missing-disposition handoff (#8157)
Recognize active routine-parent issues as having a valid continuation path during successful-run handoff recovery. This prevents unnecessary missing-disposition corrective wakes when an active routine owns the next scheduled action. Also keeps the routine-continuation guard before the productivity check so logs surface the decisive skip reason for both productive and non-productive routine-parent runs. Verification: - CI status checks passed on PR #8157 - Greptile Review passed at 5/5 - Focused recovery unit test passed locally Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -4987,6 +4987,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
existingWake,
|
||||
budgetBlock,
|
||||
pauseHold,
|
||||
activeRoutineContinuation,
|
||||
] = await Promise.all([
|
||||
issue
|
||||
? db
|
||||
@@ -5112,6 +5113,20 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
issue
|
||||
? treeControlSvc.getActivePauseHoldGate(issue.companyId, issue.id)
|
||||
: Promise.resolve(null),
|
||||
issue
|
||||
? db
|
||||
.select({ id: routines.id })
|
||||
.from(routines)
|
||||
.where(
|
||||
and(
|
||||
eq(routines.companyId, issue.companyId),
|
||||
eq(routines.parentIssueId, issue.id),
|
||||
eq(routines.status, "active"),
|
||||
),
|
||||
)
|
||||
.limit(1)
|
||||
.then((rows) => rows[0] ?? null)
|
||||
: Promise.resolve(null),
|
||||
]);
|
||||
|
||||
const decision = decideSuccessfulRunHandoff({
|
||||
@@ -5127,6 +5142,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
hasExplicitBlockerPath: Boolean(explicitBlocker),
|
||||
hasOpenRecoveryIssue: Boolean(openRecoveryIssue),
|
||||
hasPauseHold: Boolean(pauseHold),
|
||||
hasActiveRoutineContinuation: Boolean(activeRoutineContinuation),
|
||||
budgetBlocked: Boolean(budgetBlock),
|
||||
idempotentWakeExists: Boolean(existingWake),
|
||||
});
|
||||
|
||||
@@ -52,6 +52,7 @@ function decide(overrides: Partial<Parameters<typeof decideSuccessfulRunHandoff>
|
||||
hasExplicitBlockerPath: false,
|
||||
hasOpenRecoveryIssue: false,
|
||||
hasPauseHold: false,
|
||||
hasActiveRoutineContinuation: false,
|
||||
budgetBlocked: false,
|
||||
idempotentWakeExists: false,
|
||||
...overrides,
|
||||
@@ -130,6 +131,29 @@ describe("successful run handoff decision", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not queue when the issue is the recurring parent of an active routine", () => {
|
||||
expect(decide({ hasActiveRoutineContinuation: true })).toEqual({
|
||||
kind: "skip",
|
||||
reason: "active routine continuation owns the next action",
|
||||
});
|
||||
expect(decide({
|
||||
hasActiveRoutineContinuation: true,
|
||||
detectedProgressSummary: null,
|
||||
livenessState: null,
|
||||
})).toEqual({
|
||||
kind: "skip",
|
||||
reason: "active routine continuation owns the next action",
|
||||
});
|
||||
expect(decide({
|
||||
hasActiveRoutineContinuation: true,
|
||||
livenessState: "advanced",
|
||||
detectedProgressSummary: "Run produced concrete action evidence: 1 issue comment(s)",
|
||||
})).toEqual({
|
||||
kind: "skip",
|
||||
reason: "active routine continuation owns the next action",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not queue when a successful run has no progress signal", () => {
|
||||
expect(decide({ livenessState: null, detectedProgressSummary: null })).toEqual({
|
||||
kind: "skip",
|
||||
|
||||
@@ -350,6 +350,7 @@ export function decideSuccessfulRunHandoff(input: {
|
||||
hasExplicitBlockerPath: boolean;
|
||||
hasOpenRecoveryIssue: boolean;
|
||||
hasPauseHold: boolean;
|
||||
hasActiveRoutineContinuation: boolean;
|
||||
budgetBlocked: boolean;
|
||||
idempotentWakeExists: boolean;
|
||||
}): SuccessfulRunHandoffDecision {
|
||||
@@ -376,6 +377,9 @@ export function decideSuccessfulRunHandoff(input: {
|
||||
if (agent.status === "paused" || agent.status === "terminated" || agent.status === "pending_approval") {
|
||||
return { kind: "skip", reason: `agent status ${agent.status} is not invokable` };
|
||||
}
|
||||
if (input.hasActiveRoutineContinuation) {
|
||||
return { kind: "skip", reason: "active routine continuation owns the next action" };
|
||||
}
|
||||
if (!isProductiveSuccessfulRun(input)) {
|
||||
return { kind: "skip", reason: "successful run did not produce handoff-relevant progress" };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user