a73x

04de1f6a

test: e2e pins $SHELL — the suite must not run the developer's login shell

a73x   2026-08-11 15:54

Commit message
test: e2e pins $SHELL — the suite must not run the developer's login shell

Found by soak, in a scenario that predates the handoff work. Run 2/10 failed
at "auto-started daemon lost the marker" in the M10 `muxd start` block, and
the capture showed why: the session was zsh, cloning its plugins.

The chain. `muxd start` there passes no --shell, so the daemon it spawns
resolves $SHELL — the developer's login shell, with their whole rc. That rc
roots its plugin manager at $XDG_CACHE_HOME, which this suite now points at
a fresh directory per run (the handoff cache holds a key, and the M14
scenarios poison it, so it has to be ours). Every session therefore
re-cloned its plugins from the NETWORK before the shell would answer, and
the marker missed its window. The daemon was healthy throughout.

Pinning $SHELL beside the XDG homes fixes the class rather than the
instance: the M13 blocks already pin it per command for exactly this
reason, and hoisting it covers the M10 block and any scenario added later
that forgets. A test suite that executes the operator's rc is not hermetic
whatever else it controls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

test/e2e.sh
Old New
@@ -23,6 +23,20 @@ XDG_STATE_HOME="${TMPDIR:-/tmp}/mux-e2e-state-$$"
23 # touch the developer's real ~/.cache/mux. 23 # touch the developer's real ~/.cache/mux.
24 XDG_CACHE_HOME="${TMPDIR:-/tmp}/mux-e2e-cache-$$" 24 XDG_CACHE_HOME="${TMPDIR:-/tmp}/mux-e2e-cache-$$"
25 export XDG_CONFIG_HOME XDG_STATE_HOME XDG_CACHE_HOME 25 export XDG_CONFIG_HOME XDG_STATE_HOME XDG_CACHE_HOME
26 # ...and the same argument for $SHELL, which is not an XDG home but is read
27 # the same way: every daemon this suite AUTO-STARTS gets no --shell flag and
28 # resolves $SHELL, so without this the suite runs the developer's login
29 # shell and its whole rc — arbitrary code, on the session under test.
30 #
31 # Found by soak, not by reasoning. The M10 `muxd start` block inherited zsh,
32 # whose plugin manager roots itself at $XDG_CACHE_HOME; pointing that at a
33 # fresh directory (the line above) made every session re-clone its plugins
34 # from the network before the shell would answer, and the scenario's marker
35 # missed its window. The daemon was healthy the whole time. The M13 blocks
36 # already pin SHELL per command for this reason; hoisting it here covers the
37 # M10 block too, and any scenario added later that forgets.
38 SHELL=/bin/sh
39 export SHELL
26 # Second daemon, used only by the M7 abort scenario; declared here so the 40 # Second daemon, used only by the M7 abort scenario; declared here so the
27 # trap below can reference them under `set -u` before they are ever started. 41 # trap below can reference them under `set -u` before they are ever started.
28 SOCK2="${TMPDIR:-/tmp}/muxd-e2e-abort-$$.sock" 42 SOCK2="${TMPDIR:-/tmp}/muxd-e2e-abort-$$.sock"