7ec78a84
docs: the local/remote split of a restored wall line
a73x 2026-08-25 19:32
Commit message
CLAUDE.md
| Old | New | ||
|---|---|---|---|
| @@ -91,7 +91,11 @@ real pty), `wsclient` (browser stand-in), `rawmode`, `delaypipe`, `render`. | |||
| 91 | `client.recordOnState` fires when the daemon's first state arrives and never | 91 | `client.recordOnState` fires when the daemon's first state arrives and never |
| 92 | on a successful dial, so a refused attach cannot strand a line — which is why | 92 | on a successful dial, so a refused attach cannot strand a line — which is why |
| 93 | there is no `unrecordTile`. `--via` and `muxa` record nothing; `mux wall`'s | 93 | there is no `unrecordTile`. `--via` and `muxa` record nothing; `mux wall`'s |
| 94 | argv is a view, not an attach. | 94 | argv is a view, not an attach. A RESTORED line attaches-or-creates when it |
| 95 | names the local socket and joins only when it is remote | ||
| 96 | (`wallview.hydratedCreates`): a daemon restart must not leave the user's own | ||
| 97 | wall a grid of `[refused]`, and a saved file must not spawn a shell on | ||
| 98 | another host. | ||
| 95 | - **The layout sidecar is derived convenience, not authored intent.** | 99 | - **The layout sidecar is derived convenience, not authored intent.** |
| 96 | `$XDG_STATE_HOME/mux/layout` stores the pane tree on last detach, hydrated | 100 | `$XDG_STATE_HOME/mux/layout` stores the pane tree on last detach, hydrated |
| 97 | walls only (no-argv `mux wall` or post-fold; argv walls and `mux TARGET` | 101 | walls only (no-argv `mux wall` or post-fold; argv walls and `mux TARGET` |
README.md
| Old | New | ||
|---|---|---|---|
| @@ -309,6 +309,16 @@ That file is what `mux wall` opens on, and what `Ctrl-\` `w` folds in | |||
| 309 | one-shot — the saved wall with the tile you are standing on among them. | 309 | one-shot — the saved wall with the tile you are standing on among them. |
| 310 | There is one wall, and no way to end up looking at a second one by accident. | 310 | There is one wall, and no way to end up looking at a second one by accident. |
| 311 | 311 | ||
| 312 | A daemon restart takes every session the file names with it. A line on a | ||
| 313 | local socket (`--sock PATH#NAME`) comes back as a fresh session rather than | ||
| 314 | `[refused]`: recreating a shell on your own box is cheap, and a wall you have | ||
| 315 | to forget line by line after every reboot is not what attach history is for. | ||
| 316 | A remote line (`HOST#NAME`, `quic://HOST#NAME`) is joined only, and a refused | ||
| 317 | tile stays refused for as long as the wall is up: it says `[refused]` until you | ||
| 318 | reopen the wall with that session already there — a saved file must not spawn | ||
| 319 | shells on a machine you are not looking at. Spellings on `mux wall`'s own command line | ||
| 320 | are a view, so they join and never create. | ||
| 321 | |||
| 312 | `mux wall add`/`rm` are the same edit without an attach: file operations | 322 | `mux wall add`/`rm` are the same edit without an attach: file operations |
| 313 | only, so neither dials, and `rm` never touches the session. A wall or hub | 323 | only, so neither dials, and `rm` never touches the session. A wall or hub |
| 314 | that is already running does not notice the change until you reopen it. | 324 | that is already running does not notice the change until you reopen it. |
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -6714,3 +6714,44 @@ client only when its attach frame arrives, so simultaneous dials contend for | |||
| 6714 | `max_observers` (4), not `max_clients` (8) — 7 at once lost 11 of 31 fills to a | 6714 | `max_observers` (4), not `max_clients` (8) — 7 at once lost 11 of 31 fills to a |
| 6715 | closed connection, 3 at once lost none. `muxd stop` on a full table of `/bin/sh` | 6715 | closed connection, 3 at once lost none. `muxd stop` on a full table of `/bin/sh` |
| 6716 | took 51ms, which is the grace-cost claim above, measured. | 6716 | took 51ms, which is the grace-cost claim above, measured. |
| 6717 | |||
| 6718 | ## 2026-08-25 — a saved local line attaches-or-creates | ||
| 6719 | |||
| 6720 | `mux wall` with no argv reads `$XDG_STATE_HOME/mux/wall` and puts up a tile per | ||
| 6721 | line. Until now every one of those tiles attached at 0×0, which the daemon reads | ||
| 6722 | as JOIN — so the wall could only ever show sessions that already existed. The | ||
| 6723 | daemon that held them dies on every reboot, and the wall that came back was a | ||
| 6724 | grid of `[refused]` stripes the user had to forget by hand, one `Ctrl-\ x` at a | ||
| 6725 | time. The marker fix (bf18c8d) is what made that visible rather than merely | ||
| 6726 | true: the focus marker now moves across tiles with no pump, so a wall of dead | ||
| 6727 | tiles is something you sit in and step around instead of something you leave. | ||
| 6728 | |||
| 6729 | Ruled: a wall-file line whose target is the LOCAL daemon (`--sock PATH[#NAME]`) | ||
| 6730 | attaches-or-creates; a remote line (`HOST`, `quic://…`) still joins only. | ||
| 6731 | Recreating a shell on your own box is cheap and expected — it is the workspace | ||
| 6732 | the file is a record of. Spawning one on another host out of a file the user | ||
| 6733 | last edited by attaching once is not: the saved line is history, not standing | ||
| 6734 | permission to run something over there. | ||
| 6735 | |||
| 6736 | One consequence worth stating, because nothing else says it: a wall tile | ||
| 6737 | redials for as long as the wall is up, and a redial carries the tile's | ||
| 6738 | `creates`. So a restored local tile whose daemon is restarted UNDER a running | ||
| 6739 | wall comes back as a fresh session on its next redial rather than landing | ||
| 6740 | `[refused]` — the same ruling, one step further along. It does not auto-start a | ||
| 6741 | daemon: a pump only dials, and auto-start lives in `mux TARGET`'s path alone, so | ||
| 6742 | a saved line whose socket is gone still sits reconnecting exactly as before. | ||
| 6743 | |||
| 6744 | Two exceptions stay as they were. `mux wall SPELLING...` is a view rather than | ||
| 6745 | an attach, so an argv wall joins only whatever it names — the same reason argv | ||
| 6746 | walls record nothing and save no layout sidecar. And the `Ctrl-\ w` fold reads | ||
| 6747 | the same file through the same rule, because a fold IS the saved wall arriving | ||
| 6748 | late. | ||
| 6749 | |||
| 6750 | The wire needed nothing. Create-vs-join was already encoded in the attach's size | ||
| 6751 | claim (`server.resolveSession`), so the change is one predicate on the client | ||
| 6752 | side — `wallview.hydratedCreates`, used by the fold row of the birth table and | ||
| 6753 | by the startup tile loop — deciding whether `sendAttach` puts the tile's rect or | ||
| 6754 | 0×0 on the frame. A tile that flips to creating and finds the session ALREADY | ||
| 6755 | there is unaffected either way: it joins, and its rect arrives in the attach | ||
| 6756 | instead of in the resize doorbell one frame later. Nothing about a cross-version | ||
| 6757 | attach changes: an old daemon reads the same size claim. | ||
src/server.zig
| Old | New | ||
|---|---|---|---|
| @@ -967,7 +967,7 @@ pub const Server = struct { | |||
| 967 | 967 | ||
| 968 | /// Attach-or-create. Creation demands a size the session can live at, the | 968 | /// Attach-or-create. Creation demands a size the session can live at, the |
| 969 | /// SAME threshold `applySize` enforces. A 0x0 attach makes no size claim | 969 | /// SAME threshold `applySize` enforces. A 0x0 attach makes no size claim |
| 970 | /// at all — muxa, the wall's stripes, an unzoomed browser tile — and a | 970 | /// at all — muxa, the wall's view stripes, an unzoomed browser tile — and a |
| 971 | /// client with no size must never be the reason a shell spawns. | 971 | /// client with no size must never be the reason a shell spawns. |
| 972 | /// | 972 | /// |
| 973 | /// 1x1 is a size a client genuinely sends, so gating on merely nonzero | 973 | /// 1x1 is a size a client genuinely sends, so gating on merely nonzero |