fix(tmux): compose draft-transition with landed box detection (#1332, O1)
ci/woodpecker/pr/ci Pipeline was successful

Rebased onto current next and composes the two sibling mechanisms per the
O1 ruling: the cursor-row draft transition stays the authoritative
runtime-agnostic delivered verdict; next's locate_input_box (two shapes:
prompt glyph, pi rule box) is adopted as positive DRAFT evidence only,
covering the cursor-row check's blind spot on redrawn TUIs that park the
cursor off the input line. Box-absence proves nothing and can never
produce UNDELIVERED: a shapeless-but-submitting pane delivers via the
cursor-row transition regardless.

The verdict suite gains fixtures 6 and 6b: the 2026-09-04 scratch probe
is the regression test (shapeless REPL that submits must report
delivered; shape probing alone exited 2 with retry advice on a consumed
message), plus the raw/no-echo shapeless guard arm. Measured limit
recorded in the suite: a shapeless cooked non-reading pane is
indistinguishable from a delivering one by any runtime-agnostic signal
available to the sender.

Rerun evidence: pipefail scanner 7/7, glyph-agnostic suite 6/6, verdict
suite 8/8, live shapeless probe rc=0 delivered with consumption proof.
This commit is contained in:
2026-09-04 16:51:15 -05:00
parent 9d721fa6f1
commit acd15ed144
2 changed files with 115 additions and 11 deletions
@@ -32,7 +32,11 @@
# 0 delivered (submitted) or queued (agent busy; will process when free)
# 1 tmux target not found
# 2 submission NOT confirmed — either still an unsubmitted draft, or the REPL
# input prompt could not be located to confirm the message actually landed.
# input box could not be located to confirm the message actually landed.
# Delivered verdicts are runtime-agnostic (cursor-row draft transition, or
# the queued banner); locate_input_box() below adds positive DRAFT evidence
# for panes that render a recognizable box, and never gates delivery on a
# runtime's rendering shape.
# 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.
@@ -119,6 +123,51 @@ _draft_on_input() { # true iff our message tail is sitting on the input line now
grep -qF "$snippet" <<<"$(_cursor_line)"
}
# Locate the REPL input box in a captured pane. Prints the box's contents on
# stdout and returns 0 when the box was FOUND; returns 1 when it could not
# be located at all. Found-but-empty is a real, distinct answer (an empty input
# box is what a submitted message leaves behind), so the caller must branch on
# the return code, never on whether the output is empty.
#
# Two REPL shapes are recognised:
# * a prompt-glyph line — ``, a leading `>`, or `│ >`. Claude Code and most
# readline REPLs.
# * a box drawn as two horizontal `─` rules with the input between them and NO
# prompt glyph anywhere. pi renders this. Anchoring on the LAST rule pair is
# what makes it safe: agent output can contain its own rules, but nothing is
# drawn below the input box except the status line.
#
# Compose authority rule (#1332 O1): this function is POSITIVE DRAFT EVIDENCE
# ONLY. A located box still carrying our tail is affirmative proof the message
# was not consumed (the cursor-row check's blind spot: a redrawn TUI can park
# the cursor off the input line, which the draft-transition anchor cannot see).
# Its failure to find a box proves NOTHING and must never produce an
# UNDELIVERED verdict: a shapeless-but-submitting pane delivers via the
# cursor-row transition regardless (measured, scratch probe 2026-09-04;
# shapeless REPL consumed the message while shape probing alone reported
# "may be UNDELIVERED" — the exact #1257 regression this split prevents).
locate_input_box() {
local pane=$1 glyph_line rule_lines top bottom
glyph_line=$(printf '%s\n' "$pane" | grep -E '|^>|│ >' | tail -1)
if [ -n "$glyph_line" ]; then printf '%s\n' "$glyph_line"; return 0; fi
rule_lines=$(printf '%s\n' "$pane" | grep -nE '^[[:space:]]*─{4,}[[:space:]]*$' | cut -d: -f1 | tail -2)
[ -n "$rule_lines" ] || return 1
# Split the (at most two) captured line numbers with parameter expansion. Not
# `head -1`: piping into an early-exiting consumer SIGPIPEs the producer, which
# under `set -euo pipefail` aborts the caller with rc=141 and no output. The
# scripts/pipefail-early-exit.test.mjs guard reds on that shape, correctly.
# With one rule captured both halves resolve to the same value and the
# ordering test below rejects it, which is the answer we want anyway.
top=${rule_lines%%$'\n'*}
bottom=${rule_lines##*$'\n'}
[ "$top" != "$bottom" ] || return 1
[ "$bottom" -gt "$top" ] || return 1
# An empty range (adjacent rules) prints nothing and still returns 0: found,
# empty, which is the delivered shape.
printf '%s\n' "$pane" | sed -n "$((top + 1)),$((bottom - 1))p"
return 0
}
# Baseline: after the paste, our draft must be on the input line. This is positive
# proof we are on the right pane and the paste landed — the anchor the transition
# check measures against.
@@ -134,16 +183,20 @@ for attempt in $(seq 1 $((RETRIES + 1))); do
if grep -qF "$QUEUED_RE" <<<"$pane"; then
status="queued"; break
fi
# POSITIVE draft evidence from a located prompt box, when one exists. This is the
# cursor-row check's blind spot: a pane in COOKED mode (a plain shell whose
# foreground process never reads stdin) echoes our paste via the kernel line
# discipline and moves the cursor off it on Enter, which is indistinguishable from
# a real submit by cursor row alone. If a prompt box IS locatable and still carries
# our tail, that is affirmative proof the message was not consumed. Absence of a
# glyph is still never used for anything — that inference is the original E7 bug.
promptline=$(printf '%s' "$pane" | grep -E '|^>|│ >' | tail -1)
if [ -n "$promptline" ] && [ -n "$snippet" ] && grep -qF "$snippet" <<<"$promptline"; then
status="draft"; continue
# POSITIVE draft evidence from a located input box, when one exists. This is
# the cursor-row check's blind spot: a redrawn TUI (pi's box) can park the
# cursor off the input line, which the draft-transition anchor cannot see,
# while a pane in COOKED mode (a plain shell whose foreground process never
# reads stdin) echoes our paste via the kernel line discipline and moves the
# cursor off it on Enter, indistinguishable from a real submit by cursor row
# alone. If a locatable box still carries our tail, that is affirmative proof
# the message was not consumed. Absence of a recognizable shape is never used
# for anything — that inference is the original E7 bug, and the delivered
# verdict stays with the runtime-agnostic cursor-row transition.
if inputbox=$(locate_input_box "$pane"); then
if [ -n "$snippet" ] && grep -qF "$snippet" <<<"$inputbox"; then
status="draft"; continue
fi
fi
if [ "$saw_draft" = 1 ]; then
if _draft_on_input; then