{"spec":"AIP-BS-REMITTANCE-SPLIT","version":"2026-10-24","standard":"remittance-split-build-standard","source":"https://aipathway.com.au/explore-ai/remittance-split-build-standard","markdown":"https://aipathway.com.au/explore-ai/remittance-split-build-standard.md","licence":"CC BY 4.0","how_to_read":"Twelve of these fourteen checks run on your own machine, against the stub, before you talk to anybody. The last one is a live tenant, and that one is quoted as a scoped build. One is not a software check at all: it asks what you measured before you started.","verdicts":{"pass":"asserted green here","fail":"asserted red here. This is a fixture: it names the check to price","shape_only":"shape holds on the stub, truth needs the live provider","with_us":"cannot be proved on the stub; the live step is quoted as a scoped build (see with_us_next)","evidence_required":"not a software check. Attach what proves it"},"tally":{"total":14,"onStub":12,"productionOnly":1,"partial":2,"preconditions":1},"envelope_version":"1.0","port_shape":{"read_ledger":{"verb":"read","subject":"ledger"},"read_job":{"verb":"read","subject":"job"},"read_asset":{"verb":"read","subject":"asset"},"read_rule":{"verb":"read","subject":"rule"},"read_contacts":{"verb":"read","subject":"contacts"},"read_segment":{"verb":"read","subject":"segment"},"resolve_address":{"verb":"read","subject":"address"},"write_job":{"verb":"write","subject":"job"},"read_pricebook":{"verb":"read","subject":"pricebook"},"draft_quote":{"verb":"write","subject":"quote"},"write_invoice":{"verb":"write","subject":"invoice"},"write_ledger":{"verb":"write","subject":"ledger"},"record_promise":{"verb":"write","subject":"promise"},"record_service":{"verb":"write","subject":"service_record"},"release_payment":{"verb":"write","subject":"payment"},"talk":{"verb":"say","subject":"turn"},"disclose":{"verb":"say","subject":"disclosure"},"send_sms":{"verb":"say","subject":"sms"},"notify":{"verb":"say","subject":"alert"},"escalate":{"verb":"say","subject":"handoff"},"issue_notice":{"verb":"say","subject":"notice"},"answer":{"verb":"say","subject":"answer"},"publish_report":{"verb":"say","subject":"report"},"record_consent":{"verb":"write","subject":"consent"},"read_file":{"verb":"read","subject":"file"},"write_file":{"verb":"write","subject":"file"},"raise_task":{"verb":"write","subject":"task"},"read_node":{"verb":"read","subject":"node"},"write_node":{"verb":"write","subject":"node"},"read_register":{"verb":"read","subject":"register"},"write_register":{"verb":"write","subject":"register"},"index_passage":{"verb":"attach","subject":"passage"},"search_index":{"verb":"read","subject":"index"},"read_inbox":{"verb":"read","subject":"message"},"file_message":{"verb":"attach","subject":"message"},"write_order":{"verb":"write","subject":"order"},"read_quote":{"verb":"read","subject":"quote"},"write_log":{"verb":"write","subject":"log"},"read_sales":{"verb":"read","subject":"sales"},"read_sale":{"verb":"read","subject":"sale"},"draft_message":{"verb":"write","subject":"draft"},"send_message":{"verb":"say","subject":"message"},"evidence":{"verb":"attach","subject":"evidence"},"cite":{"verb":"attach","subject":"citation"},"rank":{"verb":"decide","subject":"order"},"gate":{"verb":"decide","subject":"gate"}},"provider":{"boundary":{"never":["write off a short payment, or spread a shortfall across other invoices","create a credit note or an overpayment, or edit an invoice","match a line on its amount or a name alone","work out an unreadable amount from the deposit less the other lines","apply part of a deposit, or a second batch for the same deposit"],"stays_with_person":["every exception line: short, over, already paid or credited, unknown, unreadable","accepting a short payment as a part payment","write-offs, credit notes and overpayments, done by hand in the ledger","asking the payer for a corrected remittance"]},"job":"one deposit split across the invoices it pays, as one batch that equals it","skill":"remittance-split","stub":"local.mock_remittances","production":null,"product":null,"insert":null,"with_us":{"kind":"scoped_build","note":"No hosted product applies payments. The batch is built against the business's own ledger and bank feed, quoted as a scoped build."}},"with_us_next":"No hosted product applies payments. The batch is built against the business's own ledger and bank feed, quoted as a scoped build. Send the failing check id for a fixed price; do not build the live step yourself.","default_path":null,"boundary":{"never":["write off a short payment, or spread a shortfall across other invoices","create a credit note or an overpayment, or edit an invoice","match a line on its amount or a name alone","work out an unreadable amount from the deposit less the other lines","apply part of a deposit, or a second batch for the same deposit"],"stays_with_person":["every exception line: short, over, already paid or credited, unknown, unreadable","accepting a short payment as a part payment","write-offs, credit notes and overpayments, done by hand in the ledger","asking the payer for a corrected remittance"]},"substrate":{"how_to_read":"These are roles, not products. Work out which you already have by matching `providers[].detect` against the repository, then build or add only the gap. No check asserts against a vendor, so any substrate that answers the role passes the same checks.","requires":[{"role":"record","label":"Somewhere the book lives","need":"Durable storage that the workflow owns the write path to, keyed so the same record can be found twice.","when_missing":"Half a day. Any Postgres will do and the free tiers are ample at this size. The trap is not the database, it is letting people edit around the app that is supposed to own the value.","ports":["read_ledger","evidence","read_rule","write_ledger","read_file","write_file"]},{"role":"schedule","label":"Something that runs it on time","need":"A trigger that fires on a cadence and can call an authenticated endpoint.","when_missing":"An hour. Expose one authenticated endpoint that starts the work and let anything call it, so the schedule is never welded to whoever is hosting you.","ports":["gate"]},{"role":"surface","label":"Something a person opens","need":"A view of the book that whoever does this job can read and act on without asking a developer.","when_missing":"Start at tier 0 and stop there until the business asks for more. The cheapest surface is not a screen, it is a scheduled email carrying the list, and for one person doing this job that is not a compromise, it is the right answer. On Microsoft 365 or Workspace you can skip straight past this: a list and a sheet are already what people open. Build upward only when two people need the same list at the same time.","tiers":[{"tier":0,"label":"A scheduled digest carrying the list","cost":"About an hour. Needs nothing you do not already have.","satisfiedBy":"send","whenYouNeedIt":"One person does this job. They want to know what is waiting when they sit down, and they act on it from their inbox."},{"tier":1,"label":"A read-only page showing the book","cost":"An afternoon. One query and one table.","whenYouNeedIt":"Somebody needs to look mid-day without waiting for tomorrow's digest, or to answer a customer on the phone."},{"tier":2,"label":"Editable rows, a queue and a log","cost":"A day or two with a coding agent, which is what agents are best at. It is bounded work: the fields are the schema, the queue is whatever escalate hands off, the log is the trace.","whenYouNeedIt":"More than one person shares the book and they need to correct it without treading on each other, and you need to see afterwards who changed what."}],"ports":["escalate"]}],"registry":"https://aipathway.com.au/substrates.json"},"checks":[{"id":"deposit-read-from-the-bank","n":1,"title":"The deposit comes from the bank, not from the remittance","statement":"The amount being explained is the deposit on the bank feed or statement line. A remittance that arrives before its money lands waits; nothing is applied against a deposit the bank does not show.","assertable":"both","ports":["read_ledger","write_ledger"],"stimulus":"A remittance that arrives on Friday for a payment that lands in the bank on Monday.","assertion":"No batch is attempted before the deposit appears. Afterwards exactly one batch exists, against that deposit's id, for the deposit's amount, and the bank was read before the ledger was changed.","stubLimit":"The stub proves the order: bank first, then the batch. Only a live bank feed proves the deposit read is the real one, with the real narrative, on the day it cleared."},{"id":"remittance-read-as-written","n":2,"title":"Each remittance line is read exactly as the payer wrote it, never filled in","statement":"Invoice number, the payer's own reference (a participant or claim number) and the amount, as written, one line at a time. A line that cannot be read goes to a person; it is never worked out from the deposit minus the other lines.","assertable":"stub","ports":["read_file","escalate","evidence"],"stimulus":"A remittance of several lines where one amount is smudged and unreadable, and the other lines plus the true amount add to the deposit.","assertion":"Every line is on the record exactly as written, the smudged line is raised as unreadable, and nothing is applied. A build that plugs the gap with the deposit less the other lines fails."},{"id":"total-equals-deposit","n":3,"title":"The remittance adds up to the deposit, or nothing is applied","statement":"The lines must total exactly the deposit they explain. If they do not, the whole deposit goes to a person with both amounts, and no line of it is applied.","assertable":"stub","ports":["read_ledger","read_file","escalate","write_ledger"],"stimulus":"A remittance whose lines are all clean but total $10 more than the deposit the bank shows.","assertion":"A total_mismatch exception names both amounts, and no batch is attempted for that deposit."},{"id":"matched-by-invoice-number-then-amount","n":4,"title":"Each line is matched by its invoice number, then the amount is confirmed","statement":"The invoice number finds the invoice (ignoring case, spaces and punctuation). The amount then has to equal what is owing on that invoice. An amount that differs is an exception on that invoice, never a reason to look for another one.","assertable":"stub","ports":["read_ledger","read_file","write_ledger"],"stimulus":"A line written \"inv 1042\" for exactly what is owing, and a line for INV-1043 whose amount happens to equal what is owing on INV-1044.","assertion":"INV-1042 is matched despite the spacing. The INV-1043 line is raised as short_paid on INV-1043, and INV-1044 appears in no attempted batch."},{"id":"never-by-amount-or-name-alone","n":5,"title":"Never matched on an amount or a name alone","statement":"A line with no invoice number, or an invoice number the ledger does not hold, goes to a person, even when its amount or the name on it fits exactly one open invoice.","assertable":"stub","ports":["read_ledger","escalate","write_ledger"],"stimulus":"A line with no invoice number whose amount equals one open invoice, and a line for an invoice that does not exist whose name matches a customer with one open invoice.","assertion":"The first is raised as unreadable and the second as no_such_invoice, and neither of the invoices they happen to fit appears in any attempted batch."},{"id":"every-exception-typed-to-a-person","n":6,"title":"Short, over, already paid, unknown and unreadable lines each go to a person with the reason","statement":"Each exception names its kind from a closed list, the deposit, the line, and the amounts involved: the line's and the invoice's for a short or an over payment.","assertable":"stub","ports":["escalate"],"stimulus":"One remittance with a short payment, an overpayment, a line for a paid invoice, a line for a credited invoice, an unknown invoice and an unreadable line.","assertion":"Each line carries its own kind (short_paid, over_paid, already_settled twice, no_such_invoice, unreadable), and the short and over exceptions both name the line's amount and the invoice's."},{"id":"nothing-corrected-by-the-machine","n":7,"title":"Nothing is written off, credited or adjusted by the machine","statement":"The build never writes off a short payment, never creates a credit note or an overpayment, never edits an invoice, and never moves part of a line to another invoice. Each payment in the batch is one remittance line, for that line's amount, on the invoice it named or the one a person named.","assertable":"stub","ports":["write_ledger"],"stimulus":"A line that overpays INV-1049 by $20, from a payer whose other invoice INV-1050 is open.","assertion":"An over_paid exception is raised and nothing is applied. No attempted batch carries an invoice no line named, or an amount that differs from its line. A build that pays INV-1049 in full and puts the $20 on INV-1050 fails."},{"id":"one-batch-equal-to-the-deposit","n":8,"title":"One batch payment, equal to the deposit, so the bank line reconciles in one click","statement":"All the lines of one deposit are applied together as a single batch payment whose total is the deposit. Never one payment per invoice, and never a batch for part of a deposit.","assertable":"stub","ports":["read_ledger","write_ledger"],"stimulus":"A clean remittance of five lines that adds to the deposit.","assertion":"Exactly one batch is attempted for the deposit, it totals the deposit, it has one payment per line, every invoice is paid by its own line's amount, and the bank line reconciles against it."},{"id":"held-whole-until-every-line-resolves","n":9,"title":"One exception holds the whole deposit until a person has decided it","statement":"If any line is an exception, nothing for that deposit is applied: the matched lines wait with it. Once a person has recorded a decision on every exception line, the batch is applied once and still equals the deposit.","assertable":"stub","ports":["gate","escalate","write_ledger"],"stimulus":"Nine clean lines and one that is $10 short. The build runs, a person decides to accept the short line as a part payment, and the build runs twice more.","assertion":"No batch is attempted before the decision. After it, exactly one batch equal to the deposit, the short line applied at its own amount, and the $10 still owing on that invoice."},{"id":"once-only-per-deposit","n":10,"title":"The same remittance twice, or a second run, makes one payment","statement":"A remittance often arrives by email and again from the payer's portal. Both copies, and every later run, lead to one batch per deposit, keyed on the deposit.","assertable":"stub","ports":["read_file","read_ledger","write_ledger"],"stimulus":"The same remittance arrives as an email attachment and as a portal download, and the build runs three times.","assertion":"Exactly one batch is attempted for the deposit across all runs. A second attempt counts as the failure even if the ledger refused it."},{"id":"remittance-kept-with-the-payment","n":11,"title":"The remittance is kept with the payment it explains","statement":"The remittance file is attached to the batch payment, or filed under the batch's id where the ledger cannot take an attachment, so anyone looking at the payment can open the page that justified it.","assertable":"both","ports":["write_file"],"stimulus":"A clean remittance applied as a batch.","assertion":"The batch carries the remittance file it was built from.","stubLimit":"The stub proves the file is attached to the right batch. Whether a given ledger accepts an attachment on a batch payment, or the file has to live in a document store under the batch id, is only proved live."},{"id":"the-record-by-deposit","n":12,"title":"For any deposit, the record shows which lines, which invoices and who decided what","statement":"Ask about one deposit and the record answers: every line as written, the invoice each was matched to, every exception, every decision with the person who made it, and the batch that was applied.","assertable":"stub","ports":["evidence"],"stimulus":"A deposit with one short line, decided by a named person, then applied.","assertion":"The record for that deposit holds every line as written, the exception, the decision naming the person, each match and the batch id."},{"id":"survives-the-builder","n":13,"title":"It survives the builder","statement":"Someone other than the builder can explain what it does, and it runs on an account the business owns.","assertable":"precondition","ports":["read_rule"],"stimulus":"No stimulus. The builder names who else can explain it and whose account the matching, the batch and the record runs on.","assertion":"The runner reports what was declared and never scores it green on its own say-so.","stubLimit":"Not a runtime property. Software cannot prove a second person understands it, and outliving its author is precisely what is being asked about."},{"id":"pass-test","n":14,"title":"The pass test passes","statement":"On a real deposit and its real remittance, a line $10 short stops everything and names itself; restored, the deposit is applied as one batch equal to it, and a third run changes nothing.","assertable":"production","ports":["read_ledger","read_file","escalate","write_ledger"],"stimulus":"A real deposit with its remittance on your own ledger (or its demo company). One line on a copy of the remittance changed to be $10 short, then restored, and the build run three times.","assertion":"The altered run applies nothing and the exception names the line and both amounts. The restored run applies exactly one batch equal to the deposit, every invoice it names paid by its own line's amount. The third run applies nothing.","stubLimit":"Deliberately unpassable on a stub. A stub ledger agrees with whatever it was seeded with; a real ledger has its own rules about batch payments, part payments and locked periods, a real bank feed has its own narrative, and a real remittance is a PDF somebody else laid out."}]}