From 0cc6d99f48b172a51df0fd9b42119fa4f1bccee9 Mon Sep 17 00:00:00 2001 From: joaovictor91123 Date: Sun, 26 Jul 2026 06:23:38 -0700 Subject: [PATCH] fix(db): tie-break getLatestPublishedAiReview on rowid Match sibling latest-row queries so equal published_at timestamps pick a deterministic head. Closes #8894 --- src/db/repositories.ts | 2 +- test/unit/ai-review-cache.test.ts | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/db/repositories.ts b/src/db/repositories.ts index 5dc1e44da2..96877379e1 100644 --- a/src/db/repositories.ts +++ b/src/db/repositories.ts @@ -5094,7 +5094,7 @@ export async function getLatestPublishedAiReview( ): Promise<{ notes: string; reviewerCount: number; findings: AdvisoryFinding[]; headSha?: string | undefined; metadata?: Record | undefined } | null> { const row = await env.DB .prepare( - "SELECT notes, reviewer_count AS reviewerCount, head_sha AS headSha, findings_json AS findingsJson, metadata_json AS metadataJson FROM ai_review_cache WHERE repo_full_name = ? AND pull_number = ? AND ai_review_mode = ? AND published_at IS NOT NULL ORDER BY published_at DESC LIMIT 1", + "SELECT notes, reviewer_count AS reviewerCount, head_sha AS headSha, findings_json AS findingsJson, metadata_json AS metadataJson FROM ai_review_cache WHERE repo_full_name = ? AND pull_number = ? AND ai_review_mode = ? AND published_at IS NOT NULL ORDER BY published_at DESC, rowid DESC LIMIT 1", ) .bind(repoFullName, pullNumber, mode) .first<{ notes: string; reviewerCount: number; headSha: string; findingsJson: string | null; metadataJson: string | null }>(); diff --git a/test/unit/ai-review-cache.test.ts b/test/unit/ai-review-cache.test.ts index be31837459..b27dd9458b 100644 --- a/test/unit/ai-review-cache.test.ts +++ b/test/unit/ai-review-cache.test.ts @@ -470,6 +470,31 @@ describe("AI review cache (#1)", () => { } }); + it("breaks ties on identical published_at via rowid DESC (#8894)", async () => { + const env = createTestEnv(); + const publishedAt = "2026-07-09T12:00:00.000Z"; + // Insert order ⇒ rising rowid; with equal published_at the higher rowid must win. + await env.DB.prepare( + `INSERT INTO ai_review_cache (repo_full_name, pull_number, head_sha, ai_review_mode, notes, reviewer_count, findings_json, metadata_json, cacheable, published_at, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ) + .bind("o/r", 56, "sha-earlier-rowid", "block", "earlier rowid", 1, "[]", "{}", 1, publishedAt, publishedAt) + .run(); + await env.DB.prepare( + `INSERT INTO ai_review_cache (repo_full_name, pull_number, head_sha, ai_review_mode, notes, reviewer_count, findings_json, metadata_json, cacheable, published_at, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ) + .bind("o/r", 56, "sha-later-rowid", "block", "later rowid", 1, "[]", "{}", 1, publishedAt, publishedAt) + .run(); + + expect(await getLatestPublishedAiReview(env, "o/r", 56, "block")).toEqual({ + notes: "later rowid", + reviewerCount: 1, + findings: [], + headSha: "sha-later-rowid", + }); + }); + it("omits headSha from the payload when the stored head_sha is empty", async () => { const env = createTestEnv(); await env.DB.prepare(