From 809c833c43d732e580259b75c21dd950737b82f2 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 15 Aug 2026 08:33:01 +0000 Subject: [PATCH] fix(server): preserve coalesced feedback retry identity Co-Authored-By: Paperclip --- server/src/__tests__/heartbeat-process-recovery.test.ts | 3 +++ server/src/services/heartbeat.ts | 9 +++++++++ 2 files changed, 12 insertions(+) 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;