Store::verify compares the whole SQLite projection, every table row for
row, against a fresh projection of the journal; verify_and_heal rebuilds a
drifted database (keeping the old file) at every server start and via the
new books.verify tool, which also re-hashes every blob. The GUI checks the
server's database read-only and every stored file, and shows the result.
rebuild() also drops stale WAL/SHM files.
site/: the wanderbooks website (just site, just publish). install.sh:
OS detection (Windows points to WSL2), C compiler and git checks,
WANDERBOOKS_REPO. Windows: no directory fsync off Unix; .exe-aware lookup.
Repo justfile: check, build, install, demo, site.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
egui/wgpu app for owners and their accountant: Overview, Cash, Profit,
Transactions (with rendered receipts and journal history), Reports
(statements with interval columns, comparisons, drill-down), Accounts,
Documents, Company, Journal; Ctrl+K palette; back/forward; light/dark.
Follows the journal live (~30 ms from write to screen). --demo opens a
deterministic demo company, also used for screenshot tests.
Engine: ledger-store Snapshot (read-only, incremental, tolerant of a
half-written tail); ledger-core analysis (intervals, balance series,
P&L by interval, cash flows); ledger-config extracted from ledger-mcp
with the full company profile and cash_accounts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
Vendors carry form_1099 (NEC/MISC) and tin_on_file (never the TIN).
Entries carry the vendor they paid; bills, bill payments, and reversals
are tagged automatically. books.vendor_1099 counts cash paid per vendor
per calendar year (card payments excluded) against $600 through 2025 and
$2,000 from 2026, or an override, and flags missing W-9s. Four new tools
(64), two journal events, schema v7. Docs: finished R-FUTURE work is no
longer listed as future.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
Amortization schedules spread a prepaid expense or deferred revenue over
the months it covers, to the cent, posting on month ends; runs preview
first, are idempotent, honor closed periods, and are exempt from the
receipt policy like depreciation. Recurring entries generate drafts only,
never posts. Six new tools (59 total); four new journal events, replayed
and checked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
Every business has owners, and how owners put money in and take it out
depends on what the business is. wanderbooks now knows the four common
US shapes:
- books.chart_template {entity_type, owners}: a starter chart for a sole
proprietorship (owner's capital and draws), partnership or
multi-member LLC (capital and distributions per partner, guaranteed
payments), S corporation (stock, APIC, shareholder distributions,
officer compensation, retained earnings) or C corporation (dividends,
income tax expense), plus common income and expense accounts with
tax categories, and the wanderbooks.toml settings that go with it
(tax form, members with equal-share placeholders that sum to exactly
100, close target). A proposal for the agent to review with the human.
- Closing: CloseTo is now RetainedEarnings { sweep }, Account { into,
sweep } (a sole proprietor's capital) or Members([MemberClose { capital,
share, distributions }]). Sweeps zero owner-pay equity accounts into
their target at close: draws into owner's capital, dividends or
distributions into retained earnings, each partner's distributions
into their own capital. Validation (equity, distinct, shares sum to 1)
runs in the pre-close preview, before anything is journaled. The
journal records the target; journals with no target or the old
"retained_earnings" string still replay.
- Members' equity: a partner's distributions account is part of their
capital; its activity reports as distributions.
- Config: members' distribution_account; [books] close_into and
close_sweep for single-owner businesses (rejected alongside members).
The cash flow names members on their distribution accounts too.
Tests: a sole proprietor (net 2,000 and 500 of draws into capital: 6,500,
the cash); a corporation (dividends 300 into retained earnings: 600); a
partnership with distribution accounts (Bob 100 + 25 - 20 = 105, swept at
close, February begins there); bad sweep accounts; templates round-trip
through the chart parser, unique codes, equity per entity, every tax
category valid for the entity's return; stdio: template -> settings ->
import -> postings -> close with the exact closing lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
The close zeroed every revenue and expense account's all-time movement,
so closing a period after later entries were posted swept those too:
close January in March and February's and March's income landed in
January's closing entry. The preview had the same bug and never showed
where the net went. Now:
- close_period_to(id, to) zeroes balances as of the period end; entries
dated later stay open for their own period. Periods must close in
order (LedgerError::CloseOutOfOrder): a later close has already swept
the earlier period.
- CloseTo::Members: for a partnership (multi-member LLC) the net goes to
each member's capital account by share, to the cent, the last member
absorbing rounding, which is what K-1 capital accounts track.
CloseTo::RetainedEarnings (3999) stays the default; close_period is
shorthand for it. books.close_period picks Members when
wanderbooks.toml lists members.
- The journal records the close target; replay re-derives the closing
entry and now compares every line with the journal, not just the id,
so a change to closing math can never silently rewrite history.
- The tool's preview is the real close run on a clone of the ledger
(Ledger now derives Clone), so preview and post can't disagree.
- Members' equity: closing entries into a capital account are income,
not contributions. Balance sheet for a partnership: capital accounts
(with allocated earnings), then only unallocated earnings —
"Undistributed earnings, prior years" if any, and this year's unclosed
net income (loss). periods::allocate is shared by the close and the
statement.
Tests: January closed after February is posted sweeps exactly Dr 4000
2,500 / Cr 6100 800 / Cr 3999 1,700 (mutation-checked: the old code gives
3,500/120/800/2,580); out-of-order close refused; a partnership close
splits 666.67 into 333.34/333.33 with no 3999, and the members' equity
statement reads correctly before and after; store replay of a members
close (reopen, rebuild, verify); partnership and corporate balance sheet
layouts; stdio close preview equals the posted lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
Posted entries stay immutable (R-3), but their evidence can now change the
same way the ledger does: by appending events, never by editing or
deleting. New R-13a.
- books.unlink_receipt {attachment_id, reason} journals receipt.unlinked
and leaves the receipt filed, unlinked. The receipt policy holds: an
entry that requires a receipt can't lose its last one (structured
receipt_required error), so a replacement is attach-new, then
unlink-old. Drafts and reversals are exempt, as at posting.
- books.withdraw_attachment {attachment_id, reason} journals
attachment.withdrawn for an unlinked receipt or a filed document: hidden
from list_receipts (unless include_withdrawn), never linkable again. The
blob stays in the store, hash-verified and readable.
- link_receipt now refuses an already-linked or withdrawn attachment.
Before, re-linking silently moved a receipt off a posted entry,
bypassing the policy; evidence now leaves an entry only through a
recorded unlink. dispose_asset checks its receipt ids before posting
instead of after.
- Schema v5 adds attachments.withdrawn_at/withdrawn_reason; existing
stores migrate on open and replay rebuilds the projection.
Tests: store test (errors, reopen, rebuild, verify, journal history) and
an MCP stdio test (policy refusal, replace flow, withdraw, listings,
under-threshold unlink). Dry-run and live use on the Wanderland Labs books:
8 migrated .eml attachments swapped for their PDF renders, trial balance
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu