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
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. | ||