A customer wrote in to ask why they still had access. Their card had failed, the provider had moved the subscription to unpaid, and the access system was never told. Looking for others we found the opposite case in the same batch: a credit note issued at the provider that never reached the books. Both had sat inside a subscription reconciliation we ran at every cycle close, and both had passed it.
The run had not missed them. The run had never looked.
What we thought the close was checking
We treated the close as a comparison. Pull the three registers — the payment provider, whatever grants the paid capability, and the ledger — put them beside each other, mark the ones that agreed. The artifact came out saying matched and consistent, with no integer behind any comparison it claimed.
That is the failure in one line: nobody had subtracted anything. A reconciliation in which no difference was ever calculated is not a reconciliation, however clean the output. The assumption underneath was worse — that the provider held the truth and the other two were copies. Three registers record the consequences of a billing period independently, and each is right about something the others cannot see.
Repair order is not a matter of preference
Once we started sizing differences instead of eyeballing them, the tempting first move was money. It is what the customer notices, and the only irreversible action in the set. Repair it before the other registers are straight and you have not removed a difference — you have moved it. A refund creates a provider event with no ledger counterpart behind it; a re-charge does the same in reverse. One drift becomes two, and you made the second.
The order that survives contact is access, then ledger, then money. Access first because it is reversible, and because it is the only class still causing new loss while it stands open. Ledger second because a correction there is additive, auditable, and tells you whether the money move is needed at all. Money last, and prepared rather than executed.
Why a subscription reconciliation has to difference twice
The part that took us longest to see is that a repair is itself an event. A ledger correction posts a line, a refund creates a provider record, an entitlement change moves a grant window. Every fix writes new state into a register you were comparing.
A run that differences once and then repairs closes on a position that stopped existing the moment it acted: right when calculated, stale when published. Worse, because money goes last, the final repair of a pass produces exactly the drift the difference step exists to find, after that step has stopped running. Anything written to has to be differenced again, and non-zero results re-enter the full order, not a shortened one. And a loop that re-differences after every repair runs forever without a budget and an exit that is not success.
Zero, and nearly zero
None of this survives floating-point arithmetic. Amounts have to be integer counts of the currency’s minor unit — one euro is one hundred of them — because a float turns a difference of zero into a difference of almost zero: small enough to read as rounding noise, large enough to keep the row on the report every cycle. Rounding it away hides which class the drift belongs to.
The same discipline decides proration. A mid-period plan change leaves an amount nobody has re-derived, and re-deriving it in integers leaves a remainder: a 4 999 minor-unit monthly plan changed with 11 of 31 days remaining gives 1 773, remainder 26. That 26 goes to the customer, a rounding account, or the next cycle — by a stated rule, to the same place every time.
Two registers that cannot be made to agree
There is a state that is neither failure nor retry. Some pairs cannot converge: the provider account is read-only, an amount one register cannot represent, a field each side derives from a different immutable source. No number of passes closes that gap, and a subscription reconciliation that keeps writing at it is not converging, it is just running.
Naming it is the answer: the subscription is frozen for the run and carried on the residual with its remaining difference and an owner, open until something outside the procedure changes. A residual of zero reached by adjusting a figure until both sides agreed is worse than a named non-zero one.
What follows for anyone building this
A register is not reconciled by being read. It is reconciled by being subtracted — and then, once you have touched it, subtracted again. Evidence of a repair is the value read back out of the register afterwards — not the status code your write returned, not your own log line; both record intent, not result. And this cycle’s residual only means something beside the previous one: stable is one bug, growing every cycle is an unassigned remainder, vanished with no repair recorded means somebody plugged it.
The runnable procedure — the repair classes, the pass budget, and the checkpoint a third party re-derives the verdict from — is in the member library.
How we know
Grounded in: our own cycle-close runs across provider, access and ledger, and the revision made on 2026-09-04 after a run repaired and closed in a single pass. Verified: proration re-derived in integer minor units — 4 999 with 11 of 31 days remaining gives 1 773 remainder 26 — and every repair confirmed by re-querying the register written to and comparing the changed field, never the write response. The runnable procedure lives in the member library.
Leave a Reply