diff --git a/server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts b/server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts index b8fd03bd63..ca419d9e0c 100644 --- a/server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts +++ b/server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts @@ -30,17 +30,25 @@ describe("PF-4 shouldResetTaskSessionForWake", () => { ).toBe(false); }); - it("still resets for the existing reset reasons", () => { + it("keeps assignment, approval, and review-participant recovery as fresh boundaries", () => { for (const wakeReason of [ "issue_assigned", - "execution_review_requested", "execution_approval_requested", - "execution_changes_requested", + "execution_review_participant_recovery", ] as const) { expect(shouldResetTaskSessionForWake({ wakeReason })).toBe(true); } }); + it("does not reset execution handoffs by wake reason alone", () => { + for (const wakeReason of [ + "execution_review_requested", + "execution_changes_requested", + ] as const) { + expect(shouldResetTaskSessionForWake({ wakeReason })).toBe(false); + } + }); + it("still respects forceFreshSession === true", () => { expect(shouldResetTaskSessionForWake({ forceFreshSession: true })).toBe(true); }); @@ -80,19 +88,21 @@ describe("PF-4 describeSessionResetReason", () => { ).toBeNull(); }); - it("returns the existing reasons for the existing reset triggers", () => { + it("returns reasons for wake reasons that still force a fresh task session", () => { expect(describeSessionResetReason({ wakeReason: "issue_assigned" })).toBe( "wake reason is issue_assigned", ); - expect(describeSessionResetReason({ wakeReason: "execution_review_requested" })).toBe( - "wake reason is execution_review_requested", - ); expect(describeSessionResetReason({ wakeReason: "execution_approval_requested" })).toBe( "wake reason is execution_approval_requested", ); - expect(describeSessionResetReason({ wakeReason: "execution_changes_requested" })).toBe( - "wake reason is execution_changes_requested", - ); + expect( + describeSessionResetReason({ wakeReason: "execution_review_participant_recovery" }), + ).toBe("wake reason is execution_review_participant_recovery"); + }); + + it("does not report review/change-request handoffs as reset reasons", () => { + expect(describeSessionResetReason({ wakeReason: "execution_review_requested" })).toBeNull(); + expect(describeSessionResetReason({ wakeReason: "execution_changes_requested" })).toBeNull(); }); it("returns the forceFreshSession message when explicitly requested", () => { @@ -116,6 +126,7 @@ describe("PF-4 describeSessionResetReason", () => { { wakeReason: "issue_assigned" }, { wakeReason: "execution_review_requested" }, { wakeReason: "execution_approval_requested" }, + { wakeReason: "execution_review_participant_recovery" }, { wakeReason: "execution_changes_requested" }, { forceFreshSession: true }, { wakeReason: "issue_commented" }, diff --git a/server/src/__tests__/heartbeat-workspace-session.test.ts b/server/src/__tests__/heartbeat-workspace-session.test.ts index 0495f3bd93..963c10ad2f 100644 --- a/server/src/__tests__/heartbeat-workspace-session.test.ts +++ b/server/src/__tests__/heartbeat-workspace-session.test.ts @@ -1791,16 +1791,16 @@ describe("shouldResetTaskSessionForWake", () => { expect(shouldResetTaskSessionForWake({ wakeReason: "issue_assigned" })).toBe(true); }); - it("resets session context on execution review wakes", () => { - expect(shouldResetTaskSessionForWake({ wakeReason: "execution_review_requested" })).toBe(true); + it("preserves session context on execution review handoff wakes", () => { + expect(shouldResetTaskSessionForWake({ wakeReason: "execution_review_requested" })).toBe(false); }); it("resets session context on execution approval wakes", () => { expect(shouldResetTaskSessionForWake({ wakeReason: "execution_approval_requested" })).toBe(true); }); - it("resets session context on execution changes-requested wakes", () => { - expect(shouldResetTaskSessionForWake({ wakeReason: "execution_changes_requested" })).toBe(true); + it("preserves session context on execution changes-requested handoff wakes", () => { + expect(shouldResetTaskSessionForWake({ wakeReason: "execution_changes_requested" })).toBe(false); }); it("preserves session context on timer heartbeats", () => { diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index b92f81358b..e3c3b34454 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -4122,10 +4122,8 @@ export function shouldResetTaskSessionForWake( const wakeReason = readNonEmptyString(contextSnapshot?.wakeReason); if ( wakeReason === "issue_assigned" || - wakeReason === "execution_review_requested" || wakeReason === EXECUTION_REVIEW_PARTICIPANT_RECOVERY_WAKE_REASON || wakeReason === "execution_approval_requested" || - wakeReason === "execution_changes_requested" || // PF-4: unscoped timer wakes are exploratory ("any new work?") and should // not accumulate low-value inbox scans. Issue-scoped timer wakes are // continuation work, so reuse their task session to avoid paying the full @@ -4235,12 +4233,10 @@ export function describeSessionResetReason( const wakeReason = readNonEmptyString(contextSnapshot?.wakeReason); if (wakeReason === "issue_assigned") return "wake reason is issue_assigned"; - if (wakeReason === "execution_review_requested") return "wake reason is execution_review_requested"; if (wakeReason === EXECUTION_REVIEW_PARTICIPANT_RECOVERY_WAKE_REASON) { return `wake reason is ${EXECUTION_REVIEW_PARTICIPANT_RECOVERY_WAKE_REASON}`; } if (wakeReason === "execution_approval_requested") return "wake reason is execution_approval_requested"; - if (wakeReason === "execution_changes_requested") return "wake reason is execution_changes_requested"; // PF-4: paired with shouldResetTaskSessionForWake — keep the reason wording // explicit so run logs make session reuse/reset behavior legible. if (wakeReason === "heartbeat_timer" && !deriveTaskKey(contextSnapshot, null)) {