app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
← 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.
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; }