A second pair of eyes went over the last week's changes and found five places where the code did the right thing in the obvious case and quietly the wrong thing in the edge case. None had bitten a real fan yet. All five are fixed, each with the reviewer's own reproduction kept as a permanent test.
A fan who withdrew consent to transcription was still transcribed. The consent ledger never edits history — a withdrawal is a new entry, not a change to the old one. The transcription check only looked at the old entry, which still said "yes". It now looks for the withdrawal too, so changing your mind actually changes what happens.
Clearing FanMoment outputs could quietly regenerate them. Deleting a FanMoment output was designed to stop the retry sweeper. It did. It also made the photo look brand new to the other sweeper, the one that settles photos left undecided at the end of an event — which happily generated a fresh picture, charged for it, and emailed the fan again. Deleted history now still counts as history.
A storage hiccup during deletion could leave a fan's photo behind for good. Deletion removed the database record first and the files second. If the second step failed, nothing was left pointing at the files, so nothing could ever come back for them. Files go first now; if that fails, the record survives and the next hourly run tries again.
Retention could get stuck behind content still in use. The purge always took the oldest 200 items due for deletion. Anything still baked into a live fan wall is deliberately held back — but held-back items stay put, so an account with 200 of them at the front of the queue never got past them. Held-back content is now stepped over, and still reported.
Dashboards waited on transcription they didn't need. The "clip finished" signal to live dashboards had been chained after the answer-transcription step, so every clip queued behind AI work before the dashboard heard about it — and a transcription that gave up stopped the signal altogether. It now runs alongside, not in front.