a73x

test/e2e_09_hosts.sh

Ref:   Size: 83.1 KiB   History

# shellcheck shell=sh
# e2e_09_hosts.sh — sourced by test/e2e.sh after e2e_lib.sh. Scenarios run in
# the order they stand in; see the lib's header for what this file may
# assume and what it must register.
#
# The wall is a LAYOUT over a list of daemons: two daemons here, because
# a pane names a host and a session and one daemon cannot show which of the
# two a pane belongs to. Both daemons have a session named `0` for exactly
# that reason — a wall that keyed panes on the session name alone would
# show one of them twice. Every leg that wants panes writes them down
# (`seed_layout`): a poll adds none.
#
# A state home of its own, and one per leg that reads the file back: every
# `mux` in this suite now records the DAEMON it attached to, so a hosts
# file read out of the shared $XDG_STATE_HOME would be every other group's
# attaches as much as this one's.
# /tmp and not $TMPDIR, which this group and the dead-click leg in
# e2e_08_mouse.sh are the only places to say. Six assertions below grep one
# of these paths out of a tile's LABEL BAR, and a bar is as wide as the
# terminal: `labelText` keeps the state word and cuts the label's TAIL,
# which is the half carrying the `#SESSION` and the daemon's name. macOS
# puts $TMPDIR in a per-user directory whose name alone was 48 characters
# on the box this was measured on, so an 80-column bar had none of that
# left in it. /tmp is short on both
# OSes, and on Linux it is where a $TMPDIR-less run already puts them.
SOCKH1="/tmp/muxd-e2e-hosts1-$$.sock"
defer_sock "$SOCKH1"
SOCKH2="/tmp/muxd-e2e-hosts2-$$.sock"
defer_sock "$SOCKH2"
HSTATE="${TMPDIR:-/tmp}/mux-e2e-hosts-state-$$"
defer_rm "$HSTATE"
# The auto-start leg: a state home whose hosts file NAMES the local daemon,
# and a runtime dir of its own so `--sock <default>` resolves to a path
# nothing in this suite — or on this developer's machine — is already
# listening on. Without the second, the leg would start (or worse, adopt)
# the daemon the user is sitting in.
HASTATE="${TMPDIR:-/tmp}/mux-e2e-hosts-auto-$$"
defer_rm "$HASTATE"
HARUN="${TMPDIR:-/tmp}/mux-e2e-hosts-run-$$"
defer_rm "$HARUN"
HASOCK="$HARUN/muxd.sock"
defer_sock "$HASOCK"
# The empty-file leg: a state home with NO hosts file at all — a machine
# where `mux` has never run — and, like the auto-start leg, a runtime dir of
# its own so the daemon it starts is this leg's and not the developer's.
HESTATE="${TMPDIR:-/tmp}/mux-e2e-hosts-empty-$$"
defer_rm "$HESTATE"
HERUN="${TMPDIR:-/tmp}/mux-e2e-hosts-empty-run-$$"
defer_rm "$HERUN"
HESOCK="$HERUN/muxd.sock"
defer_sock "$HESOCK"
# The `x` leg builds and eats its own daemon's session, so it reads a wall
# of ONE host and gets a state home nothing else writes.
HXSTATE="${TMPDIR:-/tmp}/mux-e2e-hosts-x-state-$$"
defer_rm "$HXSTATE"

start_daemon "$SOCKH1" "$OUT.h1.d" "hosts daemon 1 never bound" --shell /bin/sh
DH1PID=$DPID
start_daemon "$SOCKH2" "$OUT.h2.d" "hosts daemon 2 never bound" --shell /bin/sh
DH2PID=$DPID

# hosts_where NEEDLE — which of the five sessions these two daemons hold
# over the group's life holds NEEDLE, one `SOCK:NAME` per line. A pane's
# content is read back from the DAEMON that owns it, never from the
# terminal: a marker on screen says a stripe painted, and what these legs
# claim is which SESSION a keystroke reached. Sessions that do not exist
# at the moment of the call simply do not answer.
hosts_where() {
    for _hw in "$SOCKH1:0" "$SOCKH1:b" "$SOCKH1:d" "$SOCKH2:0" "$SOCKH2:c"; do
        if dump_session "${_hw%%:*}" "${_hw##*:}" 2>/dev/null | grep -q "$1"; then
            printf '%s\n' "$_hw"
        fi
    done
}

# ---- the wall records DAEMONS, and starts a local one that is not there --
#
# Two halves of the same rule, and the file is the witness for both.
#
#   * `mux --sock PATH` records the line `--sock PATH` — the DAEMON — once
#     its dial has come up. Never `#0`: a wall line names nothing that can
#     be resurrected, so a `#` anywhere in this file is the regression.
#   * a line naming the LOCAL daemon is auto-started when nothing answers
#     on it. That line outlives every reboot while the daemon does not, and
#     a wall that paints the user's own machine `[unreachable]` until they
#     find some other shell to start a daemon in is the empty-file case
#     with one line in front of it.
#
# The auto-start half is asked of the OS, not of the wall: the marker is
# the SHELL's own `$$`, and `mux d stats` on the default path is a second
# process finding a daemon that was not there a moment ago.
pipe_mux "$OUT.hw1" "$OUT.hw1.err" env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" --sock "$SOCKH1"
pipe_send 'printf "hw1-%%s\\n" pin\n'
await_out "$OUT.hw1" "hw1-pin" "hw1-pin never reached the client"
pipe_detach "hosts client 1"
wait_grid "$SOCKH1" "hw1-pin" "hosts: the default session's marker"
grep -qxF -- "--sock $SOCKH1" "$HSTATE/mux/hosts" || {
    echo "e2e FAIL: hosts: the attach recorded no daemon line:"
    cat "$HSTATE/mux/hosts" 2>&1; exit 1; }
if grep -q '#' "$HSTATE/mux/hosts"; then
    echo "e2e FAIL: hosts: a session leaked into the hosts file:"
    cat "$HSTATE/mux/hosts"; exit 1
fi

# The listed-local half. The file is written by hand rather than by an
# attach, because what is under test is a line whose daemon is GONE — the
# state every hosts file is in after a reboot. A layout beside it naming
# that daemon's own session, because the wall is the layout: without a
# leaf the wall comes up empty and the keystrokes below would go to the
# picker the emptiness opens instead of to a shell.
mkdir -p "$HASTATE/mux" "$HARUN"
printf -- '--sock %s\n' "$HASOCK" > "$HASTATE/mux/hosts"
seed_layout "$HASTATE" stacked "--sock $HASOCK#0"
[ ! -S "$HASOCK" ] || { echo "e2e FAIL: hosts: the auto-start path is already served"; exit 1; }
set +e
# PATH so the ssh recipes resolve exactly the binary under test, and
# XDG_RUNTIME_DIR so `--sock <default>` is this leg's own path; the wall
# needs a terminal, so this is the pty fixture and not a pipe.
SHELL=/bin/sh PATH="$(dirname "$MUX"):$PATH" XDG_RUNTIME_DIR="$HARUN" \
    XDG_STATE_HOME="$HASTATE" timeout 60 "$PTYCLIENT" --cols 80 --rows 24 \
    --out "$OUT.hauto" --err "$OUT.hauto.err" -- "$MUX" > "$OUT.hauto.log" 2>&1 <<'EOF'
expect \x1b[?1049h 20000
settle 600 20000
send echo hapid=$$\n
expect hapid= 20000
settle 600 20000
send \x1cd
waitexit 10000
EOF
RC=$?
set -e
rc0 "hosts: the auto-start leg exited $RC (did the listed local daemon start?):" "$OUT.hauto.log" "$OUT.hauto.err"
HAPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.hauto.err" | head -1)
defer_kill "$HAPID"
[ -n "$HAPID" ] || {
    echo "e2e FAIL: hosts: the auto-start printed no pid to hold onto:"
    cat "$OUT.hauto.err"; exit 1; }
# The daemon exists and holds the shell that answered — asked of the daemon
# by a process that had nothing to do with starting it.
HASH_PID=$("$MUX" d dump --sock "$HASOCK" | sed -n 's/.*hapid=\([0-9]*\).*/\1/p' | tail -1)
[ -n "$HASH_PID" ] || {
    echo "e2e FAIL: hosts: the auto-started daemon holds no shell pid:"
    "$MUX" d dump --sock "$HASOCK"; exit 1; }
# ...and the pid on the grid is a process the OS agrees exists, in the
# daemon's own tree. A daemon reporting on itself cannot catch itself
# being wrong.
[ "$(ps -o ppid= -p "$HASH_PID" 2>/dev/null | tr -d ' ')" = "$(real_pid "$HAPID")" ] || {
    echo "e2e FAIL: hosts: the shell on the grid is not the auto-started daemon's child"
    ps -o pid,ppid,args -p "$HASH_PID" 2>&1; exit 1; }
# ...and the wall wrote its panes back: one leaf, the session that pane
# held, named exactly once. The wall the user gets back is the wall they
# left, and a save that appended a second copy of a leaf would be a file
# `seedLayout` refuses whole on the next start.
[ "$(grep -c -- "--sock $HASOCK#0\$" "$HASTATE/mux/layout")" = "1" ] || {
    echo "e2e FAIL: hosts: the auto-start wall did not save its one pane exactly once:"
    cat "$HASTATE/mux/layout"; exit 1; }
assert_stopped "$HASOCK" "$HAPID" "hosts auto-start" "$OUT.hastop"
HAPID=""
ok "mux records the daemon on the wall, never a session, and starts a listed local one that is gone"

# ---- the empty file: the first `mux` on a machine writes the wall --------
#
# The other end of the auto-start rule, and the only leg where the file
# does not exist when `mux` starts. Bare `mux` with nothing listed is the
# local daemon: it starts one, attaches, and the attach writes the line
# that makes every later `mux` a wall. Nothing here pre-writes anything —
# a leg that seeds the file cannot see a `recordHost` that never ran.
mkdir -p "$HERUN"
[ ! -e "$HESTATE" ] || { echo "e2e FAIL: hosts: the empty-file leg's state home already exists"; exit 1; }
[ ! -S "$HESOCK" ] || { echo "e2e FAIL: hosts: the empty-file leg's socket is already served"; exit 1; }
set +e
SHELL=/bin/sh PATH="$(dirname "$MUX"):$PATH" XDG_RUNTIME_DIR="$HERUN" \
    XDG_STATE_HOME="$HESTATE" timeout 60 "$PTYCLIENT" --cols 80 --rows 24 \
    --out "$OUT.hempty" --err "$OUT.hempty.err" -- "$MUX" > "$OUT.hempty.log" 2>&1 <<'EOF'
expect \x1b[?1049h 20000
settle 600 20000
send echo hepid=$$\n
expect hepid= 20000
settle 600 20000
send \x1cd
waitexit 10000
EOF
RC=$?
set -e
rc0 "hosts: the empty-file leg exited $RC (did bare mux start a local daemon?):" "$OUT.hempty.log" "$OUT.hempty.err"
HEPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.hempty.err" | head -1)
defer_kill "$HEPID"
[ -n "$HEPID" ] || {
    echo "e2e FAIL: hosts: the empty-file leg printed no daemon pid to hold onto:"
    cat "$OUT.hempty.err"; exit 1; }
# The file the attach wrote: the DAEMON it reached, and nothing else. One
# line, because a wall of one is what a machine with no file has.
[ -f "$HESTATE/mux/hosts" ] || {
    echo "e2e FAIL: hosts: the first mux wrote no hosts file under $HESTATE"; exit 1; }
grep -qxF -- "--sock $HESOCK" "$HESTATE/mux/hosts" || {
    echo "e2e FAIL: hosts: the empty file did not gain the local daemon's line:"
    cat "$HESTATE/mux/hosts"; exit 1; }
# -eq and not =: BSD wc pads its count to a column width, so the string
# comparison read "       1" and failed on a file that was exactly right.
[ "$(wc -l < "$HESTATE/mux/hosts")" -eq 1 ] || {
    echo "e2e FAIL: hosts: the first mux wrote more than the daemon it attached to:"
    cat "$HESTATE/mux/hosts"; exit 1; }
# ...and the session behind that line is a real shell, asked of the OS: the
# pid the grid shows is a live process whose parent is the daemon `mux`
# started. A daemon reporting on itself cannot catch itself being wrong.
HESH_PID=$("$MUX" d dump --sock "$HESOCK" | sed -n 's/.*hepid=\([0-9]*\).*/\1/p' | tail -1)
[ -n "$HESH_PID" ] || {
    echo "e2e FAIL: hosts: the empty-file leg's grid shows no shell pid:"
    "$MUX" d dump --sock "$HESOCK"; exit 1; }
[ "$(ps -o ppid= -p "$HESH_PID" 2>/dev/null | tr -d ' ')" = "$(real_pid "$HEPID")" ] || {
    echo "e2e FAIL: hosts: the shell on the grid is not the auto-started daemon's child"
    ps -o pid,ppid,args -p "$HESH_PID" 2>&1; exit 1; }
# ...and the LAYOUT the entry pane's own save wrote: one leaf, the session
# the entry attach opened on — written when the pane was added and not at
# the exit. Two files, both written by a `mux` that was handed neither — the hosts file says which daemons, the layout says which panes,
# and a machine with no files at all ends up with a wall of one either way.
_he_leaves=$(sed -n 's/^ *leaf [0-9][0-9]* //p' "$HESTATE/mux/layout" | tr '\n' ' ')
[ "$_he_leaves" = "--sock $HESOCK#0 " ] || {
    echo "e2e FAIL: hosts: the first mux saved leaves '$_he_leaves',"
    echo "          want the one pane it attached to; the file holds:"
    cat "$HESTATE/mux/layout" 2>&1; exit 1; }
assert_stopped "$HESOCK" "$HEPID" "hosts empty file" "$OUT.hestop"
HEPID=""
ok "the first mux on a machine starts the local daemon and writes it down"

# ---- a wall shows its layout's panes and no more -------------------------
#
# The headline, and the model in one leg. Two daemons on the wall, four
# sessions between them, and a LAYOUT naming three of them — two on daemon
# 1 and one on daemon 2. On a real pty, because a stripe is a rect on a
# terminal and a pipe has none.
#
# Four claims:
#
#   * the three leaves are three bars, each naming the daemon it belongs
#     to. Both daemons have a session called `0`; a wall keyed on the
#     session name would paint two tiles and call it three.
#   * the fourth session — daemon 2's `c`, live on a listed, answering,
#     once-a-second-polled daemon — is NOWHERE. That is the whole change:
#     until 2026-09-02 a host's list was where tiles came from, so every
#     session on every listed box was on every wall over it.
#   * a session born BETWEEN the two walls is nowhere either. `c` covers
#     the live case — it is on daemon 2's list for every poll of run 1 —
#     and `d` covers the other order: born after run 1 detached, on a
#     daemon run 2 then polls for two and a half seconds, and still not a
#     pane. The poll grades panes; it does not add them, whenever the
#     session appeared.
#   * `Ctrl-\ n` walks exactly the three and wraps to the first. Where each
#     marker LANDED is read off the daemons, so a step that only moved a
#     `>` on screen fails here, and a fourth session quietly on the ring
#     would show as the wrap landing somewhere new.
env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" hosts add "--sock $SOCKH2" > "$OUT.hadd" 2>&1 || {
    echo "e2e FAIL: hosts: 'hosts add' refused the second daemon:"
    cat "$OUT.hadd"; exit 1; }
pipe_mux "$OUT.hb" "$OUT.hb.err" env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" --sock "$SOCKH1" --session b
pipe_send 'printf "hb-%%s\\n" pin; echo bshell=$$\n'
await_out "$OUT.hb" "hb-pin" "hb-pin never reached the client"
pipe_detach "hosts session b"
wait_grid "$SOCKH1" "hb-pin" "hosts: session b's marker" b
# The pid the shell printed of ITSELF, kept for the `hosts rm` leg: what
# "hosts rm ends nothing" claims is about a PROCESS, and only the OS can
# answer that.
wait_grid "$SOCKH1" "bshell=" "hosts: session b's shell pid" b
BSHELL=$(dump_session "$SOCKH1" b | sed -n 's/.*bshell=\([0-9]*\).*/\1/p' | tail -1)
[ -n "$BSHELL" ] || { echo "e2e FAIL: hosts: session b printed no shell pid"; exit 1; }
kill -0 "$BSHELL" 2>/dev/null || {
    echo "e2e FAIL: hosts: session b's shell pid $BSHELL is not a live process"; exit 1; }
pipe_mux "$OUT.hc" "$OUT.hc.err" env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" --sock "$SOCKH2" --session c
pipe_send 'printf "hc-%%s\\n" pin\n'
await_out "$OUT.hc" "hc-pin" "hc-pin never reached the client"
pipe_detach "hosts session c"
wait_grid "$SOCKH2" "hc-pin" "hosts: session c's marker" c
wait_sessions "$SOCKH1" 2 "hosts: daemon 1 should hold 0 and b"
wait_sessions "$SOCKH2" 2 "hosts: daemon 2 should hold 0 and c"

# The wall: three of the four. Daemon 2's `c` is deliberately left out, so
# every later assertion about it is about a session the wall could reach
# and does not.
#
# Tall, and NOT wide: 80 columns is under the aspect rule's
# `cols >= 2 * rows`, so a bar gets the full width. Beside, three stripes
# would each be twenty-odd columns and `labelText` would spend them on the
# state word — the socket path these assertions grep for is the first thing
# a narrow bar drops.
seed_layout "$HSTATE" stacked \
    "--sock $SOCKH1#0" "--sock $SOCKH1#b" "--sock $SOCKH2#0"
HFHASH=$(sha256_of "$HSTATE/mux/hosts")
set +e
XDG_STATE_HOME="$HSTATE" timeout 90 "$PTYCLIENT" --cols 80 --rows 44 \
    --out "$OUT.hwcap" --err "$OUT.hwcap.err" -- "$MUX" > "$OUT.hwpc" 2>&1 <<'EOF'
expect hw1-pin 25000
settle 1500 25000
send printf 'walk-%s\\n' one\n
expect walk-one 20000
settle 800 20000
send \x1cn
expect 2> --sock 20000
settle 800 20000
send printf 'walk-%s\\n' two\n
expect walk-two 20000
settle 800 20000
send \x1cn
expect 3> --sock 20000
settle 800 20000
send printf 'walk-%s\\n' three\n
expect walk-three 20000
settle 800 20000
send \x1cn
expect 1> --sock 20000
settle 800 20000
send printf 'walk-%s\\n' four\n
expect walk-four 20000
settle 1500 25000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "hosts: the wall leg exited $RC (did the layout's leaves become panes?):" "$OUT.hwpc"
# Three bars, each naming its own daemon.
for _hl in "$SOCKH1#0" "$SOCKH1#b" "$SOCKH2#0"; do
    grep -q -- "--sock $_hl \[up\]" "$OUT.hwcap" || {
        echo "e2e FAIL: hosts: the leaf '--sock $_hl' never became a pane on the wall:"
        cat "$OUT.hwpc"; exit 1; }
done
# ...and the fourth session is nowhere: not a bar, not its own output. Its
# daemon answered every poll for the whole run — daemon 2's OTHER session
# is one of the three bars above, so "the host never reported" is not a
# way this can pass. `wc -l` on the matches and never `grep -c`, which
# counts LINES: a pty capture is escape bytes with almost no newlines.
[ "$(grep -aoF -- "$SOCKH2#c" "$OUT.hwcap" | wc -l)" -eq 0 ] || {
    echo "e2e FAIL: hosts: session c is on a wall whose layout never named it —"
    echo "          the poll is taking panes from the daemon's list again:"
    cat "$OUT.hwpc"; exit 1; }
[ "$(grep -aoF "hc-pin" "$OUT.hwcap" | wc -l)" -eq 0 ] || {
    echo "e2e FAIL: hosts: session c's own output painted on a wall with no pane for it:"
    cat "$OUT.hwpc"; exit 1; }
# The walk, read off the daemons. One holder each, three different
# sessions, and the fourth press back on the first: the ring is the
# LAYOUT's panes and has exactly three stops.
HW1=$(hosts_where walk-one)
HW2=$(hosts_where walk-two)
HW3=$(hosts_where walk-three)
HW4=$(hosts_where walk-four)
for _hp in "one:$HW1" "two:$HW2" "three:$HW3" "four:$HW4"; do
    [ "$(printf '%s\n' "${_hp#*:}" | grep -c .)" = "1" ] || {
        echo "e2e FAIL: hosts: 'walk-${_hp%%:*}' landed in [${_hp#*:}], want exactly one session"
        exit 1; }
done
[ "$HW1" != "$HW2" ] && [ "$HW2" != "$HW3" ] && [ "$HW1" != "$HW3" ] || {
    echo "e2e FAIL: hosts: Ctrl-\\ n did not walk three distinct panes: [$HW1] [$HW2] [$HW3]"
    exit 1; }
[ "$HW4" = "$HW1" ] || {
    echo "e2e FAIL: hosts: the fourth Ctrl-\\ n landed in [$HW4], not back on [$HW1] —"
    echo "          the ring has a stop the layout never named"; exit 1; }
for _hw in "$HW1" "$HW2" "$HW3"; do
    [ "$_hw" != "$SOCKH2:c" ] || {
        echo "e2e FAIL: hosts: a Ctrl-\\ n stop is session c, which no leaf names"; exit 1; }
done
# The hosts file did not move: a wall is opened FROM these two files and
# writes back only to the layout.
[ "$HFHASH" = "$(sha256_of "$HSTATE/mux/hosts")" ] || {
    echo "e2e FAIL: hosts: the wall edited the hosts file:"
    cat "$HSTATE/mux/hosts"; exit 1; }
# ...and the layout still holds the three leaves it was opened on. This run
# changed no pane and no weight, so it wrote nothing at all — the file is
# the one the leg seeded, and what is asserted is that a wall which opens a
# layout and walks it does not disturb it.
_hw_leaves=$(sed -n 's/^ *leaf [0-9][0-9]* //p' "$HSTATE/mux/layout" | tr '\n' ' ')
[ "$_hw_leaves" = "--sock $SOCKH1#0 --sock $SOCKH1#b --sock $SOCKH2#0 " ] || {
    echo "e2e FAIL: hosts: the layout holds leaves '$_hw_leaves', want the three it opened on:"
    cat "$HSTATE/mux/layout"; exit 1; }

# A session born ELSEWHERE, while nothing was looking. Its own state home,
# so the birth writes no line into the wall this leg reads.
pipe_mux "$OUT.hd" "$OUT.hd.err" env XDG_STATE_HOME="$HXSTATE" timeout 40 "$MUX" --sock "$SOCKH1" --session d
pipe_send 'printf "hd-%%s\\n" pin\n'
await_out "$OUT.hd" "hd-pin" "hd-pin never reached the client"
pipe_detach "hosts session d"
wait_grid "$SOCKH1" "hd-pin" "hosts: session d's marker" d
wait_sessions "$SOCKH1" 3 "hosts: daemon 1 should hold 0, b and d"

set +e
# A SECOND wall, opened after the birth: the file is the same three
# leaves, so the wall is the same three panes. Two and a half seconds of
# quiet is several poll answers on both daemons.
XDG_STATE_HOME="$HSTATE" timeout 90 "$PTYCLIENT" --cols 80 --rows 44 \
    --out "$OUT.hw2cap" --err "$OUT.hw2cap.err" -- "$MUX" > "$OUT.hw2pc" 2>&1 <<'EOF'
expect [up] 25000
settle 2500 30000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "hosts: the second wall exited $RC:" "$OUT.hw2pc"
for _hn in "$SOCKH1#d" "$SOCKH2#c"; do
    [ "$(grep -aoF -- "$_hn" "$OUT.hw2cap" | wc -l)" -eq 0 ] || {
        echo "e2e FAIL: hosts: '$_hn' is a session no leaf names and it is on the"
        echo "          next wall — a birth anywhere still reaches every wall:"
        cat "$OUT.hw2pc"; exit 1; }
done
"$RENDER" --cols 80 --rows 44 < "$OUT.hw2cap" > "$OUT.hw2grid" || {
    echo "e2e FAIL: hosts: render oracle failed"; cat "$OUT.hw2grid"; exit 1; }
[ "$(grep -c -- '--sock ' "$OUT.hw2grid")" = "3" ] || {
    echo "e2e FAIL: hosts: the second wall's final screen does not hold exactly"
    echo "          three bars:"; cat "$OUT.hw2grid"; exit 1; }

# ...and then session d goes, so the counts the legs below assert are the
# ones this file set up.
pipe_mux "$OUT.hkill" "$OUT.hkill.err" env XDG_STATE_HOME="$HXSTATE" timeout 40 \
    "$MUX" --sock "$SOCKH1" --session d
pipe_send 'exit 0\n'
pipe_waitexit "hosts: the session born elsewhere"
wait_sessions "$SOCKH1" 2 "hosts: daemon 1 back to 0 and b"
wait_sessions "$SOCKH2" 2 "hosts: daemon 2 back to 0 and c"
ok "a wall shows its layout's panes and no more; a session born elsewhere never appears"

# ---- the PICKER's x ends a session, and asks first -----------------------
#
# Ending a session is the picker's job now: `Ctrl-\ x` on the wall takes a
# pane off this terminal and leaves the shell running, so the one key that
# destroys something lives beside the count of who else is holding it.
# Two clients on one session, and the three facts that make the two-step a
# two-step rather than a delay:
#
#   * the first `x` is REFUSED, by count, and the shell is still there —
#     asked of the OS with `kill -0` on the pid the shell itself printed,
#     not of the daemon that would be reporting on its own refusal.
#
#     The count is TWO, and that is BY DESIGN rather than an artefact this
#     leg is working around (ruled 2026-09-02). `pickEnd` asks over a side
#     connection on the poller's recipe, and the daemon answers honestly
#     about every client holding the session other than the connection
#     asking — which includes this wall's own pane for `xb`, because that
#     pane IS a client of that session and losing it is a thing the user
#     is entitled to be warned about. So the pin is the number the user
#     reads, and the fixture is built so both holders are real and
#     distinguishable.
#
#     A kinder wording — auto-forcing when the only other holder is this
#     wall's own pane — is a deferred collab issue, not a bug. If that
#     lands, this pin moves with it; it is the ruling that is pinned here,
#     not an accident.
#   * the second `x` ends it. The shell is gone from the process table.
#   * the OTHER client, which typed nothing at all, comes back with the
#     session's exit status. A hangup is not a detach: it reaches everyone
#     attached.
#
# Two ptyclient runs rather than one, because `kill -0` between the two
# presses is this script's to do and ptyclient's script cannot stop for it.
# The three-second latch is per CLIENT, so run B has to press `x` twice.
#
# The row is chosen by digit and the assertion NAMES the session: rows are
# the daemon's own list order (`0`, `b`, `xb`), so row 3 is `xb` — and a
# list that came back in another order fails on the notice's first word
# rather than ending somebody else's shell.
pipe_mux "$OUT.hxmk" "$OUT.hxmk.err" env XDG_STATE_HOME="$HXSTATE" timeout 40 \
    "$MUX" --sock "$SOCKH1" --session xb
pipe_send 'echo xbpid=$$\n'
await_out "$OUT.hxmk" "xbpid=" "xbpid never reached the client"
pipe_detach "hosts x setup"
wait_grid "$SOCKH1" "xbpid=" "hosts x: session xb's shell pid" xb
XBPID=$(dump_session "$SOCKH1" xb | sed -n 's/.*xbpid=\([0-9]*\).*/\1/p' | tail -1)
[ -n "$XBPID" ] || { echo "e2e FAIL: hosts x: session xb printed no pid"; exit 1; }
XBEFORE=$("$MUX" d stats --sock "$SOCKH1" | sed -n 's/.*sessions=\([0-9]*\).*/\1/p')
[ -n "$XBEFORE" ] || {
    echo "e2e FAIL: hosts x: no sessions= in stats (the field moved?); it says:"
    "$MUX" d stats --sock "$SOCKH1"; exit 1; }

# The other client: attached, silent, and holding its stdin open so nothing
# but the session's own end can bring it back.
pipe_mux "$OUT.hxo" "$OUT.hxo.err" env XDG_STATE_HOME="$HXSTATE" timeout 90 \
    "$MUX" --sock "$SOCKH1" --session xb
await_out "$OUT.hxo" "xbpid=" "hosts x: the other client never got the grid"

set +e
XDG_STATE_HOME="$HXSTATE" timeout 60 "$PTYCLIENT" --cols 80 --rows 30 \
    --out "$OUT.hx1cap" --err "$OUT.hx1cap.err" -- \
    "$MUX" --sock "$SOCKH1" --session xb > "$OUT.hx1pc" 2>&1 <<'EOF'
expect xbpid= 20000
settle 800 20000
send \x1cs
expect x forget 15000
send 1
settle 400 15000
send \r
expect Enter add to wall 15000
send 3
settle 400 15000
send x
expect attached - x again to end 15000
settle 600 15000
send \x1b
settle 300 15000
send \x1b
settle 400 15000
send \x1cd
waitexit 10000
EOF
RC=$?
set -e
rc0 "hosts x: the refusal leg exited $RC (did the first x say who else was there?):" "$OUT.hx1pc"
grep -qF -- "[xb: 2 others attached - x again to end]" "$OUT.hx1cap" || {
    echo "e2e FAIL: hosts x: the refusal did not name the session and the count:"
    grep -ao '\[[^]]*other[^]]*\]' "$OUT.hx1cap" | head -3; exit 1; }
kill -0 "$XBPID" 2>/dev/null || {
    echo "e2e FAIL: hosts x: the FIRST x ended the shell despite another client"; exit 1; }

set +e
XDG_STATE_HOME="$HXSTATE" timeout 60 "$PTYCLIENT" --cols 80 --rows 30 \
    --out "$OUT.hx2cap" --err "$OUT.hx2cap.err" -- \
    "$MUX" --sock "$SOCKH1" --session xb > "$OUT.hx2pc" 2>&1 <<'EOF'
expect xbpid= 20000
settle 800 20000
send \x1cs
expect x forget 15000
send 1
settle 400 15000
send \r
expect Enter add to wall 15000
send 3
settle 400 15000
send x
# NAMED, unlike a bare "attached - x again to end": the second `x` below
# forces an end by ROW POSITION, so this is the only thing standing
# between a list that came back in another order and somebody else's
# shell. Run 1 makes the same claim in a grep after the fact; run 2 has
# to make it before it presses again.
expect [xb: 2 others attached - x again to end] 15000
settle 600 15000
send x
settle 2500 25000
send \x1b
settle 300 15000
send \x1b
settle 400 15000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "hosts x: the forcing leg exited $RC:" "$OUT.hx2pc"
wait_pid_gone "$XBPID" "hosts x: the shell after the second x"
# The other client was hung up on, with the session's status in hand. 128
# is what a shell killed by a signal reports (pty.zig checkExited), and it
# is the one number that says the daemon ended the session rather than the
# client noticing a closed socket.
pipe_waitexit "hosts x: the other client" 128
wait_sessions "$SOCKH1" 2 "hosts x: only xb ended"
# ...and the daemon's own count went down by exactly one.
XAFTER=$("$MUX" d stats --sock "$SOCKH1" | sed -n 's/.*sessions=\([0-9]*\).*/\1/p')
[ "$XAFTER" = "$(( XBEFORE - 1 ))" ] || {
    echo "e2e FAIL: hosts x: daemon 1 holds $XAFTER sessions, want $(( XBEFORE - 1 ))"
    "$MUX" d stats --sock "$SOCKH1"; exit 1; }
ok "the picker's x refuses while others hold the session, ends on the second press, and the other client sees the exit"

# ---- ...and on a WALL, x removes the pane and ends NOTHING ---------------
#
# The other half of the split. `Ctrl-\ x` used to end the focused tile's
# session; it takes the pane off THIS wall now and leaves the session where
# it is, running, for whoever else holds it. The layout is the wall, so the
# removal is a leaf leaving a file — and nothing on any daemon changes.
#
# Three panes, because a leg with one could not tell "the pane went" from
# "the wall went": the two neighbours have to still be there afterwards,
# and the two REMAINING leaves are what the detach writes.
#
# Five oracles, four of them outside the wall's own word for it:
#
#   * the notice, which IS the wall's word and is here because the ORDER
#     inside `removePane` is what makes it reliable — the sentence is set
#     before the vanish arms the incoming pump's claim, so the claim that
#     takes the terminal is the claim that paints it.
#
#   * the screen: the removed pane's bar is gone from the final grid and
#     its neighbours' are not (render, so a bar that merely scrolled past
#     in the stream does not count).
#   * the daemon: `mux d stats` still names the session, and says one
#     client holds it — the pipe client below, which never typed a key.
#   * that client: it takes a line and echoes it back, so it is attached
#     and pumping rather than merely un-reaped.
#   * the OS: the shell's own pid, printed by the shell itself before any
#     of this, is still a live process.
#
# The layout is read from a copy a SURVIVING PANE'S SHELL makes while the
# wall is still on the screen, not off the disk afterwards. The run's exit
# saves the tree too, so a leg that read the file after the detach would
# pass a `removePane` that wrote nothing at all — the removal's own save is
# what carries a wall that is killed rather than left.
wait_sessions "$SOCKH1" 2 "hosts wall x: daemon 1 should hold 0 and b"
wait_sessions "$SOCKH2" 2 "hosts wall x: daemon 2 should hold 0 and c"

# The other holder of the pane's session, attached before the wall exists
# and silent until the removal is over.
pipe_mux "$OUT.hxb" "$OUT.hxb.err" env XDG_STATE_HOME="$HXSTATE" timeout 120 \
    "$MUX" --sock "$SOCKH1" --session b
await_out "$OUT.hxb" "hb-pin" "hosts wall x: the other client never got session b's grid"

set +e
# The same three leaves the headline leg opened on; pane 2 is
# `--sock $SOCKH1#b`, which is the one the other client is holding.
seed_layout "$HSTATE" stacked \
    "--sock $SOCKH1#0" "--sock $SOCKH1#b" "--sock $SOCKH2#0"
rm -f "$OUT.hxlive"
XDG_STATE_HOME="$HSTATE" timeout 90 "$PTYCLIENT" --cols 80 --rows 44 \
    --out "$OUT.hxmcap" --err "$OUT.hxmcap.err" -- "$MUX" > "$OUT.hxmpc" 2>&1 <<EOF
expect hw1-pin 25000
settle 1500 25000
send \x1c2
settle 700 20000
send \x1cx
# The sentence, with no keystroke between the x and it. removePane sets
# the notice BEFORE the vanish that hands the focus on, so the claim the
# vanish arms is the claim that paints it; the other way round the notice
# lost that race about one press in four (2026-09-02), and this expect is
# what would catch a reordering that brought it back. (No backticks: this
# heredoc is unquoted, so a backtick would be a command substitution.)
expect the session is still on its daemon 20000
settle 2500 25000
send cp $HSTATE/mux/layout $OUT.hxlive; printf 'xsn%s' ap; echo\n
expect xsnap 20000
settle 700 20000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
[ "$RC" -eq 0 ] || {
    echo "e2e FAIL: hosts wall x: the wall leg exited $RC:"
    cat "$OUT.hxmpc"; echo "--- stderr ---"; cat "$OUT.hxmcap.err"; exit 1; }
# The screen, judged on the GRID: the bar is in the stream either way, and
# only the render says what was still there when the wall came down.
"$RENDER" --cols 80 --rows 44 < "$OUT.hxmcap" > "$OUT.hxmgrid" || {
    echo "e2e FAIL: hosts wall x: render oracle failed"; cat "$OUT.hxmgrid"; exit 1; }
grep -q -- "--sock $SOCKH1#b" "$OUT.hxmgrid" && {
    echo "e2e FAIL: hosts wall x: the removed pane is still on the final wall:"
    cat "$OUT.hxmgrid"; exit 1; }
for _hx in "$SOCKH1#0" "$SOCKH2#0"; do
    grep -q -- "--sock $_hx" "$OUT.hxmgrid" || {
        echo "e2e FAIL: hosts wall x: '$_hx' lost its pane when its neighbour"
        echo "          was removed:"
        cat "$OUT.hxmgrid"; exit 1; }
done
# The layout lost the leaf and kept the other two, in their order — read
# from a copy the SURVIVING PANE'S OWN SHELL made while the wall was still
# up, so the writer under test is `removePane`'s save and not the detach's.
# (Measured 2026-09-02: with `wall_layout.persist` dropped from `removePane`
# the file on disk after the detach is still right, because the run's exit
# saves the tree it has — so a leg that read it only afterwards would grade
# a removal that never wrote anything as green.)
[ -f "$OUT.hxlive" ] || {
    echo "e2e FAIL: hosts wall x: the surviving pane never copied the live layout"
    cat "$OUT.hxmpc"; exit 1; }
_hx_live=$(sed -n 's/^ *leaf [0-9][0-9]* //p' "$OUT.hxlive" | tr '\n' ' ')
[ "$_hx_live" = "--sock $SOCKH1#0 --sock $SOCKH2#0 " ] || {
    echo "e2e FAIL: hosts wall x: the file the removal wrote is '$_hx_live',"
    echo "          want the two panes that stayed:"; cat "$OUT.hxlive"; exit 1; }
# ...and the detach agrees with it, so the two writers do not disagree.
_hx_leaves=$(sed -n 's/^ *leaf [0-9][0-9]* //p' "$HSTATE/mux/layout" | tr '\n' ' ')
[ "$_hx_leaves" = "$_hx_live" ] || {
    echo "e2e FAIL: hosts wall x: the detach saved '$_hx_leaves' over the"
    echo "          removal's '$_hx_live':"; cat "$HSTATE/mux/layout"; exit 1; }
# The daemon still has the session, and one client — the pipe client — is
# still holding it. `x` is a pane leaving a file, not a word to a daemon.
wait_sessions "$SOCKH1" 2 "hosts wall x: daemon 1 must still hold 0 and b"
"$MUX" d stats --sock "$SOCKH1" > "$OUT.hxstats" 2>&1
grep -q "session b clients=1 " "$OUT.hxstats" || {
    echo "e2e FAIL: hosts wall x: session b is gone, or its other client went with"
    echo "          the pane; the daemon says:"; cat "$OUT.hxstats"; exit 1; }
wait_sessions "$SOCKH2" 2 "hosts wall x: the other daemon must be untouched"
# ...and that client is still PUMPING, not merely counted: a line typed
# into it comes back off the session's own grid.
pipe_send 'printf "hxlive-%%s\\n" pin\n'
await_out "$OUT.hxb" "hxlive-pin" "hosts wall x: the other client stopped pumping when the pane went"
pipe_detach "hosts wall x: the other client"
# The OS's word on the shell, which no daemon can catch itself being wrong
# about.
kill -0 "$BSHELL" 2>/dev/null || {
    echo "e2e FAIL: hosts wall x: session b's shell (pid $BSHELL) died when its pane"
    echo "          was taken off the wall — removing a pane is not ending a session"
    ps -o pid,ppid,stat,args -p "$BSHELL" 2>&1; exit 1; }
ok "x on a wall removes the focused pane and ends nothing: the session keeps its other client, and the layout loses the leaf"

# ---- a daemon that goes and comes back re-creates NOTHING ---------------
#
# The model's sharpest edge. Sessions live in the daemon, so a daemon that
# dies takes them with it — and the wall, which is a list of DAEMONS, has
# nothing left to resurrect them from. The old wall file could and did.
#
# Two states of one host, each with its own witness:
#
#   * down: its remembered panes STAY, wearing [unreachable], and not one
#     of them is [up]. Only a host's own list may take a pane, and a quiet
#     box has no list — losing an eight-pane setup to one dark machine is
#     worse than reading the word until the user decides. Judged on the
#     final grid (render) after the settle, not on transient frames.
#     Daemon 1 is up throughout and its tiles are what the leg waits on,
#     so "the poll has not landed yet" and "daemon 2 is dark" cannot be
#     confused.
#   * back: its own default session and nothing else. `c`, which was live
#     when the daemon died, is not there — asked of `mux d stats` on the
#     real daemon, not of the wall that would be reporting on its own
#     resurrection. The PANE for `c` is still on the wall, and that is not
#     a contradiction: it stands wearing [session ended] because the host answered
#     and did not name the session (gone panes, 2026-09-01), which is a
#     rect the user saved and an offer to start it anew, not a session.
#     `x` closes it and Enter creates it; until then the daemon holds one
#     session and the wall says so.
assert_stopped "$SOCKH2" "$DH2PID" "hosts restart" "$OUT.h2stop"
DH2PID=""
# Four panes, two per daemon, written down before the box goes dark: the
# claim is about panes a user SAVED on a machine that then stopped
# answering, so they have to be in the file rather than discovered.
seed_layout "$HSTATE" stacked \
    "--sock $SOCKH1#0" "--sock $SOCKH1#b" "--sock $SOCKH2#0" "--sock $SOCKH2#c"
set +e
XDG_STATE_HOME="$HSTATE" timeout 60 "$PTYCLIENT" --cols 80 --rows 44 \
    --out "$OUT.hdcap" --err "$OUT.hdcap.err" -- "$MUX" > "$OUT.hdpc" 2>&1 <<EOF
expect --sock $SOCKH1#b [up] 25000
settle 1000 25000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "hosts restart: the down-host wall exited $RC:" "$OUT.hdpc"
# The down daemon's two remembered panes on the SETTLED screen. The
# `expect` above is the control: it waited on daemon 1's bar, so this grid
# is a PAINTED screen and not a blank one, and the settle past every
# opening host's first answer is what makes it the settled one.
"$RENDER" --cols 80 --rows 44 < "$OUT.hdcap" > "$OUT.hdcap.final"
for _hd in "$SOCKH2#0" "$SOCKH2#c"; do
    grep -q -- "--sock $_hd \[unreachable\]" "$OUT.hdcap.final" || {
        echo "e2e FAIL: hosts restart: the pane the user saved on a dark daemon is"
        echo "          gone, or does not say the machine is: expected $_hd [unreachable]"
        cat "$OUT.hdcap.final"; exit 1; }
done
# ...and none of them claims to be attached: nothing dialled a daemon that
# is not there, so no pane on it may read [up].
if grep -- "--sock $SOCKH2" "$OUT.hdcap.final" | grep -q '\[up\]'; then
    echo "e2e FAIL: hosts restart: a pane on a stopped daemon read [up]:"
    cat "$OUT.hdcap.final"; exit 1
fi

start_daemon "$SOCKH2" "$OUT.h2b.d" "hosts daemon 2 never came back" --shell /bin/sh
DH2PID=$DPID
set +e
# Unquoted heredoc: the needle names the REBORN daemon's own tile, which is
# what the wall is being waited on for. A duration here would be a guess at
# how long a poll takes on a loaded machine.
XDG_STATE_HOME="$HSTATE" timeout 60 "$PTYCLIENT" --cols 80 --rows 44 \
    --out "$OUT.hrcap" --err "$OUT.hrcap.err" -- "$MUX" > "$OUT.hrpc" 2>&1 <<EOF
expect --sock $SOCKH2#0 [up] 25000
settle 800 25000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "hosts restart: the reborn-host wall exited $RC:" "$OUT.hrpc"
grep -q -- "--sock $SOCKH2#0 \[up\]" "$OUT.hrcap" || {
    echo "e2e FAIL: hosts restart: the reborn daemon's own session never became a tile:"
    cat "$OUT.hrpc"; exit 1; }
"$RENDER" --cols 80 --rows 44 < "$OUT.hrcap" > "$OUT.hrcap.final"
# The pane the dead session left behind stands, and says why: the host
# ANSWERED and did not name `c`, which is [session ended] and not [unreachable].
grep -q -- "--sock $SOCKH2#c \[session ended\]" "$OUT.hrcap.final" || {
    echo "e2e FAIL: hosts restart: the pane for the session the dead daemon held did"
    echo "          not stand wearing [session ended] on the reborn daemon:"
    cat "$OUT.hrcap.final"; exit 1; }
# ...and it is a rect, not a session: nothing attached to a `c` that does
# not exist. A pane the wall had resurrected would read [up].
if grep -- "--sock $SOCKH2#c" "$OUT.hrcap.final" | grep -q '\[up\]'; then
    echo "e2e FAIL: hosts restart: the wall resurrected the session the dead daemon held:"
    cat "$OUT.hrcap.final"; exit 1
fi
# The daemon's own account of it, which is the one that matters: a session
# the wall re-created would be here whether or not it ever got a tile.
[ "$("$MUX" d stats --sock "$SOCKH2" | sed -n 's/.*sessions=\([0-9]*\).*/\1/p')" = "1" ] || {
    echo "e2e FAIL: hosts restart: the reborn daemon holds more than its own default session:"
    "$MUX" d stats --sock "$SOCKH2"; exit 1; }
ok "a daemon that goes down keeps its panes wearing unreachable, and comes back re-creating nothing"

# ---- hosts rm takes a daemon off the wall and ends nothing --------------
#
# The file is a VIEW. Removing a line is forgetting where to look, never
# an instruction to the daemon — which is why the sessions are asked for
# again afterwards and have to still be there.
#
# The two refusals ride along because they are the same file's rules:
# `rm` of a line that is not there is an error naming the file, and `add`
# of a spelling with a `#` in it is the grammar's one refusal.
# `timeout`, and a verdict of its own: `mux hosts` dials every listed
# daemon, and while each dial is bounded inside the binary (hosts_list_ms) a
# hang here would hang the whole suite with no line naming it. Bare under
# `set -e` a non-zero exit would abort the run with no message at all.
env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" hosts > "$OUT.hlist" 2>&1 || {
    echo "e2e FAIL: hosts rm: 'mux hosts' exited nonzero on two live daemons:"
    cat "$OUT.hlist"; exit 1; }
grep -qF -- "--sock $SOCKH1" "$OUT.hlist" || {
    echo "e2e FAIL: hosts rm: 'mux hosts' does not list the daemon it is about to remove:"
    cat "$OUT.hlist"; exit 1; }
# The COUNT, which is the whole point of the verb — and the two daemons hold
# different numbers on purpose: a fixture where every row carried the same
# figure could not tell the daemon's own answer from a constant, and one
# where every row failed (the only case pinned before) never ran the column
# at all. `wait_sessions` above already settled both.
for _hc in "$SOCKH1:2" "$SOCKH2:1"; do
    _hcs=${_hc%:*}; _hcn=${_hc##*:}
    grep -qxF -- "$(printf -- '--sock %s\t%s' "$_hcs" "$_hcn")" "$OUT.hlist" || {
        echo "e2e FAIL: hosts: 'mux hosts' does not say $_hcs holds $_hcn sessions:"
        cat "$OUT.hlist"; exit 1; }
done
# The layout is the wall, so the removal has a second file to keep true.
# A leaf's host part must be a line of the hosts file — `seedLayout`
# refuses the WHOLE layout over one that is not — so a `rm` that edited the
# hosts file alone left the user's OTHER panes to be thrown away at the
# next start, with a printed line and no undo. Two hosts and three leaves:
# the removal has to take two of them and leave the third exactly as it is.
seed_layout "$HSTATE" stacked \
    "--sock $SOCKH1#0" "--sock $SOCKH1#b" "--sock $SOCKH2#0"
env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" hosts rm "--sock $SOCKH1" > "$OUT.hrm" 2>&1 || {
    echo "e2e FAIL: hosts rm: removing a listed daemon failed:"; cat "$OUT.hrm"; exit 1; }
if grep -qF -- "$SOCKH1" "$HSTATE/mux/hosts"; then
    echo "e2e FAIL: hosts rm: the line is still on the wall:"
    cat "$HSTATE/mux/hosts"; exit 1
fi
# The OS first, and it is the assertion the spec names: `hosts rm` is a
# line leaving a file, never an instruction to a daemon, so the SHELL that
# was running before it is the same process afterwards. A daemon reporting
# its own session count cannot catch itself being wrong about that.
kill -0 "$BSHELL" 2>/dev/null || {
    echo "e2e FAIL: hosts rm: session b's shell (pid $BSHELL) died when its daemon"
    echo "          was taken off the wall — forgetting is not ending"
    ps -o pid,ppid,stat,args -p "$BSHELL" 2>&1; exit 1; }
[ "$(ps -o stat= -p "$BSHELL" 2>/dev/null | cut -c1)" != "Z" ] || {
    echo "e2e FAIL: hosts rm: session b's shell is a zombie, so kill -0 proved nothing"
    ps -o pid,ppid,stat,args -p "$BSHELL" 2>&1; exit 1; }
wait_sessions "$SOCKH1" 2 "hosts rm: daemon 1's sessions must survive being forgotten"
wait_grid "$SOCKH1" "hb-pin" "hosts rm: session b's grid outlived the wall line" b

# What the NEXT `mux` on this device gets — the assertion the file edit is
# for. The surviving host's pane, on a wall that read its layout without a
# word: a `rm` that left the removed daemon's leaves behind would have this
# run print `layout ignored` and open the empty wall's picker instead.
set +e
XDG_STATE_HOME="$HSTATE" timeout 60 "$PTYCLIENT" --cols 80 --rows 44 \
    --out "$OUT.hrmcap" --err "$OUT.hrmcap.err" -- "$MUX" > "$OUT.hrmpc" 2>&1 <<EOF
expect --sock $SOCKH2#0 [up] 25000
settle 800 25000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "hosts rm: the wall after the removal exited $RC:" "$OUT.hrmpc" "$OUT.hrmcap.err"
if grep -qF "layout ignored" "$OUT.hrmcap.err"; then
    echo "e2e FAIL: hosts rm: the layout was refused after the removal, so the"
    echo "          removal left leaves naming the daemon it took off the wall:"
    cat "$OUT.hrmcap.err"; exit 1
fi
"$RENDER" --cols 80 --rows 44 < "$OUT.hrmcap" > "$OUT.hrmcap.final"
grep -q -- "--sock $SOCKH2#0 \[up\]" "$OUT.hrmcap.final" || {
    echo "e2e FAIL: hosts rm: the surviving host's pane is not on the wall the"
    echo "          removal left behind:"
    cat "$OUT.hrmcap.final"; exit 1; }
if grep -q -- "--sock $SOCKH1" "$OUT.hrmcap.final"; then
    echo "e2e FAIL: hosts rm: a pane on the removed daemon is still on the wall:"
    cat "$OUT.hrmcap.final"; exit 1
fi

set +e
env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" hosts rm "--sock $SOCKH1" > "$OUT.hrm2" 2>&1
RC=$?
set -e
[ "$RC" -eq 1 ] || {
    echo "e2e FAIL: hosts rm: removing an absent daemon exited $RC, want 1:"
    cat "$OUT.hrm2"; exit 1; }
grep -q "not on the wall" "$OUT.hrm2" || {
    echo "e2e FAIL: hosts rm: an absent rm never said so:"; cat "$OUT.hrm2"; exit 1; }
set +e
env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" hosts add "--sock $SOCKH1#b" > "$OUT.hadd2" 2>&1
RC=$?
set -e
[ "$RC" -eq 2 ] || {
    echo "e2e FAIL: hosts add: a '#' spelling exited $RC, want 2:"; cat "$OUT.hadd2"; exit 1; }
grep -q "names a session" "$OUT.hadd2" || {
    echo "e2e FAIL: hosts add: the '#' refusal never named the rule:"; cat "$OUT.hadd2"; exit 1; }
if grep -qF -- "$SOCKH1" "$HSTATE/mux/hosts"; then
    echo "e2e FAIL: hosts add: a refused spelling still edited the file:"
    cat "$HSTATE/mux/hosts"; exit 1
fi
# Asking what a command does is not mistyping its verb. `--help` and
# `--version` used to be answered inside the `add` branch only, so `mux
# hosts --help` exited 2 calling the flag a subcommand that does not exist,
# and sent the reader looking for one. Asserted here rather than in a unit
# test because the answer is a page on fd 1, which under `zig build test` is
# the runner's own protocol stream.
for _hh in "--help" "-h"; do
    set +e
    env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" hosts "$_hh" > "$OUT.hhelp" 2>&1
    RC=$?
    set -e
    rc0 "hosts $_hh: exited $RC, want 0:" "$OUT.hhelp"
    grep -q "mux hosts add SPELLING" "$OUT.hhelp" || {
        echo "e2e FAIL: hosts $_hh: answered something other than the usage:"
        cat "$OUT.hhelp"; exit 1; }
done
set +e
env XDG_STATE_HOME="$HSTATE" timeout 40 "$MUX" hosts --version > "$OUT.hver" 2>&1
RC=$?
set -e
rc0 "hosts --version: exited $RC, want 0:" "$OUT.hver"
grep -q "^mux " "$OUT.hver" || {
    echo "e2e FAIL: hosts --version: did not name the binary and its version:"
    cat "$OUT.hver"; exit 1; }

assert_stopped "$SOCKH1" "$DH1PID" "hosts rm" "$OUT.h1stop"
DH1PID=""
assert_stopped "$SOCKH2" "$DH2PID" "hosts rm" "$OUT.h2stop2"
DH2PID=""
ok "hosts rm takes the daemon off the wall and ends nothing"

# ---- an empty wall is a place you can stand in, and leave ---------------
#
# The wall shows live sessions and nothing else, so a wall whose only host
# has nothing live on it has no TILE — not a bar, not a placeholder. That
# is a screen the keyboard has to survive: `tiles[shared.sel]` is a slot
# nothing has ever written here, so the run refuses every chord rather
# than reading one off it, and `Ctrl-\ d` is the one it honours.
#
# One claim, and the sentence is the whole of it: it says `mux: left the
# wall`. `detached (session still running)` is what the ordinary road
# prints, and on a wall with no session that sentence is a lie about the
# user's shells.
#
# NOT a deadline. `awaitDetach` bounds the ordinary road at 400ms, so a `d`
# that fell through to it still exits inside any waitexit worth writing: a
# bound both branches satisfy asserts nothing and only imports flake — the
# `waitexit 1000` that used to stand here needed 600ms of fixture teardown
# under a loaded Debug build to turn this leg red. Pinning "leaves at once"
# wants a measurement either side of the run with a threshold under 400ms,
# which is a different leg.
#
# ...and, ahead of it: the empty wall OPENS THE PICKER by itself.
# A blank screen is no place to act from, and the list of machines is the
# only thing left to act on. Esc there leaves the one-line text standing —
# once, so the popup does not come straight back over the answer.
#
# The socket is one nothing has ever bound, and deliberately NOT the
# default path: a listed LOCAL daemon is auto-started, which would give
# this wall the very tile it is asserting the absence of.
hostroom empty
EMPTYSOCK="${TMPDIR:-/tmp}/muxd-e2e-neverbound-$$.sock"
# Registered although nothing here binds it — this leg ASSERTS nothing
# does. The regression it guards against is a wall auto-starting a daemon
# on a listed local path, and that failure leaves a live daemon on exactly
# this socket: unregistered, the trap's residue guard unlinks the path from
# under it instead of asking `mux d stop` first.
defer_sock "$EMPTYSOCK"
mkdir -p "$HOSTROOM/mux"
printf -- '--sock %s\n' "$EMPTYSOCK" > "$HOSTROOM/mux/hosts"
[ ! -e "$EMPTYSOCK" ] || {
    echo "e2e FAIL: empty wall: $EMPTYSOCK exists, so the wall may get a tile"; exit 1; }
set +e
XDG_STATE_HOME="$HOSTROOM" timeout 40 "$PTYCLIENT" --cols 80 --rows 24 \
    --out "$OUT.emcap" --err "$OUT.emcap.err" -- "$MUX" > "$OUT.empc" 2>&1 <<'EOF'
expect the wall is empty 20000
expect c new session 20000
send \x1b
settle 500 15000
send \x1cd
waitexit 10000
EOF
RC=$?
set -e
[ "$RC" -eq 0 ] || {
    echo "e2e FAIL: empty wall: Ctrl-\\ d did not leave the empty wall (exit $RC):"
    cat "$OUT.empc"; echo "--- stderr ---"; cat "$OUT.emcap.err"; exit 1; }
# mux's own last word, off its stderr — ptyclient gives the client a real
# stderr file, so this is the process's sentence and not something the
# screen happened to be holding.
EMLAST=$(grep -v '^[[:space:]]*$' "$OUT.emcap.err" | tail -1)
[ "$EMLAST" = "mux: left the wall" ] || {
    echo "e2e FAIL: empty wall: leaving an empty wall said"
    echo "          '$EMLAST', want 'mux: left the wall'"
    cat "$OUT.emcap.err"; exit 1; }
# The Esc, judged on the GRID and not on the stream: the popup's rows are
# in the capture either way, and only the render says what the user was
# still looking at when the wall came down.
"$RENDER" --cols 80 --rows 24 < "$OUT.emcap" > "$OUT.emgrid" || {
    echo "e2e FAIL: empty wall: render oracle failed"; cat "$OUT.emgrid"; exit 1; }
grep -q "the wall is empty" "$OUT.emgrid" || {
    echo "e2e FAIL: empty wall: Esc did not leave the one-line text:"
    cat "$OUT.emgrid"; exit 1; }
grep -q "c new session" "$OUT.emgrid" && {
    echo "e2e FAIL: empty wall: the picker came back over the answer:"
    cat "$OUT.emgrid"; exit 1; }
ok "an empty wall opens the picker, Esc leaves the one line, and Ctrl-\\ d leaves at once"

# ---- the picker: the wall shows sessions, the popup shows machines ------
#
# `Ctrl-\ s` over a wall of two daemons. Everything a host needs doing to
# happens here and nowhere else, so the four keys are asserted in one run
# and each against an oracle outside the terminal:
#
#   * `c` births on the SELECTED host — the daemon's own session count,
#     read back with `mux d stats`, says which machine took it. A birth
#     driven off a tile could only ever have landed on the focus's daemon.
#   * Enter on that host then LISTS its sessions, and the one just born
#     wears `on this wall` beside the daemon's own count of who holds it.
#     Esc backs out one level rather than closing, so the forget below is
#     typed at the rows the Esc came back to.
#   * `x` forgets a host: the file loses the line and the host's SHELL is
#     still there, asked of the OS with `kill -0` on the pid the shell
#     itself printed. "remove is detach" — `hosts rm` ends nothing.
#   * `a` puts it back, so the file is two lines again.
#   * Esc closes, and the keys after it reach the focused session — the
#     popup ate every byte until then, which is what stops a `j` meant for
#     the rows from landing in somebody's editor.
#
# Its own daemons and its own state home: the legs above stop theirs, and
# a hosts file this leg edits from inside must be nobody else's.
PKSOCK1="${TMPDIR:-/tmp}/muxd-e2e-pick1-$$.sock"
defer_sock "$PKSOCK1"
PKSOCK2="${TMPDIR:-/tmp}/muxd-e2e-pick2-$$.sock"
defer_sock "$PKSOCK2"
PKSTATE="${TMPDIR:-/tmp}/mux-e2e-pick-state-$$"
defer_rm "$PKSTATE"
start_daemon "$PKSOCK1" "$OUT.pk1.d" "picker daemon 1 never bound" --shell /bin/sh
PK1PID=$DPID
start_daemon "$PKSOCK2" "$OUT.pk2.d" "picker daemon 2 never bound" --shell /bin/sh
PK2PID=$DPID

# Daemon 1's shell prints its own pid: the forget below claims the SESSION
# outlives the wall line, and only a pid the shell itself reported can be
# asked of the process table afterwards.
pipe_mux "$OUT.pk1a" "$OUT.pk1a.err" env XDG_STATE_HOME="$PKSTATE" timeout 40 \
    "$MUX" --sock "$PKSOCK1"
pipe_send 'printf "pk-shell-%%s\\n" $$\n'
await_out "$OUT.pk1a" "pk-shell-[0-9]" "picker: daemon 1's shell never printed its pid"
pipe_detach
wait_grid "$PKSOCK1" "pk-shell-[0-9]" "picker: daemon 1's shell pid"
# The shell's ECHO of the command comes first and carries the prefix with
# no digits behind it, so the pattern demands at least one.
PKSHELL=$(grep -o 'pk-shell-[0-9][0-9]*' "$OUT.pk1a" | head -1 | cut -d- -f3)
[ -n "$PKSHELL" ] || {
    echo "e2e FAIL: picker: never read daemon 1's shell pid:"; cat "$OUT.pk1a"; exit 1; }
kill -0 "$PKSHELL" 2>/dev/null || {
    echo "e2e FAIL: picker: daemon 1's shell $PKSHELL is not running before the leg"; exit 1; }

# Daemon 2's marker, printed before this wall exists: what proves the tile
# on the far side of the popup is really daemon 2's and not an echo.
pipe_mux "$OUT.pk2a" "$OUT.pk2a.err" env XDG_STATE_HOME="$PKSTATE" timeout 40 \
    "$MUX" --sock "$PKSOCK2"
pipe_send 'printf "pk-two-%%s\\n" mark\n'
await_out "$OUT.pk2a" "pk-two-mark" "picker: daemon 2's marker never reached the client"
pipe_detach
wait_grid "$PKSOCK2" "pk-two-mark" "picker: daemon 2's marker"

mkdir -p "$PKSTATE/mux"
printf -- '--sock %s\n--sock %s\n' "$PKSOCK1" "$PKSOCK2" > "$PKSTATE/mux/hosts"
# One pane per daemon, so the popup opens over a wall rather than over the
# empty-wall line — and so the forget below has a pane of daemon 1's to
# take away.
seed_layout "$PKSTATE" stacked "--sock $PKSOCK1#0" "--sock $PKSOCK2#0"

PKBEFORE=$("$MUX" d stats --sock "$PKSOCK2" | tr ' ' '\n' | grep -c '^session$')
set +e
XDG_STATE_HOME="$PKSTATE" timeout 90 "$PTYCLIENT" --cols 100 --rows 30 \
    --out "$OUT.pkcap" --err "$OUT.pkcap.err" -- \
    "$MUX" > "$OUT.pkpc" 2>&1 <<EOF
expect pk-two-mark 20000
settle 700 20000
send \x1cs
expect x forget 15000
send 2
settle 400 15000
send c
expect 3> 20000
settle 700 20000
send \x1cs
expect x forget 15000
send 2
settle 400 15000
send \r
# Enter changes the prefix filter to the session level before the poller's
# post-birth reply necessarily arrives. Wait for the unselected row for the
# newly born session 1 as the readiness event, so the following numeric
# selection cannot race the stale list containing only session 0.
expect  2  1 15000
# The BORN session's row, named. Daemon 2's list is 0 then the session c
# just made, so row 2 is the new one, and a bare "on this wall" would be
# satisfied by row 1 -- the pane the layout seeded -- whatever the birth
# did. The needle is pickerRow's selected marker at a fixed-width head, so
# it is that row and no other. (No backticks in here: this heredoc is
# unquoted, so a backtick would be a command substitution the shell runs.)
send 2
settle 400 15000
expect  2> 1 15000
# ...and the mark and the daemon's own holder count are painted at all.
# Read apart from the row above deliberately: the padding between a name
# and its state column is as wide as the box, so no one needle can carry
# both, and pretending otherwise would be a claim about spaces.
expect on this wall, 1 client 15000
settle 400 15000
send \x1b
expect x forget 15000
send 1
settle 400 15000
send x
settle 900 20000
send \x1cs
expect x forget 15000
send a--sock\x20
settle 300 15000
send $PKSOCK1\r
settle 700 15000
send \x1b
settle 700 20000
send printf 'pk-after-%s\n' esc\n
expect pk-after-esc 15000
send \x1cd
waitexit 10000
EOF
RC=$?
set -e
[ "$RC" -eq 0 ] || {
    echo "e2e FAIL: picker: ptyclient leg exited $RC:"
    cat "$OUT.pkpc"; echo "--- stderr ---"; cat "$OUT.pkcap.err"; exit 1; }

# Both spellings really were on the popup — the rows are the hosts file's
# daemons, and a box that listed one of two would still have satisfied the
# footer the `expect` waited on.
grep -qa -- "$PKSOCK1" "$OUT.pkcap" || {
    echo "e2e FAIL: picker: daemon 1 was never a row:"; cat "$OUT.pkpc"; exit 1; }
grep -qa -- "$PKSOCK2" "$OUT.pkcap" || {
    echo "e2e FAIL: picker: daemon 2 was never a row:"; cat "$OUT.pkpc"; exit 1; }

# The birth's oracle is the DAEMON's count, not the screen: row 2 was
# selected, so daemon 2 is the one that gained a session.
PKAFTER=$("$MUX" d stats --sock "$PKSOCK2" | tr ' ' '\n' | grep -c '^session$')
[ "$PKAFTER" -eq "$((PKBEFORE + 1))" ] || {
    echo "e2e FAIL: picker: daemon 2 holds $PKAFTER sessions, want $((PKBEFORE + 1))"
    "$MUX" d stats --sock "$PKSOCK2"; "$MUX" d stats --sock "$PKSOCK1"; exit 1; }

# The forget: the line left the file and came back, and the SESSION never
# noticed either. `kill -0` on the shell's own pid, never the daemon's word
# for it.
kill -0 "$PKSHELL" 2>/dev/null || {
    echo "e2e FAIL: picker: forgetting daemon 1 killed its shell $PKSHELL"; exit 1; }
grep -qxF -- "--sock $PKSOCK1" "$PKSTATE/mux/hosts" || {
    echo "e2e FAIL: picker: a re-added host is not back in the file:"
    cat "$PKSTATE/mux/hosts"; exit 1; }
[ "$(grep -c . "$PKSTATE/mux/hosts")" = "2" ] || {
    echo "e2e FAIL: picker: hosts file is not exactly two lines after x then a:"
    cat "$PKSTATE/mux/hosts"; exit 1; }

# Esc gave the keyboard back. The needle is looked for in the DAEMONS, so
# this says which session the keys reached and not merely that something
# painted: the focus was on a daemon-2 tile when the forget took daemon
# 1's tiles off the wall.
PKWHERE=""
for _pk in 0 1; do
    if dump_session "$PKSOCK2" "$_pk" 2>/dev/null | grep -q "pk-after-esc"; then PKWHERE="$_pk"; fi
done
[ -n "$PKWHERE" ] || {
    echo "e2e FAIL: picker: the keys after Esc reached no session on daemon 2:"
    cat "$OUT.pkpc"; exit 1; }
if dump_session "$PKSOCK1" 0 2>/dev/null | grep -q "pk-after-esc"; then
    echo "e2e FAIL: picker: the keys went to the host the forget took off the wall:"
    cat "$OUT.pkpc"; exit 1
fi
assert_stopped "$PKSOCK1" "$PK1PID" "picker" "$OUT.pk1stop"
PK1PID=""
assert_stopped "$PKSOCK2" "$PK2PID" "picker" "$OUT.pk2stop"
PK2PID=""
ok "the picker births on the host a digit names, lists its sessions, forgets a host without ending it, adds one back, and gives the keyboard back"

# ---- two devices, one pair of daemons: each wall is its own layout -------
#
# The whole point of the change, and the one thing no single-wall leg can
# say. Two state homes stand in for two machines, both listing the SAME two
# daemons. Device 1 has a wall of two panes; device 2 has no layout at all,
# so it starts with nothing and gets exactly what it adds — and device 1
# never learns of it.
#
# Under the old model this leg could not exist: tiles came from each host's
# own list, so any two walls over one pair of daemons were the same wall,
# and a session either of them started appeared on both.
#
# Daemon 2 holds TWO sessions on purpose. "Device 2 added ONE pane" is only
# a claim if there was another it could have taken, and the row it picks is
# the SECOND one — a first-row fixture would pass whatever the navigation
# did.
TWSOCK1="${TMPDIR:-/tmp}/muxd-e2e-twodev1-$$.sock"
defer_sock "$TWSOCK1"
TWSOCK2="${TMPDIR:-/tmp}/muxd-e2e-twodev2-$$.sock"
defer_sock "$TWSOCK2"
TWDEV1="${TMPDIR:-/tmp}/mux-e2e-twodev-a-$$"
defer_rm "$TWDEV1"
TWDEV2="${TMPDIR:-/tmp}/mux-e2e-twodev-b-$$"
defer_rm "$TWDEV2"
start_daemon "$TWSOCK1" "$OUT.tw1.d" "two-device daemon 1 never bound" --shell /bin/sh
TW1PID=$DPID
start_daemon "$TWSOCK2" "$OUT.tw2.d" "two-device daemon 2 never bound" --shell /bin/sh
TW2PID=$DPID

# Three markers, each printed by its own shell before either wall exists:
# what proves a pane is a real attach and not an echo of anything typed.
TWMKSTATE="${TMPDIR:-/tmp}/mux-e2e-twodev-mk-$$"
defer_rm "$TWMKSTATE"
for _tw in "$TWSOCK1:0:alpha" "$TWSOCK2:0:bee" "$TWSOCK2:beta:beta"; do
    _twsock=${_tw%%:*}; _twrest=${_tw#*:}; _twsess=${_twrest%%:*}; _twmark=${_twrest##*:}
    pipe_mux "$OUT.tw.$_twmark" "$OUT.tw.$_twmark.err" env XDG_STATE_HOME="$TWMKSTATE" \
        timeout 40 "$MUX" --sock "$_twsock" --session "$_twsess"
    pipe_send 'printf "tw-%%s\\n" '"$_twmark"'\n'
    await_out "$OUT.tw.$_twmark" "tw-$_twmark" "two devices: $_twmark never reached its client"
    pipe_detach "two devices: the $_twmark maker"
    wait_grid "$_twsock" "tw-$_twmark" "two devices: $_twmark's marker" "$_twsess"
done
wait_sessions "$TWSOCK2" 2 "two devices: daemon 2 should hold 0 and beta"

# Device 1: both daemons listed, and a wall of two panes over them.
mkdir -p "$TWDEV1/mux"
printf -- '--sock %s\n--sock %s\n' "$TWSOCK1" "$TWSOCK2" > "$TWDEV1/mux/hosts"
seed_layout "$TWDEV1" stacked "--sock $TWSOCK1#0" "--sock $TWSOCK2#0"
set +e
XDG_STATE_HOME="$TWDEV1" timeout 60 "$PTYCLIENT" --cols 100 --rows 30 \
    --out "$OUT.tw1cap" --err "$OUT.tw1cap.err" -- "$MUX" > "$OUT.tw1pc" 2>&1 <<'EOF'
expect tw-alpha 25000
settle 1500 25000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "two devices: device 1's wall exited $RC:" "$OUT.tw1pc" "$OUT.tw1cap.err"

# Device 2: the same two daemons, no layout. `mux` opens on an empty wall
# with the picker over it; Enter on host 2 lists its sessions, `j` steps to
# the second, and Enter makes it a pane. Nothing else arrives.
mkdir -p "$TWDEV2/mux"
printf -- '--sock %s\n--sock %s\n' "$TWSOCK1" "$TWSOCK2" > "$TWDEV2/mux/hosts"
[ ! -e "$TWDEV2/mux/layout" ] || {
    echo "e2e FAIL: two devices: device 2 already has a layout"; exit 1; }
set +e
XDG_STATE_HOME="$TWDEV2" timeout 60 "$PTYCLIENT" --cols 100 --rows 30 \
    --out "$OUT.tw2cap" --err "$OUT.tw2cap.err" -- "$MUX" > "$OUT.tw2pc" 2>&1 <<'EOF'
expect the wall is empty 20000
expect x forget 20000
send 2
settle 400 15000
send \r
expect Enter add to wall 15000
send j
settle 300 15000
send \r
expect tw-beta 25000
settle 1500 20000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "two devices: device 2's wall exited $RC:" "$OUT.tw2pc" "$OUT.tw2cap.err"
# One pane, the one it added.
_tw2_leaves=$(sed -n 's/^ *leaf [0-9][0-9]* //p' "$TWDEV2/mux/layout" | tr '\n' ' ')
[ "$_tw2_leaves" = "--sock $TWSOCK2#beta " ] || {
    echo "e2e FAIL: two devices: device 2's wall is '$_tw2_leaves', want the one pane it added:"
    cat "$TWDEV2/mux/layout"; exit 1; }
# ...and nothing device 1 has. Both `#0` sessions were live on both listed
# daemons for the whole run, and device 2 saw neither.
[ "$(grep -aoF "#0" "$OUT.tw2cap" | wc -l)" -eq 0 ] || {
    echo "e2e FAIL: two devices: device 2 saw a pane it never added:"
    cat "$OUT.tw2pc"; exit 1; }
for _twm in tw-alpha tw-bee; do
    [ "$(grep -aoF "$_twm" "$OUT.tw2cap" | wc -l)" -eq 0 ] || {
        echo "e2e FAIL: two devices: device 1's session $_twm painted on device 2:"
        cat "$OUT.tw2pc"; exit 1; }
done
# Device 1 is unchanged: its file still holds the two panes it was given,
# and nothing device 2 added.
_tw1_leaves=$(sed -n 's/^ *leaf [0-9][0-9]* //p' "$TWDEV1/mux/layout" | tr '\n' ' ')
[ "$_tw1_leaves" = "--sock $TWSOCK1#0 --sock $TWSOCK2#0 " ] || {
    echo "e2e FAIL: two devices: device 1's wall changed under device 2's add: '$_tw1_leaves'"
    cat "$TWDEV1/mux/layout"; exit 1; }
assert_stopped "$TWSOCK1" "$TW1PID" "two devices" "$OUT.tw1stop"
TW1PID=""
assert_stopped "$TWSOCK2" "$TW2PID" "two devices" "$OUT.tw2stop"
TW2PID=""
ok "two walls on the same daemons are two layouts; neither learns of the other's panes"

# ---- reading a host never starts a daemon; asking does ------------------
#
# `mux d endpoint` used to auto-start a daemon before answering, and the
# wall runs it over ssh once a second per listed host. A listed box with
# nothing on it therefore got a daemon — and a shell in session 0 — from a
# READ, and a `mux d stop` typed on that box was undone a second later.
#
# The rule now: a daemon starts when somebody asks, and the ask is a WORD.
# `mux HOST` and the picker's `c` spell `mux d endpoint --start`, which
# ensures a daemon and announces in the same run; the poller, `mux hosts`
# and every redial spell the bare verb, which can start nothing whatever
# the client does with the answer.
#
# The oracle is the OS, never the daemon: the socket INODE on the far side,
# and a walk of every pid on the box for a `mux d start` whose argv names
# that box's socket, which is to say born under its runtime dir. A
# daemon asked whether it exists cannot answer no. The shim log is the
# other half — it records the remote command, so "a read started nothing"
# is checked against the word that ran and not only against the outcome.
#
# Two fake boxes, because one cannot show that a birth landed on the row
# the user chose: the shim reads the host word and hands the remote command
# a runtime dir of that box's own, so `mux d`'s default socket resolves to a
# different path per host — which is exactly what two machines are.
NSHIM="${TMPDIR:-/tmp}/mux-e2e-nostart-shim-$$"
defer_rm "$NSHIM"
NSTATE="${TMPDIR:-/tmp}/mux-e2e-nostart-state-$$"
defer_rm "$NSTATE"
NRUNA="${TMPDIR:-/tmp}/mux-e2e-nostart-a-$$"
defer_rm "$NRUNA"
NRUNB="${TMPDIR:-/tmp}/mux-e2e-nostart-b-$$"
defer_rm "$NRUNB"
NSOCKA="$NRUNA/muxd.sock"
NSOCKB="$NRUNB/muxd.sock"
defer_sock "$NSOCKA" "$NSOCKB"
NSHIMLOG="$NSHIM/hosts"
export NRUNA NRUNB NSHIMLOG
mkdir -p "$NSHIM" "$NRUNA" "$NRUNB" "$NSTATE/mux"
: > "$NSHIMLOG"
# The option loop and the arity guard are `ssh_shim_head`'s; what is this
# leg's own is the runtime dir per host word, which is what makes two fake
# boxes two machines — `mux d`'s default socket resolves to a different path
# on each.
ssh_shim_head "$NSHIM/ssh"
cat >> "$NSHIM/ssh" <<'SHIM'
case "$1" in
    nostart-a@*) XDG_RUNTIME_DIR="${NRUNA:?}" ;;
    nostart-b@*) XDG_RUNTIME_DIR="${NRUNB:?}" ;;
    *) exit 96 ;;
esac
export XDG_RUNTIME_DIR
printf '%s\n' "$*" >> "${NSHIMLOG:?}"
shift
exec /bin/sh -c "$*"
SHIM
chmod +x "$NSHIM/ssh"
NPATH="$NSHIM:$(cd "$(dirname "$MUX")" && pwd):$PATH"
printf 'nostart-a@127.0.0.1\nnostart-b@127.0.0.1\n' > "$NSTATE/mux/hosts"

# The process walk: every pid on the box, and its ARGV asked through the
# oracle. `mux d start` and not any `mux` — the transient `mux d endpoint`
# processes share the runtime dir and are not daemons — and the box is
# identified by the socket path in that argv rather than by
# `XDG_RUNTIME_DIR` in the environment, because a process's environment
# block is not readable on every OS while its argv is. Nothing is lost:
# every starter spells `mux d start --sock PATH` and PATH is the runtime
# dir this variable holds, resolved by the ensure that forwarded it. The
# shim gives each fake host a runtime dir of its own, which is the whole of
# what makes them two machines. The daemon spells `d start` because that is
# the argv spawn.zig execs it with, which is also what makes `ps` legible.
#
# Matched with `case` rather than `grep`, so the path is a literal: a tmp
# directory whose name held a regex character would otherwise match a box
# it does not name.
nostart_daemons() {
    _nd=0
    for _np in $(ps -Ao pid= | tr -d ' '); do
        # A pid that exits mid-walk, or one this uid may not read, answers
        # nothing — which is not a daemon under $1 either way.
        _na=$(pid_args "$_np" 2>/dev/null) || continue
        case "$_na" in *"mux d start"*) ;; *) continue ;; esac
        case "$_na" in *"$1"/*) ;; *) continue ;; esac
        _nd=$((_nd + 1))
    done
    echo "$_nd"
}

nostart_empty() {  # $1 = runtime dir, $2 = socket, $3 = what was just done
    _nc=$(nostart_daemons "$1")
    [ "$_nc" -eq 0 ] || {
        echo "e2e FAIL: no-start: $3 left $_nc mux d start under $1, want 0"; exit 1; }
    [ ! -e "$2" ] || {
        echo "e2e FAIL: no-start: $3 bound $2 — a read started a daemon"; exit 1; }
}

# (1) `mux hosts` READS every listed box. Both rows unreachable, both boxes
# untouched.
XDG_STATE_HOME="$NSTATE" PATH="$NPATH" timeout 40 "$MUX" hosts > "$OUT.nsh" 2>&1 || {
    echo "e2e FAIL: no-start: mux hosts failed:"; cat "$OUT.nsh"; exit 1; }
[ "$(grep -c '\[unreachable\]' "$OUT.nsh")" = "2" ] || {
    echo "e2e FAIL: no-start: mux hosts did not call both boxes unreachable:"
    cat "$OUT.nsh"; exit 1; }
nostart_empty "$NRUNA" "$NSOCKA" "mux hosts"
nostart_empty "$NRUNB" "$NSOCKB" "mux hosts"
# The shim really ran, so the emptiness above is a refusal and not a leg
# that never dialled: one line per box, at least.
grep -q 'nostart-a@' "$NSHIMLOG" && grep -q 'nostart-b@' "$NSHIMLOG" || {
    echo "e2e FAIL: no-start: mux hosts dialled neither box:"; cat "$NSHIMLOG"; exit 1; }
# ...and it ran the READING word. The emptiness above is what a bare
# `mux d endpoint` leaves behind; `--start` here would be a read that
# starts, which is the whole bug, and it would leave the boxes empty only
# by luck of the spawn failing.
NSTARTS=$(grep -c -- '--start' "$NSHIMLOG" || true)
[ "$NSTARTS" -eq 0 ] || {
    echo "e2e FAIL: no-start: mux hosts spelled the asking word $NSTARTS times:"
    cat "$NSHIMLOG"; exit 1; }

# (2) and (3) in one wall. The wall has no layout, so it opens empty with
# the picker over it — which is where the keystrokes below go. It polls
# both boxes once a second — three seconds of that must change nothing —
# and then `c` on ROW 2 asks, which must give box B a daemon and box A
# none. Row 2 and not row 1: a birth that reached for the first row, or for
# the focus's host, would pass a one-row fixture and this one catches it.
set +e
SHELL=/bin/sh XDG_STATE_HOME="$NSTATE" PATH="$NPATH" timeout 90 "$PTYCLIENT" \
    --cols 100 --rows 30 --out "$OUT.nscap" --err "$OUT.nscap.err" -- \
    "$MUX" > "$OUT.nspc" 2>&1 <<'EOF'
expect unreachable 25000
settle 3000 25000
send 2
settle 500 15000
send c
settle 2000 30000
send printf 'ns-born-%s\n' ok\n
expect ns-born-ok 25000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
[ "$RC" -eq 0 ] || {
    echo "e2e FAIL: no-start: the picker leg exited $RC:"
    cat "$OUT.nspc"; echo "--- stderr ---"; cat "$OUT.nscap.err"; exit 1; }
# Box A was polled for the whole run and never asked for: still nothing.
nostart_empty "$NRUNA" "$NSOCKA" "a wall polling it"
# Box B was chosen. EXACTLY one daemon — a start per backoff, or a start on
# every poll cycle, would show as several.
NCB=$(nostart_daemons "$NRUNB")
[ "$NCB" -eq 1 ] || {
    echo "e2e FAIL: no-start: the picker birth left $NCB mux d start under $NRUNB, want 1"; exit 1; }
[ -S "$NSOCKB" ] || {
    echo "e2e FAIL: no-start: the picker birth started nothing on box B ($NSOCKB)"; exit 1; }
# ONE ask in the whole run, and it is box B's. Three seconds of polling
# both boxes ran the reading word many times over and the asking word
# never; the `c` ran it once. A poll that had inherited the flag would
# show a count here in the tens, and box A would not be empty above.
NASKS=$(grep -c -- '--start' "$NSHIMLOG" || true)
[ "$NASKS" -eq 1 ] || {
    echo "e2e FAIL: no-start: the picker leg spelled the asking word $NASKS times, want 1"
    grep -- '--start' "$NSHIMLOG"; exit 1; }
grep -q -- 'nostart-b@.*--start' "$NSHIMLOG" || {
    echo "e2e FAIL: no-start: the one ask did not go to box B, the row c chose:"
    grep -- '--start' "$NSHIMLOG"; exit 1; }
# ...and it is a real session, named `0` off the daemon's own empty list.
dump_session "$NSOCKB" 0 | grep -q 'ns-born-ok' || {
    echo "e2e FAIL: no-start: box B's session 0 never ran the marker:"
    "$MUX" d stats --sock "$NSOCKB"; exit 1; }

# (3b) A picker-born tile RECONNECTS, and a reconnect is nobody's ask. The
# ask buys one dial; if it survived into the redial loop, the poll's bug
# would simply have moved onto a tile — `mux d stop` typed on box B would be
# undone by the tile's next backoff, forever, and the fallback line would
# print onto the alternate screen once a cycle.
#
# A second wall, because the daemon-side oracles above have to run while
# that daemon is still alive. This one opens the picker on a wall that
# already has box B's session 0 as a tile, births a SECOND session there
# (so the tile under test is picker-born, not list-born — a list-born tile
# was never asked for and would pass this vacuously), and then stops the
# daemon from inside its own shell. `mux d stop` dies with the shell it
# killed; that is fine, the request was already sent, and the assertion
# below is on the process table rather than on that command's word.
set +e
SHELL=/bin/sh XDG_STATE_HOME="$NSTATE" PATH="$NPATH" timeout 90 "$PTYCLIENT" \
    --cols 100 --rows 30 --out "$OUT.nscap2" --err "$OUT.nscap2.err" -- \
    "$MUX" > "$OUT.nspc2" 2>&1 <<EOF
expect ns-born-ok 25000
send \x1cs
expect x forget 15000
send 2
settle 500 15000
send c
settle 2000 30000
send printf 'ns-two-%s\n' ok\n
expect ns-two-ok 25000
send mux d stop --sock $NSOCKB\n
settle 6000 30000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
[ "$RC" -eq 0 ] || {
    echo "e2e FAIL: no-start: the reconnect leg exited $RC:"
    cat "$OUT.nspc2"; echo "--- stderr ---"; cat "$OUT.nscap2.err"; exit 1; }
nostart_empty "$NRUNB" "$NSOCKB" "a picker-born tile reconnecting after mux d stop"
# Still ONE ask in the log. `nostart_empty` above says no daemon came back;
# this says no redial even SPELLED the word that could have brought one —
# the outcome and the argv, because a redial that asked and failed would
# leave the box empty too and pass the check above vacuously.
NASKS_3B=$(grep -c -- '--start' "$NSHIMLOG" || true)
[ "$NASKS_3B" -eq 1 ] || {
    echo "e2e FAIL: no-start: a reconnect spelled the asking word — log holds $NASKS_3B, want 1"
    grep -- '--start' "$NSHIMLOG"; exit 1; }

# (4) A cold `mux HOST` entry, on the box the wall left alone. One daemon,
# and the REMOTE said so in its own voice — `mux d endpoint --start`'s
# progress reaches the user's terminal over the same relayed stderr ssh's
# own diagnostics ride on, `narrate` being the entry dial's alone.
pipe_mux "$OUT.nsa" "$OUT.nsa.err" env SHELL=/bin/sh XDG_STATE_HOME="$NSTATE" \
    PATH="$NPATH" timeout 60 "$MUX" "nostart-a@127.0.0.1"
pipe_send 'printf "ns-cold-%%s\\n" ok\n'
await_out "$OUT.nsa" "ns-cold-ok" "no-start: the cold entry never served a session"
pipe_detach "no-start cold entry client"
NCA=$(nostart_daemons "$NRUNA")
[ "$NCA" -eq 1 ] || {
    echo "e2e FAIL: no-start: a cold mux HOST left $NCA mux d start under $NRUNA, want 1"
    cat "$OUT.nsa.err"; exit 1; }
[ -S "$NSOCKA" ] || {
    echo "e2e FAIL: no-start: the cold entry started nothing on box A"; exit 1; }
[ "$(grep -c '^mux d endpoint: starting' "$OUT.nsa.err")" = "1" ] || {
    echo "e2e FAIL: no-start: the cold entry did not ensure a daemon exactly once:"
    cat "$OUT.nsa.err"; exit 1; }
# ...by ONE ssh run carrying the asking word at box A. The count is
# cumulative over this whole block, so box B's picker ask above is the
# other one.
NASKS_A=$(grep -c -- 'nostart-a@.*--start' "$NSHIMLOG" || true)
[ "$NASKS_A" -eq 1 ] || {
    echo "e2e FAIL: no-start: the cold entry spelled the asking word $NASKS_A times at box A, want 1"
    grep -- '--start' "$NSHIMLOG"; exit 1; }
# TWO in the whole block, and no more: the picker Enter at box B and this
# entry at box A. Every other line in that log — two `mux hosts` dials, some
# seconds of two-host polling, and every redial of two walls — is the
# reading word. The total is what says the count above is not one ask
# hiding among several.
NASKS_ALL=$(grep -c -- '--start' "$NSHIMLOG" || true)
[ "$NASKS_ALL" -eq 2 ] || {
    echo "e2e FAIL: no-start: $NASKS_ALL asks in the whole block, want exactly 2"
    grep -- '--start' "$NSHIMLOG"; exit 1; }
# ...and box A goes down by the daemon's own verb, which makes the teardown
# one more assertion rather than a daemon handed to the trap: what a `mux d
# stop` owes on a box somebody started is exactly what an untouched box
# already shows — no `mux d start` under that runtime dir, and no socket.
# `assert_stopped` is not usable here: it wants a pid, and this daemon was
# forked by an ssh shim on the far side of a client.
"$MUX" d stop --sock "$NSOCKA" > "$OUT.nsastop" 2>&1 || {
    echo "e2e FAIL: no-start: mux d stop on box A exited nonzero:"
    cat "$OUT.nsastop"; exit 1; }
nostart_empty "$NRUNA" "$NSOCKA" "mux d stop"
ok "reading a host leaves it alone; a picker birth and a cold mux HOST each start one daemon"

# ---- ssh's stderr is mux's: no byte of it reaches the wall --------------
#
# The bug, as the user met it: a hosts line naming a box that is down. The
# wall polls it over ssh, ssh dies with `ssh: connect to host ... No route
# to host`, and that line landed on the wall's ALTERNATE SCREEN every few
# seconds — over tiles and rails, from a writer no repaint of ours can
# reach, because the ssh child's stderr was inherited.
#
# It is a pipe mux reads now. The bytes go nowhere on their own; the last
# line becomes the dial's reason, and the picker row — the one place that
# was saying a bare `unreachable` — quotes it.
#
# The oracle for the regression is the client's own STDERR FILE, not the
# capture. `ptyclient` requires `--err PATH` and gives the client fd 2 as
# that file, so an inherited ssh writes there and never onto the pty —
# the capture cannot see the original bug at all. The file is the
# faithful stand-in precisely because it is the same fd: on a real
# terminal fd 2 IS the screen mux is painting, under the alternate
# screen, which is where those bytes landed. The capture is the second
# pin, and a different claim — that every copy of the sentence ON SCREEN
# is one the picker row painted.
RSHIM="${TMPDIR:-/tmp}/mux-e2e-reason-shim-$$"
defer_rm "$RSHIM"
RSTATE="${TMPDIR:-/tmp}/mux-e2e-reason-state-$$"
defer_rm "$RSTATE"
mkdir -p "$RSHIM" "$RSTATE/mux"
RSAID='ssh: connect to host 10.255.255.1 port 22: No route to host'
# An ssh that fails the way an unplugged box fails: one line on stderr,
# 255, and nothing on stdout at all. 10.255.255.1 is a literal in the
# message, never dialled — the shim never reaches the network.
ssh_shim_head "$RSHIM/ssh"
cat >> "$RSHIM/ssh" <<SHIM
printf '%s\n' '$RSAID' >&2
exit 255
SHIM
chmod +x "$RSHIM/ssh"
RPATH="$RSHIM:$(cd "$(dirname "$MUX")" && pwd):$PATH"
printf 'noroute@127.0.0.1\n' > "$RSTATE/mux/hosts"

# The wall. One host, no sessions, so the picker opens by itself — which
# is also the row this leg is about. The settle is for the paint, not for
# a second cycle: a failed `.hand` poll answers `.pipe`, so `pollDelayMs`
# stretches the next one to ten seconds and three of them see exactly one.
# One is enough — the first cycle is where the inherited write happened.
set +e
SHELL=/bin/sh XDG_STATE_HOME="$RSTATE" PATH="$RPATH" timeout 60 "$PTYCLIENT" \
    --cols 100 --rows 30 --out "$OUT.rcap" --err "$OUT.rcap.err" -- \
    "$MUX" > "$OUT.rpc" 2>&1 <<'EOF'
expect unreachable 25000
settle 3000 25000
send \x1b
settle 500 15000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
[ "$RC" -eq 0 ] || {
    echo "e2e FAIL: reason: the wall leg exited $RC:"
    cat "$OUT.rpc"; echo "--- stderr ---"; cat "$OUT.rcap.err"; exit 1; }
# THE regression. A wall's own stderr is the terminal it is painting —
# under the alternate screen, over tiles and rails — and an INHERITED ssh
# writes straight onto it. `ptyclient` gives the client a real stderr
# FILE, so that file is where those bytes land here, and its emptiness of
# them is the same claim the screen makes in production. Mutate
# `spawnPipe`'s `.Pipe` back to `.Inherit` and this is what fires
# (measured: one copy of the line, above `mux: left the wall`).
grep -qF "$RSAID" "$OUT.rcap.err" && {
    echo "e2e FAIL: reason: ssh's own stderr reached the wall's own fd 2,"
    echo "          which on a terminal is the alternate screen:"
    cat "$OUT.rcap.err"; exit 1; }
# And on the SCREEN, as a count rather than an absence: mux itself puts
# ssh's sentence there now, inside the picker row, so "the bytes are not
# there" is no longer the question. Every copy in the capture must be one
# the row painted.
# `wc -l` on the matches and never `grep -c`, which counts LINES: a pty
# capture is escape bytes with almost no newlines in it, so five raw
# copies and one row would both come back as 1.
RALL=$(grep -aoF "$RSAID" "$OUT.rcap" | wc -l)
RROW=$(grep -aoF "unreachable: $RSAID" "$OUT.rcap" | wc -l)
[ "$RROW" -gt 0 ] || {
    echo "e2e FAIL: reason: the picker row never quoted ssh's line:"
    grep -aoF 'unreachable' "$OUT.rcap" | head -3; exit 1; }
[ "$RALL" -eq "$RROW" ] || {
    echo "e2e FAIL: reason: ssh's own stderr reached the wall's screen —"
    echo "          $RALL copies of its line, only $RROW of them in a row"
    exit 1; }
ok "a down host's ssh says why on the picker row and nothing on the wall"

# ---- ...and the entry dial says it in mux's own failure line ------------
#
# The other half of the rule: `narrate` is the entry dial's alone, so this
# is the ONE caller that relays ssh's bytes onward as they come — there is
# no wall yet, and the user is sitting in front of the wait. The failure
# line then quotes the same sentence, because `UnterminatedLine` names
# what mux observed while `No route to host` names what happened.
set +e
XDG_STATE_HOME="$RSTATE" PATH="$RPATH" timeout 40 "$MUX" "badhost@127.0.0.1" \
    > "$OUT.rentry" 2> "$OUT.rentry.err"
RC=$?
set -e
[ "$RC" -ne 0 ] || {
    echo "e2e FAIL: reason: a dial that never reached a daemon exited 0"; exit 1; }
# Relayed, not swallowed: the bytes ssh wrote reached the user's stderr.
grep -qF "$RSAID" "$OUT.rentry.err" || {
    echo "e2e FAIL: reason: the entry dial did not relay ssh's stderr:"
    cat "$OUT.rentry.err"; exit 1; }
# The LAST mux line is the verdict, and it is ssh's sentence rather than
# an error name only mux understands.
RLAST=$(grep -a '^mux: ' "$OUT.rentry.err" | tail -1)
[ "$RLAST" = "mux: badhost@127.0.0.1 over ssh: $RSAID" ] || {
    echo "e2e FAIL: reason: the entry dial's failure line is"
    echo "          '$RLAST'"
    echo "          want 'mux: badhost@127.0.0.1 over ssh: $RSAID'"
    cat "$OUT.rentry.err"; exit 1; }
ok "the entry dial relays ssh's stderr and fails in ssh's own words"

# ---- a replaced binary under a running daemon reaches the bar ------------
# `make install` swaps the file on disk and touches nothing that is
# running: the daemon keeps serving the old image, every long-lived wall
# keeps painting with it, and for months nothing anywhere said so — the
# night of 2026-09-01 was a wall flooding its screen with a bug that had
# been FIXED on disk for three days. The daemon now stats its own image's
# path per sessions_req and reports a different inode than the one it
# booted on as `stale`; the poll carries it; the bar wears it.
#
# Client and daemon here are the SAME build, so the version half of the
# drift word stays silent and `daemon stale` is the whole of it — which
# makes run 1 the other half of the claim: a live image, same version,
# paints NOTHING. The needle is asserted with the state word beside it
# (`[up, daemon stale]`), pinning where in the bar the word lands.
SBSTATE="${TMPDIR:-/tmp}/mux-e2e-sb-state-$$"
defer_rm "$SBSTATE"
SBBIN="${TMPDIR:-/tmp}/mux-e2e-sb-bin-$$"
defer_rm "$SBBIN"
SBSOCK="${TMPDIR:-/tmp}/muxd-e2e-sb-$$.sock"
defer_sock "$SBSOCK"
mkdir -p "$SBBIN" "$SBSTATE/mux"
cp "$MUX" "$SBBIN/mux"
"$SBBIN/mux" d start --sock "$SBSOCK" --shell /bin/sh > "$OUT.sb.d" 2>&1 &
defer_kill "$!"
wait_sock "$SBSOCK" "$OUT.sb.d" "stale-bit daemon never bound"

# Run 1: the image on disk IS the image running — the bar says nothing.
set +e
XDG_STATE_HOME="$SBSTATE" timeout 60 "$PTYCLIENT" --cols 100 --rows 30 \
    --out "$OUT.sb1" --err "$OUT.sb1.err" -- \
    "$MUX" --sock "$SBSOCK" > "$OUT.sb1.pc" 2>&1 <<'EOF'
expect [up] 15000
settle 2500 20000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "stale-bit: the pre-replace wall leg" "$OUT.sb1.pc" "$OUT.sb1.err"
grep -aq "daemon stale" "$OUT.sb1" && {
    echo "e2e FAIL: stale-bit: a daemon on its own living image was called stale"
    exit 1; }

# The install, by the same inode dance install(1) does: a new file RENAMED
# over the old, so the path the daemon booted on now names a different
# inode. A truncating copy would reuse the inode and prove nothing.
cp "$MUX" "$SBBIN/mux.new"
mv -f "$SBBIN/mux.new" "$SBBIN/mux"

# Run 2: the same daemon, one poll cycle later, wearing the word.
set +e
XDG_STATE_HOME="$SBSTATE" timeout 60 "$PTYCLIENT" --cols 100 --rows 30 \
    --out "$OUT.sb2" --err "$OUT.sb2.err" -- \
    "$MUX" --sock "$SBSOCK" > "$OUT.sb2.pc" 2>&1 <<'EOF'
expect [up, daemon stale] 25000
send \x1cd
waitexit 15000
EOF
RC=$?
set -e
rc0 "stale-bit: the post-replace wall leg" "$OUT.sb2.pc" "$OUT.sb2.err"
ok "a replaced binary under a running daemon reaches the bar as daemon stale"