Skip to content

Commit 82148ee

Browse files
fix(queue): audit draft-dodge stand-down when agent is paused or frozen
Closes #6604 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 8ac47e3 commit 82148ee

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

src/queue/review-evasion.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ type CloseEnforcementGateResult =
7979
* grant rather than attempting a doomed mutation -- the reopen-reclose guard was the last holdout (#6603).
8080
* The parameter stays nullable rather than required: it is the shape a future non-mutating caller would
8181
* need, and narrowing it is a signature change #6603 deliberately left out of scope.
82-
* `paused: null` records NO audit event on a paused/frozen repo -- draft-dodge's existing, preserved gap
83-
* (every other guard here DOES audit a paused stand-down; draft-dodge simply never has). */
82+
* Every caller also passes a real `paused` object so a paused/frozen stand-down is audited the same way for
83+
* all five close-enforcement guards (#6604). */
8484
async function evaluateCloseEnforcementGate(args: {
8585
env: Env;
8686
installationId: number;
@@ -278,9 +278,10 @@ async function closeDraftDodgeAttemptIfBlocked(
278278
detail: `dry-run: would close draft-dodge attempt by ${draftDodgeAuthor} — prior gate failure on headSha ${pr.headSha} stands`,
279279
metadata: { ...gateMetadata, mode: "dry_run" },
280280
},
281-
// Draft-dodge is the one guard of the 5 that records NO audit event on a paused/frozen repo -- a
282-
// pre-existing gap this extraction preserves rather than fixes (a refactor must not change behavior).
283-
paused: null,
281+
paused: {
282+
detail: `agent actions paused -- draft-dodge close not enforced for ${draftDodgeAuthor}`,
283+
metadata: gateMetadata,
284+
},
284285
permissionReadiness: {
285286
detail: `denied draft-dodge close for ${draftDodgeAuthor} — pull_requests: write not granted`,
286287
metadata: gateMetadata,

test/unit/queue-lifecycle-guards.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,9 @@ describe("converted_to_draft gate-close (draft-dodge prevention)", () => {
13671367
await repositoriesModule.setGlobalAgentFrozen(env, true);
13681368
await processJob(env, { type: "github-webhook", deliveryId: "draft-frozen", eventName: "pull_request", payload: draftPayload("contributor") });
13691369
expect(calls.some((c) => c.method === "PATCH" && c.url.endsWith("/pulls/42"))).toBe(false); // never closed under freeze
1370-
expect(await env.DB.prepare("select count(*) as n from audit_events where event_type = ?").bind("github_app.draft_dodge_closed").first<{ n: number }>()).toMatchObject({ n: 0 });
1370+
const audit = await env.DB.prepare("select outcome, detail from audit_events where event_type = ?").bind("github_app.draft_dodge_closed").first<{ outcome: string; detail: string }>();
1371+
expect(audit?.outcome).toBe("denied");
1372+
expect(audit?.detail).toContain("agent actions paused -- draft-dodge close not enforced for contributor");
13711373
});
13721374

13731375
it("dry-run: audits a would-be draft-dodge close without touching GitHub (#killswitch-gap)", async () => {
@@ -1542,7 +1544,7 @@ describe("converted_to_draft gate-close (draft-dodge prevention)", () => {
15421544
expect(calls.some((c) => c.includes("PATCH") && c.includes("/pulls/42"))).toBe(false);
15431545
});
15441546

1545-
it("no-ops when the agent is paused (agentPaused=true)", async () => {
1547+
it("no-ops when the agent is paused (agentPaused=true) and audits the paused stand-down (#6604)", async () => {
15461548
const calls: string[] = [];
15471549
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
15481550
const url = input.toString();
@@ -1558,6 +1560,9 @@ describe("converted_to_draft gate-close (draft-dodge prevention)", () => {
15581560
await processJob(env, { type: "github-webhook", deliveryId: "draft-paused", eventName: "pull_request", payload: draftPayload("contributor") });
15591561

15601562
expect(calls.some((c) => c.includes("PATCH") && c.includes("/pulls/42"))).toBe(false);
1563+
const audit = await env.DB.prepare("select outcome, detail from audit_events where event_type = ?").bind("github_app.draft_dodge_closed").first<{ outcome: string; detail: string }>();
1564+
expect(audit?.outcome).toBe("denied");
1565+
expect(audit?.detail).toContain("agent actions paused -- draft-dodge close not enforced for contributor");
15611566
});
15621567

15631568
it("no-ops when the agent autonomy is not configured (autonomy=null)", async () => {

0 commit comments

Comments
 (0)