feat(ops): reclaim audio directories no session refers to (#406) #466
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/406-reconcile-orphaned-audio"
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 #406 (MEDIUM). The last of #416's cross-referenced stuck states — that issue can close once this and #409 are both in.
The defect
Neither existing sweep can see these directories. The backend's retention passes iterate database rows, so a directory whose row is gone is invisible by construction. The bot's startup sweep skips anything marked handed-off "regardless of age" — correct for a live session, wrong forever afterwards. A hard-deleted campaign leaves its recordings on the volume permanently, with no operator-visible signal.
What ships
A daily Beat task reconciles the volume against the sessions table, removes directories nothing refers to after a 7-day grace, and writes an audit entry naming what went.
That grace is also what satisfies the fourth criterion. Rather than adding cleanup to the campaign-delete path — a second mechanism to keep correct — a delete is simply reclaimed within a bounded time by the one mechanism that already has to exist.
This task deletes, so most of the design is about refusing to
An empty sessions table deletes nothing. "Remove everything not in the database" removes everything the instant that set comes back empty — a failed query, a half-migrated schema, a database pointed at the wrong host. Directories on disk with no sessions at all is a database problem, not a pile of orphans, and acting on it automatically would destroy every recording on the volume. It logs loudly and stops.
This is the same shape as #399, whose sweep destroyed real recordings while acting with complete confidence on a premise ("the backend never received the upload so there is nothing to preserve") that was exactly backwards. That is the failure this milestone exists to stop repeating, so the guard is a first-class part of the task rather than a defensive afterthought.
Only directories named like a session id are candidates. A
lost+found, a mount point, or an operator's scratch copy on the same volume was not created by this pipeline and is not ours to delete.Recent directories are left alone regardless of whether a row exists. The bot creates the directory before the backend records the path, and during a long recording that window is hours.
One evidence bullet in the issue is now stale
It lists a session stuck in
processingas permanently exempt from every sweep. #398's watchdog now moves those tofailedwithin 8 hours, so they reach a sweepable state on their own. No special handling is needed, and adding some would have been dead code.Verification
Mutation-checked: removing the empty-table guard fails
test_an_empty_sessions_table_deletes_nothing, which asserts all three seeded directories survive.Nine tests, of which four are about not deleting — a reclaimer that passes only its happy path is the dangerous kind. There is also a test that an idle run writes no audit entry, since an entry per quiet day buries the ones that mean something.
1,512 backend tests pass (was 1,503). Lint clean at pinned ruff 0.4.4.
🤖 Generated with Claude Code
dd7df59e0f108d730862