Skip to main content

The test-case reference

GreatBook’s claim is that judgment-heavy accounting work can be turned into deterministic, inspectable behaviour. This page is the evidence for that claim, enumerated rather than asserted. Three surfaces hold the whole system to account, and they are not the same kind of thing.

68 eval cases

Judgment, measured against a reference answer.

104 Close checksums

Reconciliations that run at close and report.

18 GL invariants

Preconditions that run before the write and refuse.
Where a language model exercises judgment - which of 146 document types, which accounts, whose lane - the behaviour is graded against a written expectation, because it cannot be proven. Where the ledger engine acts, the rule is enforced in code and by database constraints, and there is no probability distribution over whether it holds.
The 18 GL invariants and the 104 Close checksums are two different sets, and this documentation never calls the second set invariants. An invariant is a precondition that refuses a write. A Close checksum is a reconciliation that reports a break to a human. The canonical phrasing when both appear together is “18 GL invariants + 104 Close checksums”.

What is being tested, and why the split matters

The engine is not evaluated, it is proven. An unbalanced entry does not post most of the time; it is refused by an invariant, every time, before anything is written. What cannot be made correct by construction is judgment. No database constraint makes a misclassification impossible, so the classification, the extraction, the account proposal and the scope decision are each driven against a real compiled graph with the real model and graded on a written reference answer. Every case on this page runs the real thing. No eval stubs the component it is testing, and a missing prerequisite reports SKIP rather than a pass - the rule and the guards behind it are set out in Never stub what you test.

The board

Pass rates are from the runs dated 2026-07-26 to 2026-07-27.
Seventeen of the current 68 cases were not part of the last 51-case tracked sync. The Bookkeeper has one additional case and the Auditor has grown from the 10 cases in that run to 26.The scoreboard is the single source of truth for every rate on this page, with the run that produced it.

How to read a case

Each case is one row of a JSON dataset, and it has the same four parts everywhere. A case passes when every score it received is 1.0. An evaluator that returns nothing scores nothing, so a mixed dataset never manufactures a 1.0 for a case that was not graded on that dimension.
A case that fails for a known, accepted reason declares it in metadata.expected_baseline, naming the score keys it excuses and the exact run shape that excuses them. Those cases report XFAIL rather than turning the board red, and an XPASS - the defect appearing fixed - fails the run so the marker gets removed. A bare “this case may fail” flag would swallow a new regression inside a case that was already red, so naming the keys is the load-bearing part.
All scenarios below are synthetic. The fixtures are written documents, not customer records.

Clerk · 15 cases

The Clerk classifies an uploaded document against the 146-type registry, extracts its fields against that type’s real schema, and flags rather than guesses. Real compiled graph, real keyless Agent SDK extractor; only the queue store and file sink are in-memory doubles, so no eval run writes into the live intake queue.

Classification · 5 cases

Extraction · 4 cases

Each case is written against the doc type’s real extraction schema, not against what seems reasonable, because the graph’s allowlist drops anything outside it.
The bank-statement case once demanded the ISO form of period alone, which graded a free formatting choice and failed roughly one run in three. Accepting any faithful rendering is the fix, and it is why the expectation is a list.

Uncertainty and governance · 2 cases

Scope gate · 4 cases

scoped_answer_judge is the only model-judged score in the entire suite, and it fails closed: an unavailable judge scores 0, never a pass. Nothing accounting-shaped is ever judged by a model - account codes, balance, entry counts, chain findings, flag fields and scope verdicts are exact comparisons.

Bookkeeper · 11 cases

One approved intake record becomes the right balanced Dr/Cr in the canonical GL, behind a human posting gate, exactly once. Real graph, real posting rules and the real LedgerService writer against a real alembic-migrated Postgres, so the advisory lock, the 18 GL invariants, the hash chain and the GL idempotency key are all live.

Proposal correctness · 5 cases

Posting, idempotency and refusal · 6 cases

raises_expected_error is graded as an actual raise, not as “nothing was posted”. A silent no-op would satisfy the weaker check while leaving the caller with no way to tell refusal from success.

Auditor · 26 cases

Continuous read-only verification of the book of record. Each case seeds a real migrated GL through the real single writer, injects at most one fault out of band, then asks the real auditor graph a question. Every case additionally asserts read_only_guarantee: the org’s entry, line and intake counts and the chain tail are unchanged across the run.

Reconciliation graph · 4 cases

Close phases · 4 cases

Document completeness · 4 cases

Durable register · 3 cases


Accountant · 5 cases

The close checklist runs the real workbook Close-list oracle over a real migrated GL, and an adjusting entry is routed through the real Bookkeeper graph to its human posting gate. acct_no_direct_ledger_write is checked by counting rows in journal_entries, never by reading what the answer claims.

Typewriter · 11 cases

A described document or a pasted list becomes schema-conformant proposals: the right one of the 146 types, the stated facts in the fields the ontology declares for them, the declared types honoured, nothing undeclared, and nothing written. This graph has one injected boundary - the model - and no storage at all, so nothing is substituted.

Composition · 4 cases

Selection, tabulation and anti-fabrication · 4 cases

Scope gate · 3 cases

None of these three reaches the model.

The Close checksums

A Close checksum is a reconciliation over the whole book: it runs at close and it reports. close_checks.py declares 104 checksums - 82 must-be-zero and 22 explainable balances - and the number that actually executes on a run is data-dependent, because nine definitions expand per rail, per currency, per stock item or per facility and sixteen are conditional. Each one carries a generated phase and layer tag, which is what lets 78 of them run nightly rather than at close. The distinction is load-bearing. A deposit held on behalf of a customer is supposed to be a non-zero liability, and reporting it as a break would train people to ignore breaks.

The seven families

Ties the sub-ledgers to their control accounts, and the book to external bank evidence per rail per currency.Representative must-be-zero: AP-CONTROL, AR-CONTROL, PAYMENT-CROSSFOOT, RECEIPT-CROSSFOOT, RAIL-CATALOG, FLOAT-GL-TIE, and RAIL-AP:<rail>:<ccy> / RAIL-AR:<rail>:<ccy> per active rail.Explainable: FLOAT-HELD, DEPOSIT-244, DEPOSIT-344, AP-OUTSTANDING, AR-OUTSTANDING.AP-CONTROL is the one to read first: it compares GL 2000 outstanding against the sum of bill dues, and catches a posting to the control account that bypassed the AP sub-ledger entirely.
Representative must-be-zero: WIP-154, POOL-627, GRNI, TOTAL-154, INVENTORY-GL-TIE, NEG-STOCK-QTY, NEG-STOCK-VALUE, LANDED-COST, BILL-LINE-FOOT, ITEM-CATALOG.Explainable: SAMPLE-POOL, and STOCK:<item> per item on hand.This is the largest family, because the bill-line spine and the cost catalogs are each checked for orphans as well as for totals.
Representative must-be-zero: EMPLOYER-COST, LABOUR-ALLOC, CONTRACT-ASOF, PIT-TIE, NET-TIE, PIT-GL-CARRY, PAYROLL-SETTLEMENT-EVIDENCE, VARIANCE-UNEXPLAINED, VARIANCE-CLASSIFIED, LEAVE-NONNEG.Explainable: MEAL-PIT-BASE, EMPLOYER-COST-TOTAL, THIRTEENTH-LIABILITY.PIT-TIE and NET-TIE compare what payroll computed against the obligations actually raised, which is the tie a spreadsheet cannot make.
Representative must-be-zero: INTEREST-OVERPAID, FLOAT-FACILITY-TIE, NONRECOURSE-NODEBT, and FACILITY-BAL:<id>, HEADROOM:<id>, FACTOR-FLAG:<id> per facility.Explainable: FACILITY-OWED:<id>, FACTOR-RECOURSE:<id>, FACTOR-DERECOG:<id>.
Representative must-be-zero: VAT-INTEGRITY-IN, VAT-INTEGRITY-OUT, VAT-133-TIE, VAT-3331-TIE, VAT-CASH-5M.Explainable: VAT-INPUT, VAT-OUTPUT, VAT-NET, VAT-NONDEDUCTIBLE, FCT-PAYABLE.VAT-INTEGRITY-OUT is a GreatBook addition: the workbook’s per-line integrity check only ever covered input VAT.
Representative must-be-zero: ADJ-ORPHAN, ADJ-QTYPRICE, ADJ-STOCK-LEG, ADJ-QTY-CEILING, ADJ-RATE-MATCH, ADJ-OUTSTANDING, ADJ-BASIS, ADJ-AUTHORITY.Explainable: ADJ-NET-AP, ADJ-NET-AR.A credit note may not exceed the quantity of the document it adjusts, and it must carry the original’s VAT rate.
Representative must-be-zero: IC-MIRROR, FX-REVAL, DIVIDEND-PIT.Explainable: IC-OUTSTANDING, FX-RESERVE.GROUP-TB is deliberately absent because adding balanced journals would restate a structural GL rule without proving group membership, translation, completeness, or eliminations.
The payroll family needs a period. Without one it is skipped rather than run against an undefined scope, which is the same fail-honestly rule the evals use for a missing prerequisite.
Two related numbers, and it is worth saying which you mean: the v12 workbook carries roughly 91 Close-list rows, and the code declares 104 checksums, of which a live run executes however many they expand to against that book’s data. The Close checksums page enumerates every code in every family with its phase and its layer, generated from the inventory rather than hand-maintained.

What runs them

test_close_list is the capstone: one close touching six families with live worked data, asserting the whole surface is green.

The 18 GL invariants

A GL invariant is a precondition. It runs before the write and it refuses - and on any failure the writer raises a typed error and writes nothing. All eighteen are validated inside one function, LedgerService.post_entry(), and they are numbered in the source so a refusal can be traced to a rule. backend/tests/test_ledger_engine.py covers every rule below against a throwaway migrated Postgres. The last column names what else exercises it - a second unit suite, or the eval case that reaches it through a real agent run.
Invariants 12, 14 and 16 are what make the chain evidence rather than decoration, and the 18 GL invariants page sets out each rule’s typed error and exactly what it rejects.

Where these cases live

Running them

Useful flags: --only <case-id> …, --concurrency N, --json out.json, and --strict, which makes a SKIP fatal in an environment that is supposed to have everything wired.

The test that would fail if it broke

langgraph_chat/agents/evals/tests/test_evals_ci.py is the guard on the guards. test_every_reference_key_is_graded_by_some_evaluator asserts that every key in every case’s outputs is read by at least one evaluator, because an expectation nobody reads is worse than no expectation - the case still shows PASS. test_every_served_graph_is_graded asks langgraph.deploy.json which graphs production serves and requires each to be bound in the registry, which is what closes the hole the Typewriter shipped through: an agent that was never registered at all keeps every guard green.