fix(retention): gate raw-media purge on derived per-file state, fail closed master
A failed or empty extraction also writes the extraction JSONL, so the old presence-only completion gate treated a failed audio transcription as complete and deleted the only copy of the raw media. Because the gate is whole-segment, a failed audio extraction also exposed the segment's video. Re-key eligibility on derive_modality_state per extraction file. Fail closed on failed, malformed, and unreadable files. The gate computes has_chunks only from rows after the header so a stray start or timestamp merged via SEGMENT_META cannot mask a failure. Legacy chunk-bearing files with no processing record stay eligible. Add the adjacent honesty fixes: a distinct segments_blocked_failed counter and per-segment detail surfaced in the result, CLI, settings route, and logs; per-segment pruning-runs audit writes that survive a mid-loop crash, with AuditOutcome captured and surfaced; retention.log now also writes for real runs that block but delete nothing, while dry-run still writes nothing. Correct the docstring's false 7-day default claim: the real default is mode="keep". Regenerate docs/deletion-sites-inventory.md with current line refs and the omitted log_retention.py rows, and update logs.md for the new audit record shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>