From 20ce618b41416faec627e266e02b672f2688009b Mon Sep 17 00:00:00 2001 From: ghost <49853598+JSONbored@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:04:24 -0700 Subject: [PATCH] fix(queue): honor paused autoreview markers --- src/queue/processors.ts | 15 ++++++++++ test/unit/queue.test.ts | 62 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/queue/processors.ts b/src/queue/processors.ts index 4a782950c..61ae1eadb 100644 --- a/src/queue/processors.ts +++ b/src/queue/processors.ts @@ -3240,6 +3240,7 @@ async function reReviewStoredPullRequest( ]); let pr = await getPullRequest(env, repoFullName, prNumber); if (!pr || pr.state !== "open") return; + if (await hasAutoreviewPausedMarker(env, repoFullName, prNumber)) return; const liveFacts = createLiveGithubFacts(); // #sweep-resync: RESYNC the stored PR to its LIVE head before reviewing. The self-host relay can drop the // `synchronize` webhook (relay down), so a push/rebase never refreshes the stored head SHA + cached files; the @@ -10952,6 +10953,20 @@ async function recordAutoreviewPausedSkip(env: Env, deliveryId: string, repoFull await recordGithubProductUsage(env, "autoreview_paused_skipped", { actor, repoFullName, targetKey, outcome: "skipped", metadata: { reason } }); } +async function hasAutoreviewPausedMarker(env: Env, repoFullName: string, prNumber: number): Promise { + try { + const row = await env.DB.prepare( + "select 1 from audit_events where event_type = ? and target_key = ? and outcome = ? order by created_at desc limit 1", + ) + .bind("github_app.autoreview_paused", `${repoFullName}#${prNumber}`, "completed") + .first(); + return Boolean(row); + } catch { + /* v8 ignore next -- audit lookup failures fail open so a stale/corrupt ledger cannot wedge review processing. */ + return false; + } +} + /** * `@gittensory explain ` (#2169, part of #1960): a contributor/maintainer asks for more detail on a * specific posted review finding. Read-only — it looks the finding up in THIS PR's current advisory (the same diff --git a/test/unit/queue.test.ts b/test/unit/queue.test.ts index ab10b0c33..7ef317410 100644 --- a/test/unit/queue.test.ts +++ b/test/unit/queue.test.ts @@ -4258,6 +4258,68 @@ describe("queue processors", () => { expect(skipAudit).toBeUndefined(); }); + it("does not publish the re-review surface once a PR has an autoreview pause marker (#2164 regression)", async () => { + let aiCalls = 0; + let commentPosted = false; + let checkRunWritten = false; + const env = createTestEnv({ + GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), + AI: { run: async () => { aiCalls += 1; return { response: JSON.stringify({ assessment: "Looks fine.", blockers: [], nits: [], suggestions: [] }) }; } } as unknown as Ai, + AI_SUMMARIES_ENABLED: "true", + AI_PUBLIC_COMMENTS_ENABLED: "true", + AI_DAILY_NEURON_BUDGET: "100000", + }); + await seedRegateChurnRepo(env); + await upsertRepositorySettings(env, { + repoFullName: "JSONbored/gittensory", + commentMode: "all_prs", + publicSurface: "comment_only", + autoLabelEnabled: false, + checkRunMode: "enabled", + gateCheckMode: "enabled", + aiReviewMode: "block", + gatePack: "oss-anti-slop", + }); + await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { + number: 83, + title: "Ready feature", + state: "open", + draft: false, + user: { login: "contributor" }, + head: { sha: "a83" }, + labels: [], + body: "Closes #1", + } as never); + await upsertPullRequestDetailSyncState(env, { repoFullName: "JSONbored/gittensory", pullNumber: 83, status: "complete", reviewsSyncedAt: new Date().toISOString() }); + await env.DB.prepare( + "insert into audit_events (id, event_type, actor, target_key, outcome, detail, metadata_json, created_at) values (?, ?, ?, ?, ?, ?, ?, ?)", + ) + .bind("pause-83", "github_app.autoreview_paused", "maintainer1", "JSONbored/gittensory#83", "completed", "stop reviewing", "{}", new Date().toISOString()) + .run(); + vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => { + const url = input.toString(); + const method = init?.method ?? "GET"; + if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" }); + if (url.endsWith("/pulls/83")) return Response.json({ number: 83, title: "Ready feature", state: "open", draft: false, user: { login: "contributor" }, head: { sha: "a83" }, labels: [], body: "Closes #1", mergeable_state: "clean" }); + if (url.includes("/issues/83/comments") && method === "POST") { + commentPosted = true; + return Response.json({ id: 83 }, { status: 201 }); + } + if (url.includes("/check-runs") && (method === "POST" || method === "PATCH")) { + checkRunWritten = true; + return Response.json({ id: 983, html_url: "https://github.com/check/983" }, { status: method === "POST" ? 201 : 200 }); + } + return Response.json({}); + }); + + await expect( + processJob(env, { type: "agent-regate-pr", deliveryId: "paused-autoreview", repoFullName: "JSONbored/gittensory", prNumber: 83, installationId: 123 }), + ).resolves.toBeUndefined(); + expect(aiCalls).toBe(0); + expect(commentPosted).toBe(false); + expect(checkRunWritten).toBe(false); + }); + it("threads review.ai_model through the full webhook pipeline into ai.run's options (#selfhost-ai-model-override)", async () => { let seenOptions: Record = {}; const env = createTestEnv({