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
3 changes: 3 additions & 0 deletions src/db/retention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export const RETENTION_POLICY: readonly RetentionRule[] = [
{ table: "signal_snapshots", column: "generated_at", days: 90 },
{ table: "score_previews", column: "generated_at", days: 90 },
{ table: "repo_snapshots", column: "fetched_at", days: 90 },
// One payloadJson blob per agent run (#3896); a per-run diagnostic snapshot with no cross-run rollup
// depending on it, so a shorter window than the audit/usage-log tables above is appropriate.
{ table: "agent_context_snapshots", column: "created_at", days: 30 },
];

export type PruneResult = { table: string; column: string; cutoff: string; deleted: number };
Expand Down
20 changes: 19 additions & 1 deletion test/unit/retention.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import { createApp } from "../../src/api/routes";
import { getDb } from "../../src/db/client";
import { dedupeSignalSnapshots, pruneExpiredRecords, RETENTION_POLICY } from "../../src/db/retention";
import { aiUsageEvents, webhookEvents } from "../../src/db/schema";
import { agentContextSnapshots, aiUsageEvents, webhookEvents } from "../../src/db/schema";
import { processJob, runRetentionPrune } from "../../src/queue/processors";
import { createTestEnv } from "../helpers/d1";

Expand Down Expand Up @@ -102,6 +102,24 @@ describe("pruneExpiredRecords", () => {
await expect(pruneExpiredRecords(env, { policy: [{ table: "webhook_events; DROP TABLE x", column: "received_at", days: 1 }] })).rejects.toThrow("Unsafe retention identifier");
});

it("prunes agent_context_snapshots older than its window and keeps recent runs (#3896)", async () => {
const env = createTestEnv();
const db = getDb(env.DB);
await db.insert(agentContextSnapshots).values([
{ id: "ctx-old", runId: "run-old", createdAt: daysAgo(40) },
{ id: "ctx-recent", runId: "run-recent", createdAt: daysAgo(2) },
]);

const results = await pruneExpiredRecords(env, {
nowMs: NOW,
policy: [{ table: "agent_context_snapshots", column: "created_at", days: 30 }],
});

expect(results[0]?.deleted).toBe(1);
const rows = await env.DB.prepare("SELECT id FROM agent_context_snapshots").all<{ id: string }>();
expect(rows.results.map((row) => row.id)).toEqual(["ctx-recent"]);
});

it("the policy only targets append-only/log/snapshot tables (no current-state tables)", () => {
const tables = RETENTION_POLICY.map((r) => r.table);
for (const protectedTable of ["webhook_events", "repositories", "repository_settings", "pull_requests", "issues", "repository_ai_keys", "contributors"]) {
Expand Down