diff --git a/packages/mosaic/framework/tools/tmux/agent-send.sh b/packages/mosaic/framework/tools/tmux/agent-send.sh index 675f4d63..68ce783e 100755 --- a/packages/mosaic/framework/tools/tmux/agent-send.sh +++ b/packages/mosaic/framework/tools/tmux/agent-send.sh @@ -155,15 +155,19 @@ B64=$(printf '%s' "$FULL" | base64 -w0) vflag=""; [ "$VERBOSE" = 1 ] && vflag="-v" -# Socket default resolution (B1, 2026-08-29): explicit -L wins; else the -# launcher-exported MOSAIC_TMUX_SOCKET; else the tmux default socket. When -# the target session exists on MULTIPLE socket servers and no explicit -L -# was given, refuse with a disambiguation error instead of silently picking -# one (measured 2026-08-28/29: tasking sends landed in a stale default- -# socket twin of the target seat; rc 0 reported honest delivery to the -# wrong pane). Local targets only; remote targets pass -L through as-is. +# Socket default resolution (B1, 2026-08-29). Precedence: explicit -L > +# launcher-exported MOSAIC_TMUX_SOCKET > unique socket hit > refusal on +# ambiguity > tmux default socket. The ambiguity refusal fires ONLY when +# no explicit or env choice exists and the session name lives on multiple +# servers (measured 2026-08-28/29: tasking sends landed in a stale +# default-socket twin; rc 0 reported honest delivery to the wrong pane). +# Socket discovery scans tmux's own socket dir, ${TMUX_TMPDIR:-/tmp}/tmux-UID +# (codex PR #1466: TMPDIR is not where tmux keeps -L sockets). +if [ -z "$SOCKET_NAME" ] && [ -n "${MOSAIC_TMUX_SOCKET:-}" ]; then + SOCKET_NAME="$MOSAIC_TMUX_SOCKET" +fi if [ -z "$SOCKET_NAME" ] && [ -z "$SSH_TARGET" ]; then - socket_dir="${TMPDIR:-/tmp}/tmux-$(id -u)" + socket_dir="${TMUX_TMPDIR:-/tmp}/tmux-$(id -u)" hits="" for sf in "$socket_dir"/*; do [ -S "$sf" ] || continue @@ -173,17 +177,12 @@ if [ -z "$SOCKET_NAME" ] && [ -z "$SSH_TARGET" ]; then hit_count=$(printf '%s' "$hits" | wc -w) if [ "$hit_count" -gt 1 ]; then echo "agent-send.sh: REFUSING - session '$DST_SESSION' exists on multiple sockets:$hits" >&2 - echo " Pass -L explicitly (or set MOSAIC_TMUX_SOCKET)." >&2 + echo " Pass -L explicitly (or export MOSAIC_TMUX_SOCKET to disambiguate)." >&2 exit 4 elif [ "$hit_count" -eq 1 ]; then SOCKET_NAME="${hits# }" - elif [ -n "${MOSAIC_TMUX_SOCKET:-}" ]; then - SOCKET_NAME="$MOSAIC_TMUX_SOCKET" fi fi -if [ -z "$SOCKET_NAME" ] && [ -n "${MOSAIC_TMUX_SOCKET:-}" ] && [ -n "$SSH_TARGET" ]; then - SOCKET_NAME="$MOSAIC_TMUX_SOCKET" -fi socket_args=() if [ -n "$SOCKET_NAME" ]; then diff --git a/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh b/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh index 5e72b555..79c3434a 100755 --- a/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh +++ b/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh @@ -88,17 +88,34 @@ done 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=$? +env -u MOSAIC_TMUX_SOCKET "$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 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" >/tmp/a2.out 2>/tmp/a2.err || a2_rc=$? +[ "$a2_rc" -eq 0 ] || fail "env disambiguation: rc=$a2_rc (stderr: $(cat /tmp/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. uniq_rc=0 -"$AGENT_SEND" -s "$TARGET" -m "autoresolved hello" >/tmp/uniq.out 2>/tmp/uniq.err || uniq_rc=$? +env -u MOSAIC_TMUX_SOCKET "$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)"