#!/bin/sh set -eu GUI_CORE_SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) GUI_ENGINE="$GUI_CORE_SCRIPT_DIR/emacs-gui-verifier.el" GUI_RECORDER="$GUI_CORE_SCRIPT_DIR/record-screen.exp" GUI_EMACS_APP=${EMACS_APP:-/Applications/Emacs.app} GUI_EMACS_BIN="$GUI_EMACS_APP/Contents/MacOS/Emacs" GUI_EMACSCLIENT="$GUI_EMACS_APP/Contents/MacOS/bin/emacsclient" GUI_EMACS_APP_NAME=${EMACS_APP_NAME:-Emacs} GUI_CODEX_ROOT=${CODEX_HOME:-${HOME}/.codex} GUI_DYNAMIC_SKILL=${EMACS_DYNAMIC_UI_SKILL_DIR:-$GUI_CODEX_ROOT/skills/emacs-dynamic-ui-verification} GUI_CHECKPOINT_EL="$GUI_DYNAMIC_SKILL/scripts/capture-checkpoint.el" GUI_EVIDENCE_PY="$GUI_DYNAMIC_SKILL/scripts/evidence.py" usage() { echo "usage:" >&2 echo " $0 doctor" >&2 echo " $0 review RUN_DIR" >&2 echo " $0 run ADAPTER_EL ENTRY_FUNCTION [--run-dir DIR] [--load-path DIR]..." >&2 exit 2 } require_file() { [ -f "$1" ] || { echo "required file is missing: $1" >&2 exit 1 } } doctor() { require_file "$GUI_EMACS_BIN" require_file "$GUI_EMACSCLIENT" require_file "$GUI_ENGINE" require_file "$GUI_RECORDER" require_file "$GUI_CHECKPOINT_EL" require_file "$GUI_EVIDENCE_PY" command -v osascript >/dev/null 2>&1 command -v expect >/dev/null 2>&1 command -v ffmpeg >/dev/null 2>&1 command -v python3 >/dev/null 2>&1 echo "EMACS-GUI-VERIFIER DOCTOR PASS" } review_run() { [ "$#" -eq 1 ] || usage GUI_REVIEW_DIR=$1 require_file "$GUI_REVIEW_DIR/manifest.jsonl" require_file "$GUI_REVIEW_DIR/recording.mov" python3 "$GUI_EVIDENCE_PY" finalize \ --run-dir "$GUI_REVIEW_DIR" --temporal-reviewed } GUI_DAEMON="" GUI_DAEMON_PID="" GUI_RECORDER_PID="" GUI_RECORDER_CHILD_PID="" GUI_DAEMON_STARTED=false valid_pid() { case $1 in ''|*[!0-9]*) return 1 ;; *) [ "$1" -gt 1 ] ;; esac } process_alive() { valid_pid "$1" && kill -0 "$1" 2>/dev/null } wait_for_exit() { GUI_WAIT_PID=$1 GUI_WAIT_LIMIT=$2 GUI_WAIT_ATTEMPT=0 while process_alive "$GUI_WAIT_PID"; do [ "$GUI_WAIT_ATTEMPT" -lt "$GUI_WAIT_LIMIT" ] || return 1 sleep 0.1 GUI_WAIT_ATTEMPT=$((GUI_WAIT_ATTEMPT + 1)) done } force_owned_exit() { GUI_FORCE_PID=$1 GUI_FORCE_LABEL=$2 process_alive "$GUI_FORCE_PID" || return 0 echo "forcing owned $GUI_FORCE_LABEL process to exit: $GUI_FORCE_PID" >&2 kill -TERM "$GUI_FORCE_PID" 2>/dev/null || return 1 if ! wait_for_exit "$GUI_FORCE_PID" 20; then kill -KILL "$GUI_FORCE_PID" 2>/dev/null || return 1 wait_for_exit "$GUI_FORCE_PID" 10 || return 1 fi return 0 } stop_recorder() { [ -n "$GUI_RECORDER_PID" ] || return 0 GUI_RECORDER_STOP_FAILED=0 if process_alive "$GUI_RECORDER_PID"; then if ! kill -TERM "$GUI_RECORDER_PID"; then GUI_RECORDER_STOP_FAILED=1 elif ! wait_for_exit "$GUI_RECORDER_PID" 50; then GUI_RECORDER_STOP_FAILED=1 if ! force_owned_exit "$GUI_RECORDER_PID" "recorder wrapper"; then GUI_RECORDER_STOP_FAILED=1 fi fi fi if process_alive "$GUI_RECORDER_CHILD_PID"; then GUI_RECORDER_STOP_FAILED=1 if ! force_owned_exit "$GUI_RECORDER_CHILD_PID" "screen recorder"; then GUI_RECORDER_STOP_FAILED=1 fi fi if ! wait "$GUI_RECORDER_PID" 2>/dev/null; then GUI_RECORDER_STOP_FAILED=1 fi GUI_RECORDER_PID="" GUI_RECORDER_CHILD_PID="" [ "$GUI_RECORDER_STOP_FAILED" -eq 0 ] } stop_daemon() { [ "$GUI_DAEMON_STARTED" = true ] || return 0 GUI_DAEMON_STOP_FAILED=0 if ! "$GUI_EMACSCLIENT" -n -s "$GUI_DAEMON" -e '(kill-emacs 0)' \ >"$GUI_RUN_DIR/daemon-shutdown.log" 2>&1; then GUI_DAEMON_STOP_FAILED=1 fi if ! wait_for_exit "$GUI_DAEMON_PID" 50; then GUI_DAEMON_STOP_FAILED=1 if ! force_owned_exit "$GUI_DAEMON_PID" "Emacs daemon"; then GUI_DAEMON_STOP_FAILED=1 fi fi GUI_DAEMON_STARTED=false GUI_DAEMON_PID="" GUI_DAEMON="" [ "$GUI_DAEMON_STOP_FAILED" -eq 0 ] } cleanup() { GUI_CLEANUP_FAILED=0 if ! stop_recorder; then echo "GUI recorder cleanup failed" >&2 GUI_CLEANUP_FAILED=1 fi if ! stop_daemon; then echo "GUI daemon cleanup failed" >&2 GUI_CLEANUP_FAILED=1 fi [ "$GUI_CLEANUP_FAILED" -eq 0 ] } on_exit() { GUI_MAIN_STATUS=$? trap - EXIT HUP INT TERM if ! cleanup; then [ "$GUI_MAIN_STATUS" -ne 0 ] || GUI_MAIN_STATUS=1 fi exit "$GUI_MAIN_STATUS" } trap on_exit EXIT trap 'exit 130' HUP INT TERM activate_emacs() { osascript -e "tell application \"$GUI_EMACS_APP_NAME\" to activate" GUI_ACTIVATE_ATTEMPTS=0 while [ "$GUI_ACTIVATE_ATTEMPTS" -lt 30 ]; do GUI_FRONTMOST=$(osascript -e \ 'tell application "System Events" to get name of first application process whose frontmost is true') case $GUI_FRONTMOST in *Emacs*) sleep 0.2 return 0 ;; esac sleep 0.1 GUI_ACTIVATE_ATTEMPTS=$((GUI_ACTIVATE_ATTEMPTS + 1)) done echo "Emacs did not become the frontmost application" >&2 exit 1 } start_recorder() { /usr/bin/expect "$GUI_RECORDER" "$GUI_RUN_DIR/recording.mov" \ >"$GUI_RUN_DIR/recorder.log" 2>&1 & GUI_RECORDER_PID=$! GUI_RECORDER_ATTEMPTS=0 while [ "$GUI_RECORDER_ATTEMPTS" -lt 30 ]; do if ! kill -0 "$GUI_RECORDER_PID" 2>/dev/null; then echo "screen recorder exited before the scenario started" >&2 exit 1 fi GUI_RECORDER_CHILD_PID=$( pgrep -P "$GUI_RECORDER_PID" -x screencapture 2>/dev/null | sed -n '1p' ) if process_alive "$GUI_RECORDER_CHILD_PID"; then return 0 fi sleep 0.1 GUI_RECORDER_ATTEMPTS=$((GUI_RECORDER_ATTEMPTS + 1)) done echo "screen recorder did not create its PTY child" >&2 exit 1 } parse_run_arguments() { [ "$#" -ge 2 ] || usage GUI_ADAPTER=$1 GUI_ENTRY=$2 shift 2 GUI_RUN_DIR="" GUI_LOAD_PATHS="" while [ "$#" -gt 0 ]; do case $1 in --run-dir) [ "$#" -ge 2 ] || usage GUI_RUN_DIR=$2 shift 2 ;; --load-path) [ "$#" -ge 2 ] || usage case $2 in *:*) echo "load path cannot contain a colon: $2" >&2 exit 1 ;; esac if [ -n "$GUI_LOAD_PATHS" ]; then GUI_LOAD_PATHS="$GUI_LOAD_PATHS:$2" else GUI_LOAD_PATHS=$2 fi shift 2 ;; *) usage ;; esac done require_file "$GUI_ADAPTER" if [ -z "$GUI_RUN_DIR" ]; then GUI_RUN_DIR=$(mktemp -d /private/tmp/emacs-gui-verification.XXXXXX) else mkdir -p "$GUI_RUN_DIR" fi if [ -e "$GUI_RUN_DIR/manifest.jsonl" ] || [ -e "$GUI_RUN_DIR/recording.mov" ]; then echo "run directory already contains GUI evidence: $GUI_RUN_DIR" >&2 exit 1 fi } run_adapter() { doctor >/dev/null export ETAF_GUI_RUN_DIR="$GUI_RUN_DIR" export ETAF_GUI_LOAD_PATHS="$GUI_LOAD_PATHS" export ETAF_GUI_ENTRY="$GUI_ENTRY" GUI_DAEMON="emacs-gui-verify-$$" "$GUI_EMACS_BIN" -Q --daemon="$GUI_DAEMON" \ --eval '(setq native-comp-jit-compilation nil load-prefer-newer t)' GUI_DAEMON_STARTED=true GUI_DAEMON_PID=$( "$GUI_EMACSCLIENT" -n -s "$GUI_DAEMON" -e '(emacs-pid)' ) valid_pid "$GUI_DAEMON_PID" || { echo "Emacs daemon returned an invalid process id: $GUI_DAEMON_PID" >&2 exit 1 } "$GUI_EMACSCLIENT" -n -s "$GUI_DAEMON" -c -e \ "(progn (dolist (path (split-string (or (getenv \"ETAF_GUI_LOAD_PATHS\") \"\") path-separator t)) (add-to-list 'load-path path)) (load \"$GUI_CHECKPOINT_EL\" nil nil t) (load \"$GUI_ENGINE\" nil nil t) (load \"$GUI_ADAPTER\" nil nil t) (when (fboundp 'tool-bar-mode) (tool-bar-mode -1)) (when (fboundp 'menu-bar-mode) (menu-bar-mode -1)) (when (fboundp 'scroll-bar-mode) (scroll-bar-mode -1)) (set-frame-parameter nil 'fullscreen 'maximized) (raise-frame) (redisplay t) t)" >"$GUI_RUN_DIR/bootstrap.out" activate_emacs start_recorder "$GUI_EMACSCLIENT" -n -s "$GUI_DAEMON" -e \ '(funcall (intern (getenv "ETAF_GUI_ENTRY")))' \ >"$GUI_RUN_DIR/scenario.out" stop_recorder require_file "$GUI_RUN_DIR/recording.mov" [ -s "$GUI_RUN_DIR/recording.mov" ] || { echo "screen recording is empty" >&2 exit 1 } stop_daemon set +e python3 "$GUI_EVIDENCE_PY" finalize --run-dir "$GUI_RUN_DIR" \ >"$GUI_RUN_DIR/finalize.out" 2>&1 GUI_FINALIZE_STATUS=$? set -e cat "$GUI_RUN_DIR/finalize.out" if [ "$GUI_FINALIZE_STATUS" -eq 1 ]; then echo "GUI verification assertions failed: $GUI_RUN_DIR" >&2 exit 1 fi if [ "$GUI_FINALIZE_STATUS" -ne 2 ]; then echo "fresh capture unexpectedly bypassed temporal review" >&2 exit 1 fi echo "EMACS-GUI-VERIFIER CAPTURE COMPLETE verdict=INCOMPLETE entry=$GUI_ENTRY run-dir=$GUI_RUN_DIR" } [ "$#" -ge 1 ] || usage GUI_COMMAND=$1 shift case $GUI_COMMAND in doctor) [ "$#" -eq 0 ] || usage doctor ;; review) review_run "$@" ;; run) parse_run_arguments "$@" run_adapter ;; *) usage ;; esac