Settings, conventions (posting templates, approval rules, policies, close steps, calendar, mappings, notes), decisions, open items and entry approvals are journal events (journal format 2). Books that keep their settings never read wanderbooks.toml or config.json; the server and the viewer take the vault key from --vault-key-file instead. books.import_settings moves a settings file in once, exactly as the server reads it. Approval rules, when enforced, gate every posting path. Replay refuses what it can't read as newer, never as corrupt. Lean: a record changes no figure. Mutation on the whole diff: every survivor killed by a test but two equivalent (docs/testing.md). The Company page says a late open item is overdue in words, not colour alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
71 files changed
+12249
-461
No files matched your search
@@ -279,6 +279,81 @@ since `asset_long_life` alone takes three minutes a run):
|
||||
ran past 9999 never trued up and could depreciate past its salvage
|
||||
(`spec_replayed_extremes.rs` `a_life_past_9999_trues_up_to_the_cent`).
|
||||
|
||||
## P1: the books keep how they are kept (2026-10-17)
|
||||
|
||||
| Law | Test |
|
||||
|---|---|
|
||||
| Replay is pure: after any sequence of settings, convention, decision, open-item, template and approval commands, valid or refused, the live store, the store reopened from its journal and the viewer's snapshot hold the same settings and conventions, and the store verifies (database row for row) | `ledger-store` `tests/settings.rs` `settings_replay_is_pure` (proptest, 512 cases per check; 2,000 by hand, release, passed) |
|
||||
| The first such change declares journal format 2, once; a change to what is set journals nothing; refused changes journal nothing | `the_first_change_declares_format_2`, `refused_changes_journal_nothing` |
|
||||
| Approval rules block nothing until enforced; then an entry a rule covers posts only once its approver approved it as it stands | `approval_rules_off_by_default`; `ledger-mcp` `spec_kept.rs` `approval_rules_through_the_server` |
|
||||
| A template drafts exactly, odd cents by largest remainder, its slots named by each draft, all or nothing | `templates_draft_exactly_or_not_at_all`, `template_slots_take_the_account_each_draft_names`, `templates_draft_through_the_server`, core `splits_are_exact` |
|
||||
| Books written under P1 open to the same record forever | `books_written_under_p1_open_to_their_record` (fixture `old-books/p1_record`, also in every old-books test of the store, the viewer and the server) |
|
||||
| A settings file imports exactly, reads as it did, and an export round-trips; a masked EIN never imports | `ledger-config` codec unit tests, `tests/codec_props.rs` `exports_round_trip` (512 cases), `ledger-cli` `the_toml_and_its_examples_import_and_round_trip` |
|
||||
| Moving settings in: set refused before, the preview records nothing, the file never read after (not even to start), an import that reads differently or loses the vault key refused, the EIN only ever masked | `spec_kept.rs` `a_settings_file_moves_into_the_books`, `an_import_that_reads_differently_is_refused`, `an_import_needs_the_vault_key_on_the_command_line` |
|
||||
| books.start fits in 8 KB and says what it cut | `start_fits_in_8_kb` |
|
||||
| Values are canonical; no full TIN but the company's EIN; settings hold together; replay takes shapes | core `settings::tests` |
|
||||
| Every new tool on the happy path, schema-checked; read-only tools change nothing; idempotent tools repeat as no-ops | `spec_mcp.rs` `inv1_*`, `read_only_tools_change_nothing`; `spec_vault.rs` `idempotent_tools_change_nothing_when_repeated` |
|
||||
| The connect text names books.start in its lead and where settings live, in budget, journaled or not | `instructions_budget`, `a_typical_company_gets_the_whole_connect_text` |
|
||||
| Fuzzed journals of the new events open in the writer and the viewer alike, verify, and read every way without a panic | `fuzz_settings_replay` (and `fuzz_store_replay` with the new types) |
|
||||
| A record changes no figure | Lean `Step.record_changes_no_figure`, `Reachable.records` |
|
||||
|
||||
### P1 mutation testing (2026-10-17)
|
||||
|
||||
Every mutant in the P1 diff (`--in-diff`), per crate, against that
|
||||
crate's tests and its nearest callers' (the recipes' settings:
|
||||
`STORE_CASES=64`, `MCP_CASES=16`, no shrinking). First run, then after
|
||||
targeted tests:
|
||||
|
||||
| Crate | Mutants | First run: caught / timeouts / unviable / missed | Missed after |
|
||||
|---|---|---|---|
|
||||
| `ledger-core` (with `ledger-store`'s tests) | 486 | 318 / 5 / 41 / 122 | 1 (equivalent) |
|
||||
| `ledger-config` (with `ledger-cli`'s) | 72 | 44 / 0 / 2 / 26 | 0 |
|
||||
| `ledger-store` | 113 | 81 / 0 / 28 / 4 | 1 (equivalent) |
|
||||
| `ledger-mcp` | 260 | 156 / 0 / 96 / 8 | 0 |
|
||||
| `ledger-gui` | 25 | 7 / 12 / 3 / 3 | 0 |
|
||||
|
||||
No survivor was a bug; each was a test gap, now closed by a test of
|
||||
the law it broke:
|
||||
|
||||
- **Core.** `tests/settings_units.rs`: every settings accessor's exact
|
||||
value, the company profile and its order, every party-policy
|
||||
spelling, fiscal-year bounds off January, the TIN detector's joiner
|
||||
limits, each text, share and month-day boundary, every tax
|
||||
classification and the return it names, the one-quadrillion cap,
|
||||
today in the company's time zone, and the display of members, lists
|
||||
and the masked EIN. `tests/conventions_units.rs`: slot names (32 and
|
||||
33 characters), summaries cut at 160 characters, template bounds (2
|
||||
and 50 lines kept, 1 and 51 refused, a share out of (0, 1] named by
|
||||
its line), the approval fingerprint against an independent FNV-1a of
|
||||
the hashed text, approval rules at the limit and one cent over
|
||||
(debits only), and which fields make a convention's revision.
|
||||
- **Store.** An empty import still moves the books in (once); a
|
||||
posting template replay can't read is newer.
|
||||
- **Config.** The TOML comment scanner (a `#` in basic and literal
|
||||
strings, an escaped quote), comments carried by an import, a masked
|
||||
EIN matching, not matching and into empty books, an empty list
|
||||
changing nothing, `differences` naming each field, the export's
|
||||
provenance lines, `effective_receipt_policy` with and without
|
||||
`config.json` (`tests/effective_policy.rs`).
|
||||
- **MCP.** `Live::journaled` reads no file, `sync` reloads once per
|
||||
change; the books://start, settings and conventions resources; a
|
||||
renumber names exactly the conventions that name the old code.
|
||||
- **Viewer.** The Company page's cards (`tests/kept.rs`): live
|
||||
conventions and not retired ones, standing decisions and not
|
||||
superseded ones, open items and not resolved ones, who decided each
|
||||
and when, a late item saying so in words ("overdue since", added for
|
||||
this: colour alone told only the sighted), and the empty-profile copy
|
||||
of books that keep their settings. `--vault-key-file` wins over the
|
||||
settings and an unknown argument exits 2 (`tests/vault_key_flag.rs`).
|
||||
The first run's 12 timeouts were builds slowed by parallel work: the
|
||||
rerun caught them all.
|
||||
|
||||
Equivalent: `holds_printed_tin` mapping a full-width digit by `/`
|
||||
instead of `-` (every such digit becomes the digit 1: it asks only
|
||||
whether a character is a digit), and `approval_gate`'s fast path (`||`
|
||||
made `&&`: with rules off or none, the probe's `approvals_missing` is
|
||||
empty either way, so only the cost differs).
|
||||
|
||||
## Benchmarks (baseline 2026-10-13)
|
||||
|
||||
No budget, by decision (Stephen, 2026-10-13: the books do little work
|
||||
|
||||
Reference in new issue
Block a user