Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<boolean> {
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 <finding>` (#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
Expand Down
62 changes: 62 additions & 0 deletions test/unit/queue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown> = {};
const env = createTestEnv({
Expand Down