a73x

020d91c1

docs: n and p in the key table, and why a lone session refuses to step

a73x   2026-08-19 17:17

Commit message
docs: n and p in the key table, and why a lone session refuses to step

The no-op is the part a reader would otherwise call a bug: with one
session the chord does nothing, on purpose. Because a switch is an
attach, stepping to yourself costs a torn-down terminal and a
re-dialled slot and pays back the same grid.

README.md
Old New
@@ -38,6 +38,7 @@ Inside a session:
38 | `Ctrl-\` | command prefix — press it, then a command key | 38 | `Ctrl-\` | command prefix — press it, then a command key |
39 | `Ctrl-\` `d` (or `Ctrl-\` `Ctrl-\`) | detach (session keeps running) | 39 | `Ctrl-\` `d` (or `Ctrl-\` `Ctrl-\`) | detach (session keeps running) |
40 | `Ctrl-\` `c` | create a session and switch to it (the old one keeps running) | 40 | `Ctrl-\` `c` | create a session and switch to it (the old one keeps running) |
41 | `Ctrl-\` `n` / `Ctrl-\` `p` | switch to the next / previous session, wrapping |
41 | `Shift+PageUp` / `Shift+PageDown` | scrollback (any other key returns to live) | 42 | `Shift+PageUp` / `Shift+PageDown` | scrollback (any other key returns to live) |
42 43
43 `mux` again reattaches — full TUI screens included. Kill the client with 44 `mux` again reattaches — full TUI screens included. Kill the client with
@@ -45,8 +46,10 @@ Inside a session:
45 46
46 One daemon hosts up to four named sessions; the first is called `0`. 47 One daemon hosts up to four named sessions; the first is called `0`.
47 `Ctrl-\` `c` creates one named after the lowest free number and switches 48 `Ctrl-\` `c` creates one named after the lowest free number and switches
48 to it, and `mux --session NAME` attaches to any of them by name (creating 49 to it, `Ctrl-\` `n` and `Ctrl-\` `p` step through them in the daemon's
49 it if there is room). 50 order and wrap at the ends, and `mux --session NAME` attaches to any of
51 them by name (creating it if there is room). With only one session open
52 `n` and `p` do nothing — there is nowhere to step to.
50 53
51 ## Quick start, remote over ssh 54 ## Quick start, remote over ssh
52 55
docs/decisions.md
Old New
@@ -3936,3 +3936,26 @@ refused attach. That used to be fatal, and on a first attach it still is; a
3936 switch's arrival has somewhere to go instead, so it reports `.refused` and 3936 switch's arrival has somewhere to go instead, so it reports `.refused` and
3937 `attach()` returns to the session the chord was typed in with one line of 3937 `attach()` returns to the session the chord was typed in with one line of
3938 explanation. 3938 explanation.
3939
3940 **A step with nowhere to go stays put.** `Ctrl-\ n` / `Ctrl-\ p` walk the same
3941 `sessions_reply` list `Ctrl-\ c` reads, stepping one place from where the client
3942 stands and wrapping at both ends; slot order is a ring precisely because the
3943 daemon reports it the same way every time, so `n` then `p` returns you to where
3944 you started. When the step has no neighbour to land on, the chord does nothing
3945 rather than switching to self. A switch is an attach (above), so a switch to the
3946 session already on screen would tear the terminal down, re-dial, and paint the
3947 same grid back — a visible flicker bought with a lost scroll position and a
3948 freed-then-reclaimed daemon slot, in exchange for no change at all. Two cases
3949 reach it: a one-session daemon, and a current name the reply does not carry,
3950 which means the session died between the request and the reply — there being no
3951 position to step from, any neighbour would be a guess, and a guess here moves
3952 the user somewhere they did not ask to go. The picker returns null for both and
3953 the caller reads null as "stay".
3954
3955 The three switching chords share one `SwitchIntent` field rather than a flag
3956 each. A client can have exactly one question outstanding on the wire, and an
3957 enum makes that a fact the reply arm can rely on instead of a rule every future
3958 chord has to remember; the arm picks a name by intent and everything after the
3959 pick — write `.detach`, return `.switch_to` — is one path. The intent is spent
3960 on arrival whichever name it picks, which is what keeps an unasked-for reply
3961 harmless.