feat: configurable audio & transcript retention (#119) #201

Merged
claude-bot merged 2 commits from feat/119-retention into main 2026-07-18 07:32:23 +00:00
Contributor

Closes #119. Second issue of v3.8.0 Privacy & Data Lifecycle.

Makes retention explicit + configurable (instance defaults + per-campaign overrides) instead of the implicit "keep audio until GM approval, transcripts forever." Recommended posture: transcript-only (delete raw audio after processing).

Backend (f31d7f8)

  • Settings: audio_retention_policy {mode: delete_after_processing|retain_days|retain_indefinitely, days} (default delete_after_processing/7) + transcript_retention_policy {mode: retain_indefinitely|retain_months, months} (default indefinitely). get_effective_retention resolves per-field — campaign override wins, null inherits.
  • Campaign gains 4 nullable override columns; migration f1a2b3c4d5e6 (round-trips). GET/PUT /admin/settings/retention.
  • Pipeline: apply_post_processing_audio_retention deletes the WAV dir at the end of a successful process_audio under delete_after_processing (→ terminal approved); defensive — a delete error never fails the pipeline; the failure path keeps audio for debugging.
  • enforce_retention Beat task (generalizes cleanup_trashed_audio): anchors audio deletion on transcript_updated_at (processing-complete) regardless of approval state — fixes unapproved sessions hoarding audio forever; retain_months clears transcript (FTS auto-clears) + purges LoreExtractCache (summaries untouched). One idempotent retention.enforced audit entry per run that deleted anything.
  • README privacy/data-retention section.

Frontend (<head>)

  • Admin "Data retention" tab (mode selects + conditional day/month inputs + a delete_after_processing warning).
  • Per-campaign "Data retention overrides" — "Use instance default" (null) inherits; count sent only when the mode needs it.

Tests

  • Backend: +9 (test_retention.py) — delete_after_processing, never-approved retain_days, transcript+FTS+cache expiry, idempotency, override resolution. Full suite 550 pass; migration round-trip verified.
  • Frontend: +10. 286 pass, eslint clean, vite build green.

Notes

  • No bot/ changes → no contract concern.
  • Audit uses the campaign-scoped log_event from #120.

🤖 Generated with Claude Code

Closes #119. Second issue of **v3.8.0 Privacy & Data Lifecycle**. Makes retention explicit + configurable (instance defaults + per-campaign overrides) instead of the implicit "keep audio until GM approval, transcripts forever." Recommended posture: **transcript-only** (delete raw audio after processing). ## Backend (`f31d7f8`) - Settings: `audio_retention_policy {mode: delete_after_processing|retain_days|retain_indefinitely, days}` (default delete_after_processing/7) + `transcript_retention_policy {mode: retain_indefinitely|retain_months, months}` (default indefinitely). `get_effective_retention` resolves **per-field** — campaign override wins, null inherits. - Campaign gains 4 nullable override columns; migration `f1a2b3c4d5e6` (round-trips). `GET/PUT /admin/settings/retention`. - Pipeline: `apply_post_processing_audio_retention` deletes the WAV dir at the end of a successful `process_audio` under `delete_after_processing` (→ terminal `approved`); defensive — a delete error never fails the pipeline; the failure path keeps audio for debugging. - `enforce_retention` Beat task (generalizes `cleanup_trashed_audio`): anchors audio deletion on **`transcript_updated_at` (processing-complete) regardless of approval state** — fixes unapproved sessions hoarding audio forever; `retain_months` clears `transcript` (FTS auto-clears) + purges `LoreExtractCache` (**summaries untouched**). One idempotent `retention.enforced` audit entry per run that deleted anything. - README privacy/data-retention section. ## Frontend (`<head>`) - Admin "Data retention" tab (mode selects + conditional day/month inputs + a `delete_after_processing` warning). - Per-campaign "Data retention overrides" — "Use instance default" (null) inherits; count sent only when the mode needs it. ## Tests - Backend: +9 (`test_retention.py`) — delete_after_processing, never-approved `retain_days`, transcript+FTS+cache expiry, idempotency, override resolution. Full suite **550 pass**; migration round-trip verified. - Frontend: +10. **286 pass**, eslint clean, vite build green. ## Notes - No `bot/` changes → no contract concern. - Audit uses the campaign-scoped `log_event` from #120. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Makes retention explicit and configurable — instance defaults + per-campaign
overrides — instead of the implicit "keep audio until GM approval, transcripts
forever" behavior. Recommended posture: transcript-only (delete raw audio
after processing).

- Settings (settings_service): audio_retention_policy {mode: delete_after_
  processing|retain_days|retain_indefinitely, days} (default delete_after_
  processing/7) + transcript_retention_policy {mode: retain_indefinitely|
  retain_months, months} (default indefinitely). get_effective_retention
  resolves per-field: campaign override wins, null inherits instance default.
- Campaign gains 4 nullable override columns; migration f1a2b3c4d5e6
  (round-trips). GET/PUT /admin/settings/retention for instance defaults.
- Pipeline: apply_post_processing_audio_retention deletes the WAV dir at the
  end of a successful process_audio when delete_after_processing (→ terminal
  approved state); defensive (never fails the pipeline); failure path keeps
  audio for debugging.
- enforce_retention Beat task (generalizes cleanup_trashed_audio, daily):
  anchors audio deletion on transcript_updated_at (processing-complete)
  REGARDLESS of approval state — fixes unapproved sessions hoarding audio
  forever; retain_months clears transcript (FTS auto-clears) + purges
  LoreExtractCache (summaries untouched). One retention.enforced audit entry
  per run that deleted anything; idempotent/state-driven.
- README: privacy/data-retention section.

Tests: +9 (test_retention.py) — delete_after_processing, never-approved
retain_days, transcript+FTS+cache expiry, idempotency, override resolution,
schema round-trip. Full suite 550 pass; migration round-trip verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(frontend): retention settings UI — admin defaults + campaign overrides (#119)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 47s
CI / Docker image build (pull_request) Successful in 50s
CI / Frontend tests, audit, and build (pull_request) Successful in 1m56s
CI / Bot tests and audit (pull_request) Successful in 2m16s
CI / Backend migration, tests, and audit (pull_request) Successful in 4m1s
605e11c668
- api/sessions.js: fetch/updateRetentionPolicy admin wrappers.
- Admin "Data retention" tab: audio-mode + transcript-mode selects with
  conditional days/months inputs and a warning under delete_after_processing
  (deletes raw audio, no reprocess).
- CampaignDetail: per-campaign "Data retention overrides" — each select's
  first option "Use instance default" = null (inherit); count field sent only
  when its mode needs it, else null.

Tests: +10 (wrappers 2, RetentionSection 5, campaign override 3). 286 pass,
eslint clean, vite build succeeds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/119-retention 2026-07-18 07:32:23 +00:00
Sign in to join this conversation.
No description provided.