Where you are. The RTGS lessons taught you what immediacy costs, and lesson 7 bought cheapness by giving it up: collect instructions until a cutoff, cancel offsetting flows into multilateral net positions, and settle only the differences over RTGS. But lesson 7’s file was a well-behaved one - every payment in it landed. This lesson runs the day as it actually goes: instructions written at all hours, two cutoffs sorting them into cycles, stragglers rolling to tomorrow, and one entry that cannot land and so comes back. The machinery you add is one small function, the return path; the idea you leave with is the most practically useful sentence in the module: on a batch rail, “arrived” and “final” are different claims, and real money is lost in the gap between them.
The rent that came back
Your landlord collects rent the boring way: a pull from the tenant’s account that the tenant authorised once, carried by the batch rail on the first of the month. Tuesday morning the landlord’s banking app shows the rent in and the balance up. Wednesday, trusting the balance, the landlord pays a builder out of it. Thursday morning the app shows a new line: the rent leaving again, marked returned unpaid - the tenant’s account could not cover the pull. Nothing was hacked and nothing malfunctioned; the rail worked exactly as designed. The credit the landlord “received” on Tuesday was an entry in a file, and an entry in a file can come back. By the end of this lesson you will have built the machinery that took the rent back, and you will be able to say exactly what the Tuesday credit did and did not mean.
The idea in one paragraph
A batch rail runs on a clock, and the clock gives every instruction one of three verdicts. Cutoffs decide which settlement cycle - and so which day - a payment belongs to: written before a cutoff, it rides that cycle’s file; written after the last cutoff, it rolls to tomorrow, unexamined and unfailed. At the cutoff the file is netted and the net positions settle in reserves with finality, exactly as lesson 7 built. But the nets were computed over the file as written, so the cash has already moved by the time each receiving bank works through its entries - and an entry that cannot land (account closed, account frozen, nothing there to pull) cannot be rejected out of a batch that has already settled. It comes back instead as a return: a new, reversing payment carried by a later cycle across the same three ledgers. The settlement was final the moment the nets landed; the entry at the edge stays provisional until the return window has passed. That gap is the whole deceit inside the word “arrived”.
Cutoffs sort the day
Run the day the exercise runs. Six banks - Alder, Birch, Cedar, Damson, Elm and Fir - each with one corporate customer; 301 instructions written across a 1,440-minute day, each stamped with the minute it was written; cutoffs at minute 720 and minute 1200. The numbers are the module’s seeded fixture, stylised and deterministic: your run prints these numbers.
A cutoff is nothing but a filter on written-time. The first cycle’s file is every instruction stamped minute 720 or earlier: 163 of them. The second cycle’s file is everything from minute 721 to 1200: 90 more. The 48 instructions written after minute 1200 are not examined, not refused and not failed - no machinery for them exists today at all. They belong to tomorrow’s first file. From the customer’s side this is the familiar “payments after the evening cutoff arrive tomorrow”; from the rail’s side, tomorrow is simply the day whose filter will catch them.
When an entry cannot land
The fixture deals 300 clean payments and one doomed one: at minute 300, alder-co instructs 140 to fir-shop, a customer of Fir. Between the writing of the instruction and the first cutoff, Fir closes the shop’s account. Nobody notices, because nobody is positioned to notice: your miniledger’s submit can refuse a payer who cannot cover, since the payer’s bank holds the payer’s balance, but no one on the sending side holds any knowledge of the payee. On a deferred rail, the payee is checked when the file is processed, not when the instruction is written. The first party able to say “this account does not exist” is the receiving bank, and it first meets the payment inside a file that has already settled.
So at minute 720 the cutoff runs as normal. The 140 goes into the nets like every other entry; Alder’s net includes it, Fir’s net includes it, the nets settle in reserves, and alder-co is debited. Only then does Fir walk its entries and hit a credit addressed to nobody. It cannot apply the credit, and it cannot refuse money it has already received at net - so it posts the 140 to a holding pen: an account named unapplied, a liability like any deposit, money the bank owes but not yet to anyone with a name. Arrived, in the most literal sense. Final to no one. The payment joins the return list for the next cycle.
The return itself is this lesson’s exercise, and it is a payment in reverse: three balanced postings on the same three ledgers that settled the original. Fir empties the pen against its own reserves; Alder re-credits alder-co against its reserves; the central bank swings the two reserve lines back. Every module 1 rule holds in the reverse direction too - balanced legs, squared sheets, tiers agreeing - and assert_world runs green afterwards. Notice what did not happen: nothing was deleted or edited. The ledger only ever adds entries, so alder-co’s history keeps both the debit and the compensating credit for ever, summing to zero and reading true: the payment happened, and then its return happened.
Wider than the screen; scroll it sideways.
Two kinds of final
Hold two sentences apart here, because module 1 taught you to respect the word final and this lesson seems to bend it. It does not. The cutoff’s settlement was final: the nets moved reserves at the central bank irrevocably, and no return will ever claw those reserves back out of that settlement. The return does not unsettle anything - it is a second payment, which rides a later cycle and settles with the same finality. Finality attaches to settlements, and every settlement on the rail has it. What lacks it is your entry: until the return window closes, any fresh batch credit can be answered by an equal and opposite one. Both sentences are true at once: every settlement is final, and no just-arrived credit is.
Review
Cutoffs sort the day, and the last one sorts it into tomorrow
A batch rail runs on a clock, and the clock gives every instruction one of three verdicts. A cutoff is nothing but a filter on the minute an instruction was written. Written before a cutoff, it rides that cycle’s file. Written after the last cutoff of the day, it rolls to tomorrow: not examined, not refused and not failed, because no machinery for it exists today at all. From the customer’s side that is the familiar payments after the evening cutoff arrive tomorrow. From the rail’s side, tomorrow is simply the day whose filter will catch them.
Why arrived is not final
At the cutoff the file is netted and the net positions settle in reserves with finality. But the nets were computed over the file as written, so the cash has already moved by the time each receiving bank works through its entries. An entry that cannot land, because the account is closed or frozen or has nothing to pull, cannot be rejected out of a batch that has already settled. It comes back instead as a return: a new, reversing payment carried by a later cycle across the same three ledgers. So the settlement was final the moment the nets landed, and the entry at the edge stays provisional until the return window has passed. That gap is the whole deceit inside the word arrived.
Check yourself
1. Your miniledger’s submit refuses a payer who cannot cover. The doomed payment’s payee account was closed well before the first cutoff - why did nothing refuse it?
Refusal happens where the knowledge lives. The payer’s bank holds the payer’s balance, so cover can be checked the moment the instruction is written. Only the receiving bank knows whether the payee’s account exists, and on a deferred rail the receiving bank first meets the payment inside the settled file - after netting, after the reserves moved. The earliest possible “no” comes too late to be a rejection, so it takes the only form left: a return, a reversing payment in a later cycle.
2. After cycle 1, Fir’s unapplied account holds 140. Whose money is it at that moment?
It is a liability of Fir with no named creditor - money Fir received at net and owes, addressed to an account that does not exist. It is not Fir’s to keep, and not yet anyone’s to spend. The holding pen keeps Fir’s sheet balanced while the return is arranged, and the exercise asserts it drains to zero: unapplied money is owed back, not kept.
3. The return credits alder-co with a new posting rather than deleting the original debit. Why is deletion never an option?
Two reasons, stacked. Mechanically, the batch already settled: the 140 travelled inside multilateral nets that moved real reserves, and every other bank’s position was computed on top of it - no edit can unwind one entry out of a settled net. And by construction the ledger only ever adds entries: the debit and its compensating credit both stay in the history, sum to zero, and read true - the payment happened, then its return happened. A ledger that could delete would be a ledger whose past cannot be audited.
4. 48 instructions were written after minute 1200 and none of them settled today. Did they fail?
No - they rolled. Cutoffs decide which day a payment belongs to, and an instruction written after the last cutoff belongs to tomorrow’s first file. Nothing examined it, nothing refused it, nothing bounced. Rolled is a verdict about scheduling, not about the payment; the only cost is delay, which is exactly the currency the batch rail spends to buy its cheapness.
5. Cycle 1’s nets settled with finality in reserves, yet the landlord’s Tuesday credit was reversible. How are both true at once?
Finality attaches to the settlement, not to the entry. The reserves that moved at the cutoff will never move back on account of this payment - the return is a new payment, netted and settled in a later cycle with its own finality. What stays provisional is the entry at the edge: until the return window closes, a fresh batch credit can be answered by an equal and opposite one. Final settlements, provisional entries - the batch rail’s bargain in four words.
Do this
Twenty minutes, from module-02-domestic-rails. Open code/batch_day.py. The day is scaffolded: fixture() deals the module’s 300-payment set plus the doomed 140 (seeded, so your numbers are the lesson’s numbers), build_world() opens the six banks - each with a customer, an unapplied holding pen, and deposits topped up by a large loan so cover never binds - and net_and_settle() is lesson 7’s cycle with one addition: a credit whose payee is missing books to unapplied and joins the bounce list. The harness closes fir-shop before any file runs, splits the day at the two cutoffs, rolls the after-hours stragglers, and asserts the whole story - exactly one bounce, the pen holding exactly 140, the pen draining to zero, alder-co made whole by a new entry, and total reserves conserved at 18,000.
Yours is return_unpaid(): the return path, a payment in reverse. Three balanced postings on the same three ledgers that settled the original - Fir empties the pen against its reserves, Alder re-credits its customer against its reserves, the central bank swings the two reserve lines back - then world.assert_world(). The TODO(you) comment spells out each leg and its sign.
python3 code/batch_day.py
As shipped, it prints the cycle 1 line and stops at your NotImplementedError. Done right, it prints both cycle lines and the day’s ledger - settled 252, rolled to tomorrow 48, returned 1 - and ends with the line
a batch entry can bounce after the batch settles: on deferred rails, arrived is not the same as final
If assert_world raises tier mismatch inside your function, you moved a bank’s own reserves without swinging the central bank’s matching lines - the mirror rule from module 1: every reserve movement is told twice, once on the bank’s books and once at tier 1. If post refuses your legs, check the signs against the docstring: the pen and Fir’s reserves both fall, alder-co and Alder’s reserves both rise. The completed version is solutions/batch_day.py; compare once you are green.
What you can now do. You can run a full batch day and hand every instruction its verdict: settled at a cutoff inside a multilateral net, rolled past the last cutoff into tomorrow, or returned - carried back by a later cycle as a new compensating payment across the same three ledgers, with every invariant green in both directions. And you can say precisely why “arrived” lies on a batch rail: finality belongs to settlements, never to a fresh entry, so the batch that delivered a credit was final while the credit itself was not yet - and anyone who treats the two as one sentence is carrying the return risk without knowing it. Lesson 9 builds the rail designed to close the gap: liquidity parked in advance so a payment settles with finality in seconds, around the clock, and the message that says “arrived” is only ever sent after the money can no longer come back.