⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
src/db/schema.ts:1145-1169's notification_deliveries table is a pure append-only, one-row-per-delivery-event log -- the same shape src/db/retention.ts:16-30's own doc comment describes for webhook_events. It has a createdAt column and a ready-made index (notification_deliveries_recipient_channel_created_idx on (recipientLogin, channel, createdAt) -- exactly the shape a retention prune would use), but no entry in RETENTION_POLICY, so rows accumulate forever (fan-outs from issue_watch_match/AMS events can produce many rows per event).
Requirements
Add { table: "notification_deliveries", column: "created_at", days: 90 } (or a similarly-reasoned retention window) to RETENTION_POLICY in src/db/retention.ts. Confirm dedupeSignalSnapshots/pruneExpiredRecords's identifier-safety regex and batching handle this table without further code changes (the existing index should make this a pure config addition).
Deliverables
All of the above Deliverables are required in the same PR unless the deliverable text itself states otherwise.
Test Coverage Requirements
src/** -- 99%+ patch coverage, branch-counted, on the new retention-policy entry and its test.
Expected Outcome
notification_deliveries rows are pruned on the same retention schedule as other append-only event logs, instead of accumulating unbounded.
Links & Resources
src/db/schema.ts:1145-1169
src/db/retention.ts:16-30
Context
src/db/schema.ts:1145-1169'snotification_deliveriestable is a pure append-only, one-row-per-delivery-event log -- the same shapesrc/db/retention.ts:16-30's own doc comment describes forwebhook_events. It has acreatedAtcolumn and a ready-made index (notification_deliveries_recipient_channel_created_idxon(recipientLogin, channel, createdAt)-- exactly the shape a retention prune would use), but no entry inRETENTION_POLICY, so rows accumulate forever (fan-outs fromissue_watch_match/AMS events can produce many rows per event).Requirements
Add
{ table: "notification_deliveries", column: "created_at", days: 90 }(or a similarly-reasoned retention window) toRETENTION_POLICYinsrc/db/retention.ts. ConfirmdedupeSignalSnapshots/pruneExpiredRecords's identifier-safety regex and batching handle this table without further code changes (the existing index should make this a pure config addition).Deliverables
RETENTION_POLICYincludes an entry fornotification_deliveriespruneExpiredRecordscorrectly prunes expirednotification_deliveriesrows using the existing indexAll of the above Deliverables are required in the same PR unless the deliverable text itself states otherwise.
Test Coverage Requirements
src/**-- 99%+ patch coverage, branch-counted, on the new retention-policy entry and its test.Expected Outcome
notification_deliveriesrows are pruned on the same retention schedule as other append-only event logs, instead of accumulating unbounded.Links & Resources
src/db/schema.ts:1145-1169src/db/retention.ts:16-30