31a7bd88
docs: correct the version caveat, the --via rule and the picker's close key
a73x 2026-09-03 05:20
Commit message
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 |