P2 slice 6: the viewer

`wanderbooks gui [BOOK]` finds the data root, reads settings from the
journal, the vault of the book, presence through ops/, Save a copy /
Open on every document, Save the W-9, the Export menu, and colour
schemes. One builder for the CPA package and the 1099 worksheet
(ledger-package), shared by the server and the viewer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-20 11:14:21 -06:00
1 parent b26c099a68
commit 5bc0633cdd
77 files changed
+5833 -2427

No files matched your search

+13
View File
@@ -259,6 +259,16 @@ The server, the CLI and the GUI find the data root with one shared function (`le
- `[viewer] open_at_start` becomes a per-human eframe preference (`app.rs:833`), not journaled.
- **Color schemes** (owner, 2026-10-19: "full blown colorscheme support … all the top neovim style colorschemes", "on the order of 20 families, each with a flyout of all its variants", a light/dark/all filter; Ayu included). The button in the sidebar's lower corner opens a menu: the filter, then, alphabetically and all on screen at once, the viewer's own family (Wanderbooks Light and Dark) and twenty families of editor schemes (Ayu, Catppuccin, Cyberdream, Dracula, Everforest, GitHub, Gruvbox, Kanagawa, Material, Melange, Monokai Pro, Night Owl, Nightfox, Nord, One Dark, Oxocarbon, Rosé Pine, Solarized, Sonokai, Tokyo Night), each a flyout of all its variants, alphabetical, each a row with a strip of its colors that chooses it; a row rested on for 50 ms is worn live until the pointer moves on (owner, 2026-10-19), and only a click keeps it. The choice and the filter are per-human eframe preferences like `open_at_start`; nothing is journaled and no figure changes (`schemes.rs` paints nine pages in all 80 schemes and checks every painted word and figure is the same, in the same place, and readable on what is under it). Every palette keeps every text color (figures, gains and losses, warnings, links, the faintest text) at WCAG AA contrast (4.5:1) on every background it is drawn on: a scheme's own color that falls short keeps its hue and only changes lightness, and gains, losses and text stay plainly different to the eye. The scheme pins egui's light or dark theme, so the system flipping changes nothing. The palettes come from each scheme's official source (`docs/color-schemes.md`).
**As built (P2 slice 6), and where it departs:**
- **Finding the books.** `wanderbooks gui [BOOK]` (`ledger-gui --root PATH --book ID`): the data root from `--root`, else the one `setup` recorded (`Root::find`), never the working directory or a toml; the book named, else the root's only one (or the first by id). `--data-dir` still opens an older books dir's `books-data` (tests, and the first company's books until the cutover), and `--books-toml` names its settings file, read only while its journal keeps none.
- **Settings come from the journal.** A book of the data root never reads `wanderbooks.toml` or `config.json`: books that keep no settings show the defaults. An older books dir whose journal keeps none is read as its server reads it, from its `wanderbooks.toml` (or `--books-toml`) and `config.json`, followed live, until its settings move in (`books.import_settings`). The demo company journals its settings.
- **The vault.** For a book in the data root (`inside_a_root`), its own `vault/` and `keys/legacy.key`, whatever a setting says; for an older books dir, as before (`--vault-key-file`, the setting, the default). No passphrase: TINs get no handling beyond the vault (D-29); a click copies one to the clipboard as before.
- **`books.show` and presence** (`ledger_store::ops`, which replaces `viewer.rs`): the server makes the book's `ops/` (0700) and `ops/viewer.lock` (0600) when it opens the books; a show request is a line of `ops/show.jsonl` (`{seq, at, target}`, at most 32, the file replaced whole); the viewer watches `ops/` once it exists and holds a shared `flock` on `ops/viewer.lock` through a read-only descriptor while it runs, let go of when it closes; `books.show` answers `viewer_open` and says so. `ops/` is not the books: the read-only tool check leaves it out.
- **Save a copy / Open** on every document, **Save the W-9** on a party whose W-9 is in the vault, and the **Export** menu (the CPA package as of today, the 1099 worksheet for this year or last, full TINs included): each a new file in the Downloads folder (`ledger_store::saving`: never over a file, written aside and linked in; the viewer's report PDFs now save the same way instead of overwriting), what came of it shown beside the button. The menu's files are built off the UI thread from a fresh read-only snapshot; a document's copy and a W-9 (one stored file each, hash-checked as read) are saved at the click. "Open" is a copy in Downloads opened with the system's app (the one folder the viewer may write once confined, slice 8).
- **One builder** (`ledger-package`): the CPA package, the 1099 worksheet and the report helpers the server's tools share moved out of `server.rs` into a crate both the server and the viewer use, over `Store` or `Snapshot` alike (`PackageBooks`), the TINs passed in. Proven unchanged: 91 tool responses, 19 packages and 366 entries byte-identical before and after (only the XLSX and zip timestamps, which differ between any two runs, aside). The package now names files of the same name `name (2).ext` instead of failing, its README names only what it holds, and a K-1 worksheet whose rows don't match the members is left out with the reason.
- **The agent's side (D-29):** see 4.7's slice 5 list; `export_cpa_package`, `vendor_1099` (`csv: true`) and `get_w9` hand their files over as exports (`GET /b/{book}/x/{export}`, `wanderbooks download ex_…`), inline over stdio; no tool takes a path.
- **Not in this slice:** the menu's owner intents ("Close the books", "Back up now", "Restore…", "New books…", "Connect Claude Code / Codex") need the viewer's helper and the lease socket, and come with backups (slice 7) and setup (slice 8); so does running the viewer under read-only Landlock and Open-with through its helper.
### 3.7 Backup and tamper evidence
The archive process (`ledger-mcp archive`: locally started by the host after the server, and stopped with it, 3.9; a system unit on self-host) runs on a head-advance signal (debounced 30 s), on `period.closed`, on a timer while it runs, and once when it starts, for anything left unpushed. The books change only while they are open, so a backup is never missed while they are closed. "Back up now" in the viewer is the lease socket's `back_up_now` intent (3.9), opening the books first if they are closed. Each run:
@@ -1227,6 +1237,9 @@ The owner decisions of 2026-10-16 (at the top of this document) replace the form
| D-26 | Where releases are hosted (owner, 2026-10-19: "I'm assuming the GH releases page will do the hosting?") | **GitHub Releases** on the public mirror (`swaits/wanderbooks`): each release carries the `.dmg`, the setup `.exe`, the AppImage, the `.deb` and `.rpm`, and the signed release manifest; the self-updaters read the manifest at the fixed `releases/latest/download/` address, so no server of ours is needed. CI builds and code-signs on release tags on GitHub's macOS, Windows and Linux runners and holds no release key (P11). The apt and dnf repositories' signed indexes are served from GitHub Pages on the same repository; the AUR package points at the Release assets. |
| D-27 | The agent dock and books homes (owner, 2026-10-19: bundle the agents "then it'd be a one stop shop … a chat window maybe full span across the bottom"; "Yeah I like the idea of the embedded alacritty launching claude/codex … maybe MVP defaults to claude"; "you'd want like a 'books home' dir somewhere that makes sense so it has a place to work"; "add it to the plan!") | The app docks a terminal (alacritty_terminal; ConPTY on Windows) along the bottom of its window and runs Claude Code (the default) or Codex in it, found or installed through the vendors' installers, never bundled; started by the viewer's helper, in the book's **books home**: a visible folder per book holding only the agent's pointer and configuration for that book, never the data root. Trust in that folder is the person's to give. **Never required** (owner, 2026-10-19: "we wouldn't want to prevent people from using their OWN AI agent. They should never HAVE to use our built in tty+claude session! Ideally they could even use the claude.ai browser version to connect to it. And of course, eventually we'd host the entire MCP (and gui!) remotely ... use any agent they prefer to remote connect into that.. and they'd view the GUI in a browser."): the books stay fully usable from any agent the person runs themselves, locally or (P9) remotely, claude.ai in a browser included; the dock's one extra is pasting the app's suggested instructions straight into its session. A hosted MCP and a browser viewer are the later service (section 11). P11 (the agent dock and books homes). |
| D-28 | Who does what, locally (owner, 2026-10-19: "Just have the agent ask for them and the user replies. The interface is through claude/codex and that's that. Don't fucking stray from it. TIN, W-9 is NOT critical or super secret PII." and "for downloading the CPA package or other assets, I don't mind if that can happen through the GUI app ... the GUI is for READING the data. The claude/codex is for MUTATAING (and reading) the data!!!") | **Two interfaces, no third.** Every change to the books goes through the agent (Claude Code or Codex) over the MCP; the agent asks the human for what it needs, TINs and W-9s included, and seals them in the vault (`books.set_tin`, `books.set_w9`): no human web pages, terminal commands or open ports for that locally. Reading and downloading (the CPA package and other exports, receipts, documents) happen in the viewer, which still never writes the books. This supersedes, for local books, the `/h/` pages of 3.3, 4.7 and P7's TIN entry: I-7's "the agent never holds a full TIN" becomes "a TIN passes through the agent only on its way into the vault; no result shows one". `/h/` pages remain for self-hosted books reached remotely (P9). |
| D-29 | The full-TIN outputs, from both doors (owner, 2026-10-19, asked whether the agent's CPA export should go away once the viewer has one: "Both WITH TINs. Stop acting like TINS MATTER SO MUCH THEY DONT") | **The agent and the viewer both make them, TINs included.** `books.export_cpa_package` (TINs by default), `books.vendor_1099` with `include_tins` and `books.get_w9` keep working for the agent; their files are made by the server and handed over by a command, `wanderbooks download`, like a receipt too large for a call (no path parameter, nothing in the agent's context). The viewer's menu makes the same files into Downloads. Over stdio (the development transport) the file comes back inline. TINs get no handling beyond the vault: no passphrase, no extra prompts, no special cleanup. |
| D-30 | Q-1 answered: where the backups go (owner, 2026-10-19, asked "Where should the books' automatic backups go?" with a private git repo, a folder or other disk, or both: chose "Both") | **Two targets.** The archive pushes to a private git repository the owner names and also copies to a folder the owner names (an external disk or a synced folder); both hold only the encrypted archive. The repository's address and the folder are asked for when backups are first set up (slice 7, or at the cutover for the first company), and kept as the book's backup settings. |
| D-31 | Backups are not encrypted (owner, 2026-10-19, asked where to keep the backup's recovery key: "Why encrypt them? How about we let the backup system handle the security???? You're over complicating everything.") | **A backup is the book as it is, in git.** No age encryption, no archive key, no recovery key: the archive is a plain git repository of the book's journal, receipts, vault (as the vault keeps it) and keys, one commit per backup, pushed to the owner's private git repository and to a git repository in the owner's folder (D-30). The destinations are trusted to keep it private. This replaces 3.7's and 3.4's age encryption on push and 3.5's archive key. |
### 10.2 Deferred