test(core): spec-blind suites and a live GnuCash oracle; four fixes they found

Max-testing pass, steps 2-4.

Spec-blind tests (rank 1-2): four agents, each given only the specs and
the public interface (doc comments and signatures, every body removed,
never src/ or the existing tests), wrote tests/spec_reports.rs (62),
spec_ledger.rs (72), spec_arap.rs (42), spec_assets_payroll.rs (50):
hand-computed examples per scenario and a proptest law per invariant
(conservation, partition, invariance, scaling, reversal cancels,
"a refusal leaves the books unchanged" over random invalid commands).
They found:

1. A refused payroll run changed the books: an all-zero run passed
   validation, then failed at post after creating seven payroll
   accounts and using up an entry id. validate_payroll_lines now
   refuses a run with no gross pay.
2. A refused add_account used up an account id (empty code, duplicate,
   unknown parent). The id is now taken only when the account is added.
3. Stale prose: close_period_to and members_equity still described the
   old last-member rounding.
4. Open (left #[ignore]d pending Stephen): depreciation_schedule's doc
   says "through the month containing" the date; the code prorates that
   month by days.

GnuCash oracle (rank 3, an oracle never the spec): `just oracle` builds
GnuCash 5.10 in a container pinned by base digest and package versions
(oracle/gnucash/) and runs tests/oracle_gnucash.rs: 200 seeded random
books built through wanderbooks (entries, foreign lines, reversals,
monthly closes), every account's balance at month ends and random dates
and each month's P&L, compared exactly: 15,800 comparisons. First run:
4,724 differences, all from foreign lines; without them, zero.
Adjudicated from principles (rules.md B-3): wanderbooks booked
foreign * rate unrounded ($108.34567); a ledger in one money holds no
fractions of its smallest unit, and its cash must match a bank. Stephen
decided: book in the base currency's minor unit, half away from zero.
add_line_fx now rounds; minor units per ISO 4217 list one (2026-09-17,
SIX), generated into Currency::minor_units and recorded in
docs/spec/sources/iso-4217.md. Replay now takes the journaled base
amount (replay_add_line_fx), so lines booked before replay exactly as
booked (contract: books_written_under_older_rules_still_open). Now:
zero differences, foreign lines included.

Tests: 449, from 223.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-13 14:39:06 -06:00
1 parent 0d4a5f8f53
commit beb1fcf233
20 files changed
+8110 -51

No files matched your search

+9 -1
View File
@@ -79,7 +79,15 @@ periods, closing to net income), and a property test bridges the two.
`just prove`; see `docs/verification.md`. Its first pass found three
engine bugs that Layers 1–4 had all missed.
**Layer 5 — Cross-implementation diff harness (roadmap phase 3).** A
**Layer 5 — Cross-implementation diff harness (live: GnuCash).**
`just oracle` builds GnuCash 5.10 in a pinned container
(`oracle/gnucash/`) and runs `tests/oracle_gnucash.rs`: 200 random books
built through wanderbooks (entries, foreign lines, reversals, closes),
every account's balance at month ends and random dates, and each month's
P&L, compared exactly (15,800 comparisons). Its first run found one real
difference, adjudicated from principles: wanderbooks kept sub-cent
amounts on foreign lines; it now books in cents (rules.md B-3). The
original plan follows. A
`ledger-oracle` crate will run fixture books through our engine and through
other implementations (GnuCash headless first), then diff `snapshot()`
output automatically: `cargo xtask oracle`. This is the direct analog of
+10 -1
View File
@@ -59,8 +59,17 @@ that every line reads one way (2026-10-13, found by the formal pass).
functional currency of the recording entity by use of the exchange rate
in effect at that date."
A foreign line is booked in the base currency's smallest unit (ISO 4217
minor units, `sources/iso-4217.md`), half away from zero; a ledger in one
money holds no fractions of its smallest unit, and its cash can then
match a bank statement: DECISION (Stephen, 2026-10-13), after the GnuCash
cross-check found wanderbooks kept $108.34567 where GnuCash booked
$108.35. Lines booked before then replay as booked.
**Enforced:** `Ledger::add_line` (base currency only),
`Ledger::add_line_fx` (base amount = foreign amount × rate, original kept).
`Ledger::add_line_fx` (base amount = foreign amount × rate, rounded to
the minor unit; original kept). **Checked:**
`a_foreign_line_is_booked_in_whole_base_units`; the GnuCash oracle.
**Checked:** `foreign_currency_line_is_rejected`, `fx_overflow_is_an_error`.
## B-4 Five elements, closed list
+24
View File
@@ -0,0 +1,24 @@
# ISO 4217: currency minor units
Governs `Currency::minor_units`, which sets the smallest unit a ledger
books in (rules.md B-3): foreign-currency lines are rounded to it.
Source: ISO 4217 "list one" (current currency & funds), published
2026-09-17 by SIX Financial Information, the ISO 4217 maintenance agency.
<https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml>,
retrieved 2026-10-13, SHA-256
`33139b438657d1cee116ba737807ea71d19d6de4b90f799a09c56f0cc6a1b0ff`.
The list itself is ISO's, so it is not reproduced here; these are the facts
the code uses, generated from it (the `CcyMnrUnts` of each `Ccy`):
| Minor units | Currencies |
|---|---|
| 0 | BIF CLP DJF GNF ISK JPY KMF KRW PYG RWF UGX UYI VND VUV XAF XOF XPF |
| 3 | BHD IQD JOD KWD LYD OMR TND |
| 4 | CLF UYW |
| none (N.A.) | XAG XAU XBA XBB XBC XBD XDR XPD XPT XSU XTS XUA XXX |
| 2 | the other 139 codes |
To update: fetch the list again, regenerate the table and
`Currency::minor_units`, and record the new publication date and hash.