ci/woodpecker/pr/ci Pipeline was canceled
Measured defect (2026-08-28/29): tasking sends without -L landed in a stale default-socket TWIN of the target seat; rc 0 reported honest delivery to the wrong pane. Resolution order now: explicit -L > MOSAIC_TMUX_SOCKET (launcher-exported) > unique socket hit; multiple hits refuse with rc 4 naming the sockets. Local targets only; remote pass-through unchanged. Socket test gains arms A (twin session on two sockets -> rc 4 with the socket list) and B (unique session auto-resolves and delivers on the right socket, no cross-delivery). Test remains CI-excluded per its signed exclusion (no tmux in the CI image, #1017).
112 lines
4.8 KiB
Bash
Executable File
112 lines
4.8 KiB
Bash
Executable File
#!/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)
|
||
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"' 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
|
||
"$AGENT_SEND" -s "$TWIN" -m "must refuse" >/tmp/amb.out 2>/tmp/amb.err || amb_rc=$?
|
||
[ "$amb_rc" -eq 4 ] || fail "ambiguity refusal: rc=$amb_rc want 4 (stderr: $(cat /tmp/amb.err))"
|
||
grep -q "multiple sockets" /tmp/amb.err || fail "ambiguity refusal message missing socket list"
|
||
grep -qF "$SOCKET" /tmp/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 B: session unique to ONE socket, no -L -> auto-resolve to that socket
|
||
# and deliver there.
|
||
uniq_rc=0
|
||
"$AGENT_SEND" -s "$TARGET" -m "autoresolved hello" >/tmp/uniq.out 2>/tmp/uniq.err || uniq_rc=$?
|
||
[ "$uniq_rc" -eq 0 ] || fail "unique auto-resolution: rc=$uniq_rc (stderr: $(cat /tmp/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"
|