fix(tmux): compose draft-transition with landed box detection (#1332, O1)
ci/woodpecker/pr/ci Pipeline was successful
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user