Commit Graph
6 Commits
Author SHA1 Message Date
swaitsandClaude Fable 5.1 0e6b6bb768 feat(bookspec): the checker derives the examples' figures from the rules (P13 step 5)
An engine-free reference model with exact decimals replays an example's
entries, reversals, close, bills and invoices and computes its
trial_balance, balances, profit_and_loss and entry figures from the
rules' symbolic forms (signed sums, normal sides, A = L + E + (R - X),
net income), and checks that each refusal an example expects is justified
by its rule (debits differ from credits, an amount is not positive, finer
than the minor unit, an entry already reversed, a date inside a closed
period). An example whose figure disagrees fails the specification's
build before any engine is consulted; what the model cannot derive is
reported as skipped, never as passed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
2026-10-29 12:51:30 -06:00
swaitsandClaude Fable 5.1 1fbc1383cb feat(bookspec): the specification text and an example for the ledger's own laws (P13 step 4)
bookspec/spec holds the first edition: the model and the five laws
(00-model.md) and one file per area, each B-rule a section with its
status, a MUST/MAY statement, sources pinned either to a vendored source
with a verbatim quotation or to an exact external paragraph, its symbolic
form, its examples and its applicability. B-39, B-40, B-43 and B-45 are
reserved sections (allocated, not yet stated in rules.md); B-44 is stated
in rules.md and now carries its bookspec id and CONVENTION status.

Eight new examples show the laws that had none, each figure worked by
hand: an unbalanced entry is refused (Pacioli ch. 36, Pub. 583), a
negative or zero line is refused, a fraction of a cent is refused (ISO
4217), a child account takes its parent's type, the trial balance and the
accounting equation on one small book (cash 6550.25 = loan 2000 + capital
5000 + (sales 750.50 - rent 1200.25)), a reversal leaves the original
standing and a second reversal is refused, a close sweeps revenue and
expenses into Retained Earnings (3999) and the closed quarter takes no
late entry, and cents add exactly. The refusal vocabulary gains
unbalanced, negative_amount, zero_amount, too_few_lines,
finer_than_minor_unit, parent_type_mismatch, already_reversed and
period_closed; a refused entry may carry a non-positive amount.

The checker's spec.rs fails the build on a rule without its section, a
statement without MUST/SHOULD/MAY, a pin that is not verbatim in its
vendored source, examples that differ from the catalogue, or more than 60
pages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
2026-10-29 12:51:30 -06:00
swaitsandClaude Fable 5.1 055decf7e2 feat(bookspec): the five laws as Lean theorems (P13 step 4)
The first edition's five laws (debits equal credits; the accounting
equation; the trial balance balances; net income articulates to equity;
the cash-flow sections sum to the change in cash) are theorems in
bookspec/lean, ported from verify/lean (Basic, Entry, Statements, Closing,
Ledger under namespace Bookspec) and restated by name in Laws.lean, each a
statement about every reachable state of the books. Audit.lean prints
their axioms; the checker's lean.rs test fails on a sorry, a law not
audited, or any axiom beyond propext, Quot.sound and Classical.choice.
Money is Int, the exact count of the smallest unit: every law is a law of
addition, so the model is faithful to exact decimals.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
2026-10-29 12:51:30 -06:00
swaits f91798cd6e fix(bookspec): the schema refuses a key only its query may name (P13)
`accepted` and `refused` are a command's (and `refused` a period's): the
schema now refuses either on a query that does not take it, as the loader
already did, instead of letting a malformed example reach the runner.
The no-op table walk in the same branch goes with it.
2026-10-26 21:23:16 -06:00
swaits cb4e955942 feat(bookspec): rule identifiers from B-1 to B-45 (P13)
Every rule of `docs/spec/rules.md` now has one bookspec identifier,
`BS-<area>-<n>`: eleven areas (entry, ledger, statement, period, partner,
asset, prepaid, arap, revenue, 1099, format), numbered in B-rule order
within its area. The table is `bookspec/spec/rules-index.md`, and each
stated rule carries its id under its heading in `docs/spec/rules.md`, so
a reader of either can find the other.

rules.md's DECISION status becomes bookspec's CHOICE: the specification
names the options and an engine declares its pick (`Claims::with_choice`),
which is what an example already meant by `status = "CHOICE"`. B-39 to
B-45 are allocated in `plan/served.md` 8.0.3 and not yet stated: they
carry an id and an area and no status, so a later unit fills them in
without moving anyone else's number.

The catalogue names the examples that demonstrate each rule (56 files,
each under at least one rule) and the rule each refusal reason belongs
to. A rule with no example cannot be claimed: a claim needs evidence.
`docs/architecture.md`, `docs/testing.md` (tests and the For-P12
mutation targets) and `docs/parity.md` record the new shape; the
bookspec README points at the checker, the runner and the adapter.
2026-10-26 21:23:16 -06:00
swaits b7be5a5472 feat(bookspec): the checker crate, the Engine trait and the runner (P13)
bookspec is a specification first: every rule normative and sourced,
every rule demonstrated by concrete examples, and any accounting engine
checked against them through one interface (D-34). This moves the
examples' schema out of the test that happened to parse them and makes
the interface explicit.

- `bookspec/checker` (its own workspace, inside this repository until
  the harness is stable): `Example`, `Setup`, `Event`, `Expect`, `Query`,
  `Refusal` and the TOML parser, every structure `deny_unknown_fields`,
  with `Example::from_toml` checking the cross-field shape (closed lists,
  exact decimal amounts, dates, one event kind, `reverses` counting from
  1, the shares summing to 1, commands accepted or refused and not both).
- `Engine` (`load`, `apply`, `query`, `claims`) and `Refusal { rule,
  reason }`; the refusal's rule is the bookspec identifier, so a refusal
  names the rule it belongs to. `Claims` also carries the option an
  engine picks for a CHOICE rule.
- `bookspec/runner` drives any `Engine` through the examples and reports
  per rule: pass, fail, not claimed. It compares the answer itself; an
  engine is never asked whether it agrees with itself.
- `crates/ledger-core/tests/bookspec.rs` is the wanderbooks adapter over
  `Store`: the loader parses through the checker, its own checks are
  unchanged, and the new `the_runner_reports_every_claimed_rule_green`
  runs all 56 examples through the trait. The IIF appendix is answered
  through `ledger_package::export_iif`, the code its own test checks.
- `just check` runs the bookspec workspace's fmt, clippy, rustdoc and
  tests too, so an example cannot rot (D-34, Z-17).

The plan's trait has four methods and no path for an example's
`[[changes]]` (a rename, the policy, an asset correction), which post no
entry: `change` is the fifth, required so an engine cannot skip those
examples silently. This is reported to the EM as the one departure.

One money rule holds here too: an answer's figures are exact decimal
strings, never JSON numbers, so no engine can round a cent through a
float.
2026-10-26 21:22:46 -06:00