Files
wanderbooks/plan/STATUS.md
T

52 KiB
Raw Blame History

STATUS — 2026-10-26: "Served" P0 to P3 and P4b S1-S3 live; the plan reworked for a team

Now: the plan is a hand-off (plan/served.md §8.0); P4b S4 next

2026-10-26: the plan was reviewed and rewritten for a team of engineers working in parallel worktrees. A nine-stage Elephant-Goldfish review (plan/review-2026-10-26/, consolidated in consolidated_review_report.md) found plan/served.md sound as a design record and not executable as a hand-off: no remaining unit could be built from the text alone, P7 contradicted D-28/D-29, P4 was never reconciled with the P4b slices built after it, every code pointer was stale, and nothing stated status, dependencies, file ownership or done criteria. Section 8 of the plan is new: 8.0 (the status and dependency table, the merge order, the shared files and allocated names, the eight-field unit template, the recipes R-A to R-G, the journal-safety checklist, how to work in a worktree), 8.1 (what landed, with As-built blocks for P4b S1-S3), 8.2 (every remaining unit in merge order: U0a, U0b, U0c, P4b S4 to S7, P4, P5, P6, P7, P8, P9a, P10, P11a, P11b, P12, P13), 8.3 (the corrected P4b reference design; the owner's answers numbered A-1 to A-14), 8.4 (what is not scheduled, and why). Decisions D-47 to D-54 and open questions Q-4 to Q-7 were added; superseded sentences in sections 1 to 7 and 9 are struck through with the decision that superseded them. The hand-off prompt for the implementing team is plan/HANDOFF.md. Nothing in the code changed. Next: U0a and U0b (the pre-steps), then P4b S4 with S5 and S7 beside it (8.0.2). The first owner questions in line are Q-5 (one upgrade after S7, or an earlier one after S4) and Q-4 (the release manifest, before P11a's signing step).

2026-10-26: the team's first wave is open; U0c landed (198aa2a1). Eight units started in parallel worktrees: U0a (split the MCP server by tool group), U0b (the attention engine over PackageBooks), U0c, P4b S4.1 (terms, numbers, line accounts), P4b S4.2 (settings and party fields), P7 (the TIN scanner's separators), P9a (the self-host recipe) and P13 steps 1-3 (the bookspec checker). U0c is the first to land: the three missing docs/testing.md sections (P4b S1 part B, S2, S3), each with its For-P12 paragraph, and the A/R and A/P section of docs/mcp-interface.md checked against the shipped tools. It changed no code and no golden. It found a code gap, recorded in U0c's As-built block and here: books.documents' party filter matches a customer's or vendor's name, never the party id, while the parameter's schema description offers an id; the doc now says name, and the schema or the handler needs the fix in a unit that may change code (P4b S4.4 or P5's surface pass). The baseline just check on main before this wave was green.

2026-10-26: P7 landed (3e65c83f). The TIN scanner now reads the commas, parentheses and any run of spaces a TIN's groups are written with (123,45,6789, (123) 45-6789, 12 3456789), while an amount's comma-grouped thousands (1,234,567.89, 1-3-3-2) and the company's nine-digit D-U-N-S number stay what they are. A space run counts once however long; a comma or a parenthesis joins only a 3-2-4 or 2-7 group. The old tests that pinned the narrow separator set were replaced, not deleted: has_full_tin_never_forgets_a_tin is a property over the old scanner (any TIN it found, the new one finds), and the truth table gained the comma, parenthesis, space-run, amount, D-U-N-S and phone cases. tin_shapes_with_any_separator_are_found generates the separators; the access log and the filing scan have their own tests, and the TIN canaries in spec_vault.rs still pass. Replay is unchanged (B-28), so old journals open as they did; vault v2 stays closed (D-48). Under this machine's parallel load (load average ~55) closing_during_a_burst_of_posts_through_the_bridge_loses_nothing flaked twice ("nothing posted"); it passes alone and passed on main quietly, and it is unrelated to the scanner. P12 or the test's own unit should make that burst test load-tolerant rather than let the gate depend on an idle machine.

2026-10-26: P13 steps 1-3 landed (579d0eed); the unit is in progress, not done. bookspec now has its own Cargo workspace in the repository with the checker (the example schema and parser, every structure deny_unknown_fields, the cross-field checks, the rule catalogue) and the runner, plus the Engine trait and the wanderbooks adapter over Store. crates/ledger-core/tests/bookspec.rs is the adapter and still checks all 56 examples; the runner reports per rule, and a rule with no example is NotClaimed, never a silent pass. docs/spec/rules.md carries a **bookspec:** id under each of its 38 stated rules; bookspec/spec/rules-index.md maps B-1 to B-45, with B-39 to B-45 allocated and unstated. 15 rules are claimed today because 15 have examples; step 4 owes an example for every rule, the normative text and sources, and the five laws as Lean theorems; steps 5-6 owe the spec's own checker and the GnuCash adapter. just check now runs the bookspec workspace's fmt, clippy, rustdoc and tests beside the main workspace's, and was green at the merge.

Two departures, both reviewed. The trait gained a required Engine::change(&ChangeSpec) (the plan's four methods have no path for an example's [[changes]], and skipping those examples would have rejected part of the corpus). And the rule catalog's DECISION status is now CHOICE, bookspec's word: docs/spec/rules.md, AGENTS.md and docs/spec/README.md say so, and the rebased P7 sentence that still said DECISION was fixed in the same landing. Every later unit that states a rule must use CHOICE where a human chose and must add its **bookspec:** id line (B-39 S4.5, B-40 S4.5, B-41 S7, B-43 P4, B-44 P6, B-45 P8); rules_md_carries_every_id fails the tree otherwise.

2026-10-27: P4b S4.1 landed (13282cf8); P4b S4 is in progress. The core can now carry a document's terms, its number, its line's own account, quantity and unit price, the parties' snapshots and the bill's vendor reference: Terms/TermsKind/EarlyDiscount with exact due dates (Net n, Net n EOM, due on receipt, custom), kernel::discount_max and kernel::line_amount (Kani, with bridge properties), PartySnapshot, Invoice/Bill's new optional fields, Ledger::next_invoice_number and Ledger::check_vendor_ref. Ten tests in spec_documents.rs; just check and just prove green (51 fast Kani harnesses, Lean 90 headline theorems, standard axioms only); no golden moved.

One guard, and the reason it exists: Store::create_invoice and create_bill refuse a line carrying account, quantity or unit_price (StoreError::DocumentFieldsAwaitUpgrade) because the journal does not write those fields yet. A store caller could otherwise post a document whose replay silently loses its line account, which I-3 forbids and no MCP path reaches today. S4.3 removes the guard in the same change that journals the three fields and adds its byte-for-byte test; document_line_fields_wait_for_documents_v2 proves the refusal, the unchanged journal and the unchanged ordinary posting, and the guard sits before rehearse in both commands. Departures to carry forward: the Sent/SentVia/SendKind types are defined here though S5 owns them (S5 adds Ledger::mark_sent and must not redefine them); InvoiceFields/BillFields and create_invoice_with/create_bill_with are the carrier for the create inputs the plan names but does not place; and check_document_line_account's fallback to the store's name-based cash-account rule is a second copy of that rule, to unify later. The bookspec examples payment-terms-due-date and invoice-numbers-run-and-voids-keep-theirs remain owed by a later S4 slice, since P13's checker now takes an unknown key as an error.

2026-10-27: P4b S4.2 landed (bf91c5a7). The invoicing, bills and company-contact settings are journaled keys (eleven of them) with the new Kind::{Count, Terms, PaymentMethods}, and a party can carry an email and default terms as customer and as vendor. documents_v2 exists early — its constant, KNOWN_FEATURES entry and documents_v2_is_its_own_feature landed here because the party and settings writes must call ensure_feature; S4.3 must not redeclare them, and it owns the document write call sites, schema 16 and the byte-for-byte test. The branch's stub Terms was deleted at the rebase; arap.rs is S4.1's alone, and settings::Kind::Terms canonicalizes through Terms::parse / describe. LedgerError::FirstNumberLocked exists as the contract only: S4.3 enforces it at set_setting. The 8.0.6 work is in the branch: a hand-written new-shape line in books_written_under_older_rules_still_open, the p4_s42_settings_parties old-books fixture written by 1bfce915, the invoicing.default_terms refusal in books_p2_served_cut_over_whole, and documents_v2_is_its_own_feature; no figure or golden moved. One test adaptation, not a weakening: terms_keep_one_canonical_spelling now asserts that Due 2026-1-5 is accepted and stored as Due 2026-01-05, which is S4.1's Terms::parse.

The 2026-10-16 audit of the first company's books (62 verified findings, 20 engine defects) and the design discussion that followed produced plan/served.md: the books become a server that owns its data, agents reach them only through the MCP from anywhere, settings and conventions move into the journal, no scaffold. Phases P0-P13; the owner's decisions of 2026-10-16 are at the top of the plan and bind every phase. P0 turned up defects 21 (the CPA package's IIF ignores as_of) and 22 (depreciation's final month plugs mid-month); both are for P3.

The owner's bar (2026-10-17): this is bookkeeping software and it has to be perfect. Full verification every phase (review rounds until clean, just check, just prove, goldens); economize only on waste. Mutation testing waits for P12 from P2 slice 3 on (D-33, owner 2026-10-20: focus on building; P12 reruns the whole max-testing pass, on the owner's desktop or CI).

P0 (guard rails): done. On main at 391634a, pushed (origin + GitHub): pins, transports doc, tools-only template; the store's format gate (newer is not corrupt; the envelope and its hash frozen across formats; no repair of newer books; command vs replay validators, B-28); the short connect text and books.handbook; tool annotations; the import roots' lockdown (Downloads only by default); fail closed; no file with a full TIN filed into the books. Four review rounds, the last clean. Cutover on the first company's books, 2026-10-17: books.verify ok (366 events, head unchanged), and the CPA package as of 2026-10-11 and 2026-10-17 canonically byte-identical before and after (record in that books dir, goldens/p0-cutover-2026-10-17/). Connect text with their settings: 1,396 characters.

P1 (journal-native settings and conventions): landed. Settings, conventions (posting templates, approval rules, policies, close steps, calendar, mappings, notes), decisions, open items and approvals are journal events (format 2); books that keep their settings never read wanderbooks.toml or config.json, and adoption moves a file in exactly once (books.import_settings did too, until D-44). Two review rounds (all findings fixed), just check, just prove (Kani 20/20, Lean 31 theorems, standard axioms only), and mutation on the whole diff (956 mutants; every survivor killed by a test but two equivalent: docs/testing.md). Live on the first company's books 2026-10-17: settings imported (20; the time zone set after), its conventions, decisions 1-13 and open items 1-7 recorded, and every company document filed (D-20). The cutover found two defects, both fixed in 94338b7: the K-1 worksheet printed a share read from the journal at the scale it was written ("0.5", not "0.50"), and the file guard refused documents printing the company's own EIN. On the rebuilt server books.verify ok (434 events) and the CPA package as of 2026-10-11 and 2026-10-17 canonically byte-identical to before, and to the P0 goldens (record in that books dir, goldens/p1-cutover-2026-10-17/).

Before-snapshots for later phases: the first company's reports as of 2026-10-11 and 2026-10-16 (journal head d1ccd2fd…, 355 events) are saved in that books dir under goldens/p0-2026-10-16/ (exported through the MCP, no TINs), privately, by decision, never in this public repo (plan 5.6). P1+ must reproduce them except where the P3 change log adjudicates a difference (cash flow, defect 1).

P2 (one executable, open while held, the cutover): built, slices 1-8. Slices 1-2 on main earlier. Slices 3-8 (2026-10-19 to 2026-10-20), each reviewed until a round came back clean: 3, opening and closing the books (the server on local sockets, the host and its leases, the agents' bridge; no background service, D-21/D-23); 4, access (tokens in the keyring, the door, scopes, the access log, the owner passphrase); 5, files in and out through the server (uploads, exports); 6, the viewer on the data root and one CPA-package builder (ledger-package); 7, backups and restore (D-30, D-31: plain git to a remote and a folder, drills, wanderbooks books restore); 8, the viewer's Books menu, connect, books back-up, no confinement (D-32), the scaffold deleted. Then the tests mutation had found before the pause (D-33), with three real bugs fixed: the admin answer's four-MiB line, and the CPA package's file naming (a twin's number in another folder; a name of dots climbing out of its folder). docs/testing.md lists what P12 must cover.

P2 live on the first company's books, 2026-10-20 (slice 9). Asked one at a time: the old copies move aside (Q-3), backups go to a new private repository on the owner's git server and to his Syncthing folder (Q-1, D-30). Adopted as "fernhill" at event 466, 66 stored files and the TIN vault checked; books.verify ok on the new server with the same head; the CPA package as of 2026-10-11, 2026-10-17 and 2026-10-20 canonically byte-identical before and after (record in that books dir, goldens/p2-cutover-2026-10-20/); both backups whole by a restore preview. The cutover added wanderbooks books put-aside (three review rounds of data safety), which moved the old copies whole into the private folder's legacy/. Agents connect with wanderbooks connect, the viewer is wanderbooks gui fernhill, and the books dir is a books home holding no books data. The before-check found defect 21 again (the IIF ignores as_of).

P3 (engine correctness): live since 2026-10-22. Every defect group of plan P3 is on main (2c5b5a7), each built from its brief, reviewed until a round came back clean, and with its regression tests written as bookspec examples (51 in bookspec/examples/, every one checked by cargo test): the odd cent (defect 17, D-10, D-38), the IIF in plain ASCII and the CPA package as of one day (defects 7, 21, D-36), the cash flow statement by ASC 230's direct method (defect 1, D-35, D-42), rename, one set of period defaults and tax categories that warn or refuse (defects 15, 16, 8), party totals by what moved the money (defects 14, 11, 24), dated 1099 decisions, payment methods and the card exception (defects 23, 12, 18, D-40), depreciation straight-line by days through the final month and daily schedules (defects 22, 9, D-37), the format upgrade only with the owner's go-ahead (D-39), and assets' posted and scheduled depreciation, their bases and the K-1 on the tax basis (defect 13, D-41); then three review commits: the seams between the pieces (one fictional year checked across engine, viewer, tools and package; three defects found and fixed), asset basis (schema 14, shipped migrations pinned by hash; the tax K-1's net income always open), and data safety (three new journal features, replay with P2's TIN detector, the P2 served-books fixture). Mutation waits for P12 (D-33): each piece's targets are listed in docs/testing.md.

The P3 surface review (2026-10-21) checked the agent's surface, the docs and the whole proof run on main with all of P3: just check clean; the tools' docs (docs/mcp-interface.md) corrected tool by tool against the code (about 60 fixes: wrong parameter names, missing outputs and refusals, the idempotent list, the handbook's section list, books.start's next steps, the CPA package's files) and a new test that every tool is documented and every documented tool exists (every_tool_is_documented_and_every_documented_tool_exists; 115 tools, books.access_log was undocumented); B-rule cross-references fixed in code and tests (eight wrong); bookspec's statuses made consistent (an example whose figures follow an owner decision is CHOICE and cites it: thirteen statuses changed, five more examples given the decision they rest on), its README now saying exactly what the loader takes, and the loader refusing a closed-list value spelled any other way. just prove could not finish in its 30-minute cap at four jobs (a CI runner's cores) on a heavily loaded laptop: 52 of 53 harnesses in, the reversal mirror at three lines still running. It is split, nothing dropped: that harness is proved in two halves (kernel::entry_flows is now entry_movements then flows_of) plus end to end at two lines, and just prove runs 47 harnesses and Lean (308 s at four jobs on a quiet machine; 83 headline theorems, standard axioms only) while just prove-slow runs the eight slowest, each under its own cap (467 s), and CI runs both (docs/verification.md, "Running it"). The laws are numbered 1 to 31 with no gaps, every harness listed with its runtime. The viewer's P3 pages were rendered on the demo books and looked at (~/.cache/wanderbooks/shots-p3-surface/): the figures tie across pages (the register, the asset, the balance sheet, the tax summary, the 1099 worksheet and the party agree to the cent); two faults fixed: a report's notes were cut to one line (the fixed asset register's lost its explanation of the tax columns; they wrap now), and the Accounts page listed accounts in the order they were added, not by code.

The P3 cutover (2026-10-22). The first company's books were reinstalled from main (3eda95e: P3, its four reviews, Projects with its snapshot, and the keyring fix). Before the owner's go-ahead, every report was compared with the P2 goldens at four dates: the statements, ledger, journal, members' equity, K-1 worksheet and tax summary identical; the cash flow, asset register, IIF, 1099 worksheet, README and the new tax-basis K-1 file different, each difference adjudicated twice in the private change log with every changed figure recomputed, none unexplained. The owner ran wanderbooks books upgrade (a verified backup first); after it the packages were byte-identical to before it, books.verify ok, both backups whole by a restore preview. Then the P3 facts were recorded: the devices' values and §723 bases with the traditional method (D-41), every vendor's 1099 decision (D-40), the payment methods with every member- and card-paid entry tagged, the creditors of 2050 and 2100, and policy.require_tax_category = refuse.

Projects (2026-10-22, live with P3). A project is a class with facts (B-37), four tools, and a project page in the viewer opening on a snapshot: revenue, direct costs, contribution before overhead and its margin, in the black or red, the on-spec cost unrecovered, by month with the net to date, and costs by category. The sidebar measures its footer.

The keyring (2026-10-22). One keyring item per token, read back when made; the bridge says a locked keyring instead of "make a token"; token check tells a kept token live, revoked, missing or duplicated.

Invoices and bills take a project (2026-10-22). Each line of books.create_invoice and books.create_bill takes an optional class (a project, by id or name): the entry's revenue or expense side is one line per project, the untagged lines' as one, the receivable or payable line and the payments never tagged (rules.md B-37 item 8), so a client project's invoiced revenue and a contractor's billed cost count in its figures, the P&L by class, and its snapshot; the P&L, the 1099 worksheet and party totals don't move. Journaled as the line's class_id behind a new feature, document_line_projects (not projects, which the live books already declared); a document naming none is journaled byte for byte as before. Late-tag notes as books.add_line's; books.get_entry shows a line's project; the viewer's invoice and bill pages list the document's lines, each project a link; the demo's "Client work" carries its invoices. The handbook's caveat is gone for books with the feature.

One way to serve books (2026-10-23, D-44). The owner: "We should probably just delete the stdio path altogether. THere's no point in that. And it sitting there unused will just lead to bitrot. Nuke it?" Gone: wanderbooks dev-stdio, the ledger-mcp binary and ledger_mcp::run (stdio and --http over a bare data dir), with what only it had: files the books make coming back inline (every handed file is a download now), books with no access log or backups, the settings file read and reloaded (legacy_files), the vault key named by a file or --vault-key-file. BooksServer is built one way, from a book as the books server serves it (Served); an unknown tool is a protocol error. Every suite that drove the stdio server moved to crates/ledger-cli/tests/ and drives a real wanderbooks server on a data root of its own (tests/common), files by their download. Inline file input was unchanged then (D-46 removed it, below). The owner then decided (2026-10-23, the question tool): books.import_settings goes (118 tools; old books come in whole by books adopt; books.chart_template gives its settings as books.set_setting changes); the viewer's loose-books-folder mode goes (--data-dir, --books-toml, --vault-key-file, the settings file followed): it opens only books of the data root; and wanderbooks dev-stdio goes with no stub, an unknown command like any other.

Files only by handle (2026-10-23, D-46). The owner, via the question tool: "Uploads only"; and before it: "What we would DEFINITELY want to avoid is passing a bunch of BLOBs through AI context windows. That's just a pure waste." No file passes through an agent's context, either way. In: content_base64 and its sha256 (books.attach_receipt, books.file_document), w9_content_base64 and w9_sha256 (books.set_w9) and csv_text (the two CSV imports) are gone with the 256 KiB inline limit; files come only by books.begin_upload and wanderbooks upload; those tools take no other argument, and an inline argument to any tool is refused before it runs as unknown, pointing to books.begin_upload. Out: books.export (every format), books.asset_labels and books.get_receipt view bytes hand the file over as a download (HandedFile), as the CPA package and the W-9 did; a token keeps up to 64 handed files (was 16). The tests file by upload (tests/common: Wire::upload, upload_on) and read every file by its download (docs/testing.md, "D-46").

P4b slice S1, receivables and payables read their figures from the ledger (2026-10-24, D-45). Part A (ledger-core, store, viewer, Lean control_agrees, Kani bounds): an invoice's or bill's paid, outstanding and status are derived from its tagged lines that still stand, so a reversed payment no longer counts and a void is exact; accounts in the receivables or payables role (or holding document lines) refuse hand lines; format feature document_standing (the owner upgraded 2026-10-24). Part B (MCP): books.void_document, books.control_check, instrument and reference on books.receive_payment and books.pay_bill (status and remaining from the derived figures), the tie-out in books.verify and control_problems in books.digest; the over-the-wire test documents_void_and_tie_out_over_the_wire. Still for later slices: the processor fee on a payment (S6, with multi-document payments and discounts), Needs attention in the viewer for the control check, and S2 to S7. S2 (2026-10-24): books.documents, books.document, and the viewer's Invoices & Bills (Money in, Money out, a document's page, overview tiles). S3 (2026-10-24): the invoice PDF (ledger-import invoice_pdf, templates/invoice.typ; ledger-package invoice_document): re-created from the books, each payment on its own line with its date, method and reference, stamped PAID with the day, PARTIALLY PAID, PAST DUE or VOID (DRAFT, gray, for S4's preview); by download from books.document pdf, and Save invoice PDF in the viewer. The copy issued is kept from S4 on and never changes. Still open after P3 (each for the phase named, or the lead):

  • Invoices and bills take a project now (above), behind the new journal feature document_line_projects: the first company's books declared projects but not it, so until the owner's next format upgrade (wanderbooks books upgrade, D-39) their invoices and bills still take none (the handbook says so).
  • Raised by the cutover review (private change log, "Open points"): its three points are done. The third (2026-10-22, B-36 with D-38): the tax-basis K-1's odd cents of property basis no longer all go to the first member; they run cumulatively, D-38's largest remainder, one count per set of members with a part in an entry, in date order whatever the span, a reversal taking its original back out (tax_capital::RunningSplit, entry_parts; tests tied_odd_cents_of_property_basis_run_cumulatively, a_member_outside_an_entry_never_takes_its_cents, an_entrys_odd_cent_is_the_same_in_every_span, the_split_runs_in_date_order, the law a_running_split_never_drifts_a_cent; bookspec partnership-k1-tax-basis-odd-cents). Left as it was, and said in B-36: the book property each member contributed in an entry is still split entry by entry, ties to the first member (each member's property must stay within their own credit in the entry); its odd cents arise only in an entry part cash and part property whose credits don't divide the property evenly. Whether the first company's three-cent gap between its members came wholly from the basis split is to be checked on its books when the K-1 is next run, at the year-end close. Its second point is done (2026-10-22, B-14 rule 5): a payment to a government (ASC 230-10-45-17(c), "Cash payments to governments for taxes, duties, fines, and other fees or penalties") is other operating cash payments, (g), never cash paid to suppliers, decided by its payee (a party with the government or tax agency role) on any account, as well as by a taxes_licenses account as before; a payment shared with a supplier is split by the parties' shares (45-22); payroll remittances from the payroll payables stay (d) as before; a government selling goods or services is a supplier (add it as a vendor). The first company's state filing fee, booked to its organization-costs account, moves from "Cash paid to suppliers and employees" to "Other operating cash payments" at every date (no total changes), provided (1) its entry names the state's agency with the government_agency role (check with books.party, give the role with books.set_party_roles if not), and (2) the fee's own entry is the one whose cash the statement lists: paid from the bank; charged to a card that is a lender's credit (the financing_accounts setting, D-42, borrowed and spent); or paid by a member personally through the members' account (Due to Shareholders) while that account is in financing_accounts (B-14 rule 11: borrowed from the member and spent on the fee's date, the operating part classed (g) with its on_behalf, and split the same way when shared: a_shared_government_payment_a_member_paid_splits_both_ways). The first company's fee was paid the last way (the books home's goldens change log, entry #1), so check its members' account is a financing account. Charged to a card, or paid through a members' account, that is an operating payable, its cash leaves in the later payment (to the bank, or the member's reimbursement), whose payee is not the state, and it stays with suppliers: check how it was paid (books.get_entry). Expected golden change: once this ships, the cash-flow snapshots in the books home's goldens/ (each cutover's) differ from a fresh run on two operating lines, "Cash paid to suppliers and employees" smaller and "Other operating cash payments" larger by the fee, every section total, net change and cash balance the same. That is this rule, not a regression: diff the class lines against it at the next cutover and refresh the snapshot then. Its first point is done (2026-10-22, B-34 rule 8): the IIF writes each line's project in CLASS, the projects declared first in a !CLASS list, their names held to QuickBooks' 31 characters, no two alike, and listed in the README when written differently (tests iif_writes_each_lines_class_and_declares_the_classes_first, iif_class_names_fit_quickbooks_and_every_change_is_listed, iif_class_names_written_as_the_books_have_them_are_kept, iif_refuses_an_undeclared_or_unfit_class, the property iif_class_names_never_clash, the_package_iif_carries_each_lines_project; bookspec iif-quickbooks-journal-file). The first company's package changes at its next build: its project-tagged lines carry the class, and the class list is new.
  • The viewer recomputes a long-life asset's whole schedule every frame (ledger-gui pages/assets.rs schedule, called from the asset page's draw; defect 22's note: slow, never wrong).
  • A schedule month a run leaves for a human (dated in a closed period, or reversed after it ended) has nothing to mark it booked by hand, so its schedule never shows complete; a schedule or an asset registered by mistake has no way to stop. Both want a design.
  • A reversal dated before what it reverses is refused for depreciation runs and schedule postings only (B-9); every other entry still takes one: a general rule to settle (P4's reversal as a draft is the natural place).
  • Sources not yet read or vendored (docs/spec/README.md, "Open"): ASC 606-10-25-23 (DART only), 25-31, 25-32, 55-20 (B-16); 230-10-45-15(f) (B-14 rule 11); the ASC 250 paragraph B-15 rule 6 means; 606-10-55-39 (B-17); 105-10-05-1 to 05-3's current wording. Public domain but not vendored: IRC §701 and §6225 (B-14 rule 5), Treas. Reg. §1.704-1(b)(2)(iv)(d)(1) and (g)(3) (B-25, cited only). Several cited pages in sources/intuit-iif.md carry no SHA-256, and irs-entity-classification.md and file-names.md no Source, Retrieved or SHA-256 lines; Unicode UAX #14 (B-34) has no source file; Rev. Proc. 98-25's excerpt (irs-records.md) carries no "§5.01" label.
  • Eight tool descriptions are over 700 characters (listed in tool_descriptions_fit_700_characters): shortened in P5.
  • The every-order asset search runs three commands deep in just check (four and five clean by hand, WB_ORDERS_DEPTH); six is P12's. The ledger-store test ops::tests::the_server_makes_the_folder_and_the_viewer_only_reads_it failed once in a whole-workspace run (likely a forked git child holding the viewer lock's descriptor); it passed in this review's runs.

Next: the P3 cutover, then P4-P13 as planned. P11: one app for Windows, Mac and Linux (D-24). P12: max testing again (D-33). P13: bookspec (D-34), a formal, sourced accounting specification first, then its own formal validation and a conformance harness for any engine; P3's regression tests are its first examples.


Earlier status — 2026-10-11 (all 10 natural next steps done; repo now public)

Where we are

All ten natural next steps are done, tested, and pushed. The repo is now public. Parity is 12 of 12 against the spreadsheet workflow.

Done:

  • Repo swaits/wanderbooks on git.swaits.com (public as of 2026-10-11).
  • ledger-core 0.1.0: Money (exact Decimal + ISO currency), five account types with normal sides, chart of accounts (codes, hierarchy, placeholders), journal entries with full validation, Ledger command surface (create/add-line/post/reverse), append-only posting log.
  • Reports: trial balance, P&L, balance sheet (accounting equation holds; current earnings folded into equity pre-close), canonical JSON snapshot() for oracle diffing.
  • 14 tests green: validation rejections, posted-entry immutability, reversal semantics, the accounting laws on a fixture company, a 300-entry randomized invariant test, and the first golden file.
  • ledger-store 0.1.0: the source of truth is an append-only, hash-chained JSONL journal (fsync per append; tamper-evident); content-addressed SHA-256 blob store (immutable, deduplicated, hash-verified on read); SQLite as a disposable projection (TEXT amounts, WAL, synchronous=FULL, integrity_check on open). Single write path: validate → journal → mutate → apply. verify replays the journal and diffs reports three ways; rebuild drops SQLite and replays; a missing database heals on open. 7 tests green: round-trip, tamper detection, torn-tail truncation, rebuild, blob dedup/corruption detection, unbalanced-post rejection.
  • ledger-mcp 0.1.0: stdio server on official rmcp 3.5 (23 narrow typed tools, amounts as decimal strings, initialize.instructions carrying the agent operating contract). Draft → add lines → preview → confirmed post; server-side receipt policy (strictly greater than $25, per-account overrides, typed receipt_required rejection); digest; search; CSV/JSON export; period P&L and as-of balance sheet; fixed-asset tools (register, schedule, list, run-depreciation, dispose with preview). End-to-end stdio protocol tests green (initialize → tools → draft → preview → receipt_required → attach → post → reports → digest → reverse; and a full asset flow: register → schedule → idempotent runs → disposal preview → receipt_required → bill-of-sale → confirmed disposal → gain posted).
  • Fixed assets (ledger-core + ledger-store + ledger-mcp): fixed-asset register (description, purchase date, cost, salvage, useful life, linked cost/accumulated-depreciation/expense accounts), straight-line book depreciation with day-prorated partial months and a final-month true-up so the lifetime total is exactly (cost − salvage), idempotent monthly runs, disposal with auto-posted final depreciation and gain/loss. Tax depreciation stays with the CPA. Tests: 10 asset unit tests (ledger-core), 6 store tests, 7 property tests, MCP e2e.
  • Testing initiative (in progress): proptest property suite on ledger-core (trial balance always balances, A = L + E, reversals restore zero, posted-entry immutability, JSON round-trip, depreciation exactness — the last caught a real month-length-drift bug, fixed with the final-month true-up); cargo-mutants run on ledger-core (228 mutants); cargo-fuzz targets (journal-line parsing, entry validation, op sequences) scaffolded.
  • 120+ tests green workspace-wide; cargo fmt clean; cargo clippy --workspace --all-targets clean, zero warnings (a hard rule: the owner, 2026-10-11).
  • Docs: AGENTS.md (operating manual), requirements, architecture, oracle design, parity catalog (floor: 10 done), glossary, mcp-interface (incl. the 5 asset tools), receipt-store (journal design), capability-parity (10 of 12 rows covered). Plan: this file + roadmap.
  • Formal verification (2026-10-13, docs/verification.md): the money arithmetic moved into one generic kernel (kernel.rs) that the engine calls. 18 Kani harnesses prove it exhaustively within bounds, with covers against vacuity and a canary for a Kani soundness bug. 24 Lean 4 theorems prove the accounting laws for books of any size, after any sequence of commands. A property test bridges i64 and Decimal. just prove. Found and fixed: negative line amounts accepted by the engine, an overflow crash on huge amounts, and an overflow crash in FX conversion.

In progress: nothing — the next arcs are queued below, unstarted.

Next arcs (in order)

The project went AI-first on 2026-10-10 (the owner's call): the primary interface is MCP, not a cloned GUI. The first storage + MCP slice is done (arcs 1–3 below). What remains:

  1. ledger-store done — journal + blob store + SQLite projection.
  2. Blob store done — folded into ledger-store.
  3. ledger-mcp done — 18 tools, receipt policy, digest, export. Remaining from the spec: MCP resources (rmcp 3.x has no resource macros; hand-roll list_resources/read_resource next), Streamable HTTP transport, XLSX/IIF export formats.
  4. Capability parity (docs/capability-parity.md): 11 of 12 rows covered; full CPA export (xlsx/iif) remains. Then the month-long parallel run against the GSheet; cutover per the cutover rule.
  5. Then: oracle harness (cross-check diffs, GnuCash first), read-only GUI on the event log, workflow parity.

Decisions log

  • 2026-10-23: D-46, files only by handle: no inline file bytes in or out; uploads in, downloads out (plan/served.md D-46).

  • 2026-10-23: D-44, one way to serve books: the stdio server, its inline file outputs and its settings-file mode are gone; tests drive a real books server (plan/served.md D-44).

  • 2026-10-13: Formal methods are part of the bar (the owner: "This shit is fucking GOLD"). Money arithmetic lives in kernel.rs with Kani harnesses; accounting laws get Lean theorems; just prove stays green. The engine now rejects negative and out-of-range line amounts (max one quadrillion, ten decimal places). Every dev tool is pinned in mise.toml. See AGENTS.md "Formal methods".

  • 2026-10-10: Single currency per ledger for day one (R-5). Multi-currency is designed-for, not built.

  • 2026-10-10: Money arithmetic is checked and returns Result; no panicking ops, no float paths anywhere.

  • 2026-10-10: Negative zero is normalized away at account_balance — money never prints "-0". (Caught by the golden test on day one; the oracle works.)

  • 2026-10-10: License MIT OR Apache-2.0, matching the storytold projects. GnuCash clean-room boundary documented in docs/oracle.md.

  • 2026-10-10: AI-first pivot (the owner). Primary interface is MCP (docs/mcp-interface.md), not a cloned GUI. Receipts/documents become a content-addressed blob store (docs/receipt-store.md). Parity target for the phase is the GSheet+GDrive system (docs/capability-parity.md), with a parallel-run cutover rule. GUI drops to read-only, event-log-driven, phase 4.

  • 2026-10-10: Import/export targets QuickBooks formats (CSV, IIF, XLSX) — "could not give a single shit about GnuCash XML." GnuCash stays a behavioral cross-check only. (2026-10-13, the owner: it is "an" oracle, not "the" oracle; never trust it to be correct.) Receipt enforcement is server-side policy in the MCP layer (threshold + per-account overrides, typed receipt_required error, checklist in every preview); the engine stays policy-free.

  • 2026-10-10: Storage durability (the owner). SQLite is the query engine, not the source of truth: the source of truth is an append-only, hash-chained JSONL journal (fsync per append; tamper-evident; git-backup-friendly), SQLite is a disposable projection rebuilt from it, blobs stay content-addressed. Single write path: journal → fsync → apply to SQLite.

  • 2026-10-10: Draft durability (house decision). Drafts are durable, not ephemeral: entry.created + entry.line_added journal lines record the draft as the agent builds it, so drafts and their attached receipts survive restarts. Drafts never touch the SQLite projection until posted. Rationale: the journal is the source of truth (phantom entry.created lines without draft lines were the worst of both worlds), and the draft → attach receipts → post workflow spans multiple MCP calls.

  • 2026-10-10: Corrupt SQLite heals itself. If the projection fails integrity_check (or isn't a database at all), Store::open quarantines it to wanderbooks.corrupt.<timestamp> and rebuilds from the journal, instead of failing open. WAL/SHM are dropped as derived state.

  • 2026-10-10: Cash flow statement (R-FUTURE-3) done. Engine reports::cash_flow (indirect method) + books.cash_flow MCP tool (24 tools total). Disposals identified structurally; gains/losses move from operating to investing; the three sections always reconcile to the net change in the designated cash accounts. Two invariant tests, including a full purchase → depreciate → dispose cycle.

  • 2026-10-10: MCP resources done (hand-rolled list_resources / read_resource: chart, trial-balance, balance-sheet, profit-and-loss, digest, receipts). #[tool_handler] must sit on the impl ServerHandler block, not the inherent impl — misplacing it silently drops all tools.

  • 2026-10-10: QuickBooks interop (R-FUTURE-4) done. New ledger-import crate: QBO bank CSV parse (3-col and 4-col layouts), chart CSV parse, IIF journal generation, XLSX report generation (rust_xlsxwriter, MIT). MCP: books.import_bank_csv (drafts + categorization rules), books.import_chart_csv (all-or-nothing), books.export gains xlsx (base64) and iif formats plus cash_flow report. 26 tools total. Capability parity 12 of 12 rows covered.

  • 2026-10-10: Streamable HTTP transport done. ledger-mcp --http ADDR serves MCP at /mcp via rmcp's tower StreamableHttpService (axum). 27 tools total. Verified with curl: initialize returns SSE stream.

  • 2026-10-10: Drive backfill path done. books.import_local_file reads a file from the server's filesystem into the blob store (receipt/document). The agent downloads from Drive via the CLI, then calls this — Drive credentials never enter the server.

  • 2026-10-11: wanderbooks init done. New ledger-cli crate (binary wanderbooks, std-only): init [DIR] scaffolds a books dir — mise.toml (pinned Rust toolchain; tasks: build, mcp-add, backup), AGENT.md (agent operating contract: bootstrap, MCP contract, first-run onboarding incl. migration via conversation, ongoing rules), wanderbooks.toml (company profile, filled during onboarding), books-data/ + .gitignore (SQLite projection ignored; journal is the record). Fresh dirs get git init and a scaffold commit so the journal archive starts on day one. One generic init by design — no per-situation flags; the agent is the import layer (QBO/CSV/email/sheets, conversational). 5 integration tests green; clippy clean. Bootstrap: mise trust && mise install, mise run build, mise run mcp-add, then claude in the dir.

  • 2026-10-11: Receipts are mutable through events, never edits (the owner: "the journal is just a history of every single thing that happens"). books.unlink_receipt and books.withdraw_attachment (both require a reason) journal receipt.unlinked / attachment.withdrawn; nothing is erased from the journal or blob store. Unlinking can't strand an entry the receipt policy requires evidence for (attach the replacement first). link_receipt now refuses an already-linked or withdrawn attachment, which closes a silent path for moving a receipt off a posted entry; disposals check their receipt ids before posting. Schema v5. First real use: swapping the migrated the first company .eml receipts for their PDF renders.

  • 2026-10-11: The cash flow statement is a first-class output everywhere the other two statements are: cash_flow_pdf in ledger-import, a pdf format on books.export (P&L, balance sheet, cash flow), and cash-flow.pdf in the CPA package, which now requires cash_accounts (the statement reconciles to them). Known gaps, next: the P&L and balance sheet PDF/XLSX ignore start/end/as_of (all-time balances under the requested label; core needs period-scoped statements); P&L and balance sheet XLSX are totals-only; PDF amounts don't align (padding in a proportional font), no company name, negatives as "-x".

  • 2026-10-11: Every statement export (csv, json, xlsx, pdf, CPA package) now renders core's period statements: the date range and as-of date are real, with a line per account. One layout (ledger-import::report: sections, lines, subtotals, totals; exact "(1,234.56)" formatting) feeds every renderer. The server reads wanderbooks.toml: company name for report headers, fiscal_year_start for fiscal YTD (end derived), and receipt_threshold (it was silently ignored before). Reports carry "Prepared from journal through seq N".

  • 2026-10-11: PDFs are typeset with Typst (typst 0.15 via typst-as-lib, fonts bundled by typst-assets; printpdf removed). One template, ledger-import/templates/statement.typ, takes the laid-out statement as JSON data (labels are never markup). US accounting presentation in PDF and XLSX alike: tabular figures, "$" on each section's first amount and on totals, negatives in parentheses with the closing paren hanging so decimal points align, zero as a dash, single rule over sums, double rule under totals, amounts never bold (bold figures are wider). Pages flow with a repeated header and "Page n of m". Core's cash flow now orders lines by account code (it followed HashMap order, so line order could change between runs).

  • 2026-10-11: The canonical report set. Core gained the general ledger by account (running balances; QuickBooks' "split" = the opposite side of the entry, "-Split-" when several accounts), the trial balance as of a date, and the statement of changes in members' equity (capital roll-forward per member, allocation by share to the cent, ties to balance-sheet equity). The entry-ordered listing is now correctly called the general journal. Reports render QuickBooks-style (the owner's reference): Inter (bundled, OFL) instead of the typst-assets serif fonts, centered title block, a column-header row, multi-column statements (trial balance Debit/Credit, one column per member), "$" on sums, rules above sums, a double rule under totals, zero as 0.00, entity terms (Members' equity, Net income (loss), standard cash-flow wording) and an "Accrual basis · Prepared …" footer. "Today" is local time, not UTC (evening exports were dated tomorrow).

  • 2026-10-11: Period close fixed and made partnership-aware. The close swept every revenue/expense posting ever made, so a period closed after later entries existed swallowed them (closing January in March closed February and March too); it now zeroes balances as of the period end, refuses to close out of order, and its preview is the real close run on a copy of the ledger. A multi-member LLC closes into each member's capital account by share (CloseTo::Members), the partnership convention, so capital accounts carry allocated earnings; the members'-equity statement treats those closes as income, not contributions, and the balance sheet shows only unallocated earnings. Replay now checks closing lines, not just ids.

  • 2026-10-11: Tax categories (the owner: wanderbooks is a general MCP-first accounting application, so features ship for every small business, not when the first company needs them). Accounts map to stable categories rather than form line numbers; each category knows its returns and default deductible percent; the tax summary shows book vs. tax by category and every uncategorized account. tax_form comes from config or the tax classification. Schema v6.

  • 2026-10-11: Owner equity for every entity type. Chart templates for sole proprietorships, partnerships/multi-member LLCs, S corps, and C corps (equity and owner-pay accounts, tax categories, matching config). Closes sweep draws, distributions, and dividends into their target; partners' distribution accounts count as capital on the members' equity statement and close into it. CloseTo became RetainedEarnings { sweep } / Account { into, sweep } / Members([MemberClose]); journals without a target, or with the old plain "retained_earnings", still replay.

  • 2026-10-11: Schedules. Prepaid expenses and deferred revenue amortize monthly on month ends, to the cent; runs are previewed, idempotent, and exempt from the receipt policy (the schedule is the evidence). Recurring entries generate drafts, never posts. Journal events schedule.registered, schedule.posted (with the full entry, checked on replay), recurring.registered, recurring.generated. No schema change. 59 tools.

  • 2026-10-11: Payouts. books.record_payout grosses a platform deposit up into revenue (by class), fees, and adjustments, refusing unless it reconciles to the cent; it creates a draft so the payout statement is attached as the receipt. Pure core logic (payouts.rs), no new journal events (it journals as a draft and lines). 60 tools. Statement parsers (Apple, Google, Stripe) wait for sample files.

  • 2026-10-11: 1099 tracking. Vendors get form_1099 (NEC/MISC) and tin_on_file (never the TIN); entries get an optional vendor tag (bills, bill payments, and reversals inherit it). books.vendor_1099 counts cash paid per vendor per calendar year against $600 (≤2025) / $2,000 (2026+, OBBBA) or an override, and lists reportable vendors missing a W-9. Journal events vendor.1099_set and entry.vendor_set. Schema v7. 64 tools. Also: requirements and parity no longer list finished R-FUTURE work as future (only budgets remain, declined).

  • 2026-10-12: ledger-gui, the read-only desktop view (egui 0.36, wgpu). Overview (the answer in a sentence, KPI tiles with honest comparisons, cash chart, needs-attention with copyable agent prompts, recent activity), Cash (accounts, in/out, burn and runway, coming up), Profit (by month with margin, spending vs. its recent average, by customer and class), Transactions (search, filters, cash direction, detail with lines, the rendered receipt via hayro, and journal history), Reports (P&L, balance sheet, cash flow, owners' equity, trial balance, GL, tax summary, 1099, agings; date presets, month/quarter/year columns, prior period/year comparison, % of income, drill-down to the account register for exactly those dates, copy as TSV), Accounts and registers, Documents, Company, Journal (every event, hash, payload), Ctrl+K palette, back/forward, light/dark. Live: notify-driven, ~30 ms from write to screen. Engine additions: ledger-store Snapshot (read-only, incremental, torn-tail tolerant), ledger-core analysis (intervals, balance series and history, P&L by interval, flows), ledger-config (extracted from ledger-mcp; full profile, cash_accounts). Fonts: Inter with tnum frozen in and private-use glyphs removed (Phosphor icons).

  • 2026-10-12: One-command onboarding. wanderbooks new DIR installs the server and GUI if missing, writes AGENTS.md (+ CLAUDE.md importing it), wanderbooks.toml, a justfile (agent, gui, backup, update), .gitignore, .mcp.json (the server registered for that dir only) and .claude/settings.json (approved, no prompt), commits, and starts claude with the onboarding prompt. install.sh does Rust + cargo install + wanderbooks new in one line. mise is no longer part of the scaffold.

  • 2026-10-12: Integrity, end to end. Store::verify now also compares the whole SQLite projection (every table, row for row) with a fresh projection of the journal; verify_and_heal rebuilds a drifted database (keeping the old file) and runs at every server start; books.verify (65 tools) adds a re-hash of every blob. The GUI compares the server's database (read-only) with its own projection and re-hashes each file once, showing "Database matches journal · N files verified" and raising problems in Needs attention. Also: Windows-safe blob writes (no directory fsync off Unix), .exe-aware binary lookup, installer OS and C-compiler checks with WANDERBOOKS_REPO, the website (site/, just site, just publish), and a repo justfile (check, build, install, demo).

  • 2026-10-12: The CPA story. The CPA package now also carries every receipt behind the period's entries (receipts/, with index.csv tying each to its entry, date, amount, and SHA-256), the company's documents, the journal as IIF (QuickBooks Desktop), the 1099 worksheet, and a README with the verified journal size; include_receipts=false for a light one. The scaffolded AGENTS.md teaches the bookkeeper to find receipts in email, migrate from QuickBooks/Xero/spreadsheets (match every statement to the cent; keep the old system read-only for a month), and work with the CPA (package, read-only clone + GUI). The site leads with owning your books and an AI bookkeeper: example prompts, a QuickBooks comparison, an honest "what it doesn't do yet", the CPA section, FAQ.

  • 2026-10-12: The agent can drive the viewer. books.show (66 tools) asks the viewer to open an entry, account register, report (with dates), document, search, or page. The request lives outside the books (a JSON file in the runtime dir keyed by the data dir, written atomically); the viewer watches it beside the journal and applies books before navigation. ~30 ms end to end.