diff --git a/src/services/recommendation-outcomes.ts b/src/services/recommendation-outcomes.ts index 274da45636..f50c51c295 100644 --- a/src/services/recommendation-outcomes.ts +++ b/src/services/recommendation-outcomes.ts @@ -284,7 +284,11 @@ function pullRequestOutcomeState( if (hasChangesRequestedReview(pr)) return "rejected"; if (hasPositiveOpenPullRequestSignal(pr)) return "improved"; } - if (createdAt >= actionAt || updatedAt > actionAt) return "accepted"; + // A PR that already merged before the action cannot be a positive outcome of it. Without this guard a + // pre-action merge that merely receives a later updatedAt bump (e.g. a comment) would fall through to + // "accepted" -- mirror the "merged" branch's own >= actionAt discipline so it stays stale/ignored. + const mergedBeforeAction = Number.isFinite(mergedAt) && mergedAt < actionAt; + if (!mergedBeforeAction && (createdAt >= actionAt || updatedAt > actionAt)) return "accepted"; if (actionAgeMs >= staleAfterMs) return "stale"; return "ignored"; } diff --git a/test/unit/recommendation-outcomes.test.ts b/test/unit/recommendation-outcomes.test.ts index 388c2a6924..860a1baac0 100644 --- a/test/unit/recommendation-outcomes.test.ts +++ b/test/unit/recommendation-outcomes.test.ts @@ -246,6 +246,24 @@ describe("recommendation outcome feedback", () => { }), ).toMatchObject({ outcomeState: "stale", outcomeTargetType: "pull_request" }); + // A pre-action merge that merely receives a later updatedAt bump (e.g. a comment) must not be + // credited as the positive "accepted" outcome of the recommendation. + expect( + classifyRecommendationOutcome({ + ...base, + action: action(run, 2, { targetRepoFullName: "owner/old-merged", targetPullNumber: 62 }), + pullRequests: [ + prRecord(62, "owner/old-merged", { + state: "merged", + mergedAt: "2026-04-01T00:00:00.000Z", + createdAt: "2026-03-01T00:00:00.000Z", + updatedAt: "2026-05-10T00:00:00.000Z", + }), + ], + issues: [], + }), + ).toMatchObject({ outcomeState: "stale", outcomeTargetType: "pull_request" }); + expect( classifyRecommendationOutcome({ ...base,