Direct method with the operating reconciliation and noncash schedules; it foots by construction (Lean and Kani). Every non-cash account belongs to one section; reversals net; a member or lender paying for the company is cash borrowed and spent (D-42, constructive receipt and disbursement); property a member contributes stays noncash. The bookspec seed: one loader checks every example. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
bookspec: examples (seed)
bookspec is the formal accounting specification planned in
plan/served.md (P13, D-34): every rule stated normatively, sourced to its
authority, with a symbolic form and concrete examples, independent of any
engine. Its own repository comes later. Until then this directory holds its
first examples: P3's regression tests, each written here as it is built, so
the spec starts from cases a real engine got wrong.
Every example is checked against wanderbooks on every cargo test
(crates/ledger-core/tests/bookspec.rs, and for the formats appendix
crates/ledger-package/tests/package.rs): an example the engine disagrees
with fails the build, so the examples can't rot. An expected value is worked
by hand from the cited source, never copied from an engine's output.
The shape of an example
One TOML file per example in examples/, named for its id.
id = "cash-flow.reversed-property-contribution" # stable; never reused
title = "A property contribution reversed the same day"
rule = "One normative sentence: what MUST (or MUST NOT) hold."
status = "AUTHORITY" # AUTHORITY | CONVENTION | CHOICE
sources = ["ASC 230-10-45-24", "ASC 230-10-50-3"] # exact citations
tags = ["accrual", "book", "us"] # applicability
[setup]
currency = "USD" # ISO 4217
fiscal_year_start = "01-01" # MM-DD
basis = "accrual" # accrual | cash
rounding = "half-even" # half-even | half-away-from-zero |
# largest-remainder: how a figure is
# rounded to the currency's minor unit,
# where a rule rounds (none here)
[[setup.accounts]]
code = "1000"
name = "Cash"
type = "asset" # asset | liability | equity | revenue | expense
[[events]] # in order; event N is entry #N
date = "2026-01-10"
description = "Property contributed"
lines = [
{ account = "1500", side = "debit", amount = "2340.00" },
{ account = "3000", side = "credit", amount = "2340.00" },
]
[[events]]
date = "2026-01-10"
description = "Reverse entry 1"
reverses = 1 # the event (1-based) this one reverses:
# its lines, sides flipped; no `lines` here
[[expect]]
query = "cash_flow" # what is asked of the books (see below)
from = "2026-01-01" # inclusive
to = "2026-12-31" # inclusive (a query as of a day: as_of)
[expect.values] # every value an exact decimal string
operating = "0.00"
Events count from 1, as entries do: the first [[events]] is event 1
(entry #1), and reverses = N reverses the Nth event, one before it;
reverses = 0 names no event and is refused. An event is an entry
(lines), a reversal (reverses) or a close (close, below), exactly
one of them, and posts entry #N. A key the shape doesn't have is refused
too, never ignored.
rounding names the rule the example's figures are rounded by where one
rounds: half-even or half-away-from-zero to the currency's minor unit,
or largest-remainder for an amount split among parts (each part rounded
down, the units left over to the largest remainders, ties to the part
listed first; rules.md B-13). An example that closes to the members
declares largest-remainder, the rule a members close splits by.
Required everywhere: the top-level keys, [setup] with its four keys and
its accounts, the events, and at least one [[expect]] with values: a
figure query's are exact decimal strings ("2340.00", "-12.50"; never a
float), a format query's are text (below). Amounts on lines are positive;
the side gives the direction.
Optional keys, for the rules that need them
appendix = "formats"(top level): the example shows an interchange format, not an accounting rule (P13's appendix of formats; the IIF file today). It loads like any other; only it may ask a format query.parenton an account: its parent account's code. A parent is declared before its subaccounts.cash_accounts,financing_accounts,investing_accounts: arrays of account codes, the statement of cash flows' account roles (ASC 230-10-45-10 to 45-15). The cash and financing accounts are also what a payee is paid from, forvendor_1099_paid(a payment card is neither: its charges go on Form 1099-K; rules.md B-19), and the cash accounts are an IIF file's bank accounts (B-34).[[setup.fixed_assets]]{ cost_account, accumulated_account, expense_account }: accounts that hold property and equipment (their cost and accumulated depreciation are investing; ASC 230-10-45-12(c), 45-13(c)).[[setup.members]]{ name, capital_account, share? }: the members of a multi-member LLC.shareis the member's share of income, an exact decimal fraction ("0.5","0.3333"); every member has one or none does, and the shares sum to 1. A close to the members andmembers_equityneed them.[[setup.payees]]{ name, form }: a payee marked for an information return,form"1099-NEC"or"1099-MISC"; each name once.payeeon an event: the payee (by name, one ofsetup.payees) the entry paid. Not on a reversal, which takes its original's, nor on a close.close = { start, to, rounding? }as an event: closes the period fromstartthrough the event'sdate(rules.md B-11, B-13),to"members"(split by share, in the order listed) or"retained_earnings". A close to the members splits cumulatively, each close giving each member their split of everything the members' closes have closed less what earlier closes gave them;rounding = "per-close"splits that close's net on its own instead (books from an engine that rounded each close by itself). A close must have something to close: it posts entry #N like any other event.after = Non an expectation: asked of the books after event N, not after the last (1 to the number of events; not on a format query).
Queries and their values
A query over a period takes from and to; a query as of a day takes
as_of; the other is refused. A query no crate answers is refused, never
skipped.
| query | dates | values | checked by |
|---|---|---|---|
cash_flow |
from, to |
figures (below) | ledger-core tests/bookspec.rs |
trial_balance |
as_of |
each account's balance by code, debits less credits (a credit balance negative), every account listed | ledger-core tests/bookspec.rs |
profit_and_loss |
from, to |
each revenue and expense account's activity by code, on its normal side, closing entries left out, every one listed; net_income, revenue less expenses |
ledger-core tests/bookspec.rs |
entry |
entry = N, no dates |
entry N's lines, "<code>.debit" and "<code>.credit" each the sum of that account's lines on that side, every line of the entry named |
ledger-core tests/bookspec.rs |
balances |
as_of |
each account named by code, its balance on its normal side (a member's capital after a loss is negative); the example names the accounts it shows | ledger-core tests/bookspec.rs |
members_equity |
from, to |
the statement of changes in members' equity (rules.md B-13): "<member>.beginning", .contributions, .net_income, .distributions, .other, .ending; the example names the figures it shows |
ledger-core tests/bookspec.rs |
vendor_1099_paid |
from (January 1 of to's year), to |
each payee's payments by name, out of the cash and financing accounts (cash_accounts required), every payee listed |
ledger-core tests/bookspec.rs |
iif_lines |
as_of |
formats appendix: the file's lines in order, each its fields joined by tabs, without the CR LF that ends each | ledger-package the_bookspec_iif_example_matches_the_engine |
iif_changes |
as_of |
formats appendix: each text the file writes differently from the books, in file order, as [what, books' text, file's text] (the example's header gives what) |
ledger-package the_bookspec_iif_example_matches_the_engine |
A figure query is a table ([expect.values], or values = { ... }); a
format query is an array (values = [ ... ]).
cash_flow (rules.md B-14): the statement of cash flows for [from, to].
| key | the value |
|---|---|
operating, investing, financing |
each section's net cash, + in |
net_change, cash_beginning, cash_ending |
the cash accounts' change and balances |
difference |
sections less the change in cash: always "0.00" |
net_income |
revenue less expenses, closing entries excluded |
<section>.<N> |
entry N's cash in that section (financing.4) |
<section>.<N>.on_behalf |
of it, what a member or a lender paid or received for the company, no cash account moving (constructive receipt and disbursement) |
<section>.count |
how many entries the section lists |
noncash.<N> |
entry N's noncash investing and financing disclosure |
noncash.count |
how many entries are disclosed |
reconciliation.depreciation |
depreciation added back |
reconciliation.investing_gain_loss |
gains and losses whose cash is investing, taken out |
reconciliation.noncash_through.<code> |
operating movements no cash settled, through that account |
reconciliation.other_noncash |
other operating movements no cash settled |
reconciliation.balance_change.<code> |
minus the change in an operating asset or liability |
An entry named in a <section>.<N> (or .on_behalf) or noncash.<N>
key must be the only such entries when <section>.count or
noncash.count is given too; a
reconciliation key not given must be zero or absent.
Every example, in any appendix, is loaded by
crates/ledger-core/tests/bookspec.rs (every_bookspec_example_holds):
its shape (a key the shape doesn't have is refused), its setup, and every
event posted. Each query is then checked by the crate that answers it (the
table above), so the shape never rots either.
Licence and sources
The examples state rules in their own words with the paragraph cited
(FASB's Codification is copyrighted: cited, never copied; see
docs/spec/sources/fasb-asc.md). The examples are MIT OR Apache-2.0, as
the rest of this repository, until bookspec moves to its own repository
(planned: the spec under CC BY 4.0).