Two Reconciliations: Ledger-vs-Scheme and Ledger-vs-External

"Are we reconciled?" is really two questions wearing one coat. One is about individual payments: did this specific transfer end up where the scheme says it did? The other is about aggregate money: does our total position on a rail match what the outside world holds? KibiPay reconciles along both axes, because a system can pass one and fail the other, and conflating them hides real problems.
Ledger-vs-scheme: payment by payment
The first axis is payment-versus-scheme reconciliation. For each in-flight payment that's drifted past its grace window, the engine queries the relevant rail adapter's status-enquiry endpoint and compares the scheme's answer to our record. The outcome is one of the payment-level break kinds: a STATUS_MISMATCH if the scheme has a terminal answer we're missing, a STUCK if neither side has finished, or an UNRECONCILABLE if the rail offers no way to ask. This axis answers "is each payment's status true?" — and when the scheme's answer is terminal and authoritative, the engine can apply it automatically.
Ledger-vs-external: position by position
The second axis doesn't look at payments at all; it looks at positions. Treasury maintains, per rail and currency, what our ledger believes we hold and what an external source says we actually hold — think of a nostro balance, a rail's own position report, a settlement account. The engine asks treasury for its reconciliation view and, for every rail-and-currency line, checks whether the two agree.
The drift calculation
The math is simple and worth stating exactly. Drift is ledger_position minus external_position. A line is within tolerance when the absolute drift is no more than a fraction of the ledger position — the tolerance is a configurable relative bound (default one percent), with an absolute fallback when the ledger position is zero. Anything outside that band becomes a POSITION_DRIFT break, keyed by rail and currency, recording our position, the external position, and the drift so an operator can see not just that the books disagree but by how much and in which direction.
Why relative tolerance, not exact match
Insisting on penny-perfect equality between a ledger and an external position report sounds rigorous but is operationally miserable — timing differences, in-flight items, and rounding produce tiny, meaningless mismatches constantly. A relative tolerance says "a one-euro drift on a ten-million-euro position is noise; a one-euro drift on a five-euro position is a problem." It focuses attention on material divergence and suppresses the chatter, which is what makes the position-drift signal something an operator will actually trust and act on.
Why keep the axes separate
The two reconciliations catch different failures. Ledger-vs-scheme catches a specific payment whose callback got lost — a discrete, nameable event with a specific remedy. Ledger-vs-external catches the errors that no single payment reveals: a slow leak of small discrepancies, a fee posted to the wrong account, a batch that half-applied. You could have every individual payment perfectly reconciled against its scheme and still have your rail position quietly drifting because of something that never showed up as a payment-level mismatch at all. Only by checking the aggregate independently do you catch it.
Both axes feed the same breaks queue, so operators get one place to work — but the platform never pretends they're the same question. Payment truth and positional truth are reconciled on their own terms, and that's what makes the overall picture trustworthy.
The zero-position fallback
A relative tolerance has one awkward edge: when the ledger position is exactly zero, a percentage of zero is zero, and no drift would ever be "within tolerance." The engine handles this by falling back to an absolute bound when the ledger position is zero, so a brand-new or fully-drained rail-and-currency line isn't flagged for a trivial rounding difference. The treasury side computes each line the same way every time — drift is ledger minus external, and the line is within tolerance when the absolute drift stays inside the configured band — so the two services agree on what "reconciled" means. And because each position break is keyed by rail and currency, the same line is tracked as a single, persistent break across sweeps: you can watch a drift on, say, the CHAPS-GBP line grow or shrink over hours rather than seeing it re-raised as a fresh row each time. That continuity is what turns a position discrepancy from a momentary blip into something an operator can actually investigate and trend.