Skip to content

Commit 1a92ed9

Browse files
committed
remove requeue system entirely: drop requeue API route, DB table, recovery scheduler, and all related code (-669 lines)
1 parent 21c803f commit 1a92ed9

File tree

10 files changed

+5
-669
lines changed

10 files changed

+5
-669
lines changed

src/api/requeue.test.ts

Lines changed: 0 additions & 140 deletions
This file was deleted.

src/api/requeue.ts

Lines changed: 3 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,12 @@
11
/**
2-
* Requeue endpoint handlers.
2+
* Force-release endpoint handler.
33
*
4-
* Handles manual re-evaluation requests and force-release of locked
5-
* bounties. Enforces business rules: 24h max age, once per issue,
6-
* and force-release performs NO GitHub mutations.
4+
* Force-release performs NO GitHub mutations — only Redis + DB cleanup.
75
*/
86

97
import { logger } from "../logger.js";
10-
import {
11-
getBounty,
12-
getRequeueRecord,
13-
insertRequeueRecord,
14-
updateBountyStatus,
15-
unlockBounty,
16-
insertAuditLog,
17-
} from "../db/index.js";
18-
import { reopenIssue, postComment } from "../github/client.js";
19-
import { clearVerdictLabels } from "../github/mutations.js";
20-
import { enqueue } from "../queue/processor.js";
8+
import { unlockBounty, insertAuditLog } from "../db/index.js";
219
import { getRedis } from "../redis.js";
22-
import { TARGET_REPO } from "../config.js";
23-
24-
/* ------------------------------------------------------------------ */
25-
/* Config */
26-
/* ------------------------------------------------------------------ */
27-
28-
/** Maximum age (ms) of an issue eligible for requeue (default 24h). */
29-
const REQUEUE_MAX_AGE_MS = parseInt(
30-
process.env.REQUEUE_MAX_AGE_MS || "86400000",
31-
10,
32-
);
33-
34-
/* ------------------------------------------------------------------ */
35-
/* Repo helpers */
36-
/* ------------------------------------------------------------------ */
37-
38-
function parseRepo(): { owner: string; repo: string } {
39-
const parts = TARGET_REPO.split("/");
40-
if (parts.length !== 2 || !parts[0] || !parts[1]) {
41-
throw new Error(`Invalid TARGET_REPO format: "${TARGET_REPO}"`);
42-
}
43-
return { owner: parts[0], repo: parts[1] };
44-
}
45-
46-
/* ------------------------------------------------------------------ */
47-
/* Requeue handler */
48-
/* ------------------------------------------------------------------ */
49-
50-
/**
51-
* Handle a requeue request for an issue.
52-
*
53-
* Business rules:
54-
* - Issue must exist in DB
55-
* - Issue created_at must be within 24 hours
56-
* - Issue must not already have been requeued
57-
*
58-
* If valid: reopen issue, clear labels, post re-eval comment, create
59-
* requeue record, enqueue for processing.
60-
*
61-
* If invalid: return rejection with reason — NO GitHub mutations.
62-
*
63-
* @param issueNumber - GitHub issue number
64-
* @param requesterId - ID of the requester
65-
* @param requesterContext - Optional context from the requester
66-
* @returns Success/failure with optional error message
67-
*/
68-
export async function handleRequeue(
69-
issueNumber: number,
70-
requesterId: string,
71-
requesterContext?: object,
72-
): Promise<{ success: boolean; error?: string }> {
73-
// Validate: issue exists in DB
74-
const bounty = getBounty(issueNumber);
75-
if (!bounty) {
76-
logger.info({ issueNumber }, "Requeue: issue not found in DB");
77-
return { success: false, error: `Issue #${issueNumber} not found` };
78-
}
79-
80-
// Validate: issue created_at within 24 hours
81-
if (bounty.created_at) {
82-
const createdAt = new Date(bounty.created_at).getTime();
83-
const age = Date.now() - createdAt;
84-
if (age > REQUEUE_MAX_AGE_MS) {
85-
logger.info(
86-
{ issueNumber, ageMs: age, maxAgeMs: REQUEUE_MAX_AGE_MS },
87-
"Requeue: issue too old",
88-
);
89-
return {
90-
success: false,
91-
error: `Issue #${issueNumber} is older than ${REQUEUE_MAX_AGE_MS / 3600000}h — not eligible for requeue`,
92-
};
93-
}
94-
}
95-
96-
// Validate: not already requeued
97-
const existingRequeue = getRequeueRecord(issueNumber);
98-
if (existingRequeue) {
99-
logger.info({ issueNumber }, "Requeue: already requeued");
100-
return {
101-
success: false,
102-
error: `Issue #${issueNumber} has already been requeued`,
103-
};
104-
}
105-
106-
// All validations passed — proceed with requeue
107-
108-
const { owner, repo } = parseRepo();
109-
110-
try {
111-
// Reopen issue
112-
await reopenIssue(owner, repo, issueNumber);
113-
114-
// Clear verdict labels
115-
await clearVerdictLabels(issueNumber);
116-
117-
// Post re-evaluation comment
118-
await postComment(
119-
owner,
120-
repo,
121-
issueNumber,
122-
`## 🔄 Re-evaluation Requested\n\nThis issue has been requeued for re-validation by \`${requesterId}\`.\n\nPrevious verdict labels have been cleared.`,
123-
);
124-
125-
// Create requeue record
126-
insertRequeueRecord({
127-
issue_number: issueNumber,
128-
requester_id: requesterId,
129-
requester_context: requesterContext
130-
? JSON.stringify(requesterContext)
131-
: undefined,
132-
});
133-
134-
// Reset bounty status
135-
updateBountyStatus(issueNumber, "pending");
136-
137-
// Enqueue for processing
138-
enqueue({
139-
issueNumber,
140-
workspaceId: bounty.workspace_id ?? "",
141-
retryCount: 0,
142-
addedAt: new Date().toISOString(),
143-
});
144-
145-
// Audit log
146-
insertAuditLog({
147-
workspace_id: bounty.workspace_id ?? undefined,
148-
action: "bounty.requeued",
149-
actor: requesterId,
150-
details: JSON.stringify({
151-
issue_number: issueNumber,
152-
requester_context: requesterContext,
153-
}),
154-
github_ref: `#${issueNumber}`,
155-
});
156-
157-
logger.info({ issueNumber, requesterId }, "Requeue: success");
158-
return { success: true };
159-
} catch (err: unknown) {
160-
const msg = err instanceof Error ? err.message : String(err);
161-
logger.error({ issueNumber, err: msg }, "Requeue: failed");
162-
return { success: false, error: msg };
163-
}
164-
}
16510

16611
/* ------------------------------------------------------------------ */
16712
/* Force-release handler */

src/config.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,6 @@ export const DUPLICATE_THRESHOLD = parseFloat(
105105
process.env.DUPLICATE_THRESHOLD || "0.75",
106106
);
107107

108-
/**
109-
* Maximum age (ms) of an issue eligible for requeue (default 24h).
110-
*/
111-
export const REQUEUE_MAX_AGE_MS = parseInt(
112-
process.env.REQUEUE_MAX_AGE_MS || "86400000",
113-
10,
114-
);
115-
116108
/**
117109
* OpenRouter API key for LLM-assisted scoring and embeddings.
118110
* Never log this value.

src/db/index.test.ts

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ import {
1717
getInProgressBounties,
1818
insertValidationResult,
1919
getLatestValidation,
20-
insertRequeueRecord,
21-
getRequeueRecord,
22-
updateRequeueStatus,
23-
getPendingRequeues,
24-
markRequeueCompleted,
2520
insertSpamAnalysis,
2621
upsertEmbedding,
2722
getEmbedding,
@@ -229,60 +224,6 @@ describe("db/index", () => {
229224
/* Requeue records */
230225
/* ---------------------------------------------------------------- */
231226

232-
describe("requeue records", () => {
233-
beforeEach(() => {
234-
upsertBounty({ issue_number: 42001 });
235-
});
236-
237-
it("insertRequeueRecord creates a record with pending status", () => {
238-
insertRequeueRecord({ issue_number: 42001, requester_id: "user-1" });
239-
const row = getRequeueRecord(42001);
240-
expect(row).toBeDefined();
241-
expect(row!.status).toBe("pending");
242-
expect(row!.requester_id).toBe("user-1");
243-
});
244-
245-
it("getRequeueRecord returns undefined when no record exists", () => {
246-
expect(getRequeueRecord(99999)).toBeUndefined();
247-
});
248-
249-
it("updateRequeueStatus changes the status", () => {
250-
insertRequeueRecord({ issue_number: 42001 });
251-
const record = getRequeueRecord(42001)!;
252-
updateRequeueStatus(record.id, "completed");
253-
const updated = getRequeueRecord(42001)!;
254-
expect(updated.status).toBe("completed");
255-
});
256-
257-
it("getPendingRequeues returns only pending records", () => {
258-
insertRequeueRecord({ issue_number: 42001, requester_id: "a" });
259-
upsertBounty({ issue_number: 42002 });
260-
insertRequeueRecord({ issue_number: 42002, requester_id: "b" });
261-
const pending = getPendingRequeues();
262-
expect(pending).toHaveLength(2);
263-
});
264-
265-
it("markRequeueCompleted sets resolved status and callback_sent", () => {
266-
insertRequeueRecord({ issue_number: 42001 });
267-
const record = getRequeueRecord(42001)!;
268-
markRequeueCompleted(record.id, new Date().toISOString());
269-
const updated = getRequeueRecord(42001)!;
270-
expect(updated.status).toBe("resolved");
271-
expect(updated.callback_sent).toBe(1);
272-
expect(updated.completed_at).toBeDefined();
273-
});
274-
275-
it("insertRequeueRecord stores requester_context", () => {
276-
insertRequeueRecord({
277-
issue_number: 42001,
278-
requester_id: "user-1",
279-
requester_context: '{"reason":"re-check"}',
280-
});
281-
const row = getRequeueRecord(42001)!;
282-
expect(row.requester_context).toBe('{"reason":"re-check"}');
283-
});
284-
});
285-
286227
/* ---------------------------------------------------------------- */
287228
/* Spam analysis */
288229
/* ---------------------------------------------------------------- */

0 commit comments

Comments
 (0)