Files
stack/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh
T
marcie 714953e277
ci/woodpecker/pr/ci Pipeline was canceled
framework tools/tmux: exact session matching in discovery AND sender target (codex PR #1466)
tmux target syntax accepts an unambiguous PREFIX, so both the socket
discovery loop and the sender's -t could match X-old for target X.
Discovery now probes has-session -t =NAME; the sender target gets '='
prepended unless it is already exact or compound (session:win.pane).
Arm A3 proves it: a prefix-named session no longer swallows a delivery
aimed at the shorter name (sender fails target-not-found rc 1).
2026-08-28 20:57:41 -05:00

154 lines
7.0 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd)
SEND_MESSAGE="$SCRIPT_DIR/send-message.sh"
AGENT_SEND="$SCRIPT_DIR/agent-send.sh"
SOCKET="mosaic-test-$RANDOM-$$"
TARGET="target-$RANDOM"
DEFAULT_TARGET="default-target-$RANDOM"
TMPDIR=$(mktemp -d)
ART_OUT=$(mktemp)
AMB_OUT=$(mktemp)
AMB_ERR=$(mktemp)
A2_OUT=$(mktemp)
A2_ERR=$(mktemp)
UNIQ_OUT=$(mktemp)
UNIQ_ERR=$(mktemp)
TWIN="twin-$RANDOM-$$"
trap 'tmux -L "$SOCKET" kill-server >/dev/null 2>&1 || true; tmux kill-session -t "$DEFAULT_TARGET" >/dev/null 2>&1 || true; tmux kill-session -t "$TWIN" >/dev/null 2>&1 || true; rm -rf "$TMPDIR" $ART_OUT $AMB_OUT $AMB_ERR $A2_OUT $A2_ERR $UNIQ_OUT $UNIQ_ERR' EXIT
fail() {
echo "FAIL: $*" >&2
exit 1
}
require_tmux() {
command -v tmux >/dev/null 2>&1 || fail "tmux is required"
}
capture_named() {
tmux -L "$SOCKET" capture-pane -t "=$TARGET:0.0" -p
}
capture_default() {
tmux capture-pane -t "=$DEFAULT_TARGET:0.0" -p
}
require_tmux
tmux -L "$SOCKET" new-session -d -s "$TARGET" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
tmux new-session -d -s "$DEFAULT_TARGET" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
"$SEND_MESSAGE" -L "$SOCKET" -t "=$TARGET" -m "named socket hello" >/tmp/send-message-named.out
sleep 0.2
named_pane="$(capture_named)" || fail "could not capture named socket pane"
grep -qF "named socket hello" <<<"$named_pane" || fail "send-message.sh did not deliver to named socket"
default_pane="$(capture_default)" || fail "could not capture default socket pane"
if grep -qF "named socket hello" <<<"$default_pane"; then
fail "send-message.sh leaked named-socket message to default tmux server"
fi
"$AGENT_SEND" -L "$SOCKET" -S "tester:source" -s "=$TARGET" -m "agent socket hello" >/tmp/agent-send-named.out
sleep 0.2
named_pane="$(capture_named)" || fail "could not capture named socket pane"
grep -qF "[tester:source ->" <<<"$named_pane" || fail "agent-send.sh did not include preamble"
grep -qF "agent socket hello" <<<"$named_pane" || fail "agent-send.sh did not deliver to named socket"
default_pane="$(capture_default)" || fail "could not capture default socket pane"
if grep -qF "agent socket hello" <<<"$default_pane"; then
fail "agent-send.sh leaked named-socket message to default tmux server"
fi
# Concurrency: parallel senders on one server must not cross-deliver or drop.
# Locks the unique-per-invocation paste buffer (a fixed buffer name raced:
# load overwrote load, -d deleted underneath — messages swapped between panes).
CONC_N=5
for i in $(seq 1 "$CONC_N"); do
tmux -L "$SOCKET" new-session -d -s "conc-$i" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
done
pids=()
for i in $(seq 1 "$CONC_N"); do
"$SEND_MESSAGE" -L "$SOCKET" -t "=conc-$i" -m "CONCPAYLOAD-${i}-END" >/dev/null &
pids+=($!)
done
for pid in "${pids[@]}"; do
wait "$pid" || fail "concurrent send-message.sh invocation exited non-zero"
done
sleep 0.2
for i in $(seq 1 "$CONC_N"); do
pane=$(tmux -L "$SOCKET" capture-pane -t "=conc-$i:0.0" -p)
grep -qF "CONCPAYLOAD-${i}-END" <<<"$pane" \
|| fail "concurrent send dropped payload for pane conc-$i"
for j in $(seq 1 "$CONC_N"); do
[ "$j" = "$i" ] && continue
if grep -qF "CONCPAYLOAD-${j}-END" <<<"$pane"; then
fail "concurrent send cross-delivered payload $j to pane conc-$i"
fi
done
done
# B1 (2026-08-29): socket default resolution in agent-send.sh. Measured
# defect: tasking sends without -L landed in a stale default-socket twin of
# the target seat; rc 0 reported honest delivery to the wrong pane.
# Arm A: session on MULTIPLE sockets, no -L -> refuse with rc 4 naming both.
tmux -L "$SOCKET" new-session -d -s "$TWIN" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
tmux new-session -d -s "$TWIN" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
amb_rc=0
env -u MOSAIC_TMUX_SOCKET "$AGENT_SEND" -s "$TWIN" -m "must refuse" >$AMB_OUT 2>$AMB_ERR || amb_rc=$?
[ "$amb_rc" -eq 4 ] || fail "ambiguity refusal: rc=$amb_rc want 4 (stderr: $(cat $AMB_ERR))"
grep -q "multiple sockets" $AMB_ERR || fail "ambiguity refusal message missing socket list"
grep -qF "$SOCKET" $AMB_ERR || fail "ambiguity refusal message does not name the test socket"
tmux kill-session -t "$TWIN" >/dev/null 2>&1 || true
tmux -L "$SOCKET" kill-session -t "$TWIN" >/dev/null 2>&1 || true
# Arm A2: with MOSAIC_TMUX_SOCKET exported, a twin session is NOT ambiguous:
# the env var disambiguates by precedence (codex PR #1466 blocker).
tmux -L "$SOCKET" new-session -d -s "$TWIN" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
tmux new-session -d -s "$TWIN" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
a2_rc=0
MOSAIC_TMUX_SOCKET="$SOCKET" "$AGENT_SEND" -s "$TWIN" -m "env disambiguated" >$A2_OUT 2>$A2_ERR || a2_rc=$?
[ "$a2_rc" -eq 0 ] || fail "env disambiguation: rc=$a2_rc (stderr: $(cat $A2_ERR))"
sleep 0.2
a2_pane="$(tmux -L "$SOCKET" capture-pane -t "=$TWIN:0.0" -p)" || fail "cannot capture twin (arm A2)"
grep -qF "env disambiguated" <<<"$a2_pane" || fail "env disambiguation did not deliver on the named socket"
a2_default="$(tmux capture-pane -t "=$TWIN:0.0" -p)" || true
if grep -qF "env disambiguated" <<<"$a2_default"; then
fail "env disambiguation cross-delivered to the default-socket twin"
fi
tmux kill-session -t "$TWIN" >/dev/null 2>&1 || true
tmux -L "$SOCKET" kill-session -t "$TWIN" >/dev/null 2>&1 || true
# Arm B: session unique to ONE socket, no -L -> auto-resolve to that socket
# and deliver there.
# Arm A3: prefix matching must not produce false socket hits (codex PR
# #1466): a session named TWIN-old must not count as a hit for target
# TWIN (tmux target syntax prefix-matches without '=').
PSEUDO="${TWIN}-old"
tmux new-session -d -s "$PSEUDO" -c "$TMPDIR" 'PS1=" " exec bash --noprofile --norc -i'
A3_ERR=$(mktemp)
a3_rc=0
env -u MOSAIC_TMUX_SOCKET "$AGENT_SEND" -s "$TWIN" -m "prefix trap" >/dev/null 2>"$A3_ERR" || a3_rc=$?
# TWIN exists nowhere (both twins killed after arm A2); with '=' the
# PSEUDO session is not a hit, so the sender must fail target-not-found
# (rc 1) instead of delivering into the prefix-named session.
[ "$a3_rc" -eq 1 ] || fail "prefix false-hit: rc=$a3_rc want 1 (stderr: $(cat "$A3_ERR"))"
if tmux capture-pane -t "=$PSEUDO:0.0" -p 2>/dev/null | grep -qF "prefix trap"; then
fail "delivery landed in the prefix-named session (false socket hit)"
fi
tmux kill-session -t "$PSEUDO" >/dev/null 2>&1 || true
rm -f "$A3_ERR"
uniq_rc=0
env -u MOSAIC_TMUX_SOCKET "$AGENT_SEND" -s "$TARGET" -m "autoresolved hello" >$UNIQ_OUT 2>$UNIQ_ERR || uniq_rc=$?
[ "$uniq_rc" -eq 0 ] || fail "unique auto-resolution: rc=$uniq_rc (stderr: $(cat $UNIQ_ERR))"
sleep 0.2
auto_pane="$(capture_named)" || fail "could not capture named socket pane (arm B)"
grep -qF "autoresolved hello" <<<"$auto_pane" || fail "auto-resolution did not deliver to the named-socket pane"
default_pane2="$(capture_default)" || fail "could not capture default socket pane (arm B)"
if grep -qF "autoresolved hello" <<<"$default_pane2"; then
fail "auto-resolution cross-delivered to the default socket pane"
fi
echo "ok - named tmux socket send tools"