* The HTML completeness guard counted manifest ROWS, and there are up to two
per upload — a thumbnail and a full variant. Thumbnails are 400px JPEGs the
export generates itself into its own temp dir, so they are no evidence that
any original was captured. After the guard was relaxed to bail only on
"nothing written at all", that case could no longer fire while thumbnails
kept succeeding: if the media volume became unreadable after the stat pass,
every original open failed, every thumb open succeeded, and a keepsake with
100 thumbnails and ZERO full-resolution photos published green, done at the
live epoch, with the download button lit. Boot recovery skips a done job,
so nothing would ever have rebuilt it. Now counts photos, not files.
* A decoder panic failed the ENTIRE keepsake. The `?` was on the JoinError,
not on the closure's Result, so a panic in the image crate propagated out
where the same file merely failing costs one tile — and it was
deterministic, because "Neu erzeugen" reads the same poison file and dies
the same way. That is the exact failure shape the completeness guard was
relaxed to eliminate, arriving through the other door.
* delete_account armed both export jobs and then spawned the workers AFTER an
awaited file-removal loop. Axum drops a handler future on client
disconnect, and every other invalidate_and_arm call site spawns with no
intervening await. Dropped inside that loop, the keepsake is left with the
epoch bumped, both rows pending at that epoch, and no worker: the downloads
404 and the UI sits on "Wird vorbereitet..." until someone reboots the app.
Deleting your account from a phone that walks out of range is enough.
* The daily download quota was charged before the ticket could fail, so a
store-capacity 503 — a server-side condition the guest cannot see or cause
— still cost one of their three downloads. There is no refund path.