advance_paid/balance_paid (and the interim advance_payment/balance_payment)
all collapse onto the single new payment_received key, and the carry used
Map.set -- whichever row was visited second silently overwrote the first's
done_on/payment_id, counted as neither carried nor dropped, with the audit
`before` left `undefined` so the loss was unreconstructible.
pickSurvivingTick() now picks deterministically (prefers the tick with a
real linked payment_id; otherwise the later done_on), the losing tick is
counted in a new `collapsed` field, and the audit `before` payload is now
the full pre-migration row set instead of `undefined` -- any discarded tick
is reconstructible from the append-only log.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
// Pre-ship audit FIX C: advance_paid AND balance_paid (and the interim advance_payment /
// balance_payment) all collapse onto the single new `payment_received` key. The old code
// used Map.set, so whichever row was visited last silently overwrote the other — its
// done_on AND payment_id lost, counted as neither `carried` nor `dropped`, and the audit
// `before` was `undefined` so the loss was unreconstructible.
it('when two old keys collapse onto the same new key, deterministically keeps the linked tick, counts the loser, and records the full pre-migration state in the audit before payload',async()=>{
constcmId=awaitmakeSmsClientModule()
constclient=awaitdb.get<{client_id: string}>(`SELECT client_id FROM client_module WHERE id=?`,cmId)
constpaymentId='pay-collapse-1'
awaitdb.run(
`INSERT INTO payment (id, client_id, received_on, mode, amount_paise, created_by, created_at)