test: fuzzing, Miri, benchmarks, CI, and a strategy note; the journal refuses floats
check / check (push) Canceled after 0s

Max-testing pass, ranks 4-6 and step 9 for ledger-core.

Fuzzing (rank 4): fuzz_op_sequence now drives balanced pairs, foreign
lines, periods, closes, and amounts from raw decimal bits, and asserts
the books stay valid (TB balances, posted entries validate, no draft in
a closed period); new fuzz_core_serde (every persisted core type
round-trips) and fuzz_store_open (a corrupt journal is an error, never a
panic); hangs count (-timeout=10). 54M executions; one crash:
- fuzz_journal_line: a number too big for an integer parsed as a float,
  and its canonical form (which the hash chain covers) re-canonicalized
  differently. The journal never holds floats (R-2), so the reader now
  refuses them (JournalLine::parse, used by every journal reader);
  regression a_journal_line_with_a_float_is_refused. Checked first: the
  real books' journal has none. Rerun: 13.2M executions, clean.
Seeds in fuzz/seeds/ (synthetic, minimal); the grown corpora stay out of
git. `just fuzz <target> <seconds>`.

Miri (rank 4): the core's unit, bridge, and invariant tests under Miri
(our code has no unsafe; rust_decimal and chrono do): no undefined
behavior. `just miri`. A pinned nightly (nightly-2026-10-01, with miri)
is now in mise.toml.

Benchmarks (rank 6): criterion (new dev-dependency: the playbook's Rust
benchmark tool; none existed) in benches/books.rs; baselines in
docs/testing.md. The store's per-command rehearsal clone costs ~1 ms at
10k entries. No budget yet, so nothing gates on them. `just bench`.

CI: .github/workflows/check.yml runs `just check` on every push;
scheduled.yml runs proofs, the GnuCash oracle, fuzzing, and mutation
weekly. Tools come from mise.toml.

docs/testing.md: every layer, its oracle rank, command, runtime, and
gate; benchmark baselines; the ASSUMED invariants for Stephen to reject;
the gap list with triggers. docs/oracle.md's fuzzing paragraph had
claimed persistent corpora that didn't exist; corrected.

Also: ledger-import now inherits the workspace lints (nightly cargo
warned).

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 15:09:22 -06:00
1 parent 8a9a2485cf
commit c539004d1a
25 files changed
+1635 -81

No files matched your search

+13 -5
View File
@@ -65,11 +65,19 @@ case is unreachable), and untestable-by-construction mutants are noted
unbalanced ledger to falsify it).
**Layer 4 — Fuzzing (live now).** `cargo fuzz` targets in `fuzz/`
(nightly, libfuzzer): `fuzz_journal_line` (journal parsing never panics;
canonical form is a fixed point), `fuzz_entry_validate` (a validated entry
always balances exactly), `fuzz_op_sequence` (arbitrary op sequences via
`arbitrary`; the trial balance always balances). 11.5M execs, zero crashes
as of 2026-10-10. Corpora persist in `fuzz/corpus/` for regression.
(nightly, pinned in `mise.toml`; `just fuzz <target> <seconds>`):
`fuzz_journal_line` (journal parsing never panics; the canonical form is
a fixed point), `fuzz_entry_validate` (a validated entry always balances
exactly), `fuzz_op_sequence` (arbitrary command sequences with periods,
closes, foreign lines, and raw-bit amounts; the books stay valid),
`fuzz_core_serde` (every persisted core type round-trips), and
`fuzz_store_open` (a corrupt journal is an error, never a panic). Hangs
count (`-timeout=10`). Minimal seeds are checked in under `fuzz/seeds/`;
the grown corpora in `fuzz/corpus/` are not (tens of MB). On 2026-10-13,
54M executions found one crash: a float in a journal line broke the
canonical form's fixed point, and the reader now refuses floats
(`a_journal_line_with_a_float_is_refused`). Every crash is minimized and
promoted to a named regression test.
**Layer 4½ — Formal proofs (live now).** The accounting arithmetic lives
in one generic kernel (`kernel.rs`). Kani model-checks it exhaustively