a73x

31a7bd88

docs: correct the version caveat, the --via rule and the picker's close key

a73x   2026-09-03 05:20

Commit message
docs: correct the version caveat, the --via rule and the picker's close key

The compatibility caveat named a version that does not exist: build.zig
stamps 0.0.1-17 on this branch and no such tag has been cut, and 1101 is
names plus the meta line rather than names alone. Stated by what the client
was BUILT from instead — released v0.0.1-16 sizes its sessions_reply buffer
at 1056, a development build taken after the meta line and before the holds
lines at 1101 — with the per-session cost that puts the threshold near
fourteen.

The --via paragraph claimed the session was on no layout; it was, and the
next start refused the whole file over it. It now states the rule the
sibling fix commit installs, and decisions.md records the regression, why
the suite was blind to it, and how it is closed.

The picker row said Esc closes at level one; s and Ctrl-C close from either
level and Esc is the one key that backs out a level at a time.

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

CLAUDE.md
Old New
@@ -165,7 +165,10 @@ own. Test fixtures in `test/`:
165 this shell's own session, sets `layout_path` null for the WHOLE run and 165 this shell's own session, sets `layout_path` null for the WHOLE run and
166 says which on the notice line (`[layout not saved: ...]`) — it never 166 says which on the notice line (`[layout not saved: ...]`) — it never
167 rewrites the file, and a save that fails for any other reason is a notice 167 rewrites the file, and a save that fails for any other reason is a notice
168 too, never a stderr print into somebody's pane. `focus N` is the one line 168 too, never a stderr print into somebody's pane. A `--via CMD` entry nulls
169 it for the same family of reason: `hosts.zig` writes no `--via` line, so
170 the leaf `tileLabel` spells for that tile names a host no table can hold
171 and the NEXT start refuses the whole file over it. `focus N` is the one line
169 in the file that is not authored and may go stale between tree changes. 172 in the file that is not authored and may go stale between tree changes.
170 Both files are strict now: the 2026-08 asymmetry (strict hosts, lenient 173 Both files are strict now: the 2026-08 asymmetry (strict hosts, lenient
171 sidecar) is reversed, because the layout is no longer derived. 174 sidecar) is reversed, because the layout is no longer derived.
README.md
Old New
@@ -105,7 +105,7 @@ The focused tile has the terminal, and only the prefix is held back:
105 | `Ctrl-\` `1`-`9` | focus pane N — the digit on that pane's label bar. A new pane takes the lowest free digit, so remove 2 of 1 2 3 and the next pane is 2 again | 105 | `Ctrl-\` `1`-`9` | focus pane N — the digit on that pane's label bar. A new pane takes the lowest free digit, so remove 2 of 1 2 3 and the next pane is 2 again |
106 | `Ctrl-\` `x` | take the focused pane off this wall — the layout is written without it and the session keeps running on its daemon for whoever else holds it. Ending a session is the picker's `x` | 106 | `Ctrl-\` `x` | take the focused pane off this wall — the layout is written without it and the session keeps running on its daemon for whoever else holds it. Ending a session is the picker's `x` |
107 | `Ctrl-\` `w` | zoom out to the wall | 107 | `Ctrl-\` `w` | zoom out to the wall |
108 | `Ctrl-\` `s` | the picker, and the only door a session comes onto the wall through. Level one is every daemon in your hosts file with what its last poll said — `N sessions`, `no sessions`, `unreachable`, `connecting`. `j`/`k` or the arrows move, `1`-`9` pick a row, Enter opens that daemon's SESSIONS, `c` starts a new session there and adds it, `x` forgets the host (the file line, its poller and its panes go; the sessions keep running), `a` adds a host by spelling — `HOST`, `quic://HOST[:PORT]` or `--sock PATH`, Enter adds and Esc returns to the rows. Level two is one row per session that daemon has, reading `on this wall` when it is already a pane and `K client(s)` for everyone holding it, this wall's own pane counted: Enter adds it as a pane (or just zooms to it), `c` still births, `x` ends the session with the daemon's two-step, Esc backs out to the hosts. Esc or `s` at level one closes — and an arrow key split across two reads reads as a bare Escape, so it closes too. The spelling editor takes printable ASCII only, so a path or hostname with any other byte in it goes in the hosts file or on argv instead. While the picker is open every key is its own: nothing reaches a session | 108 | `Ctrl-\` `s` | the picker, and the only door a session comes onto the wall through. Level one is every daemon in your hosts file with what its last poll said — `N sessions`, `no sessions`, `unreachable`, `connecting`. `j`/`k` or the arrows move, `1`-`9` pick a row, Enter opens that daemon's SESSIONS, `c` starts a new session there and adds it, `x` forgets the host (the file line, its poller and its panes go; the sessions keep running), `a` adds a host by spelling — `HOST`, `quic://HOST[:PORT]` or `--sock PATH`, Enter adds and Esc returns to the rows. Level two is one row per session that daemon has, reading `on this wall` when it is already a pane and `K client(s)` for everyone holding it, this wall's own pane counted: Enter adds it as a pane (or just zooms to it), `c` still births, `x` ends the session with the daemon's two-step, Esc backs out to the hosts. `s` or `Ctrl-C` closes the whole popup from either level, while Esc backs out one level at a time — and an arrow key split across two reads reads as a bare Escape, so at level one it closes too. The spelling editor takes printable ASCII only, so a path or hostname with any other byte in it goes in the hosts file or on argv instead. While the picker is open every key is its own: nothing reaches a session |
109 | `Shift+PageUp` / `Shift+PageDown` | scrollback, a screen at a time (any other key returns to live) | 109 | `Shift+PageUp` / `Shift+PageDown` | scrollback, a screen at a time (any other key returns to live) |
110 | mouse wheel | scrollback, three rows a notch (arrow keys to a full-screen app) | 110 | mouse wheel | scrollback, three rows a notch (arrow keys to a full-screen app) |
111 | drag with the left button | select what it crosses, copy on release | 111 | drag with the left button | select what it crosses, copy on release |
@@ -414,8 +414,12 @@ Every `mux` that names a target records that target — `mux HOST`,
414 `mux quic://HOST`, `mux --sock PATH`, and a bare `mux`, which records your 414 `mux quic://HOST`, `mux --sock PATH`, and a bare `mux`, which records your
415 own daemon. `--via CMD` never records one, because an arbitrary command is 415 own daemon. `--via CMD` never records one, because an arbitrary command is
416 not an address and the grammar has no line to write for it; the session is 416 not an address and the grammar has no line to write for it; the session is
417 real, it is just not a machine the hosts file can name — which also means a 417 real, it is just not a machine the hosts file can name. A `--via` wall
418 `--via` session is on no layout and no wall. `mux a` records nothing 418 therefore records nothing at all, layout included: a leaf naming a `--via`
419 command would be a leaf no hosts file can ever match, and the next start
420 would refuse the whole layout over it. Such a run says
421 `[layout not saved: a --via wall is not recorded]` and leaves the file you
422 have alone. `mux a` records nothing
419 either (it attaches at 0×0 and claims nothing). Writing the file is best 423 either (it attaches at 0×0 and claims nothing). Writing the file is best
420 effort — if it cannot be written you get one warning line and the attach 424 effort — if it cannot be written you get one warning line and the attach
421 happens anyway. 425 happens anyway.
@@ -428,7 +432,7 @@ and lists everything verbatim, marking the bad one:
428 ``` 432 ```
429 --sock /run/user/1000/muxd.sock 3 433 --sock /run/user/1000/muxd.sock 3
430 box [unreachable] 434 box [unreachable]
431 #build [bad host: names a session after '#': the wall lists daemons and shows every session they have] 435 #build [bad host: names a session after '#': a host line names a daemon; the layout names sessions]
432 ``` 436 ```
433 437
434 — the line, a tab, then its live session count or what went wrong. That 438 — the line, a tab, then its live session count or what went wrong. That
@@ -681,11 +685,15 @@ install directory on its way in — they are modes of `mux` now, and a
681 One more mixed-version caveat runs the other way, daemon newer than client. 685 One more mixed-version caveat runs the other way, daemon newer than client.
682 A daemon now answers `sessions_req` with a `# holds NAME N` line per session 686 A daemon now answers `sessions_req` with a `# holds NAME N` line per session
683 beside the names, which an older client skips as it skips the version line — 687 beside the names, which an older client skips as it skips the version line —
684 but a client at v0.0.1-17 or older sized its receive buffer for the names 688 but a client built before those lines existed sized its receive buffer for
685 alone (1101 bytes) and reads anything longer as a transport error. A new 689 what it expected and reads anything longer as a transport error. Released
686 daemon holding roughly fourteen or more sessions with 32-character names 690 v0.0.1-16 sized it for the names alone, 1056 bytes; a development build off
687 therefore reads `unreachable` on such a client until that side is upgraded. 691 main after the version line and before the holds lines sized it at 1101.
688 Upgrading the client end is the fix; there is nothing to do on the daemon. 692 Either way a new daemon holding roughly fourteen or more sessions with
693 32-character names reads `unreachable` on that wall until the wall is
694 upgraded. Upgrading the client end is the fix; there is nothing to do on the
695 daemon. (Say it by what the client was BUILT from, not by a version number:
696 this branch stamps 0.0.1-17 itself, and no v0.0.1-17 tag exists yet.)
689 697
690 Copy and paste work through the session: a mux drag and an application's own 698 Copy and paste work through the session: a mux drag and an application's own
691 OSC 52 write both reach your terminal's clipboard (including from a remote box over QUIC, 699 OSC 52 write both reach your terminal's clipboard (including from a remote box over QUIC,
docs/decisions.md
Old New
@@ -8012,14 +8012,37 @@ gets wrong, since a SEEDED pane alone on a terminal wall, refused, has
8012 `keeps_wall` false and so ends mux, when under this model a saved pane's 8012 `keeps_wall` false and so ends mux, when under this model a saved pane's
8013 refusal is a pane's and not the program's. 8013 refusal is a pane's and not the program's.
8014 8014
8015 **Cross-version caveat, daemon newer than client.** A client at v0.0.1-17 or 8015 **Cross-version caveat, daemon newer than client.** Stated by what the
8016 older sizes its `sessions_reply` buffer for the names alone (1101 bytes) and 8016 client was BUILT from, because no version number says it: this branch stamps
8017 reads a longer reply as a transport error. A NEW daemon holding roughly 8017 0.0.1-17 itself and no v0.0.1-17 tag exists. The released v0.0.1-16 client
8018 fourteen or more sessions with 32-character names therefore reads 8018 sizes its `sessions_reply` buffer at `sessions_text_max` — the names alone,
8019 `unreachable` on such a wall until that wall is upgraded. `sessions_reply_max` 8019 1056 bytes; a development build off main taken after the `# mux VERSION`
8020 is now names + one holds line per session + the meta line for exactly this 8020 meta line and before the holds lines sizes it at 1101, names plus that one
8021 reason: a receiver sized to the names alone reads a full daemon that states 8021 line. Both read a longer reply as a transport error, so a NEW daemon holding
8022 its version as an unreachable box. 8022 roughly fourteen or more sessions with 32-character names reads
8023 `unreachable` on such a wall until the wall is upgraded (the payload runs
8024 about 75 bytes per session once names, holds and the meta line are counted).
8025 `sessions_reply_max` is now names + one holds line per session + the meta
8026 line for exactly this reason: a receiver sized to the names alone reads a
8027 full daemon that states its version as an unreachable box.
8028
8029 **A `--via` wall records nothing, found by this change's own review.**
8030 Writing the docs turned up a live regression the nine implementation tasks
8031 had not: `mux --via CMD` on a terminal took the entry-pane road like any
8032 other target, so the start-up `persist` wrote the leaf `tileLabel` spells
8033 for it — `--via CMD#NAME` — and the NEXT start's `seedLayout` found no
8034 hosts-file row for `--via CMD` and refused the WHOLE file. One throwaway
8035 `--via` run cost the user every pane they had authored. Reproduced on a real
8036 pty against a state dir of its own (the layout path is opened only when
8037 stdin is a terminal, so the suite's piped `--via` leg could never have seen
8038 it). Closed in the family of the trimmed-seed rule rather than by teaching
8039 the loader about `--via`: `run` nulls `Shared.layout_path` when the entry
8040 target is `.via`, before the seed and before the start-up save, and says
8041 `[layout not saved: a --via wall is not recorded]`. So a `--via` wall
8042 neither reads the file nor writes one, which is the same thing it already
8043 did with the hosts file. Pinned twice — a unit test that a via-spelled leaf
8044 refuses the whole file, and an assertion inside the boot group's `--via`
8045 leg that a pty run under a fresh state dir leaves no layout behind.
8023 8046
8024 **The pin.** The e2e suite grades 110 scenario checkpoints and 38 8047 **The pin.** The e2e suite grades 110 scenario checkpoints and 38
8025 convergence points, up from what the old model needed, and the leg that 8048 convergence points, up from what the old model needed, and the leg that