fix(framework): send-message.sh fail-loud submission verdict + regression tests (Patch 6)
Some checks failed
ci/woodpecker/pr/ci Pipeline failed

Invert the delivery verdict from negative to positive evidence. Previously,
success was declared by the ABSENCE of a draft snippet on a prompt line: if
the `❯|^>|│ >` glyph matched nothing (busy-receiver draft on an unmatched
line, or wrong-pane drift), an UNSUBMITTED message read as "✓ delivered"
exit 0 — a silent worker->lead relay stall (filed by UC-LEAD, PR #3046:
three review verdicts reported delivered but never surfaced for ~50 min).

Fix: success now requires POSITIVE evidence — either the queued banner, or
the REPL input box located AND clear of the message tail. If the input box
cannot be located, status is `unconfirmed` -> exit 2 + stderr ("could not
confirm submission ... may be UNDELIVERED"). The near-dead `*)` indeterminate
default also flips from silently returning 0 to exit 2. Bracketed-paste and
double-Enter delivery mechanics are unchanged; queued/draft/delivered
semantics are preserved, only the failure-mode default direction changes.

Adds `test-send-message-verdict.sh`: 3 real tmux-pane fixtures (delivered /
unconfirmed-glyphless / draft) locking the fail-loud verdict logic, and
upgrades `test-send-message-socket.sh` fixtures from a glyphless bash prompt
to `❯`-prompt REPLs so the patched binary can read delivery positively.

Reproduce-first evidence (baseline vs patched, glyphless-pane fixture):
  baseline: rc=0, stdout "✓ delivered to =noglyph" (silent false positive)
  patched:  rc=2, stderr "could not confirm submission ... may be UNDELIVERED"

Test results after port:
  test-send-message-verdict.sh -> PASS=3 FAIL=0
  test-send-message-socket.sh  -> ok

Firewall: grep -niE 'jason|woltje|jarvis' on changed files = 0 hits;
tools/quality/scripts/verify-sanitized.sh -> sanitization gate passed.
shellcheck: 1 pre-existing SC2034 finding (unused loop var `attempt`,
present identically in baseline) carried through; 0 new findings.

Part of #891
This commit is contained in:
mosaic-coder
2026-07-25 16:08:38 -05:00
parent 2698ddb7b5
commit 0f0fa7f1ea
3 changed files with 104 additions and 12 deletions

View File

@@ -31,7 +31,11 @@
# EXIT CODES
# 0 delivered (submitted) or queued (agent busy; will process when free)
# 1 tmux target not found
# 2 message still appears to be an unsubmitted draft after retries
# 2 submission NOT confirmed — either still an unsubmitted draft, or the REPL
# input prompt could not be located to confirm the message actually landed.
# Delivery is NEVER inferred from absence of evidence: if we cannot positively
# see the input box clear of the message (or the queued banner), we fail loud
# so the sender learns immediately instead of a silent worker->lead stall.
# 3 usage error
set -uo pipefail
@@ -93,8 +97,14 @@ printf '%s' "$MSG" | "${tmux_cmd[@]}" load-buffer -b "$BUF" -
# would otherwise accumulate forever.
sleep 0.5
# 2) Submit, then verify; flush with another Enter if it is still a draft.
status="sent"
# 2) Submit, then POSITIVELY confirm submission; flush with another Enter if it is
# still a draft. Success requires positive evidence — the queued banner, OR the
# REPL input box located AND clear of our message tail. The historical bug was
# treating ABSENCE of a draft as delivery: if the prompt glyph was never matched
# (wrong pane / prompt-glyph drift), an unsubmitted message read as "delivered"
# and worker->lead relays stalled silently. We now default to UNCONFIRMED and only
# upgrade to delivered on positive evidence; anything we cannot confirm fails loud.
status="unconfirmed"
for attempt in $(seq 1 $((RETRIES + 1))); do
"${tmux_cmd[@]}" send-keys -t "$EFFECTIVE_TARGET" Enter
sleep 1.2
@@ -103,20 +113,28 @@ for attempt in $(seq 1 $((RETRIES + 1))); do
if printf '%s' "$pane" | grep -qF "$QUEUED_RE"; then
status="queued"; break
fi
# Draft heuristic: the prompt glyph line still carries our message tail.
# (Submitted messages scroll up into history; a draft stays on the line.)
# Locate the REPL input box (prompt glyph). If we cannot see it, we have NO
# evidence of submission state — stay UNCONFIRMED and retry; never infer delivery.
promptline=$(printf '%s' "$pane" | grep -E '|^>|│ >' | tail -1)
if [ -z "$promptline" ]; then
status="unconfirmed"; continue
fi
# Input box located AND still carrying our tail => unsubmitted draft. Flush + retry.
# (Submitted messages scroll up into history; a draft stays on the line.)
if [ -n "$snippet" ] && printf '%s' "$promptline" | grep -qF "$snippet"; then
status="draft"; continue
fi
# Input box located AND clear of our tail => positively submitted. This is the
# only path to success besides the queued banner.
status="delivered"; break
done
[ "$VERBOSE" = 1 ] && { echo "--- pane tail ($TARGET) ---"; printf '%s\n' "$pane" | tail -4; echo "---"; }
case "$status" in
delivered) echo "✓ delivered to $TARGET"; exit 0 ;;
queued) echo "✓ queued to $TARGET (agent busy — will process when it returns to prompt)"; exit 0 ;;
draft) echo "✗ still an unsubmitted draft on $TARGET after $RETRIES flush attempts" >&2; exit 2 ;;
*) echo "✓ sent to $TARGET (submission state indeterminate; verify with -v)"; exit 0 ;;
delivered) echo "✓ delivered to $TARGET"; exit 0 ;;
queued) echo "✓ queued to $TARGET (agent busy — will process when it returns to prompt)"; exit 0 ;;
draft) echo "✗ still an unsubmitted draft on $TARGET after $RETRIES flush attempts" >&2; exit 2 ;;
unconfirmed) echo "✗ could not confirm submission on $TARGET: REPL input prompt not locatable after $((RETRIES + 1)) attempts — message may be UNDELIVERED (check target/pane, retry, or escalate)" >&2; exit 2 ;;
*) echo "✗ could not confirm submission on $TARGET (unexpected state '$status')" >&2; exit 2 ;;
esac