Chunk: P2-3 — Configurable retention · Requirements: FR-SUB-3, NFR-PRIV-1 · Depends on: the retention schema + purge worker issues · Constrained by: P-5, P-3
Implement zero-retention end to end: store nothing beyond what delivery needs.
Scope
In
- Ingestion (
src/lib/ingest.ts). Keep P-5 intact — the submission is still persisted before any delivery is attempted; zero-retention changes how long it lives, not whether it is written. Follow the Q-3 decision on whether rawBody is skipped for zero-retention forms.
- Post-delivery purge. As soon as every destination for a submission reaches a terminal state (
succeeded / dead_letter), delete the submission per the decision. Hook this into the delivery path in src/lib/queue.ts or the retention pass — whichever keeps the ingestion path fast and side-effect-light (P-3).
- Surfaces degrade gracefully. The inbox (
src/lib/inbox.ts, src/routes/dashboard/forms.$formId.tsx), export, and replay (src/lib/replay.ts) must show an explicit "not retained" state for a zero-retention form rather than an empty list that reads like a bug or a crash on a missing row.
- Integration tests: a zero-retention form still delivers successfully; the submission is gone after terminal delivery; a dead-lettered delivery behaves as decided; the inbox/export/replay surfaces behave sanely.
Out
- Retention UI and the generic age-based purge (separate issues).
Acceptance criteria
Chunk: P2-3 — Configurable retention · Requirements: FR-SUB-3, NFR-PRIV-1 · Depends on: the retention schema + purge worker issues · Constrained by: P-5, P-3
Implement zero-retention end to end: store nothing beyond what delivery needs.
Scope
In
src/lib/ingest.ts). Keep P-5 intact — the submission is still persisted before any delivery is attempted; zero-retention changes how long it lives, not whether it is written. Follow the Q-3 decision on whetherrawBodyis skipped for zero-retention forms.succeeded/dead_letter), delete the submission per the decision. Hook this into the delivery path insrc/lib/queue.tsor the retention pass — whichever keeps the ingestion path fast and side-effect-light (P-3).src/lib/inbox.ts,src/routes/dashboard/forms.$formId.tsx), export, and replay (src/lib/replay.ts) must show an explicit "not retained" state for a zero-retention form rather than an empty list that reads like a bug or a crash on a missing row.Out
Acceptance criteria
npm run typecheck,npm run lint,npm testall green.