Commit Graph
20 Commits
Author SHA1 Message Date
swaitsandClaude Opus 5.5 9110cd888d gui: spec-blind suite; search finds the best match, axis ticks never overflow
check / check (push) Canceled after 0s
tests/spec_gui.rs (31 spec-blind tests): exact money text round-trips,
file names never escape the folder or hit a Windows device name, history
against a browser model, fuzzy search against a subsequence model, axis
ticks, the view model against ledger-core, and the attention list.

Found and fixed:
- fuzzy matched greedily, so "ac c" scored 1 for "c" (the "c" inside
  "ac") instead of 5 for the word "c"; it now scores the best alignment.
- ticks accumulated float error (a last tick at -5.55e-17 for a top of
  0), gave more than n + 2 ticks at large magnitudes with tiny spans,
  and overflowed for spans near f64's limit. Ticks are now whole
  multiples of the step, checked, with [lo, hi] when f64 has no nice step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-14 11:39:41 -06:00
swaitsandClaude Opus 5.5 3f20df9700 config, mcp, gui: specs from sources, spec-blind suites, and the bugs they found
check / check (push) Canceled after 0s
ledger-config: parse gets a sourced spec. A fiscal year starts on the
first of a month (IRS Pub. 538); tax_classification is read by whole
words (Treas. Reg. 301.7701-3, Schedule C instructions; "business corp"
no longer reads as an S corp, "corporation or S corporation" is refused);
duplicate or blank members and shared capital accounts are refused; a
negative or non-grammatical receipt threshold is refused; codes trimmed.
tests/spec_config.rs (65 spec-blind tests, 14 properties), fuzz target
fuzz_config_parse (weekly). Mutation: 55 of 55 viable caught (from 25/38).

The receipt policy moves to ledger-config, with one loader
(effective_receipt_policy) and one decimal grammar, so the server and
the viewer enforce and show the same rule (the viewer had its own: only
expense-like debits, a silent $25 default, and no config.json).

ledger-mcp: a module spec (MCP 2025-06-18 tools, JSON-RPC 2.0, the
operating contract). Amounts in are exactly -?digits[.digits]; a number
that is one account's code and another's id (vendors, classes alike) is
refused, not guessed; the threshold is validated at startup. Fixed:
add_line with a bad class journaled the line before refusing;
profit_and_loss_by_class ignored `class`; aging buckets said "0". A wire
in front of rmcp answers malformed JSON with -32700 and invalid requests
with -32600 and their id (rmcp dropped the first, omitted the id).
tests/spec_mcp.rs (26 spec-blind tests: every tool's result against its
own outputSchema, a random-call state machine checking the journal is
untouched by every refusal, amount grammar, previews, the receipt policy).

ledger-gui: money, accounting, and dollars never round (they rounded to
cents, banker's style); export file names avoid Windows device names,
trailing dots and spaces, empty names, and 255-byte overflow; specs on
attention, history, fuzzy search, and axis ticks.

ledger-core: the lifetime profit_and_loss is renamed unclosed_earnings
(docs/testing.md #3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-14 10:13:05 -06:00
swaitsandClaude Opus 5.5 5b8910a1cd books.renumber_account: give an account a new code, history kept
check / check (push) Canceled after 0s
Ledger::renumber_account changes only the code (entries name accounts by
id): refuses an unknown account, a blank or used code, and a control code
for an account of the wrong type or a placeholder. Journaled as
account.renumbered (with the previous code), replayed, projected, shown
in the viewer's journal. The MCP tool lists wanderbooks.toml settings
that still name the old code. The server instructions now warn that the
engine can't tell a same-type account with another purpose at a control
code (a hosting expense at payroll's 6100).

Tests: core property (only the code changes; trial balance equal), core
refusals, the store's command property, MCP end to end with restart.
Mutation on the new code: 8 of 8 viable caught.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-14 08:00:58 -06:00
swaitsandClaude Opus 5.5 ea180d1d8e tax summary: Schedule K items stated separately; XLSX never shows a wrong amount
check / check (push) Canceled after 0s
From the IRS form instructions (now quoted in docs/spec/sources/
irs-tax-lines.md):
- On Forms 1065 and 1120-S, Schedule K takes "only taxable portfolio
  interest", and charitable contributions are among the items the entity
  "must state separately". The tax summary counted both in ordinary
  income and deductions; they now go in TaxSummary::separately_stated,
  outside book_net and tax_net, and the report, MCP export, and viewer
  list them under "Separately stated to owners (Schedule K)".
- Schedule C: "Do not include charitable contributions". The category no
  longer applies to Schedule C (a sole proprietor's gift is personal, an
  owner draw).
- interest_income's note: interest earned in the business itself is
  ordinary income (other_income).

XLSX: a cell number is a binary double and Excel honors 15 significant
digits (Microsoft, "Excel specifications and limits"), so an amount over
$9,999,999,999,999.99 or finer than a cent is written as its exact text,
right-aligned, instead of a number Excel would show wrong. The old
conversion also fell back to 0.0 silently; gone. Property test: every
whole-cent amount up to 15 digits round-trips exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-13 20:57:00 -06:00
swaitsandClaude Opus 5.5 f4381f170c viewer: no false "database differs" while the books are being written
check / check (push) Canceled after 0s
New concurrency test: one thread writes entries, drafts and receipts as
fast as it can while a Snapshot (the viewer) refreshes in a loop. At
every refresh the snapshot's books must balance, never go backward, and
never see the writer's database as differing from the journal; at the
end it must equal the writer in full. It failed 30 runs in 50:
check_disk read the applied position and the tables in separate
statements, so a write landing between them made a consistent database
look like it "differs", which the viewer shows as a problem in Needs
attention.

- check_disk reads in one read transaction (one consistent view).
- A database that has applied lines the viewer hasn't read yet is now
  DiskCheck::Ahead ("viewer catching up"), not Behind.
- Without the transaction the test fails 14 runs in 20; with it, 50/50.

Also: members' equity spec 3b states the exact bound (a member's period
income is within two cents of their share, one cent of rounding at each
end; the running balance chains exactly), and the spec-blind property
checks that bound. docs/testing.md gains the concurrency layer and item
12: each close into members' capital still rounds on its own (pending).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-13 18:51:15 -06:00
swaitsandClaude Opus 5.5 819cd09e0d cash flow: financing and investing gross; account roles are a setting
check / check (push) Canceled after 0s
From ASC 230 (via Deloitte DART and PwC Viewpoint), replacing two
formerly assumed behaviors:

- Financing and investing are gross (230-10-45-7): per account, cash in
  and cash out are separate lines (contributions apart from
  distributions, borrowing apart from repayment), netted only within
  one entry. Lines come in code order, in before out.
- Investing includes making and collecting loans and buying investments
  (230-10-20, 45-13), not only registered fixed assets:
  CashFlowParams gains investing_accounts.
- The roles are per-company settings: [books] financing_accounts and
  investing_accounts in wanderbooks.toml (beside cash_accounts). The MCP
  tools (cash_flow, export, CPA package) default every list to them and
  share one path; the viewer reads them too, falling back to its name
  guess for debt. A due-to-owners liability listed as financing gives
  PwC 6.9.3.1's constructive treatment of expenses an owner paid.

rules.md B-14 quotes the sources; new tests cite them; docs/testing.md
#8 is resolved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-13 18:36:21 -06:00
swaitsandClaude Opus 5.5 4af2f6c0bb store: set aside a torn last journal line on open; server waits for lock
check / check (push) Canceled after 0s
A crash or power loss mid-append can leave a partial last line. The
writer acknowledges an append only after its whole line is synced, so
that line was never acknowledged: opening now saves those bytes to
journal.jsonl.torn.<time>, truncates to the last newline, and journals
journal.tail_repaired (replay and projection ignore it; the GUI shows
it; books.digest reports it in journal_repairs). Damage before the last
newline is still corruption and still refuses to open. Tested at every
byte of the last append.

The MCP server now waits up to ten seconds for the writer lock: a
restarted server (a client reconnecting) raced the exiting one and died
with Locked, which also made mcp_schedules_and_recurring_over_stdio
flaky. The test harness now waits for each server to exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-13 17:58:29 -06:00
swaitsandClaude Opus 5.5 df38dcccf9 feat(core,mcp,gui): never apply a legal rule past what wanderbooks knows
Stephen (2026-10-13): when what wanderbooks knows is stale, check the
date, research it or ask the owner or CPA, and update the rule for
everyone with a pull request (best) or an issue (worst).

- ledger_core::rules: a registry of rules set by law that change over
  time. Each says the years it's known for, its rules.md entry, its
  vendored official source, and a review date. stale_rules(today) is
  pure; the engine has no clock.
- The 1099 threshold is the first: $600 through 2025, $2,000 in 2026,
  then indexed for inflation (26 U.S.C. §6041(h)). default_threshold
  and vendor_1099_report now return Result; for 2027+ they refuse with
  LedgerError::RuleNotKnown, whose message says what to do: get the
  figure from the official source or the owner/CPA, pass it explicitly,
  then open a PR (or issue) at github.com/swaits/wanderbooks. Before,
  2027+ silently used $2,000.
- MCP: books.vendor_1099 relays that refusal; books.digest gains
  stale_rules; the server instructions tell the agent the procedure (and
  how to handle control-account conflicts: explain, never renumber).
  The CPA package writes a NOT-INCLUDED note instead of a guessed 1099
  worksheet.
- Viewer: "Needs attention" shows each stale rule with a ready prompt
  for the agent; the 1099 page explains instead of guessing.

Review date for the 1099 rule: 2026-11-01, when the IRS usually
publishes next year's inflation adjustments (CONVENTION, marked as such
in the registry).

Tests: an_unknown_year_threshold_is_never_guessed,
dated_rules_go_stale_on_their_review_date (dates passed in, so no clock
in the test), MCP stdio: 2027 refused with guidance, explicit threshold
accepted, digest carries stale_rules (asserted date-independently).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-13 14:07:22 -06:00
swaitsandClaude Opus 5.5 bea2ee2dc2 gui: Save PDF on the general ledger too
The general ledger draws its own per-account tables, so it had no PDF
button. It now has one: each account a section, a line per posting
(date, entry, description, the other side) with debit, credit, and
running balance, its beginning balance, and totals with the ending
balance, typeset through the same statement template.

The PDF plumbing (button, status, background job) is shared by both
paths now. The ledger's rows also measure and clip their text and
scroll sideways in narrow windows, like every other table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-12 20:21:20 -06:00
swaitsandClaude Opus 5.5 12308fe3e9 gui: Save PDF on every report; short and narrow windows hold up
Reports get a Save PDF button beside Copy for a spreadsheet. It typesets
the report as it's set up on screen (range, months/quarters/years,
comparison) through ledger-import's Typst statement template, the same
one the CPA package uses, in the background, writes it to the Downloads
folder (never the books: the viewer stays read-only), and opens it.

- ledger-import: Statement.percent_from marks percentage columns
  (comparison %, % of income), typeset as 12.5% with no dollar sign;
  wide statements turn the page, and past seven columns set smaller so
  a year of months fits; statement() is public for other callers.
- Compared columns that read the same (Jan 1 - Oct 12 vs a year
  earlier) now say which year, on screen and on paper.
- Short windows: the sidebar's and rail's links scroll above the status
  box instead of under it. The search palette fits the window.
- A test typesets every table-backed report from the demo books,
  plain, by month, and compared (PDF_DIR keeps the files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-12 20:14:35 -06:00
swaitsandClaude Opus 5.5 ea996f6e7e gui: every screen lays itself out to fit, down to a 360-point tiling slot
Narrow windows overlapped and clipped: the top bar's crumb, date, and
search ran into each other; big numbers wrapped mid-figure; tables
painted columns over each other; the entry and document panes squeezed
their lists to slivers; anything too wide widened the whole page.

- Below 1000 points the sidebar becomes an icon rail (names, attention,
  and integrity checks in tooltips).
- The top bar lays out right to left: search shrinks to its icon, the
  date drops, the crumb truncates.
- Pages are pinned to the window width with narrower margins when tight,
  so one wide thing can't push everything off screen.
- Tiles (KPIs, cash accounts, documents) fit as many columns as the
  width allows, balanced across rows; KPI figures shrink rather than wrap.
- Painted tables shrink their amount columns, clip labels with an
  ellipsis, then scroll sideways rather than overlap.
- List/detail splits show only the detail when both won't fit.
- Reports: the side list becomes a picker; controls wrap by known width.
- Smaller fixes: chart labels thin out, company facts stack, journal
  timestamps yield, transaction filters become a dropdown.
- Min window size drops to 360x400; the screenshot test covers every
  report, a register, a document, and a journal event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
2026-10-12 20:02:48 -06:00
swaitsandClaude Opus 5.5 475d08b28e feat: books.show — the agent puts things on the viewer's screen
books.show asks the viewer to open an entry, an account register, a
report (dates, month/quarter/year columns, prior-period or prior-year
comparison), a document, a search, or a page. The request lives outside
the books (runtime dir, keyed by data dir, written atomically); the
viewer watches it beside the journal and applies books before
navigating. 66 tools.

Site: the dashboard is the hero again; 'Here's what using it looks like'
shows the session beside the viewer, ending in books.show; the cash page
replaces the repeated overview lower down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
2026-10-12 19:03:19 -06:00
swaitsandClaude Opus 5.5 8637c1b05f site: dark screenshots; the hero shows a Claude Code session beside the viewer, same entry
The session (an HTML terminal pane, demo company) records the Adobe
receipt; the viewer shows entry #245 with its lines and rendered receipt.
Screenshot harness: SHOT_LIVE labels a fixed-books app as live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
2026-10-12 18:56:09 -06:00
swaitsandClaude Opus 5.5 3f20858df3 fix(gui): needs-attention grows to fit with 'show N more' instead of a hidden inner scroll; receipt wording follows the policy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
2026-10-12 18:39:48 -06:00
swaitsandClaude Opus 5.5 2a00f4ef33 fix(gui): recent activity rows clip before the timestamp column; SHOT_HEIGHT for tall screenshots
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
2026-10-12 18:37:18 -06:00
swaitsandClaude Opus 5.5 54d2775a33 fix(gui): clip long row text instead of drawing over amounts; drafts aren't payments
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
2026-10-12 17:32:50 -07:00
swaitsandClaude Opus 5.5 45784266b5 feat: the CPA package carries the evidence; the site answers 'why not QuickBooks?'
CPA package: every receipt behind the period's entries with an index,
company documents, the journal as IIF, the 1099 worksheet, and a README;
include_receipts=false for a light package. The scaffolded AGENTS.md
covers receipts from email, switching from QuickBooks/Xero/spreadsheets,
and working with the CPA. GUI: a books copy without a server database
reads 'Read-only copy'. Site: example prompts, QuickBooks comparison,
what it does and doesn't do yet, the CPA section, FAQ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
2026-10-12 17:14:42 -07:00
swaitsandClaude Opus 5.5 9694d2a89a feat: end-to-end integrity checks with self-healing; website; installer and Windows fixes
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
2026-10-12 17:05:05 -07:00
swaitsandClaude Opus 5.5 714867ed58 feat: one-command onboarding — wanderbooks new, install.sh, justfile
wanderbooks new DIR installs the server and GUI if missing, writes the
books dir (AGENTS.md + CLAUDE.md, wanderbooks.toml, justfile, .mcp.json,
.claude/settings.json approving the server), commits, and starts claude
onboarding. install.sh: Rust + cargo install + wanderbooks new in one
line. The tax summary labels its net when accounts are uncategorized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
2026-10-12 16:29:29 -07:00
swaitsandClaude Opus 5.5 105fd52694 feat: ledger-gui — a read-only, live desktop view of the books
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
2026-10-12 16:20:14 -07:00