feat(retention): warn before a recording is deleted for good (#402) #458
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/402-warn-before-deletion"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #402 (HIGH). Cross-component (backend + bot), so it ships as one PR per
CLAUDE.md.The defect
A failed transcription's audio is the only copy of anything for that session — none was produced — and retention removes it when the grace expires. Nothing told the GM. The countdown existed only on the admin recordings page, so noticing required knowing to go and look somewhere a GM has no particular reason to visit. The issue's own scenario is a GM heading into a week off, which rules that out entirely.
Scope: one criterion was already met
The issue asks for the failed-case retention window to be "independently configurable… or otherwise deliberately widened". #427 already did the widening: every mode now reaches deletion through the trash, with a 7-day grace floored in code rather than merely defaulted. A failed session's audio survives
retention_window + 7 days, not "vanishes on a Beat tick" as the issue describes.Treating that as met rather than adding a separate knob — more retention settings is more ways to misconfigure the one artefact that cannot be regenerated. Confirmed with the issue's author before building.
What ships
A daily task finds trashed audio inside a 3-day lead and posts to the campaign's Discord channel: which recording, exactly when it goes, and how to keep it. It says explicitly when transcription failed, because that is the case where losing the audio loses the session rather than just its media.
Once per trip through the trash, via
sessions.audio_deletion_warned_at(migrationd2e3f4a5b6c8) — the task runs daily, and a warning that arrives every morning is one a GM learns to ignore, which is precisely the message that must not be ignored. The marker is cleared wherever audio leaves the trash, so a restored-then-retrashed recording is warned about again; a plain boolean would mark it warned forever and go silent on the trip that actually ends in deletion.The countdown is on the session page, not just the admin console.
One deadline, computed once
permanent_deletion_atwas inline in the admin router, and the sweeper derives the same boundary from the other direction. A third copy in the warning task is how the date a GM is shown drifts from when the audio actually goes — and a wrong countdown is worse than none, because it gets believed. It now lives inapp/retention.pyand all three read it.Bot side
The handler is purely additive: the dispatch table already logs and ignores unknown event types, so a backend that has this and a bot that does not degrades gracefully. No
BOT_CONTRACT_VERSIONbump — the contract rule covers/api/bot/*changes, and this adds neither an endpoint nor a shape an older bot would mishandle.The marker is written before the publish, not after. A publish that fails is retried by the stream; a publish that succeeds while the marker write fails would re-announce tomorrow, and rarity is the whole point.
Verification
Mutation-checked on both guards:
test_a_recording_with_time_left_is_not_warned_about_yetfails.test_the_warning_is_sent_once_not_every_dayfails.There is also a test that the lead time is shorter than the minimum grace it warns within — a lead longer than the grace would warn about everything the instant it was trashed, which is the same as not warning.
1,470 backend, 457 frontend, 220 bot tests pass. Migration verified applying and downgrading over the full chain. Lint clean at pinned ruff 0.4.4.
Note: the bot suite needs CI's install order (
pip install --no-deps -r requirements-git.txtafter the others) — installing all three together hits adiscord-py[voice]/pynaclresolution conflict.🤖 Generated with Claude Code