Files
site/tools/harness.sh
T
teahubxyzandClaude Fable 5.1 1aeebac6f2 harness: a resume mode (autoplay blocked); docs for the state machine and hooks
tools/harness.sh resume seeds IndexedDB with a station and one rendered
track, resumes it with no user gesture and expects the TAP TO PLAY wait.
IndexedDB needs http, so the page is served from a temp folder; headless
Chrome's virtual time doesn't wait for IndexedDB, so this mode is driven in
real time over the DevTools protocol (tools/harness-cdp.py, stdlib only).
The loop harness now prints state.phase too.

ARCHITECTURE.md describes the machine as implemented; HANDOFF.md lists what
was found and left alone; STORAGE.md gains db.get and the tapes store (the
code already had both); README points at the new files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6
2026-10-21 06:01:48 +00:00

64 lines
3.1 KiB
Bash

#!/bin/sh
# tools/harness.sh: the playback regression run. Appends a harness fragment's script to index.html, runs it in headless
# Chrome, prints one state line per step. Needs Chrome/Chromium and python3.
#
# sh tools/harness.sh # tools/harness.html: the station loop with autoplay allowed, from file://
# sh tools/harness.sh resume # tools/harness-resume.html: resume a stored station with autoplay BLOCKED, served
# # over http (IndexedDB needs it) and driven in real time over the DevTools protocol
# # (tools/harness-cdp.py); expects the TAP TO PLAY wait
#
# CHROME=/path/to/chrome and CHROME_FLAGS="--no-sandbox --disable-dev-shm-usage" when it isn't found or runs as root.
set -eu
cd "$(dirname "$0")/.."
MODE=${1:-loop}
case "$MODE" in
loop) FRAG=tools/harness.html; AUTOPLAY=--autoplay-policy=no-user-gesture-required ;;
resume) FRAG=tools/harness-resume.html; AUTOPLAY= ;;
*) echo "usage: sh tools/harness.sh [resume]" >&2; exit 2 ;;
esac
CHROME=${CHROME:-}
for c in "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" "$(command -v chromium || true)" "$(command -v google-chrome || true)"; do
if [ -z "$CHROME" ] && [ -n "$c" ] && [ -x "$c" ]; then CHROME=$c; fi
done
[ -n "$CHROME" ] || { echo "no Chrome found; set CHROME" >&2; exit 1; }
out=$(mktemp -d)
prof=$(mktemp -d)
python3 - "$out" "$FRAG" <<'EOF'
import sys, pathlib
out = pathlib.Path(sys.argv[1])
page = pathlib.Path("index.html").read_text()
h = pathlib.Path(sys.argv[2]).read_text()
script = h[h.index("<!-- harness -->"):]
(out / "run.html").write_text(page.replace("</body>", script + "</body>"))
EOF
cp brain.js "$out/brain.js"
if [ "$MODE" = resume ]; then
# a fresh profile and a port picked by the OS, so no run inherits another's IndexedDB
PORT=$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1",0)); print(s.getsockname()[1])')
python3 -m http.server "$PORT" --bind 127.0.0.1 --directory "$out" >/dev/null 2>&1 </dev/null &
SRV=$!
trap 'kill $SRV 2>/dev/null || true; rm -rf "$out" "$prof"' EXIT INT TERM
n=0
until curl -fs -o /dev/null --max-time 2 "http://127.0.0.1:$PORT/run.html"; do
n=$((n + 1)); [ "$n" -lt 40 ] || { echo "the local server on port $PORT never answered" >&2; exit 1; }; sleep 0.5
done
URL="http://127.0.0.1:$PORT/run.html"
else
trap 'rm -rf "$out" "$prof"' EXIT INT TERM
URL="file://$out/run.html"
fi
if [ "$MODE" = resume ]; then
# real time, over the DevTools protocol: --dump-dom's virtual time doesn't wait for IndexedDB (see harness-cdp.py)
# shellcheck disable=SC2086
python3 tools/harness-cdp.py "$CHROME" "$URL" 30 ${CHROME_FLAGS:-}
else
# shellcheck disable=SC2086
"$CHROME" --headless=new --disable-gpu --user-data-dir="$prof" $AUTOPLAY --virtual-time-budget=25000 ${CHROME_FLAGS:-} --dump-dom "$URL" 2>/dev/null > "$out/dom.html" || true
python3 - "$out/dom.html" <<'EOF'
import sys, re, html
s = open(sys.argv[1]).read()
m = re.search(r'<pre id="diag">(.*?)</pre>', s, re.S)
print(html.unescape(m.group(1)) if m else "no diag: the harness did not run")
EOF
fi