fix(tmux): resolve send-message targets to an exact session and window

tmux resolves the two halves of a target with different, individually
dangerous defaults, and send-message.sh took both defaults:

  * An unpinned name PREFIX-matches. With `foobar` alive and no `foo`,
    `-t foo` resolves to `foobar` at rc=0 -- pasted, Enter-ed, verified
    and reported OK against the wrong agent's pane.
  * A bare `=name` is only half a pin. capture-pane REJECTS it ("can't
    find pane") while list-panes silently PREFIX-MATCHES it, and the
    validation at :76 uses list-panes -- so for any caller already
    supplying `=name`, that rewrite was the only thing between them and
    a wrong-session pass.

The direction is what makes this expensive. Paste (:93-94), Enter (:151)
and the verifying capture (:153) all read one EFFECTIVE_TARGET, so a
wrong-window send is confirmed by a wrong-window read: it manufactures a
false "delivered", not a loud failure. A false negative gets
investigated; a false positive gets believed.

Normalise to `=session:` -- exact session, active window. Explicit tmux
ids (%pane, @window, $session) pass through untouched.

BEHAVIOUR CHANGE for callers that already pass `=name`: they previously
landed on `:0.0` (window 0 unconditionally) and now land on the session's
ACTIVE window. This is the intended fix -- window 0 is not where a
multi-window agent is sitting -- but it does move a live target rather
than being a no-op normalisation.

Test: test-send-message-target.sh covers all four arms (absent name must
not prefix-match, delivery follows the active window, an explicit window
part is preserved, a unique prefix is still refused). Proven able to go
red: against the pre-fix script it FAILs at arm 1, and with arm 1 removed
it FAILs at arm 2. The multi-window fixture is load-bearing -- a
single-window session cannot tell `=s:` from `=s:0.0`, which is why this
survived.

It is registered as a signed enumeration exclusion rather than on a CI
surface: it drives a real tmux server and the CI image ships no tmux,
the same condition its two siblings are already excluded under. It
hard-fails when tmux is absent rather than skipping, so it cannot go
quietly green where it cannot run.
This commit is contained in:
jarvis-enhance
2026-08-24 09:47:57 -05:00
parent 9014a510a9
commit a77afe6778
3 changed files with 94 additions and 6 deletions
@@ -29,6 +29,10 @@ packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh | unmeasured i
# --- tools/tmux: require a live tmux server ---
packages/mosaic/framework/tools/tmux/test-send-message-socket.sh | requires a real tmux server on a throwaway socket; CI image ships no tmux; #1017 burndown (needs tmux in image or a signed permanent exclusion)
packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh | requires real tmux-pane fixtures on a throwaway socket; CI image ships no tmux; #1017 burndown (same condition as its sibling)
# The entry below is NOT covered by the #1017 signature block above: it was
# signed by jarvis-enhance (dragon-lin, 2026-08-24) at a later base, for the
# test added alongside the send-message.sh exact-target fix.
packages/mosaic/framework/tools/tmux/test-send-message-target.sh | requires a real tmux server on a throwaway socket, and specifically a MULTI-WINDOW session (the bug it guards is invisible on a single-window fixture); CI image ships no tmux; same burndown condition as its two siblings above
# --- single-suite directories: unmeasured in CI ---
@@ -64,13 +64,31 @@ if [ -n "$SOCKET_NAME" ]; then
tmux_cmd+=(-L "$SOCKET_NAME")
fi
# tmux accepts `=session` for some commands, but pane-level commands such as
# capture-pane require a pane-qualified target. Keep exact-session addressing
# convenient while avoiding accidental prefix matches.
# Normalise the target to an EXACT session plus a window part, because tmux
# resolves the two halves with different and individually dangerous defaults:
#
# * An unpinned name is a PREFIX match. With a session `foobar` alive and
# no session `foo`, `-t foo` resolves to `foobar` at rc=0, so a message is
# delivered, verified and reported OK against the wrong agent's pane.
# * A bare `=name` is not enough on its own: capture-pane REJECTS it
# ("can't find pane") while list-panes silently PREFIX-MATCHES it, so the
# validation below would pass on a session the capture cannot read.
# * A trailing `:` follows the session's ACTIVE window. Pinning `:0.0`
# instead addresses window 0 unconditionally, and since the paste, the
# Enter and the verifying capture all use EFFECTIVE_TARGET, a multi-window
# agent gets typed into window 0 and confirmed by reading window 0 --
# a false "delivered" rather than a loud failure.
#
# Explicit tmux ids (%pane, @window, $session) are passed through untouched;
# prefixing `=` to them would break addressing that is already unambiguous.
EFFECTIVE_TARGET=$TARGET
if [[ "$TARGET" == =* && "$TARGET" != *:* ]]; then
EFFECTIVE_TARGET="${TARGET}:0.0"
fi
case "$TARGET" in
=*|%*|@*|\$*) ;;
*) EFFECTIVE_TARGET="=$TARGET" ;;
esac
case "$EFFECTIVE_TARGET" in
=*) [[ "$EFFECTIVE_TARGET" == *:* ]] || EFFECTIVE_TARGET="${EFFECTIVE_TARGET}:" ;;
esac
# Target must resolve to a live pane.
if ! "${tmux_cmd[@]}" list-panes -t "$EFFECTIVE_TARGET" >/dev/null 2>&1; then
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Target normalisation: send-message.sh must address an EXACT session and the
# session's ACTIVE window. Both halves have caused silent wrong-pane delivery:
# * an unpinned name prefix-matches, so a message for an absent session is
# delivered to a different agent and reported OK;
# * a `:0.0` pin addresses window 0 regardless of where the agent is, and
# because the paste, the Enter and the verifying capture share one target,
# the wrong window is also the window that confirms the send.
# Both rows below FAIL against the pre-fix script, which is the point of them.
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd)
SEND_MESSAGE="$SCRIPT_DIR/send-message.sh"
SOCKET="mosaic-test-target-$RANDOM-$$"
TMPDIR=$(mktemp -d)
trap 'tmux -L "$SOCKET" kill-server >/dev/null 2>&1 || true; rm -rf "$TMPDIR"' EXIT
fail() { echo "FAIL: $*" >&2; exit 1; }
command -v tmux >/dev/null 2>&1 || fail "tmux is required"
tmux_() { tmux -L "$SOCKET" "$@"; }
newsess() { tmux_ new-session -d -s "$1" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'; }
hits() { tmux_ capture-pane -p -t "$1" 2>/dev/null | grep -cF "$2" || true; }
# ── 1. an absent session must not prefix-match a live one ───────────────────
newsess sibling-long
nonce="absent-target-$RANDOM"
rc=0; "$SEND_MESSAGE" -L "$SOCKET" -t sibling -m "$nonce" >/dev/null 2>&1 || rc=$?
[ "$rc" -ne 0 ] || fail "send to absent session 'sibling' returned rc=0 (prefix-matched)"
[ "$(hits sibling-long "$nonce")" -eq 0 ] || fail "message for absent 'sibling' was delivered to 'sibling-long'"
# positive control: the detector above can see a real delivery
nonce_ok="control-$RANDOM"
"$SEND_MESSAGE" -L "$SOCKET" -t sibling-long -m "$nonce_ok" >/dev/null 2>&1 \
|| fail "send to a live session failed"
[ "$(hits sibling-long "$nonce_ok")" -gt 0 ] || fail "control: live delivery not observed — detector is blind"
# ── 2. delivery follows the ACTIVE window, not window 0 ─────────────────────
# A single-window fixture cannot tell `=s:` from `=s:0.0`; the active window
# must be non-zero or this test proves nothing.
newsess multi
tmux_ new-window -t multi -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
tmux_ select-window -t multi:1
active=$(tmux_ display-message -p -t multi '#{window_index}')
[ "$active" = "1" ] || fail "fixture setup: expected active window 1, got $active"
for target in multi "=multi"; do
nonce="active-win-$RANDOM"
"$SEND_MESSAGE" -L "$SOCKET" -t "$target" -m "$nonce" >/dev/null 2>&1 \
|| fail "send to '$target' failed"
[ "$(hits multi:1 "$nonce")" -gt 0 ] || fail "'$target' did not deliver to the active window"
[ "$(hits multi:0 "$nonce")" -eq 0 ] || fail "'$target' delivered to window 0 instead of the active window"
done
# ── 3. an explicit window part is preserved ─────────────────────────────────
nonce="explicit-win-$RANDOM"
"$SEND_MESSAGE" -L "$SOCKET" -t multi:0 -m "$nonce" >/dev/null 2>&1 || fail "send to 'multi:0' failed"
[ "$(hits multi:0 "$nonce")" -gt 0 ] || fail "explicit 'multi:0' did not deliver to window 0"
# ── 4. a unique prefix of a live session is still refused ───────────────────
nonce="prefix-$RANDOM"
rc=0; "$SEND_MESSAGE" -L "$SOCKET" -t mult -m "$nonce" >/dev/null 2>&1 || rc=$?
[ "$rc" -ne 0 ] || fail "send to prefix 'mult' returned rc=0"
[ "$(hits multi:1 "$nonce")" -eq 0 ] || fail "prefix 'mult' was delivered to 'multi'"
echo "PASS: send-message.sh target normalisation"