Arc undoes things by posting the opposite journal, never by deleting. The sender ends with exactly the balance they started with, not approximately, and not after a manual adjustment.
This is the same €1,000 transfer from the consumer flow, failed at payout.
What has already happened
Four journals are posted, three of them tracked for compensation.The network-fee journal is deliberately not tracked and therefore never reversed. Gas was really spent. Reversing it would produce a balanced ledger that misstates the expense, and where “balanced” and “true” pull apart, true wins.It balances on its own, so the trial balance stays zero regardless.
The rail says no
retryable: false is the field that decides everything next.
A timeout would be retryable.The payout may or may not have landed, and that ambiguity is the entire reason idempotency keys exist. Resubmit with the same key; the rail returns the original receipt if it already processed it.
A rejection is not.Retrying
account_closed just fails again, more slowly. The saga stops trying and starts unwinding.Compensation, backwards
The saga walks completed steps in reverse.payout never completed, so there is no payout journal to reverse, but the rail recall is still attempted, because the saga cannot assume the submission left no trace.
recall returns false once past settlesAt. You cannot recall settled funds, and pretending otherwise would let the saga believe it unwound something it did not. Here the payout was rejected outright, so there is nothing at the rail to recall.1 · Reverse settle
Every direction flipped, same accounts, same amounts.
This is a ledger-level compensation, not an on-chain reversal. It represents funds recovered from the settlement partner. Nothing un-sends a confirmed on-chain transaction: that limitation is real and is stated rather than papered over.
2 · Reverse swap
Both FX position accounts return to zero. The open exposure this transfer created is closed.
3 · Reverse reserve
The fees are reversed too. Amina is refunded €1,000.00: the full amount, not the amount net of fees. Arc absorbs the cost of a failed transfer, which is both the correct commercial answer and the only one that keeps the arithmetic clean.
Why this is safe by construction
If the original balanced, the reversal balances.Flipping every direction preserves the equality. A compensation can never itself unbalance the ledger, not because someone wrote it carefully, but because the arithmetic makes it impossible.
The pair nets to zero.Every touched account returns to precisely where it was.
reverseEntries is also an involution: reversing twice returns the original.Why the order matters
For the ledger alone, order is irrelevant: reversals commute, because addition commutes. Run them forwards and the balances come out identical. Order is not irrelevant overall:1
The rail recall must precede the settlement unwind
Unwinding the settlement while a payout might still be in flight at the rail risks recovering funds you are simultaneously paying out.
2
Each reversal journal must describe the step it actually undoes
Compensating forward would produce a journal labelled “refund sender” that reverses the payout entries. Balanced ledger, false audit trail.
Switching to forward order kept all sixteen tests green, because balance cannot detect it. Two tests asserting reversal order and account pairing were added; the mutant now fails.Balance is necessary but not sufficient: an audit trail can be false while the arithmetic is true. The full breakdown →
The final state
Everything is back to zero except the gas, which was genuinely spent and stays on the books as an expense Arc absorbed.
SagaResult.status is compensated. Amina gets a notification explaining the beneficiary number is invalid, and the entry log contains the full sequence: what was attempted, what succeeded, and exactly how each part was undone.
When compensation itself fails
The fourth terminal state exists for a reason.If a reversal itself throws, the saga returns
compensation_failed and stops rather than retrying blindly into a partially-unwound state. That status is distinct from compensated precisely so it can be alerted on.A real system escalates this to an operational case with the completed and failed compensation steps attached. That case management is Phase 8, and until it exists this is a known gap rather than a solved problem.The settlement saga
The mechanism this page exercises, step by step.
What the tests prove
The chaos suite runs this unwind at every one of the five steps.