Skip to main content

The Close checksums

A Close checksum is a reconciliation. It runs after the fact and it reports. AP control against the AP sub-ledger, cross-foot, per-rail reconciliation against external bank evidence. It is a property of the whole book across seven sub-ledgers.
Most of them no longer wait for the close. 78 of the 104 hold at every instant, and the Auditor runs those every night. The name is historical: it is the v12 workbook’s Close list, not a statement that these can only be asked at month-end.
These are not the 18 GL invariants, and this documentation never calls them invariants.A GL invariant is a precondition: it runs before the write and refuses. A Close checksum is a reconciliation: it runs at close and reports. Calling the second set “invariants” would imply the system refuses a posting that would break a Close tie-out, which it does not and should not. A broken tie-out is a finding for a human, not a rejected transaction.The canonical phrasing when both appear together is “18 GL invariants + 104 Close checksums”.

Two kinds of row

Exactly as in the workbook: The distinction is not cosmetic. A deposit held on behalf of a customer is supposed to be a non-zero liability; reporting it as a break would train people to ignore breaks. summary.ok is true when every must-be-zero checksum passes, and the explainable balances are printed alongside.

The size of the surface

close_checks.py declares 104 checksum templates: 82 must-be-zero and 22 explainable balances. One hundred and one cover the seven authoritative Books families; three Wave E KETCHUYEN-* controls cover the cross-book close procedure. That number is no longer an estimate. Every one of the 104 carries a machine-readable phase and layer tag, generated from the code and diffable:

The two tags

Phase answers when is the expected value reachable? A check is continuous when its expected value holds at every instant, which in practice is one of four shapes: a tie between two representations of one fact written in the same transaction, a referential-integrity or catalog check, an arithmetic identity inside a row, or a bound. A check is close-gated when its expected value is only reached after a period-end procedure: a drain-to-zero pool whose non-zero state is a normal in-flight condition, a tie against evidence that arrives with a lag, or a completeness assertion about a period. The 78 continuous ones are what the Auditor’s nightly continuous sweep runs, so most of this surface no longer waits for month-end to be asked. Layer answers whose work is a failure? The test is the one on the two-layers page: if the fix is “find the missing or mismatched document, or correct what it says” it is the bookkeeping layer; if the fix is “decide what this belongs to, or what the period needs” it is the accounting layer. That tag is what routes an exception to one of the two lanes in Audit → Actions.
The layer rule moves eight checks and changes neither total, which is the useful evidence that it is the right axis.Four leave the bookkeeping layer (PO-ADVANCE-POOL, SO-ADVANCE-POOL, PAYMENT-CROSSFOOT, RECEIPT-CROSSFOOT, all allocation) and four arrive (NEG-STOCK-QTY, CONTRACT-ASOF, FLOAT-FACILITY-TIE, ADJ-STOCK-LEG, all matching or movement).A naive by-sub-ledger reading would have put every one of those eight on the wrong worklist.
Both tags are judgements, so each row also carries the sentence that justifies it, which is what makes a tag arguable in a diff rather than merely asserted.

What actually executes

The number of checks that runs on a given book is data-dependent, and expands or contracts around those 104 definitions: Nine of the 104 expand at run time and 16 are conditional.
Two related numbers, and the difference matters. The v12 workbook carries roughly 91 Close-list rows. close_checks.py declares 104 checksum templates. A live run executes however many those 104 expand to against that book’s data.These are related but not identical quantities: some workbook rows became structural rather than checks (see below), and GreatBook added checksums the workbook never had, such as the output-VAT per-line twin and the split of NEG-STOCK. The generated inventory is what makes the middle number countable, and it is the one to cite when you mean “the checks that exist”.

The Books families and Wave E close group

4 continuous must-be-zero, 6 close-gated, 5 report-only. 11 on the bookkeeping layer, 4 on the accounting layer.AP-CONTROL compares GL 2000 outstanding against the sum of bill dues. It catches a posting to the control account that bypassed the AP sub-ledger, which is exactly the defect the single obligation writer closes.The four allocation checks sit on the accounting layer under the layered rule: the advance pool is allocations minus applications, and cross-foot is the sum of a settlement’s allocations against the settlement itself. Both are attribution failures, not matching failures.The two bank-rail ties are close-gated only because the bank statement arrives with a lag. If an organisation loads statements daily they are continuous, and they then become the single most valuable nightly check in the whole list.
17 continuous must-be-zero, 7 close-gated, 2 report-only. 7 on the bookkeeping layer, 19 on the accounting layer.NEG-STOCK used to be one check counting items where quantity or value was negative. It is now split, because negative quantity is a movement error the bookkeeping layer fixes and negative value is a valuation error the accounting layer fixes. That split is the only change the layer tagging made to the Close oracle, and it is worth it: the alternative routes every valuation error to the wrong worklist.GRNI stays on the bookkeeping layer even though it lives in the costing runner. The missing thing is a supplier bill, so the fix is “find the document”.
10 continuous must-be-zero, 9 close-gated, 3 report-only. 3 on the bookkeeping layer, 19 on the accounting layer.This family needs a period. Without one it is skipped rather than run against an undefined scope, which is why the continuous sweep resolves the open periods itself and runs this subset once per open period.Five of these were already gated in code behind “only meaningful once the period’s bills are raised”, long before the phase tag had a name. CONTRACT-ASOF is on the bookkeeping layer: matching every payroll line to a covering contract is matching.
7 continuous must-be-zero, 0 close-gated, 3 report-only. 1 on the bookkeeping layer, 9 on the accounting layer.Financing is judgment, so this family sits almost entirely on the accounting layer. The float-to-facility tie is the exception, because it is a reconciliation.
6 continuous must-be-zero, 0 close-gated, 5 report-only. 5 on the bookkeeping layer, 6 on the accounting layer.VAT-INTEGRITY-OUT is a GreatBook addition. The workbook’s per-line integrity check only covered input VAT.VAT-133-TIE and VAT-3331-TIE compare exact register base evidence with GL base. New register rows bind to one immutable journal line; historical rows without exact, unambiguous same-organization evidence make the result partial or inconclusive rather than producing a false pass.
10 continuous must-be-zero, 0 close-gated, 2 report-only. 12 on the bookkeeping layer, 0 on the accounting layer.Every check here is on the bookkeeping layer. An adjustment note is matched to the original document it adjusts, and the judgment behind raising one is recorded elsewhere.
2 continuous must-be-zero, 1 close-gated, 2 report-only. 0 on the bookkeeping layer, 5 on the accounting layer.GROUP-TB is intentionally absent. Adding balanced journal lines across a group would restate the GL’s structural debit/credit rule without proving membership, translation, completeness, or eliminations, so the former tautology is not counted as a green control.
Three close-gated must-be-zero controls, all on the accounting layer.These controls verify the period-end carry-forward and result transfer after the procedure has run. Asking them mid-period would turn ordinary in-flight balances into false findings, so they belong only to the explicit close-gated family.

The most credible decision in the finance layer

Where a workbook check is true by construction in a relational model, it was not reproduced as a fake zero-check. The workbook has rows that compare a stored derived column to its own derivation. In a spreadsheet that catches a pasted value. In GreatBook there is no stored derived column, so the same check compares a value to itself and passes unconditionally. Reproducing it would have produced a green row that tests nothing. Instead the value became structural - the derived value cannot drift - and the version with real teeth was implemented in its place: The same reasoning produced the WIP-154 checksum with teeth: the workbook’s WIP nets to zero because a live unit-cost formula moves both legs together, and GreatBook stamps the cost immutably on the movement so it cannot.

Running it

live_acceptance reads on a read_only=True session and rolls back the single insert the FX assertion needs, then asserts the table count is unchanged afterwards. The floor’s own pytest suite proves the engine on a throwaway database and says nothing about live.

Where it is enforced

The test that would fail if it broke

The capstone is test_close_list, which runs a single close touching six families with live worked data and asserts the whole surface is green. Each family also has its own suite: test_settlement.py, test_costing.py, test_payroll.py, test_loans.py, test_vat.py, test_adjustments.py, test_consolidation.py.

What is deliberately not built yet

Recorded honestly rather than omitted:
  • the sample-cost pool and the asset-register independent re-derivation, which need an asset and sample sub-ledger of their own;
  • per-tenant authenticated RLS policies, waiting on the JWT organisation claim.
None of these block the core specification, and each is tracked.