← Commit history

Add the churn ralph harness for #46: opens real windows, drives rescan + quit-idle + takeover-close + respawn churn, and after each cycle asserts every untouched window is still alive and visible on the desktop via OS ground truth (window_info hwnd + desktop_screenshot_window), failing if a live window vanished.

John Lauer ·e93aa8506d ·2mo ago ·parent d071322
1 file changed +95
tools/churn-ralph.shadded+95
@@ -0,0 +1,95 @@+#!/usr/bin/env bash+# churn-ralph.sh — the destructive-kill safety harness for pup-bridge#46.+#+# The #46 incident: pup's janitor killed the browser hosting John's live, visible windows after a+# FALSE "no open windows" verdict during heavy session churn. The 2.0.130 fix made every+# destructive path verify OS ground truth (a visible top-level window = never an orphan). This+# harness proves that invariant: it opens real windows, records their OS hwnds, then drives the+# exact churn that triggered #46 — rescan (adoption), takeover-close, quit-idle, reconcile — and+# after each cycle asserts EVERY window the test still wants is alive + visible on the desktop.+# A single vanished live window = FAIL.+#+# Usage:  tools/churn-ralph.sh [TARGET]      (TARGET defaults to AdomLapper)+# Runs against the live desktop; opens BACKGROUND windows and cleans them up at the end.+set -u+TARGET="${1:-AdomLapper}"+export ADOM_AI_THREAD="${ADOM_AI_THREAD:-churn-ralph}"+export ADOM_CONTAINER_NAME="${ADOM_CONTAINER_NAME:-galliaApril}"+CLI="adom-bridge-cli --target $TARGET"+PASS=0; FAIL=0++jqget() { python3 -c "import sys,json;d=json.load(sys.stdin);print(d$1)" 2>/dev/null; }+say()  { echo -e "$*"; }+ok()   { PASS=$((PASS+1)); echo "  PASS: $*"; }+bad()  { FAIL=$((FAIL+1)); echo "  FAIL: $*"; }++# Is this window alive + present on the desktop? GROUND TRUTH, independent of pup's session model:+# ask the bridge for the window's OS hwnd (pup_window_info), then capture that hwnd directly with+# ab's desktop_screenshot_window. If #46's bug killed the browser process, the hwnd is gone and the+# OS capture fails — exactly the failure the user experiences (window vanished, cannot be shot).+# The title-tag search this used before was unreliable because the title tag is OFF by default.+win_visible() {+  local sid="$1"+  local hw+  hw=$(timeout 30 $CLI pup_window_info "{\"sessionId\":\"$sid\"}" 2>/dev/null | jqget "['hwnd'] or ''")+  [ -z "$hw" ] && { echo "no"; return; }+  timeout 30 $CLI desktop_screenshot_window "{\"hwnd\":$hw}" 2>/dev/null | python3 -c "import sys,json+d=json.load(sys.stdin)+o=d.get('output')+try: o=json.loads(o) if isinstance(o,str) else (o or {})+except: o={}+shot = d.get('savedTo') or (o or {}).get('savedTo') or [s for s in ((o or {}).get('screenshots') or []) if s.get('savedTo')]+print('yes' if shot else 'no')" 2>/dev/null+}++open_win() {+  local sid="$1" url="$2"+  timeout 90 $CLI pup_open_window "{\"sessionId\":\"$sid\",\"owner\":\"churn-ralph\",\"url\":\"$url\"}" >/dev/null 2>&1+}++SIDS=(churn-a churn-b churn-c)+URLS=(https://example.com https://example.org https://example.net)++say "== churn-ralph (pup-bridge#46 safety harness) =="+say "Opening ${#SIDS[@]} windows..."+for i in 0 1 2; do open_win "${SIDS[$i]}" "${URLS[$i]}"; done+sleep 6++# Baseline: all three must be visible on the desktop.+for s in "${SIDS[@]}"; do+  [ "$(win_visible "$s")" = "yes" ] && ok "$s visible at baseline" || bad "$s NOT visible at baseline (open failed?)"+done++churn_cycle() {+  local label="$1"+  say "-- churn cycle: $label --"+  # (a) rescan: exercises the adoption/reclaim path that must never adopt a live window twice+  timeout 40 $CLI pup_rescan '{}' >/dev/null 2>&1+  # (b) quit-idle: the verb that in #46 judged a live-window browser idle and killed it+  timeout 60 $CLI pup_quit_idle_browsers '{}' >/dev/null 2>&1+  # (c) takeover-close ONE window, then immediately reopen it — the adopt/close/respawn race+  timeout 45 $CLI pup_close_window "{\"sessionId\":\"churn-c\",\"takeover\":true}" >/dev/null 2>&1+  open_win churn-c "${URLS[2]}"+  sleep 6+  # ASSERT: the two we never touched (a,b) MUST still be alive+visible — the #46 regression is them+  # vanishing when churn made the model briefly think they were orphaned.+  for s in churn-a churn-b; do+    [ "$(win_visible "$s")" = "yes" ] && ok "[$label] $s survived churn (alive+visible)" \+                                       || bad "[$label] $s WAS KILLED during churn (#46 regression!)"+  done+  # And the reopened one is back.+  [ "$(win_visible churn-c)" = "yes" ] && ok "[$label] churn-c reopened fine" \+                                        || bad "[$label] churn-c did not come back"+}++churn_cycle "round-1"+churn_cycle "round-2"+churn_cycle "round-3"++say "Cleaning up..."+for s in "${SIDS[@]}"; do timeout 45 $CLI pup_close_window "{\"sessionId\":\"$s\",\"takeover\":true}" >/dev/null 2>&1; done++say ""+say "== RESULT: $PASS passed, $FAIL failed =="+[ "$FAIL" -eq 0 ] && { say "GREEN — no live window was ever killed under churn (#46 invariant holds)."; exit 0; } \+                  || { say "RED — a live window vanished under churn; #46 has regressed."; exit 1; }