#!/usr/bin/env bash # test-send-message-verdict.sh — locks the fail-loud verdict logic of the patched # send-message.sh against three real tmux-pane fixtures on a throwaway socket: # # 1. DELIVERED — a REPL that renders a `❯ ` input box and submits on Enter # (text scrolls to history, box clears) => exit 0 "✓ delivered". # 2. DELIVERED — a pane with NO prompt glyph that DOES submit => exit 0. A pi # seat is this fixture (U+2500 rule, no glyph). Reshaped for # #1257; see the note at the fixture for why the old exit-2 # assertion was wrong. # 2b. UNCONFIRMED— a glyphless pane that never submits (raw/no-echo hung TUI) # => must fail loud. This carries the historical # false-positive guard that fixture 2 used to be credited with. # 3. DRAFT — a `❯ `-prompt pane that never submits (message stays on the # input line) => exit 2, stderr "unsubmitted draft". # 4. DELIVERED — a pane whose input box is two `─` rules with NO prompt glyph # (box shape) anywhere (pi's shape) and which submits => exit 0. Pre-#1362 # the glyph probe could not see this box at all, so EVERY send # to such a pane reported "may be UNDELIVERED" while landing. # 5. DRAFT — the same glyphless box, holding our tail across every flush # (box shape) Enter => exit 2, stderr "unsubmitted draft". Pre-#1362 this # also reported unconfirmed, so the true state was invisible. # 6. DELIVERED — a SHAPELESS REPL (no glyph, no box) that submits => exit 0. # The 2026-09-04 scratch probe regression: shape probing alone reports "may # be UNDELIVERED" on this pane while the message is consumed; the cursor-row # draft transition is the authoritative runtime-agnostic verdict. # 6b. UNCONFIRMED— a shapeless pane in raw/no-echo mode that never reads stdin # (shapeless) => exit 2 "could not confirm submission" (never delivered). set -uo pipefail HERE=$(cd -- "$(dirname -- "$0")" && pwd) SEND="${SEND:-$HERE/send-message.sh}" SOCKET="verdict-test-$RANDOM-$$" TMP=$(mktemp -d) trap 'tmux -L "$SOCKET" kill-server >/dev/null 2>&1 || true; rm -rf "$TMP"' EXIT PASS=0; FAIL=0 ok() { PASS=$((PASS+1)); printf ' ok %s\n' "$1"; } no() { FAIL=$((FAIL+1)); printf ' FAIL %s\n %s\n' "$1" "$2"; } command -v tmux >/dev/null 2>&1 || { echo "tmux required" >&2; exit 1; } # --- Fixture 1: a submitting REPL with a ❯ prompt box (interactive bash, glyph PS1). # readline strips bracketed-paste markers just like a real agent REPL; Enter # executes (text -> scrollback), leaving a fresh empty `❯ ` box. tmux -L "$SOCKET" new-session -d -s repl -c "$TMP" \ 'PS1="❯ " exec bash --noprofile --norc -i' sleep 0.3 out=$("$SEND" -L "$SOCKET" -t "=repl" -m "verdict fixture one delivered ok" 2>"$TMP/e1"); rc=$? if [ "$rc" -eq 0 ] && grep -qF "✓ delivered" <<<"$out"; then ok "delivered: ❯-prompt REPL that submits => exit 0 ✓ delivered" else no "delivered: ❯-prompt REPL that submits => exit 0 ✓ delivered" "rc=$rc out=[$out] err=[$(cat "$TMP/e1")]" fi # --- Fixture 2: NO prompt glyph, and the pane DOES submit (interactive bash). # RESHAPED 2026-08-16 (#1257), deliberately. This fixture previously asserted # exit 2 here and was labelled "false-positive FIXED". That assertion was wrong, # and locking it in is what kept E7 alive: the pane submits, so "delivered" is # the truth, and a pi seat — whose input box is a bare U+2500 rule with no glyph # — IS this fixture. Reporting exit 2 for it told operators a delivered message # may be undelivered, and the retry that advice invites is the duplicate. # # The guard this fixture was reaching for is real and is NOT dropped: "never # infer delivered from absence" is now enforced positively by fixture 2b below # (glyphless AND not submitting => must fail) and by fixture 3 (locatable box # still carrying our tail => draft). Absence alone decides nothing either way. tmux -L "$SOCKET" new-session -d -s noglyph -c "$TMP" \ 'PS1="sh-noglyph$ " exec bash --noprofile --norc -i' sleep 0.3 out=$("$SEND" -L "$SOCKET" -t "=noglyph" -m "verdict fixture two must fail loud" 2>"$TMP/e2"); rc=$? if [ "$rc" -eq 0 ] && grep -qF "✓ delivered" <<<"$out"; then ok "delivered: glyphless pane that submits => exit 0 (runtime-agnostic, E7 FIXED)" else no "delivered: glyphless pane that submits => exit 0" "rc=$rc out=[$out] err=[$(cat "$TMP/e2")]" fi # --- Fixture 2b: NO prompt glyph AND never submits — a hung managed TUI holding the # terminal in raw/no-echo, which is what a stuck agent seat actually is (measured # on live pi: stty -echo -icanon). Nothing is echoed, nothing is consumed, so # there is no positive evidence of submission and the tool MUST fail loud. This # is the historical false-positive guard, kept as a positive test. tmux -L "$SOCKET" new-session -d -s rawstuck -c "$TMP" \ 'bash --noprofile --norc -c "stty -echo -icanon min 1 time 0 2>/dev/null; exec sleep infinity"' sleep 0.3 if out=$("$SEND" -L "$SOCKET" -t "=rawstuck" -r 1 -m "verdict fixture two-b never submitted" 2>"$TMP/e2b"); then no "unconfirmed: glyphless hung TUI must NOT report success" "expected non-zero, got 0 (out=[$out])" else rc=$? if [ "$rc" -ne 0 ] && grep -qF "could not confirm submission" "$TMP/e2b"; then ok "unconfirmed: glyphless hung TUI (raw/no-echo) => non-zero + 'could not confirm submission'" else no "unconfirmed: glyphless hung TUI => non-zero + stderr" "rc=$rc err=[$(cat "$TMP/e2b")]" fi fi # --- Fixture 3: a ❯ box that never submits (sleep ignores stdin; TTY echo keeps the # pasted tail sitting on the ❯ line) => draft => exit 2. tmux -L "$SOCKET" new-session -d -s draft -c "$TMP" \ 'printf "❯ "; exec sleep infinity' sleep 0.3 if out=$("$SEND" -L "$SOCKET" -t "=draft" -r 1 -m "verdict fixture three stuck unsubmitted draft" 2>"$TMP/e3"); then no "draft: unsubmitted message must NOT report success" "expected exit 2, got 0 (out=[$out])" else rc=$? if [ "$rc" -eq 2 ] && grep -qF "unsubmitted draft" "$TMP/e3"; then ok "draft: stuck ❯-line message => exit 2 + 'unsubmitted draft'" else no "draft: stuck ❯-line message => exit 2 + stderr" "rc=$rc err=[$(cat "$TMP/e3")]" fi fi # --- Fixtures 4 and 5: a pi-shaped pane. The input box is two `─` rules with the # text between them and NO prompt glyph anywhere, so the glyph probe alone can # never locate it and every send reports "may be UNDELIVERED" (#1362). The # renderer below is the shape, not the runtime: MODE=clear submits (box empties), # MODE=keep leaves the text sitting in the box. cat > "$TMP/pibox.sh" <<'PIBOX' #!/usr/bin/env bash MODE=${1:-clear} RULE=$(printf '─%.0s' $(seq 1 60)) buf="" draw() { printf '\033[H\033[2J' printf 'fixture output line\n\n' printf '%s\n' "$RULE" printf '%s\n' "$buf" printf '%s\n' "$RULE" printf '~/fixture (main)\n' printf 'tok 0 model fixture\n' } draw while IFS= read -r line; do # keep: hold the tail across every flush Enter, which is what a stuck draft does. if [ "$MODE" = keep ]; then [ -n "$line" ] && buf=$line; else buf=""; fi draw done PIBOX chmod +x "$TMP/pibox.sh" tmux -L "$SOCKET" new-session -d -s pibox -c "$TMP" "exec bash '$TMP/pibox.sh' clear" sleep 0.3 out=$("$SEND" -L "$SOCKET" -t "=pibox" -m "pi fixture four delivered ok" 2>"$TMP/e4"); rc=$? if [ "$rc" -eq 0 ] && grep -qF "✓ delivered" <<<"$out"; then ok "delivered: glyphless box-drawn REPL that submits => exit 0 ✓ delivered" else no "delivered: glyphless box-drawn REPL that submits => exit 0 ✓ delivered" "rc=$rc out=[$out] err=[$(cat "$TMP/e4")]" fi tmux -L "$SOCKET" new-session -d -s piboxdraft -c "$TMP" "exec bash '$TMP/pibox.sh' keep" sleep 0.3 if out=$("$SEND" -L "$SOCKET" -t "=piboxdraft" -r 1 -m "pi fixture five stuck in the box" 2>"$TMP/e5"); then no "draft: glyphless box-drawn pane holding our tail must NOT report success" "expected exit 2, got 0 (out=[$out])" else rc=$? if [ "$rc" -eq 2 ] && grep -qF "unsubmitted draft" "$TMP/e5"; then ok "draft: message left in a glyphless box => exit 2 + 'unsubmitted draft'" else no "draft: message left in a glyphless box => exit 2 + stderr" "rc=$rc err=[$(cat "$TMP/e5")]" fi fi # --- Fixtures 6 and 6b: a SHAPELESS REPL. The pane renders nothing at all: no # prompt glyph and no rule box, so locate_input_box() alone can never see it # and shape-probing alone reports "may be UNDELIVERED" on a delivered message # (measured live 2026-09-04, scratch probe: a shapeless consumer CONSUMED the # message while the shipped shape probe exited 2 with retry advice - the exact # #1257 regression). The cursor-row draft transition is the authoritative, # runtime-agnostic delivered verdict: fixture 6's consumer submits => exit 0. # Fixture 6b is the guard arm: a shapeless pane whose foreground never reads # stdin keeps the echoed paste on the cursor line across every flush Enter => # DRAFT => exit 2, never delivered. cat > "$TMP/shapeless.py" <<'SHAPELESS' import sys for line in sys.stdin: pass # consume and render nothing SHAPELESS tmux -L "$SOCKET" new-session -d -s shapeless -c "$TMP" "exec python3 -u '$TMP/shapeless.py'" sleep 0.3 out=$("$SEND" -L "$SOCKET" -t "=shapeless" -m "fixture six shapeless consumed ok" 2>"$TMP/e6"); rc=$? if [ "$rc" -eq 0 ] && grep -qF "✓ delivered" <<<"$out"; then ok "delivered: shapeless REPL that submits => exit 0 ✓ delivered (probe regression)" else no "delivered: shapeless REPL that submits => exit 0 ✓ delivered" "rc=$rc out=[$out] err=[$(cat "$TMP/e6")]" fi # MEASURED LIMIT (2026-09-04, this suite's development): a shapeless pane in # COOKED mode whose foreground never reads stdin (e.g. 'sleep infinity') scrolls # its kernel echo off the cursor row on the flush Enter, so no runtime-agnostic # signal available to the sender distinguishes it from a delivering pane. The # non-reading guard therefore requires either a locatable box still carrying the # tail (fixture 3) or raw/no-echo mode (fixture 2b). Real REPL seats read stdin, # which is why this limit is not reachable against agent seats; recorded here so # nobody rediscovers it as a silent gap. tmux -L "$SOCKET" new-session -d -s shapelessraw -c "$TMP" 'stty raw -echo; exec sleep infinity' sleep 0.3 if out=$("$SEND" -L "$SOCKET" -t "=shapelessraw" -r 1 -m "fixture six b shapeless raw never consumed" 2>"$TMP/e6b"); then no "unconfirmed: shapeless raw non-reading pane must NOT report success" "expected exit 2, got 0 (out=[$out])" else rc=$? if [ "$rc" -eq 2 ] && grep -qF "could not confirm submission" "$TMP/e6b"; then ok "unconfirmed: shapeless raw non-reading pane => exit 2 + 'could not confirm submission'" else no "unconfirmed: shapeless raw non-reading pane => exit 2 + stderr" "rc=$rc err=[$(cat "$TMP/e6b")]" fi fi echo "---" echo "PASS=$PASS FAIL=$FAIL" [ "$FAIL" -eq 0 ]