The only test that reached SecretCipher::decrypt() failing was an accident of
a fixture bug fixed in the previous commit: it encrypted with the wrong key,
so decryption threw and the migration's catch(Throwable) branch ran without
anyone meaning to test it. Fixing that fixture silently removed the only
coverage of a branch that decides a payment key's mode, so it gets its own
test with a deliberately unreadable row.
down() picked whichever of the :live/:test rows an unordered get() happened to
return first to restore to the bare key, and deleted the other — so which
credential survived a rollback depended on row order, not a decision. It now
processes :live rows first unconditionally, then :test rows only where the
bare key is still free, so the live credential always wins when both exist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>