#!/usr/bin/env bash
set -uo pipefail
BIN="${1:?binary}"; LABEL="${2:?label}"; OUT="${3:?outdir}"
PAYLOAD="$OUT/ac8_payload.txt"
SOCK="ac8pair-$$"
mkdir -p "$OUT"
printf '%s\n' \
'AC8PF-L1-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
'AC8PF-L2-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' \
'AC8PF-L3-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC' \
'AC8PF-L4-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD' \
'AC8PF-L5-EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' > "$PAYLOAD"
cleanup() { tmux -L "$SOCK" kill-server 2>/dev/null; }
trap cleanup EXIT
rm -rf "$OUT/ws" && mkdir -p "$OUT/ws"
tmux -L "$SOCK" new-session -d -s p -x 140 -y 40 \
"cd $OUT/ws && env XW_COMM_AGENT=memprobe-iso XW_COMM_MACHINE=ke $BIN; exec bash"
sleep 14
tmux -L "$SOCK" capture-pane -t p -p > "$OUT/frame_before.txt" 2>/dev/null
tmux -L "$SOCK" load-buffer "$PAYLOAD"
tmux -L "$SOCK" paste-buffer -t p
sleep 8
tmux -L "$SOCK" capture-pane -t p -p -S -3000 > "$OUT/frame_raw.txt" 2>/dev/null
SUBMIT_RAW=$(grep -c '^> AC8PF' "$OUT/frame_raw.txt" 2>/dev/null); SUBMIT_RAW=${SUBMIT_RAW:-0}
tmux -L "$SOCK" kill-server 2>/dev/null
rm -rf "$OUT/ws2" && mkdir -p "$OUT/ws2"
tmux -L "$SOCK" new-session -d -s p2 -x 140 -y 40 \
"cd $OUT/ws2 && env XW_COMM_AGENT=memprobe-iso XW_COMM_MACHINE=ke $BIN; exec bash"
sleep 14
tmux -L "$SOCK" load-buffer "$PAYLOAD"
tmux -L "$SOCK" paste-buffer -p -t p2
sleep 8
tmux -L "$SOCK" capture-pane -t p2 -p -S -3000 > "$OUT/frame_brk.txt" 2>/dev/null
SUBMIT_BRK=$(grep -c '^> AC8PF' "$OUT/frame_brk.txt" 2>/dev/null); SUBMIT_BRK=${SUBMIT_BRK:-0}
KEPT_BRK=$(grep -o 'AC8PF-L[1-5]' "$OUT/frame_brk.txt" 2>/dev/null | sort -u | wc -l); KEPT_BRK=${KEPT_BRK:-0}
tmux -L "$SOCK" kill-server 2>/dev/null
SUBMITTED=$SUBMIT_RAW
rm -rf "$OUT/ws3" && mkdir -p "$OUT/ws3"
tmux -L "$SOCK" new-session -d -s p3 -x 140 -y 40 \
"cd $OUT/ws3 && env XW_COMM_AGENT=memprobe-iso XW_COMM_MACHINE=ke $BIN; exec bash"
sleep 14
tmux -L "$SOCK" load-buffer "$PAYLOAD"; tmux -L "$SOCK" paste-buffer -t p3; sleep 4
tmux -L "$SOCK" send-keys -t p3 C-c; sleep 5
tmux -L "$SOCK" capture-pane -t p3 -p -S -200 > "$OUT/ctrlc_busy.txt" 2>/dev/null
tmux -L "$SOCK" send-keys -t p3 C-c; sleep 4
tmux -L "$SOCK" capture-pane -t p3 -p -S -200 > "$OUT/ctrlc_idle.txt" 2>/dev/null
tmux -L "$SOCK" send-keys -t p3 'echo SHELL_OK_$(date +%s)' Enter; sleep 2
tmux -L "$SOCK" capture-pane -t p3 -p -S -200 > "$OUT/shell_probe.txt" 2>/dev/null
tmux -L "$SOCK" kill-server 2>/dev/null
RAW="$OUT/raw.bin"; rm -f "$RAW"
rm -rf "$OUT/rawws" && mkdir -p "$OUT/rawws"
tmux -L "$SOCK" new-session -d -s r -x 140 -y 40 \
"cd $OUT/rawws && env XW_COMM_AGENT=memprobe-iso XW_COMM_MACHINE=ke script -q -c $BIN $RAW; exec bash"
sleep 14
tmux -L "$SOCK" load-buffer "$PAYLOAD"; tmux -L "$SOCK" paste-buffer -t r; sleep 5
tmux -L "$SOCK" send-keys -t r C-c; sleep 4
tmux -L "$SOCK" send-keys -t r C-c; sleep 4
tmux -L "$SOCK" send-keys -t r C-c; sleep 4
tmux -L "$SOCK" kill-server 2>/dev/null
RAW_OK=0
for _ in $(seq 1 20); do
grep -aq 'Script done' "$RAW" 2>/dev/null && { RAW_OK=1; break; }
sleep 1
done
RAW_BYTES=$(wc -c < "$RAW" 2>/dev/null); RAW_BYTES=${RAW_BYTES:-0}
RAW_COMPLETE=$RAW_OK
emit() { local n; n=$(grep -ac -- "$1" "$RAW" 2>/dev/null); echo "${n:-0}"; }
EN2004=$(emit $'\033\[?2004h'); DIS2004=$(emit $'\033\[?2004l'); ANY2004=$(emit '2004')
C25L=$(emit $'\033\[?25l'); C25H=$(emit $'\033\[?25h'); DECR=$(emit $'\033\[r')
{
echo "=== AC8 成对判据 · label=$LABEL ==="
echo "binary : $BIN"
echo "binary sha : $(sha256sum "$(readlink -f "$BIN")" 2>/dev/null | cut -c1-32)"
echo "version : $("$BIN" --version 2>&1 | head -1)"
echo "payload : $(wc -c < "$PAYLOAD") B / $(wc -l < "$PAYLOAD") 行"
echo
echo "--- ① 交互面(★ 二法并跑; 谓词: grep -c '^> AC8PF')---"
echo " 法A 裸贴 (应用【未】声明 ⇒ 真实终端行为): 提交 $SUBMIT_RAW/5"
echo " 法B 标记贴 (应用【已】声明 ⇒ 真实终端行为): 提交 $SUBMIT_BRK/5 ∧ 输入框留 AC8PF 行 $KEPT_BRK"
echo " ★ 忠实法 = 由 ③ 发出面决定: \\e[?2004h≥1 ⇒ 取【法B】; =0 ⇒ 取【法A】"
echo
echo "--- ② Ctrl-C 二态(谓词: 语义行 grep, 非状态栏)---"
echo " [处理中] $(grep -o '请求已取消\|cancelled by user' "$OUT/ctrlc_busy.txt" 2>/dev/null | tail -1 || echo '—') ⇒ 应为【取消请求, 非退出】"
echo " [空态] $(grep -o 'restored to stderr\|end ───' "$OUT/ctrlc_idle.txt" 2>/dev/null | tail -1 || echo '—') ⇒ 应为【退出】"
echo " [shell] $(grep -o 'SHELL_OK_[0-9]\+' "$OUT/shell_probe.txt" 2>/dev/null | tail -1 || echo '★ 未见回显') ⇒ 应见回显(终端已还原)"
echo
echo "--- ③ 发出面(谓词: grep -ac 原始流)---"
echo " 取值面 : raw=$RAW_BYTES B ∧ Script done=$RAW_COMPLETE $([ "${RAW_COMPLETE:-0}" -ge 1 ] && echo '(完整 ✓)' || echo '(★ 未完 ⇒ 零命中不可采信)')"
echo " \\e[?2004h : $EN2004 $([ "${EN2004:-0}" -ge 1 ] && echo '⇒ ★ 已启用 bracketed paste' || echo '⇒ 未启用')"
echo " \\e[?2004l : $DIS2004"
echo " '2004' 任意: $ANY2004"
echo " 阳性对照 : \\e[?25l=$C25L \\e[?25h=$C25H \\e[r=$DECR (证 grep 法有效)"
} | tee "$OUT/summary.txt"
echo
echo "★★ 判据(忠实法):"
echo " 修复前 ⇒ ③ \\e[?2004h=0 ⇒ 取【法A】⇒ 期望 提交=1/5(丢失)"
echo " 修复后 ⇒ ③ \\e[?2004h≥1 ⇒ 取【法B】⇒ 期望 提交=0/5 ∧ 输入框留 5 行(完整)"
echo "★ 脚本自证: 二面【皆】须在【各自忠实法】下与上述相符"
FAIL=0
bad() { echo "FAIL(ac8): $*" >&2; FAIL=1; }
[[ "${RAW_COMPLETE:-0}" -ge 1 ]] || bad "原始流未完(未见 'Script done')⇒ 零命中不可采信, 判据不成立"
if [[ "${C25L:-0}" -lt 1 && "${C25H:-0}" -lt 1 && "${DECR:-0}" -lt 1 ]]; then
bad "阳性对照失效: 控制序列(\\e[?25l / \\e[?25h / \\e[r)于原始流零命中 ⇒ grep 法不承证力"
fi
if [[ "${EN2004:-0}" -ge 1 ]]; then
[[ "${SUBMIT_BRK:-0}" -eq 0 ]] || bad "修复后面(\\e[?2004h≥1 ⇒ 忠实法=法B): 期望 提交=0/5, 实得 ${SUBMIT_BRK}/5"
[[ "${KEPT_BRK:-0}" -eq 5 ]] || bad "修复后面(忠实法=法B): 期望 输入框留 5 行, 实得 ${KEPT_BRK}"
else
[[ "${SUBMIT_RAW:-0}" -eq 1 ]] || bad "修复前面(\\e[?2004h=0 ⇒ 忠实法=法A): 期望 提交=1/5, 实得 ${SUBMIT_RAW}/5"
fi
grep -qE '请求已取消|cancelled by user' "$OUT/ctrlc_busy.txt" 2>/dev/null || bad "[处理中] Ctrl-C 期望【取消请求】, 未见"
grep -qE 'restored to stderr|end ───' "$OUT/ctrlc_idle.txt" 2>/dev/null || bad "[空态] Ctrl-C 期望【退出】, 未见"
grep -qE 'SHELL_OK_[0-9]+' "$OUT/shell_probe.txt" 2>/dev/null || bad "[shell] 终端还原探针未见回显"
if [[ "$FAIL" -eq 0 ]]; then
echo "ac8 成对判据【成立】✓(label=$LABEL)"
else
echo "ac8 成对判据【FAIL】(label=$LABEL)—— 见上方 FAIL(ac8) 行" >&2
fi
exit "$FAIL"