merge: main into P11c (P11b's dock and books homes, the macOS harness root fix)

One conflict, ledger-host commands.rs: main moved the agent-program lookup
into find_agent_in; kept that and carried the Windows parts into it
(.exe/.cmd/.bat, npm's global folder). The harness already used
default_root, so main's fix and ours agree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
swaitsandClaude Fable 5.1 committed 2026-10-29 17:26:43 -06:00
commit 84cbe0359c
23 files changed
+3398 -91

No files matched your search

+27 -2
View File
@@ -36,8 +36,8 @@ the specification in `bookspec/` (which never depends on the engine).
| `ledger-import` | formats: QuickBooks CSV in; IIF, XLSX, Typst PDFs (statements, the invoice, labels, the audit workpaper) out | `ledger-core`, `ledger-reader` |
| `ledger-package` | the shared read-only view over `Store` or `Snapshot` (`PackageBooks`): the CPA package, the 1099 worksheet, the invoice document, Needs attention | `ledger-core`, `ledger-store`, `ledger-config`, `ledger-import`, `ledger-vault` |
| `ledger-mcp` | the books server: the MCP tools (122), resources, the handbook, scopes, the door and the access log, backups' scheduling | all of the above |
| `ledger-host` | the host and its leases, the agent bridge (`wanderbooks mcp`), tokens in 0600 files under the data root (D-63), the owner's commands, uploads and downloads, `update` and `rollback` (P11a) | `ledger-store`, `ledger-mcp`, `ledger-config` |
| `ledger-gui` | the viewer: read-only, live, egui; the Books menu hands work to the server | `ledger-core`, `ledger-store`, `ledger-config`, `ledger-import`, `ledger-package`, `ledger-vault`, `ledger-host` |
| `ledger-host` | the host and its leases, the agent bridge (`wanderbooks mcp`), tokens in 0600 files under the data root (D-63), the owner's commands, uploads and downloads, `update` and `rollback` (P11a), books homes and how to start an agent in one (P11b) | `ledger-store`, `ledger-mcp`, `ledger-config` |
| `ledger-gui` | the viewer: read-only, live, egui; the Books menu hands work to the server; the agent dock and the first-run page (P11b) | `ledger-core`, `ledger-store`, `ledger-config`, `ledger-import`, `ledger-package`, `ledger-vault`, `ledger-host` |
| `ledger-cli` | `wanderbooks`, the one executable: every mode (`serve`, `server`, `host`, `mcp`, `gui`, `setup`, `books`, `token`, `connect`, `doctor`, …) and the over-the-wire test suites | everything |
| `bookspec/checker`, `bookspec/runner` | the example schema and parser, the rule catalogue, the `Engine` trait; the per-rule runner | nothing of ours (the adapter in `ledger-core`'s tests depends on them) |
@@ -116,6 +116,31 @@ appended journal lines; the UI renders an immutable view model rebuilt per
change. Statements and series come from `ledger-core` (`reports`,
`analysis`); the GUI only arranges them. See `plan/gui.md`.
Two things sit beside the read-only view, both through the `Owner` the
program gives the viewer (never a write of the books by the viewer):
- **The agent dock** (`dock.rs`, P11b, D-27): a terminal panel along the
bottom of the window (`alacritty_terminal` over a PTY; ConPTY on Windows,
not offered until its four checks are recorded) that runs Claude Code or
Codex in the book's **books home**. The agent is an ordinary child process
of the viewer's program (D-32: no helper, no confinement) and writes the
books through the server like any other agent. The CLIs are found or
offered through their vendors' installers, never bundled; the app marks
no folder trusted (Q-7). Never required: any agent, anywhere, reaches the
same books through the MCP.
- **The first-run page** (`welcome.rs`): with no books and an owner to ask,
`wanderbooks gui` shows a page that sets up the private folder, the owner
passphrase, the first books and an agent, then opens the books, so an
installed app onboards with no terminal.
A **books home** (`ledger_host::commands::books_home`) is a visible folder,
`<Documents>/Wanderbooks/<book id>`, made by the viewer's "New books…" and by
`wanderbooks connect` (and again whenever the dock starts an agent). It holds
exactly `AGENTS.md`, `CLAUDE.md` (three lines), `.mcp.json` (the
project-level entry, `wanderbooks mcp --book ID`) and `.claude/settings.json`
(allowing `mcp__wanderbooks-ID__*`): no books data, key, token or path into
the data root (I-1 stays: the books are hidden, not here).
## Shared read-only views (`ledger-package`)
`ledger-package` is the shared read-only view: packages, the invoice PDF,
+8
View File
@@ -247,3 +247,11 @@ Three new rows, none above changed status, and the floor stands.
| Audit run: one programme of checks over the statements, the control accounts, closes, schedules, assets, evidence, drafts, tax categories, 1099 decisions, settings and the store's integrity (`books.audit`, `books.workpaper`) | An auditor's workpaper; QBO "Audit log"/"Review" and Xero's "Reports review" are a cross-check only | partial | rules.md B-45; `ledger_core::audit`, `ledger_store::audit`; tests `audit_detects_each_seeded_defect` (every code seeded), `audit_is_deterministic`, `workpaper_pdf_is_deterministic`; the fixture `p8_before` was written by the version before; `partial` until a hand-verified golden covers a workpaper |
| Attestations of receipts, entries and periods, weighed against `policy.attestation_bar` when asked (`books.attest_receipt`, `books.review_entry`, `books.attest_period`, `books.attestations`) | An accountant's sign-off on a month | partial | `below_bar_not_counted`, `amount_mismatch_never_counts`, `stale_head_period_attestation_refused`; the method is the client's own claim, and the books say so |
| The CPA package waits for a counted audit (`policy.cpa_package_requires_audit`) | A close checklist before handoff | partial | `cpa_package_refuses_without_a_counted_audit_unless_a_reason_is_given`; behind the feature `audits` |
## P11b: the agent dock, books homes, first run (2026-10-29)
| Area | Counterpart (a cross-check, not the spec) | Status | Notes |
|---|---|---|---|
| Books home: a visible folder per book, `<Documents>/Wanderbooks/<id>`, holding `AGENTS.md`, `CLAUDE.md`, `.mcp.json` and `.claude/settings.json` and nothing of the books | A QBO/Xero company has no counterpart; the point is the agent's workplace (D-27) | partial | `books_home_holds_no_books_data`, `a_books_home_made_again_keeps_what_a_person_kept` (`crates/ledger-host/tests/books_home.rs`); `partial` until the macOS home is tried |
| Agent dock: Claude Code or Codex in a terminal along the bottom of the viewer, started in the books home; never required | QBO's assistant panel is a cross-check only | partial | `dock_runs_the_chosen_agent`, `agent_missing_offers_the_installer` (`crates/ledger-gui/tests/dock.rs`); Linux checked; macOS by the CI run; Windows not offered (ConPTY unchecked) |
| First run with no terminal: the viewer sets up the private folder, the owner passphrase, the first books and an agent | QBO's "set up your company" flow | partial | `first_run_onboards_with_no_terminal` (`crates/ledger-gui/tests/welcome.rs`); the AppImage's whole path is the EM's to try on a clean machine |
+80
View File
@@ -4045,3 +4045,83 @@ this repository's files live in `tests/bookspec.rs`: `every_vendored_pin_is_verb
`rules_md_carries_every_id`. The examples named `bookspec/examples/...` in the
older sections above are files of that repository. The checker's, the
runner's and the derivation tests run in its own CI.
## P11b: books homes, the agent dock, the first run (2026-10-29)
Three layers, none touching the owner's books or data root: a home is made
under a scratch "documents" folder the test gives (`make_books_home_in`
takes it as an argument, so no test writes in an account's real
`~/Documents`); the dock runs a stand-in `claude` (a bash script) on a real
PTY under the viewer's harness; the first run is driven page by page
against an owner over a real scratch data root.
`crates/ledger-host/tests/books_home.rs`:
- `books_home_holds_no_books_data`: exactly four files
(`.claude/settings.json`, `.mcp.json`, `AGENTS.md`, `CLAUDE.md`); none
names the data root (a root with a journal sits beside the home), a token,
a key, a vault or a journal; `AGENTS.md` is three lines and names the
company and `books.start`; `.mcp.json` holds `wanderbooks-acme` running
`<exe> mcp --book acme` and no `--root`; the settings allow
`mcp__wanderbooks-acme__*`.
- `a_books_home_made_again_keeps_what_a_person_kept`: a person's
`AGENTS.md`, their other MCP server and their other settings stay; the
entry and the rule are refreshed and appear once.
- `a_books_home_is_never_made_for_a_name_that_is_not_an_id`: `../x`,
`a/b`, the empty id and `A B` make nothing; a relative documents folder is
refused.
- `an_agent_is_found_on_the_path_or_where_its_installer_puts_it`
(`agent_missing_offers_the_installer`'s host half): not found gives the
vendor's installer; found on the PATH, a file that cannot be run is not
the agent, `~/.local/bin` is searched; `start_agent_in` returns the books
home as the directory and marks nothing trusted (Q-7).
- `a_first_run_reads_where_it_stands_and_sets_the_first_passphrase_once`.
`crates/ledger-gui/tests/dock.rs` (Linux and macOS; a PTY):
- `dock_runs_the_chosen_agent`: closed, nothing runs; opened, the owner is
asked for Claude Code on book `acme`, the program starts in the books
home (its own `pwd`), its output reaches the panel, typed text and Enter
reach it, a Ctrl-C key reaches it as an interrupt (`got-interrupt`; the
agent goes on), narrowing the window resizes the terminal and the program
is told the new size (`stty size` on its `WINCH` matches `Dock::cells`);
the journal is byte for byte as it was.
- `agent_missing_offers_the_installer`: the page names the agent, shows the
vendor's install command with a Copy button and starts nothing.
- `a_chosen_agent_and_an_open_dock_are_kept`: the preferences round trip,
Claude Code the default.
- Unit tests in `dock.rs`: `keys_become_what_a_terminal_sends`,
`colors_follow_the_terminals_palette`.
`crates/ledger-gui/tests/welcome.rs`:
- `first_run_onboards_with_no_terminal`: from nothing, "Set it up" makes the
private folder, two matching passphrase fields set the passphrase (the
owner is handed 20 characters, once), "Make the books" makes `acme` and
holds it, Codex is refused with its reason and Claude Code connects,
"Open the books" shows the books (Needs attention) and the page is gone;
the owner was asked exactly: set up, passphrase, new, hold, connect
Codex, connect Claude.
- `first_run_starts_at_the_first_step_missing_and_asks_nothing_done_again`.
- `a_passphrase_too_short_or_not_repeated_is_not_sent_and_can_wait`.
Existing tests that cover the unchanged parts: `owner_menu.rs` (the menu
still hands each action to the owner), `read_only.rs`, `sidenav.rs` (the
sidebar's shape golden is unchanged: the dock sits right of the sidebar).
**Not tested here:** macOS and Windows (no machine); the real `claude` and
`codex` (not installed on CI); the real `wanderbooks gui` first run
through the AppImage on a clean machine (the EM's).
**For P12 (mutation targets):** `commands::make_books_home_in` (the
`!path.exists()` test that keeps a person's `AGENTS.md`; the merge that
dedupes the allow rule; `BookId::parse` before any path is joined);
`find_agent_in` (the order PATH then the installer folders; `is_program`'s
mode test); `start_agent_in` (the `Missing` arm); `first_run_state`'s three
fields; `set_first_passphrase`'s already-set refusal; `dock::key_bytes` (the
modifier parameter's arithmetic, the Ctrl-letter byte, the app-cursor
switch, the Alt escape); `dock::color`'s cube and gray ramps and its bold
brightening; `Session::resize`'s equality early return; the panel's
open/closed ids (a remembered size must not leak between them);
`Welcome::steps`' choice of `now` (each arm), the passphrase length and
equality gates, the `agent_later` flag.