diff --git a/src/queue/processors.ts b/src/queue/processors.ts index a05a34363f..c318e16bcf 100644 --- a/src/queue/processors.ts +++ b/src/queue/processors.ts @@ -89,7 +89,6 @@ import { upsertPullRequestFromGitHub, upsertRepositoryFromGitHub, } from "../db/repositories"; -import { dedupeSignalSnapshots, pruneExpiredRecords } from "../db/retention"; import { effectiveIssueCapForAccountAge, isBelowAccountAgeThreshold, @@ -414,6 +413,11 @@ import { maybeRecloseDisallowedReopen, type ReopenRecloseOutcome, } from "./review-evasion"; +// #4013 step 7: same shim shape for runRetentionPrune -- imported here for processJob's own internal call +// below, and re-exported so test/unit/retention.test.ts and test/unit/selfhost-pg-retention.test.ts's +// existing `import { ... } from "../../src/queue/processors"` keeps working unchanged. +import { runRetentionPrune } from "./retention"; +export { runRetentionPrune } from "./retention"; import { isVisualPath } from "../review/visual/paths"; import { buildCapture, fetchShotContentBlock, hasSuccessfulBotCapture, resolveVisualRoutes, type CaptureRoute } from "../review/visual/capture"; import { @@ -1047,38 +1051,6 @@ function reuseOrRefreshLiveCiAggregate( return refreshLiveCiAggregate(env, { repoFullName, facts, prNumber, headSha, baseRef, token, expectedCiContexts, admissionKey }); } -/** - * Run (or dry-run) the data-retention prune across the configured log/snapshot tables, plus the - * signal_snapshots dedup pass (#3810 -- signal_snapshots has no natural dedup, so within its own - * retention window a key can still accumulate many superseded rows), and audit the combined outcome. - * The per-table windows live in RETENTION_POLICY; only append-only/superseded tables are pruned. - */ -export async function runRetentionPrune( - env: Env, - requestedBy: string, - dryRun: boolean, -): Promise { - const results = await pruneExpiredRecords(env, { dryRun }); - const dedupeResults = await dedupeSignalSnapshots(env, { dryRun }); - const totalDeleted = results.reduce((sum, result) => sum + result.deleted, 0); - const totalDeduped = dedupeResults.reduce((sum, result) => sum + result.deleted, 0); - await recordAuditEvent(env, { - eventType: "retention.prune", - actor: requestedBy, - outcome: dryRun ? "completed" : "success", - detail: dryRun - ? `dry-run: ${totalDeleted} row(s) eligible, ${totalDeduped} duplicate signal_snapshots row(s) eligible` - : `pruned ${totalDeleted} row(s), deduped ${totalDeduped} signal_snapshots row(s)`, - metadata: { - dryRun, - totalDeleted, - perTable: Object.fromEntries(results.map((r) => [r.table, r.deleted])), - totalDeduped, - perSignalType: Object.fromEntries(dedupeResults.map((r) => [r.signalType, r.deleted])), - }, - }); -} - const PUBLIC_MANIFEST_POLICY_FINDING_OVERRIDES: Partial< Record< FocusManifestFinding["code"], diff --git a/src/queue/retention.ts b/src/queue/retention.ts new file mode 100644 index 0000000000..d1210d2693 --- /dev/null +++ b/src/queue/retention.ts @@ -0,0 +1,38 @@ +// Data-retention pruning (#4013 step 7 -- extracted from processors.ts, seventh step of the file's own +// module-split sequence, after transient-locks.ts, signal-snapshot.ts, duplicate-detection.ts, +// slop-detection.ts, review-evasion.ts, and ci-resolution.ts). Pure move. + +import { recordAuditEvent } from "../db/repositories"; +import { dedupeSignalSnapshots, pruneExpiredRecords } from "../db/retention"; + +/** + * Run (or dry-run) the data-retention prune across the configured log/snapshot tables, plus the + * signal_snapshots dedup pass (#3810 -- signal_snapshots has no natural dedup, so within its own + * retention window a key can still accumulate many superseded rows), and audit the combined outcome. + * The per-table windows live in RETENTION_POLICY; only append-only/superseded tables are pruned. + */ +export async function runRetentionPrune( + env: Env, + requestedBy: string, + dryRun: boolean, +): Promise { + const results = await pruneExpiredRecords(env, { dryRun }); + const dedupeResults = await dedupeSignalSnapshots(env, { dryRun }); + const totalDeleted = results.reduce((sum, result) => sum + result.deleted, 0); + const totalDeduped = dedupeResults.reduce((sum, result) => sum + result.deleted, 0); + await recordAuditEvent(env, { + eventType: "retention.prune", + actor: requestedBy, + outcome: dryRun ? "completed" : "success", + detail: dryRun + ? `dry-run: ${totalDeleted} row(s) eligible, ${totalDeduped} duplicate signal_snapshots row(s) eligible` + : `pruned ${totalDeleted} row(s), deduped ${totalDeduped} signal_snapshots row(s)`, + metadata: { + dryRun, + totalDeleted, + perTable: Object.fromEntries(results.map((r) => [r.table, r.deleted])), + totalDeduped, + perSignalType: Object.fromEntries(dedupeResults.map((r) => [r.signalType, r.deleted])), + }, + }); +}