diff --git a/server/src/__tests__/heartbeat-process-recovery.test.ts b/server/src/__tests__/heartbeat-process-recovery.test.ts index 75eea5673b7..2f47de1091c 100644 --- a/server/src/__tests__/heartbeat-process-recovery.test.ts +++ b/server/src/__tests__/heartbeat-process-recovery.test.ts @@ -1922,6 +1922,9 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => { expect(runs).toHaveLength(2); const coalescedRun = runs.find((run) => run.id === secondRunId); expect(coalescedRun?.contextSnapshot).toMatchObject({ + wakeReason: "feedback_delivery_retry", + retryReason: "feedback_delivery_retry", + retryOfRunId: fixture.runId, feedbackDeliveryRootWakeupRequestId: fixture.wakeupRequestId, feedbackDeliveryRetryGeneration: 1, wakeCommentIds: [first.commentId, secondCommentId], diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index d5cf25c9195..e5902568b88 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -10446,6 +10446,12 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {}) mergedContext.commentId = orderedCommentIds.at(-1); mergedContext.wakeCommentId = orderedCommentIds.at(-1); } + // The queued run now owns the feedback-delivery obligation. Preserve + // its retry identity even when the queued context came from a newer, + // unrelated wake so terminal disposition can recover a failed merge. + mergedContext.wakeReason = FEEDBACK_DELIVERY_RETRY_WAKE_REASON; + mergedContext.retryReason = FEEDBACK_DELIVERY_RETRY_REASON; + mergedContext.retryOfRunId = input.run.id; mergedContext.feedbackDeliveryRootWakeupRequestId = input.delivery.rootWakeupRequestId; mergedContext.feedbackDeliverySourceRunId = input.run.id; mergedContext.feedbackDeliveryRetryGeneration = 1; @@ -10487,6 +10493,9 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {}) mergedDeferredContext.commentId = orderedCommentIds.at(-1); mergedDeferredContext.wakeCommentId = orderedCommentIds.at(-1); } + mergedDeferredContext.wakeReason = FEEDBACK_DELIVERY_RETRY_WAKE_REASON; + mergedDeferredContext.retryReason = FEEDBACK_DELIVERY_RETRY_REASON; + mergedDeferredContext.retryOfRunId = input.run.id; mergedDeferredContext.feedbackDeliveryRootWakeupRequestId = input.delivery.rootWakeupRequestId; mergedDeferredContext.feedbackDeliverySourceRunId = input.run.id; mergedDeferredContext.feedbackDeliveryRetryGeneration = 1;