a73x

d88f3df5

docs: README and CLAUDE.md catch up — the wall is the product now

a73x   2026-08-20 08:26

Commit message
docs: README and CLAUDE.md catch up — the wall is the product now

README still described a zoom you could not point at and a milestone
count from before half the features existed. The worst was
self-contradicting: one table row documented the wheel scrolling a
zoomed tile's history while a paragraph two screens down said a zoomed
tile is keyboard-only. The wheel and the mouse work, because a zoomed
tile IS a plain client's loop.

Four things the file never said, ranked by how much they cost a reader
who hits them: a dead tile retries forever ON PURPOSE, so a tile
pointing at a daemon that is never coming back looks exactly like one
whose wifi is off; `mux wall TILE...` shows tiles without recording
them, where the same argv to `muxweb` adds them; `--via` records no tile
at all, because an arbitrary command is not an address; and `make
install`, which was in the Makefile and nowhere in the prose that says
to put muxd on PATH.

CLAUDE.md's layer table was missing two modules, its "never cat" list
had every count stale and omitted the third-largest file in the repo
while still warning off client.zig, which has since halved. The wall's
own invariant — attach history, written on the first state and never on
a successful dial, which is why nothing ever needs to un-record — was
not written down anywhere an agent would find it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

CLAUDE.md
Old New
@@ -20,10 +20,11 @@ First build after a clean checkout fetches ~30MB of QUIC deps (minutes, once).
20 Files are large and comment-dense (~44% of Zig bytes are `//`). Reading the repo 20 Files are large and comment-dense (~44% of Zig bytes are `//`). Reading the repo
21 costs ~800k tokens; every token stays in context and is re-billed each turn. 21 costs ~800k tokens; every token stays in context and is re-billed each turn.
22 22
23 - **Never `cat` these:** `src/server.zig` (9.1k lines, ~110k tok), 23 - **Never `cat` these:** `src/server.zig` (9.6k lines, ~110k tok),
24 `test/e2e.sh` (4.2k), `docs/decisions.md` (3.7k), `src/client.zig` (2.8k), 24 `test/e2e.sh` (7.0k), `docs/decisions.md` (4.7k), `src/wallview.zig` (3.0k),
25 `src/interact.zig` (2.2k). 25 `src/interact.zig` (2.9k).
26 Use `grep -n` for the symbol, then `sed -n 'A,Bp'` for a window. 26 Use `grep -n` for the symbol, then `sed -n 'A,Bp'` for a window.
27 `src/client.zig` is 1.9k now — cheap enough to read.
27 - Every module has a `//!` header stating its contract. `head -12 src/X.zig` 28 - Every module has a `//!` header stating its contract. `head -12 src/X.zig`
28 answers most "what is this" questions for ~200 tokens. 29 answers most "what is this" questions for ~200 tokens.
29 - Pipe Bash output: `| tail -30`, `2>/dev/null`, `grep -c`. `make test` full 30 - Pipe Bash output: `| tail -30`, `2>/dev/null`, `grep -c`. `make test` full
@@ -37,7 +38,7 @@ Layers are enforced in `build.zig`'s module table (grep `.layer =` for the graph
37 | Layer | Modules | 38 | Layer | Modules |
38 |---|---| 39 |---|---|
39 | 0 | `protocol` `engine` `pty` `quic` `keymap` `xdg` `sockpath` `proxy` `testtmp` | 40 | 0 | `protocol` `engine` `pty` `quic` `keymap` `xdg` `sockpath` `proxy` `testtmp` |
40 | 1 | `quic_server` `quic_client` `predict` `spawn` `handoff` `delta` `cmd` `shellint` `replica` `paint` | 41 | 1 | `client_core` `quic_server` `quic_client` `predict` `spawn` `handoff` `delta` `cmd` `wall` `shellint` `replica` `paint` |
41 | 2 | `server` `muxa` `interact` | 42 | 2 | `server` `muxa` `interact` |
42 | 3 | `client` `main`(muxd) | 43 | 3 | `client` `main`(muxd) |
43 | 4 | `webhub` `wallview` | 44 | 4 | `webhub` `wallview` |
@@ -65,6 +66,11 @@ real pty), `wsclient` (browser stand-in), `rawmode`, `delaypipe`, `render`.
65 falls back to `pgid`/`settle` and there is no real exit code. Every `muxa` 66 falls back to `pgid`/`settle` and there is no real exit code. Every `muxa`
66 reply names the `mechanism` that answered it. 67 reply names the `mechanism` that answered it.
67 - **OSC 52 clipboard READ is refused deliberately.** Not a gap. 68 - **OSC 52 clipboard READ is refused deliberately.** Not a gap.
69 - **The wall file is attach history, written on first state.**
70 `client.recordOnState` fires when the daemon's first state arrives and never
71 on a successful dial, so a refused attach cannot strand a line — which is why
72 there is no `unrecordTile`. `--via` and `muxa` record nothing; `mux wall`'s
73 argv is a view, not an attach.
68 - No socket stealing: `muxd run` refuses a path another daemon owns. 74 - No socket stealing: `muxd run` refuses a path another daemon owns.
69 75
70 ## Working rules 76 ## Working rules
@@ -81,9 +87,10 @@ real pty), `wsclient` (browser stand-in), `rawmode`, `delaypipe`, `render`.
81 87
82 ## Where the answers live 88 ## Where the answers live
83 89
84 `docs/roadmap.md` (579 ln) what's next · `docs/decisions.md` (3.7k ln, grep only) 90 `docs/roadmap.md` (595 ln) the ranked queue, but stale past 2026-08-16 —
85 every decision + measurement · `docs/handoff.md` (199 ln) original design · 91 `git-collab issue list` is the live order · `docs/decisions.md` (4.7k ln, grep
86 `README.md` user-facing usage. 92 only) every decision + measurement · `docs/handoff.md` (199 ln) the original
93 design, historical · `README.md` user-facing usage.
87 94
88 ## Session hygiene 95 ## Session hygiene
89 96
README.md
Old New
@@ -15,6 +15,7 @@ dependency). The Makefile points at a pinned toolchain path; override with
15 ```sh 15 ```sh
16 make build # first build fetches + compiles the QUIC deps: ~30MB, a few minutes, once 16 make build # first build fetches + compiles the QUIC deps: ~30MB, a few minutes, once
17 make test && make e2e # verify 17 make test && make e2e # verify
18 make install # the four binaries to ~/.local/bin (override BINDIR)
18 ``` 19 ```
19 20
20 Binaries land in `zig-out/bin/`. For remote machines, build a static binary 21 Binaries land in `zig-out/bin/`. For remote machines, build a static binary
@@ -57,13 +58,15 @@ Zoomed out — the wall reads the keys and nothing reaches a session:
57 58
58 | Key | Effect | 59 | Key | Effect |
59 |---|---| 60 |---|---|
60 | `j` / `k`, `1`-`9` | select a tile | 61 | `j` / `k` (or `n` / `p`), `1`-`9` | select a tile |
61 | `Enter` | zoom the selected tile | 62 | `Enter` | zoom the selected tile |
62 | `x` | forget the selected tile (off the wall file; the session keeps running) | 63 | `x` | forget the selected tile (off the wall file; the session keeps running) |
63 | `q` (or `Ctrl-\`) | leave mux | 64 | `q` (or `Ctrl-\`) | leave mux |
64 65
65 `n`, `p` and `c` are about SESSIONS, not tiles: they walk the daemon's own 66 Behind the `Ctrl-\` prefix — which is to say, zoomed in — `n`, `p` and `c`
66 session ring. A session that already has a tile is an instant zoom move; one 67 are about SESSIONS, not tiles: they walk the daemon's own
68 session ring. (Bare `n`/`p` on the wall are just aliases of `j`/`k`, because
69 there is no session to leave.) A session that already has a tile is an instant zoom move; one
67 that does not gets a tile, because visiting it is attaching to it and every 70 that does not gets a tile, because visiting it is attaching to it and every
68 attach is recorded (see "the wall is your attach history" below). The wall's 71 attach is recorded (see "the wall is your attach history" below). The wall's
69 `j`/`k` and the digits are what walk tiles. 72 `j`/`k` and the digits are what walk tiles.
@@ -107,7 +110,8 @@ really want the loop.
107 110
108 ## Quick start, remote over ssh 111 ## Quick start, remote over ssh
109 112
110 Works anywhere ssh works. On the remote host: put `muxd` on PATH, then: 113 Works anywhere ssh works. On the remote host: put `muxd` on PATH (`make
114 install` puts it in `~/.local/bin`), then:
111 115
112 ```sh 116 ```sh
113 ssh HOST 'muxd start' 117 ssh HOST 'muxd start'
@@ -198,14 +202,24 @@ spelled literally `add` or `rm` has to reach the wall some other way (the
198 `Ctrl-\` chords, `muxweb`, or an editor on the file). Naming neither a 202 `Ctrl-\` chords, `muxweb`, or an editor on the file). Naming neither a
199 target nor a spelling is a usage error, not an empty edit. 203 target nor a spelling is a usage error, not an empty edit.
200 204
201 **The wall is your attach history.** Every `mux` attach writes its own tile 205 Unlike `muxweb`, `mux wall TILE...` does not add what you name to the file:
206 spellings on this command line are a view of some tiles for this run, not an
207 attach of yours, so they are gone when you leave. `mux wall add` is how you
208 make one stick.
209
210 **The wall is your attach history**, kept in `~/.local/state/mux/wall`.
211 Every `mux` attach writes its own tile
202 to that file, so a bare `mux` leaves `--sock <default>#0` behind and 212 to that file, so a bare `mux` leaves `--sock <default>#0` behind and
203 `mux HOST --session build` leaves `HOST#build` — as does every session you 213 `mux HOST --session build` leaves `HOST#build` — as does every session you
204 reach with `Ctrl-\` `c`/`n`/`p`, because visiting a session is attaching to 214 reach with `Ctrl-\` `c`/`n`/`p`, because visiting a session is attaching to
205 it. Tiles are deduped by spelling, byte for byte: the same session reached 215 it. Tiles are deduped by spelling, byte for byte: the same session reached
206 as `HOST#build` and as `quic://HOST#build` is two tiles, deliberately. 216 as `HOST#build` and as `quic://HOST#build` is two tiles, deliberately.
207 `muxa` never adds one (it attaches at 0×0 and claims nothing), and neither 217 `muxa` never adds one (it attaches at 0×0 and claims nothing); `--via` never
208 do the wall's own stripes. Writing the file is best effort — if it cannot be 218 adds one either, because an arbitrary command is not an address and the wall
219 grammar has no line to write for it — the session is real, it is just not
220 addressable as a tile, and `Ctrl-\` `w` labels it `--via CMD#NAME` on a bar
221 that `x` will find nothing in the file to forget; and neither do the wall's
222 own stripes. Writing the file is best effort — if it cannot be
209 written you get one warning line and the attach happens anyway. 223 written you get one warning line and the attach happens anyway.
210 224
211 That file is what `Ctrl-\` `w` shows you: zooming out from any session puts 225 That file is what `Ctrl-\` `w` shows you: zooming out from any session puts
@@ -227,9 +241,8 @@ you have recorded. Forgetting stays explicit: the page's `×`, the wall's
227 241
228 Watching is read-only: each stripe is a live session (label bar + the rows 242 Watching is read-only: each stripe is a live session (label bar + the rows
229 around its cursor), attached the way `muxa` attaches — no size claim, so 243 around its cursor), attached the way `muxa` attaches — no size claim, so
230 watching never resizes anyone. The stripes are cut once, from the terminal's 244 watching never resizes anyone. Resize the terminal and the stripes are
231 size when the wall opened, so resizing that terminal while the wall is up 245 re-cut to it.
232 misplaces them until you leave and reopen it.
233 246
234 The keys are the ones listed under "Quick start, local" — `mux wall` and 247 The keys are the ones listed under "Quick start, local" — `mux wall` and
235 `mux` are the same program, entered through different doors, so there is 248 `mux` are the same program, entered through different doors, so there is
@@ -250,8 +263,18 @@ reconnect, no snapshot, no flash. Zooming out stops the keystrokes and puts
250 the stripes back; the session keeps the grid the zoom claimed, so nothing 263 the stripes back; the session keeps the grid the zoom claimed, so nothing
251 reflows when you look away. 264 reflows when you look away.
252 265
253 Typed input at a zoomed tile is predicted locally, as in a plain client. 266 Typed input at a zoomed tile is predicted locally, and the wheel and the
254 The mouse is not: a zoomed tile is keyboard-only for now. 267 mouse work exactly as they do in a plain client, because a zoomed tile *is*
268 a plain client's loop. Unzoomed, the wall asks its own terminal for no
269 mouse mode at all, so nothing a stripe's session armed can report while you
270 are looking at the wall.
271
272 A tile whose host is not answering says `reconnecting` and keeps retrying,
273 forever and on purpose — a wall is a thing you leave up, and a laptop that
274 wakes an hour later should find its sessions. The cost is that a tile
275 pointing at a daemon that is never coming back looks identical to one whose
276 network is merely down. Nothing times it out; `x`, or `mux wall rm
277 SPELLING`, is how a line leaves.
255 278
256 `x` forgets the selected tile: its line leaves the wall file, its stripe 279 `x` forgets the selected tile: its line leaves the wall file, its stripe
257 leaves the screen, and the remaining stripes are re-cut over the space. 280 leaves the screen, and the remaining stripes are re-cut over the space.
@@ -318,7 +341,7 @@ Design: `docs/handoff.md`. Every decision and measurement:
318 341
319 ## Status 342 ## Status
320 343
321 Prototype, nine milestones in, first trial cut tagged `v0.0.1-1`. All 344 Prototype, eighteen milestones in, current cut tagged `v0.0.1-10`. All
322 founding kill criteria cleared, measured on real networks rather than 345 founding kill criteria cleared, measured on real networks rather than
323 loopback: reattach into a live full-screen `nvim` in ~5ms from one 346 loopback: reattach into a live full-screen `nvim` in ~5ms from one
324 snapshot; steady-state delta traffic ~1% of snapshot-equivalent cost; the 347 snapshot; steady-state delta traffic ~1% of snapshot-equivalent cost; the
test/e2e.sh
Old New
@@ -23,8 +23,8 @@ MUXA="$9"
23 23
24 # The one thing this suite needs that it does not build: a real editor, for 24 # The one thing this suite needs that it does not build: a real editor, for
25 # the paste scenario far below. Checked HERE rather than there because there 25 # the paste scenario far below. Checked HERE rather than there because there
26 # is 25 scenarios, several daemons and a QUIC leg away — minutes of work to 26 # is the whole suite, several daemons and a QUIC leg away — minutes of work
27 # report a missing package. Required rather than guarded; that trade, and why 27 # to report a missing package. Required rather than guarded; that trade, and why
28 # a `command -v` skip would be worse than a hard failure, is argued at the 28 # a `command -v` skip would be worse than a hard failure, is argued at the
29 # scenario itself. 29 # scenario itself.
30 command -v nvim > /dev/null 2>&1 || { 30 command -v nvim > /dev/null 2>&1 || {
@@ -35,8 +35,8 @@ command -v nvim > /dev/null 2>&1 || {
35 # The same trade for the second thing this suite does not build: the hub's 35 # The same trade for the second thing this suite does not build: the hub's
36 # HTTP verbs are asserted through curl, and a `command -v` SKIP around them 36 # HTTP verbs are asserted through curl, and a `command -v` SKIP around them
37 # would let a box quietly not test the wall's whole mutation surface and 37 # would let a box quietly not test the wall's whole mutation surface and
38 # still print a green line. Required, and said here rather than 4000 lines 38 # still print a green line. Required, and said here rather than far down the
39 # down, for the reason nvim is. 39 # file, for the reason nvim is.
40 command -v curl > /dev/null 2>&1 || { 40 command -v curl > /dev/null 2>&1 || {
41 echo "e2e FAIL: this suite needs curl (the dynamic-wall leg asserts on the" 41 echo "e2e FAIL: this suite needs curl (the dynamic-wall leg asserts on the"
42 echo " hub's POST/DELETE/PUT answers); install it, or lose every" 42 echo " hub's POST/DELETE/PUT answers); install it, or lose every"