From 7bbeb25889d4d5bf86eb4e8b77cd7492a2fd2743 Mon Sep 17 00:00:00 2001 From: Stephen Waits <1+swaits@noreply.git.swaits.com> Date: Sun, 11 Oct 2026 00:45:58 +0000 Subject: [PATCH] Initial commit: wanderbooks accounting semantics engine --- docs/architecture.md | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/architecture.md diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..68d2b5e --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,72 @@ +# Architecture + +## The one big idea: engine-first, everything is a command + +`ledger-core` owns all accounting knowledge. Every state change is a method +on `Ledger`: + +- `add_account` — extend the chart of accounts +- `create_entry` / `add_line` — build a draft journal entry +- `post_entry` — validate and apply (one-way) +- `reverse_entry` — correct via reversal (one-way) + +Each command validates *before* it applies, and each failure is a typed +`LedgerError` variant. There is no other mutation path: struct fields are +private, and constructors are `pub(crate)` where it matters. This is what +makes the ledger auditable and what will make the future oracle harness +trustworthy — the harness only needs to drive the command surface. + +Reports (`trial_balance`, `balance_sheet`, `profit_and_loss`, `snapshot`) +are free functions: pure, total, and deterministic. Same ledger in, same +report out, every time, on every platform. That determinism is the entire +basis of `docs/oracle.md`. + +## Current crate layout + +``` +crates/ledger-core/ + src/ + lib.rs # crate docs, re-exports, design rules + money.rs # Currency + Money (Decimal, checked arithmetic) + account.rs # AccountType, Side, Account, ChartOfAccounts + journal.rs # JournalLine, JournalEntry, validate() + ledger.rs # Ledger: the command surface, append-only log + reports.rs # trial_balance, profit_and_loss, balance_sheet, snapshot + tests/ + invariants.rs # accounting-law tests + randomized property test + golden/ # hand-verified expected report snapshots +``` + +## Dependency rules + +`ledger-core` may depend only on: + +- `rust_decimal` — exact decimal arithmetic +- `thiserror` — the error type +- `chrono` — dates (`NaiveDate`; no wall-clock reads in the core) +- `serde` / `serde_json` — the oracle interchange format + +New dependencies need justification in the commit message. In particular: +no I/O, no networking, no async runtime, no logging framework in the core. +The core is a pure domain library and stays that way. + +## Future crates (do not create until the roadmap says so) + +| Crate | Purpose | Depends on | +|---|---|---| +| `ledger-store` | SQLite persistence of the entry log | `ledger-core` | +| `ledger-import` | GnuCash XML read-only import | `ledger-core` | +| `ledger-oracle` | Diff harness: our engine vs GnuCash on fixture books | `ledger-core`, `ledger-import` | +| `books-app` | egui UI (native + WASM), command-driven | `ledger-core`, `ledger-store` | + +Dependency direction is always inward toward `ledger-core`. Nothing in the +core may depend on a future crate. UI concerns (formatting, locale, widgets) +never leak into the core; the core speaks `Decimal` and ISO codes. + +## Why Rust + +Exact decimal semantics, exhaustive pattern matching on the five account +types, and a type system that makes invalid states (mutable posted entries, +float money) unrepresentable. The storytold pipeline this project is modeled +on is Rust + egui for the same reasons; when the UI crate arrives it will be +one codebase compiling to native and WASM.