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.
master
John Lauer
Add the churn ralph harness for #46: opens real windows, drives rescan + quit-idle + takeover-close + respawn churn, an…
e93aa85
2mo ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
#!/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; }