diff --git a/src/db/repositories.ts b/src/db/repositories.ts index 9b045c7488..b1d4be4a1a 100644 --- a/src/db/repositories.ts +++ b/src/db/repositories.ts @@ -4119,12 +4119,21 @@ export async function getLatestPublishedAiReview( }; } -/** Count distinct PR head SHAs that already received a published AI review — used by `review.auto_review.auto_pause_after_reviewed_commits`. (#2042) */ -export async function countPublishedAiReviewHeads(env: Env, repoFullName: string, pullNumber: number): Promise { - const row = await env.DB.prepare( - "SELECT COUNT(DISTINCT head_sha) AS cnt FROM ai_review_cache WHERE repo_full_name = ? AND pull_number = ? AND published_at IS NOT NULL", - ) - .bind(repoFullName, pullNumber) +/** Count distinct prior PR head SHAs that already received a published AI review — used by `review.auto_review.auto_pause_after_reviewed_commits`. (#2042) */ +export async function countPublishedAiReviewHeads( + env: Env, + repoFullName: string, + pullNumber: number, + currentHeadSha?: string | null | undefined, +): Promise { + const currentHeadClause = currentHeadSha ? " AND head_sha != ?" : ""; + const row = await env.DB + .prepare( + `SELECT COUNT(DISTINCT head_sha) AS cnt FROM ai_review_cache WHERE repo_full_name = ? AND pull_number = ? AND published_at IS NOT NULL${currentHeadClause}`, + ) + .bind( + ...(currentHeadSha ? [repoFullName, pullNumber, currentHeadSha] : [repoFullName, pullNumber]), + ) .first<{ cnt: number }>(); /* v8 ignore next -- SQL aggregate count always returns one row; fallback protects D1 driver anomalies. */ return row?.cnt ?? 0; diff --git a/src/queue/processors.ts b/src/queue/processors.ts index 7e004a092b..e22e5b31be 100644 --- a/src/queue/processors.ts +++ b/src/queue/processors.ts @@ -6352,7 +6352,7 @@ export async function resolveAutoReviewSkipForPullRequest( if (args.authorBlacklisted || args.isFrozenForManualReview) { return { skipReason: null, reviewManifest }; } - const reviewedCommitCount = await countPublishedAiReviewHeads(env, args.repoFullName, args.pr.number).catch(() => 0); + const reviewedCommitCount = await countPublishedAiReviewHeads(env, args.repoFullName, args.pr.number, args.headSha).catch(() => 0); const skipReason = resolvePullRequestAutoReviewSkipReason({ forceAiReview: args.forceAiReview, manifest: reviewManifest, diff --git a/test/unit/ai-review-cache.test.ts b/test/unit/ai-review-cache.test.ts index 5514553a88..77d05c3379 100644 --- a/test/unit/ai-review-cache.test.ts +++ b/test/unit/ai-review-cache.test.ts @@ -485,6 +485,17 @@ describe("AI review cache (#1)", () => { expect(await countPublishedAiReviewHeads(env, "o/r", 61)).toBe(2); }); + it("excludes the current published head from the pause threshold (regression for cached blocker suppression)", async () => { + const env = createTestEnv(); + await putCachedAiReview(env, "o/r", 63, "sha1", "block", { notes: "first", reviewerCount: 1 }); + await markAiReviewPublished(env, "o/r", 63, "sha1"); + await putCachedAiReview(env, "o/r", 63, "sha2", "block", { notes: "current", reviewerCount: 1 }); + await markAiReviewPublished(env, "o/r", 63, "sha2"); + + expect(await countPublishedAiReviewHeads(env, "o/r", 63, "sha2")).toBe(1); + expect(await countPublishedAiReviewHeads(env, "o/r", 63, null)).toBe(2); + }); + it("returns 0 when the count query yields no row (fail-safe)", async () => { const env = createTestEnv(); const prepareSpy = vi.spyOn(env.DB, "prepare").mockReturnValue({ diff --git a/test/unit/auto-review-wiring.test.ts b/test/unit/auto-review-wiring.test.ts index 24089d09c1..1bdd94e3d3 100644 --- a/test/unit/auto-review-wiring.test.ts +++ b/test/unit/auto-review-wiring.test.ts @@ -352,7 +352,7 @@ describe("review.auto_review wiring (#1954)", () => { headSha: "sha5", }), ).resolves.toEqual({ skipReason: "review paused (commit threshold)", reviewManifest: manifest }); - expect(countSpy).toHaveBeenCalledWith(expect.anything(), "acme/widgets", 5); + expect(countSpy).toHaveBeenCalledWith(expect.anything(), "acme/widgets", 5, "sha5"); expect(auditSpy).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ detail: "review paused (commit threshold)" }),