a73x

7ab01fe3

docs: correct the claims this change made stale

a73x   2026-08-28 19:53

Commit message
docs: correct the claims this change made stale

README.md
Old New
@@ -594,6 +594,8 @@ typing stops waiting for the round trip, and the two numbers come from one
594 keystroke on one connection so the gap is not an artefact of comparing 594 keystroke on one connection so the gap is not an artefact of comparing
595 runs. Auto-start is here for local unix-socket attaches — `mux` and `mux 595 runs. Auto-start is here for local unix-socket attaches — `mux` and `mux
596 --sock PATH` start a daemon when nothing is serving that path — and the wall 596 --sock PATH` start a daemon when nothing is serving that path — and the wall
597 starts the one on the default socket for the same reason. Not yet here: 597 starts the one on the default socket for the same reason. A remote daemon
598 auto-start on a remote host, the browser hub on the `hosts` file, reconnect 598 is started only by the dial a user ASKED for — `mux HOST`, a picker Enter —
599 backoff tuning, prediction for backspace and for multi-byte input. 599 and never by a dial nobody asked for. Not yet here: the browser hub on the
600 `hosts` file, reconnect backoff tuning, prediction for backspace and for
601 multi-byte input.
docs/decisions.md
Old New
@@ -7360,19 +7360,18 @@ fd pair — and `live` becomes the high-water mark, growth counted in `present`.
7360 7360
7361 ## 2026-08-28 — a daemon starts when somebody asks, never from a read 7361 ## 2026-08-28 — a daemon starts when somebody asks, never from a read
7362 7362
7363 `muxd endpoint` auto-started a daemon before answering, and the wall runs it 7363 `muxd endpoint` auto-started one before answering, and the wall runs it over
7364 over ssh once a second per listed host: a listed box with nothing on it got a 7364 ssh once a second per host: a listed box got a daemon and a shell in session
7365 daemon and a shell in session 0 from a READ, and a `muxd stop` typed there was 7365 0 from a READ, and a `muxd stop` typed there was undone a second later.
7366 undone a second later. `muxd proxy` had the same call. 7366 `muxd proxy` had the same call. Both now refuse an empty box.
7367 7367
7368 Both now refuse an empty box. The ASK moves to the client, on the dial a user 7368 The ASK moved to the client — `mux HOST`'s entry, the picker's Enter — which
7369 waited for — `mux HOST`'s entry, the picker's Enter — which runs `ssh HOST 7369 runs `ssh HOST 'muxd start'` from the same `handoff.recipeFor`, only when the
7370 'muxd start'` from the same `handoff.recipeFor` and reads the announce once 7370 remote exited 1 (ssh's own 255 is not an empty box), and reads the announce
7371 more. `HandoffTarget.asked` carries that permission and gates the fallback 7371 ONCE more. `HandoffTarget.report_fallback` became `asked` and now gates the
7372 line with it; `wallview.dial` spends it on the first attempt, so a start per 7372 start as well as that line; `wallview.pumpTile` spends it on the dial that got
7373 backoff is unreachable. The local client's own `spawn.ensureForAttach` is 7373 the link, so no reconnect can restart a box someone stopped. The local
7374 unchanged. 7374 `spawn.ensureForAttach` is unchanged.
7375 7375
7376 The break, deliberate: an OLD client against a new remote no longer gets a 7376 The break: an OLD client on a new remote gets no daemon from a cold `mux
7377 daemon from a cold `mux HOST` — the user runs `ssh HOST 'muxd start'`, as 7377 HOST` — `ssh HOST 'muxd start'`, as README said. No xversion probe covers it.
7378 README already said. `test/xversion.sh` has no cold-HOST probe.
src/cli/main.zig
Old New
@@ -942,7 +942,13 @@ fn logHint(alloc: std.mem.Allocator, buf: []u8) []const u8 {
942 942
943 /// `muxd proxy` with a one-line preamble: ensure a key, ask the daemon for 943 /// `muxd proxy` with a one-line preamble: ensure a key, ask the daemon for
944 /// its QUIC port, print `endpoint <port> <hex-key>` (or `endpoint none`) as 944 /// its QUIC port, print `endpoint <port> <hex-key>` (or `endpoint none`) as
945 /// the FIRST bytes on stdout, then become exactly the proxy byte pump. 945 /// the FIRST bytes on `out_fd`, then become exactly the proxy byte pump.
946 ///
947 /// `out_fd` is a parameter so a test can read the announce back instead of
948 /// writing over the runner's stdout, and every production caller passes
949 /// STDOUT_FILENO — which is what makes "the announce, then the frames, on
950 /// one stream" true, since `proxy.run` below writes the pump to the real
951 /// stdout and knows nothing of this argument.
946 /// This is what `mux HOST` runs over ssh — and what its wall polls, which 952 /// This is what `mux HOST` runs over ssh — and what its wall polls, which
947 /// is why no daemon means exit 1 and not a daemon. 953 /// is why no daemon means exit 1 and not a daemon.
948 /// 954 ///
src/cli/mux_main.zig
Old New
@@ -398,10 +398,11 @@ fn defaultSock(alloc: std.mem.Allocator) !?[]const u8 {
398 }; 398 };
399 } 399 }
400 400
401 /// Auto-start: give a local attach a daemon to land on. False when it 401 /// Auto-start: give a local attach a daemon to land on, and the ONLY
402 /// cannot — the line saying why is already printed. Unix-socket transport 402 /// auto-start left. False when it cannot — the line saying why is already
403 /// only: quic:// has nothing local to spawn, and `--via`'s auto-starter is 403 /// printed. Unix-socket transport only: quic:// has nothing local to spawn,
404 /// the remote proxy. 404 /// and `--via` starts nothing at all, because the remote proxy reads a box
405 /// (README's `ssh HOST 'muxd start'` is the answer there).
405 fn ensureLocalDaemon(alloc: std.mem.Allocator, sock_path: []const u8) !bool { 406 fn ensureLocalDaemon(alloc: std.mem.Allocator, sock_path: []const u8) !bool {
406 const muxd_path = try spawn.findInPath( 407 const muxd_path = try spawn.findInPath(
407 alloc, 408 alloc,
src/client.zig
Old New
@@ -434,7 +434,7 @@ pub const Transport = struct {
434 error.UserAbort => return err, 434 error.UserAbort => return err,
435 else => { 435 else => {
436 // The one line the fallback is allowed, and only on the 436 // The one line the fallback is allowed, and only on the
437 // attach that made the choice — see `report_fallback`. It 437 // attach that made the choice — see `asked`. It
438 // names what was tried and what is happening instead, and 438 // names what was tried and what is happening instead, and
439 // no cause: the client cannot tell a blocked port from a 439 // no cause: the client cannot tell a blocked port from a
440 // wrong key (both are silence), and guessing would be worse 440 // wrong key (both are silence), and guessing would be worse
test/e2e_03_side.sh
Old New
@@ -83,7 +83,8 @@ grep -q 'starting' "$OUT.as.err" && {
83 cat "$OUT.as.err"; exit 1; } 83 cat "$OUT.as.err"; exit 1; }
84 assert_converged "$OUT.as" "$SOCK14" "attach over proxy" 84 assert_converged "$OUT.as" "$SOCK14" "attach over proxy"
85 85
86 # Silence is the fast path: a warm attach must print NO spawn progress. 86 # Silence is now BOTH paths: nothing on this road starts a daemon, so no
87 # attach to it may print spawn progress.
87 pipe_mux "$OUT.as2" "$OUT.as2.err" env SHELL=/bin/sh timeout 30 "$MUX" --via "$MUXD proxy --sock $SOCK14" 88 pipe_mux "$OUT.as2" "$OUT.as2.err" env SHELL=/bin/sh timeout 30 "$MUX" --via "$MUXD proxy --sock $SOCK14"
88 pipe_send 'printf "auto-%%s\\n" again\n' 89 pipe_send 'printf "auto-%%s\\n" again\n'
89 await_out "$OUT.as2" "auto-again" "warm attach: marker never reached the client" 90 await_out "$OUT.as2" "auto-again" "warm attach: marker never reached the client"
@@ -99,7 +100,7 @@ grep -q 'starting' "$OUT.as2.err" && {
99 kill -0 "$APID" || { 100 kill -0 "$APID" || {
100 echo "e2e FAIL: warm attach replaced the daemon (pid $APID is gone)" 101 echo "e2e FAIL: warm attach replaced the daemon (pid $APID is gone)"
101 cat "$OUT.as2.err"; exit 1; } 102 cat "$OUT.as2.err"; exit 1; }
102 assert_converged "$OUT.as2" "$SOCK14" "auto-start warm attach" 103 assert_converged "$OUT.as2" "$SOCK14" "second attach over proxy"
103 104
104 # The verb under test is the teardown: exit 0, stopped line, socket gone, 105 # The verb under test is the teardown: exit 0, stopped line, socket gone,
105 # and the daemon OBSERVED dead by pid — never the command's claim alone. 106 # and the daemon OBSERVED dead by pid — never the command's claim alone.
test/e2e_04_handoff.sh
Old New
@@ -149,7 +149,7 @@ grep -q '^muxd: starting' "$OUT.h1.err" || {
149 HAPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.h1.err" | head -1) 149 HAPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.h1.err" | head -1)
150 defer_kill "$HAPID" 150 defer_kill "$HAPID"
151 [ -n "$HAPID" ] || { 151 [ -n "$HAPID" ] || {
152 echo "e2e FAIL: endpoint up-line carries no pid"; cat "$OUT.h1.err"; exit 1; } 152 echo "e2e FAIL: muxd start's up-line carries no pid"; cat "$OUT.h1.err"; exit 1; }
153 kill -0 "$HAPID" 2>/dev/null || { 153 kill -0 "$HAPID" 2>/dev/null || {
154 echo "e2e FAIL: the handoff daemon (pid $HAPID) is not alive"; exit 1; } 154 echo "e2e FAIL: the handoff daemon (pid $HAPID) is not alive"; exit 1; }
155 155