A failed run's audio clock no longer restarts on a write to its session (#798) #992

Merged
claude-bot merged 1 commit from fix/798-audio-received-at into main 2026-10-01 17:32:38 +00:00
Collaborator

Closes #798.

The problem

#791 anchored a processed session's audio retention on the write-once audio_processed_at. A session whose processing has only ever failed had no such stamp and still counted from sessions.updated_at, which moves on any write to the row. Erasing a member's recording from a failed session adds a note to the row, so the erase restarted the clock for everyone else's audio: a privacy control causing more to be kept.

The fix

  • sessions.audio_received_at, a nullable timestamp, written once where the bot's upload is accepted (POST /api/bot/sessions/{id}/audio). A re-upload or a retry does not move it. Nothing in the bot API's shape changes, so there is no contract bump.
  • Retention (audio_retention_anchor, the one place the anchor is computed, which the preview and the sweep both use) now takes audio_processed_at, then for a failed run audio_received_at, and updated_at only as the last fallback for a row carrying neither.
  • Migration a798c0d1e2f3 on 11d4e5f6a7b8. It adds the column and backfills audio_received_at = updated_at for rows that hold audio and have neither audio_processed_at nor transcript_updated_at. Those are exactly the rows that counted from updated_at before, so nothing is caught earlier or later on upgrade day. Downgrade drops the column.
  • The campaign export and import carry the new column, and the demo seed's failed session has one.

The two other places that set a session's audio path (Admin's retry and the session page's retry) re-queue audio already stored, so they are rightly left alone.

Tests

  • The regression: a failed session keeps its deadline after a member erase, and after a title edit.
  • The handoff stamps it once; a second upload does not move it.
  • The migration: backfills failed rows from updated_at, leaves processed rows alone, downgrade drops the column, and it is the single head.
  • Full backend suite: 6574 passed, 14 skipped. ruff 0.4.4 format and check clean; changelog check OK.

🤖 Generated with Claude Code

Closes #798. ## The problem #791 anchored a *processed* session's audio retention on the write-once `audio_processed_at`. A session whose processing has only ever failed had no such stamp and still counted from `sessions.updated_at`, which moves on any write to the row. Erasing a member's recording from a failed session adds a note to the row, so the erase restarted the clock for everyone else's audio: a privacy control causing more to be kept. ## The fix - **`sessions.audio_received_at`**, a nullable timestamp, written once where the bot's upload is accepted (`POST /api/bot/sessions/{id}/audio`). A re-upload or a retry does not move it. Nothing in the bot API's shape changes, so there is no contract bump. - **Retention** (`audio_retention_anchor`, the one place the anchor is computed, which the preview and the sweep both use) now takes `audio_processed_at`, then for a failed run `audio_received_at`, and `updated_at` only as the last fallback for a row carrying neither. - **Migration `a798c0d1e2f3`** on `11d4e5f6a7b8`. It adds the column and backfills `audio_received_at = updated_at` for rows that hold audio and have neither `audio_processed_at` nor `transcript_updated_at`. Those are exactly the rows that counted from `updated_at` before, so nothing is caught earlier or later on upgrade day. Downgrade drops the column. - The campaign export and import carry the new column, and the demo seed's failed session has one. The two other places that set a session's audio path (Admin's retry and the session page's retry) re-queue audio already stored, so they are rightly left alone. ## Tests - The regression: a failed session keeps its deadline after a member erase, and after a title edit. - The handoff stamps it once; a second upload does not move it. - The migration: backfills failed rows from `updated_at`, leaves processed rows alone, downgrade drops the column, and it is the single head. - Full backend suite: 6574 passed, 14 skipped. ruff 0.4.4 format and check clean; changelog check OK. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A failed run's audio clock no longer restarts on a write to its session (#798)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 30s
CI / Does this change touch the recording path? (pull_request) Successful in 42s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Docker image build (pull_request) Successful in 50s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 54s
CI / Bot/backend version sync (pull_request) Successful in 1m11s
CI / Bot tests and audit (pull_request) Successful in 1m49s
CI / Feedback site tests (pull_request) Successful in 2m0s
CI / Frontend (shard 2/2) (pull_request) Successful in 5m58s
CI / Frontend (shard 1/2) (pull_request) Successful in 7m39s
CI / End-to-end journeys (desktop) (pull_request) Successful in 8m15s
CI / Backend migration, tests, and audit (pull_request) Successful in 18m3s
CI / End-to-end journeys (phone) (pull_request) Successful in 20m9s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 21m29s
35e6ff4fec
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No description provided.