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
Update 2 file(s)
2 files changed
+140
dev-skills/pup-taskbar-identity/SKILL.md+38@@ -107,6 +107,44 @@ is 0 failures. Baseline proof (2.0.41): 5/5 rounds, 0 failures. bash dev-skills/pup-taskbar-identity/ralph_jumplist.sh 5 ``` +## Recovered windows: how a respawned pup re-finds handles (2.0.43–2.0.45)++A bridge respawn (every `bridge_install`) orphans birth capture — the new process created none of+the existing windows. The mimic ralph (`ralph_mimic.sh`, which replays John's real 3-thread+workload INCLUDING a respawn leg) proved every recovered window went bare, then caught the fix+STEALING handles. The resolution stack for a recovered window, in order:++1. **Continuity proof (2.0.43)**: the session file persists `_hwnd` AND Chrome's `pid`; Chrome+ outlives pup respawns, so "persisted hwnd whose current owner pid == persisted pid" is proof.+ Recovery records `session._recoveredHwndPid`; `hwndBelongsToPup` accepts the pair.+2. **Natural-title match (2.0.44)**: the OS window title mirrors the tab CHROME displays. Ask+ Chrome which tab is `document.visibilityState === 'visible'` (NOT pup's `activeTabId` — pup's+ bookkeeping says first-tab while Chrome shows last-opened; asking the wrong title made a session+ uniquely match SOMEONE ELSE'S window — the 2.0.45 theft). PID-FENCED to pup-owned pids so the+ user's own Chrome can never match. A title carried by 2+ pup rows (counted BEFORE ownership+ exclusions) is refused — excluding the true owner's row is exactly how a theft looks "unique".+3. **Off-screen re-capture (2.0.44)**: for a bare BACKGROUND window, move it via CDP and identify+ by bounds CHANGE (set-diff), never absolute position — this box CLAMPS off-screen coordinates,+ so the old -33000 position match failed silently forever. Restore the EXACT original rect+ (dumping windows at 0,0 was itself user-visible wonk).+4. **Persisted-claims map (2.0.45)**: loaded from EVERY session file at recovery START, before any+ resolve can race — an early-resolving session can otherwise steal a handle whose true owner has+ not yet restored its in-memory claim (the recovery-storm race the theft rode in on).++## Harness traps (each produced a FALSE verdict)++- **Anchor log analysis to the LAST boot marker** (`grep -n "Puppeteer Bridge running" | tail -1`).+ The pulled log spans respawns; grepping head-of-file "proves" bugs from a prior boot. Also: a+ `bridge_install` takes 40s+ (zip + npm reconstruction) and the OLD process keeps serving during+ adopt-before-reap — poll `/health` for the NEW version before asserting anything.+- **Close test windows AS their owner `--ai-thread`.** The ownership gate rightly refuses+ third-party closes; a harness closing as its own thread gets a silent no-op and leaves ghost+ windows that recover on every subsequent respawn.+- **Never reset the collision map between phases, and assert NON-test windows too.** Handles+ survive respawns, so a recovered window claiming a different session's known handle is THEFT —+ a reset map (or asserting only your own windows) grades it OK. That's how the 2.0.45 theft+ self-graded 0 failures on its first run.+ ## Known remaining gap Cold-start: the first 1-2 windows opened immediately after a bridge respawn can miss their handle
dev-skills/pup-taskbar-identity/ralph_mimic.shadded+102@@ -0,0 +1,102 @@+#!/bin/bash+# Ralph MIMIC test: reproduce John's REAL 3-thread workload (observed 2026-08-09) and the+# bridge-respawn recovery flow where the wonk actually lives.+#+# Mimics exactly what was observed live:+# - "kicad-bridge dev" -> explicit session mk-kicad-$r, wiki page + 2 issue tabs, foreground:true on OPEN+# - "fusion-bridge maintenance" -> explicit session mk-fusion-$r, 5 wiki tabs added sequentially, never foregrounded+# - "nb bridge build" -> explicit session mk-nb-$r, 6 tabs incl. query-param URL, raise AFTER open+# (multi-word --ai-thread names + explicit sessionIds — the combo the old ralph never used)+# Then the RECOVERY leg (the flow that produced "NOT resolved — 2 wiggle-matches" and the+# wrong-category stamp on John's real kicad-touched3): force-reinstall the bridge while the+# windows are OPEN, let it recover them, and assert the identity pipeline re-runs cleanly.+#+# NEVER touches John's real windows (kicad-touched3 / fusion-wiki-tabs / nb-show / pup-dashboard):+# only mk-* sessions are opened/closed, and closes are per-session, never global pup_close {}.+TGT=AdomLapper+ROUNDS=${1:-2}+LOG='C:\\Users\\john\\.adom\\bridge-logs\\puppeteer.log'+MANIFEST='https://wiki.adom.inc/api/v1/pages/pup-bridge/files/adom-bridge-puppeteer-manifest.json'+ab(){ adom-bridge-cli --target $TGT "$@" 2>&1; }+pull_log(){ ab pull_file "{\"filePaths\":[\"$LOG\"],\"saveTo\":\"/tmp/mimiclog\"}" --ai-thread "pup-bridge build" >/dev/null 2>&1+ # anchor to the LAST boot marker — the log spans respawns, and head-of-file lines are a PRIOR boot+ local raw=/tmp/mimiclog/puppeteer.log b+ b=$(grep -n "Puppeteer Bridge running" $raw | tail -1 | cut -d: -f1); b=${b:-1}+ tail -n +$b $raw > /tmp/mimiclog/current-boot.log; }+LF=/tmp/mimiclog/current-boot.log+fail=0; declare -A allhandles++# assert <sid> <phase> — full identity pipeline for one window in the CURRENT log+assert_win(){+ local sid=$1 phase=$2 prob=""+ local handle stamped overlay jl notres+ handle=$(grep -E "\"$sid\" resolved OS handle [0-9]+" "$LF" 2>/dev/null | tail -1 | sed -E 's/.*handle ([0-9]+).*/\1/')+ stamped=$(grep -cE "\"$sid\" stamped appId" "$LF" 2>/dev/null)+ overlay=$(grep -cE "\"$sid\".*(PAINT|ENFORCED)" "$LF" 2>/dev/null)+ jl=$(grep -E "\"$sid\" set [0-9]+ task" "$LF" 2>/dev/null | tail -1 | sed -E 's/.*set ([0-9]+) task.*/\1/')+ notres=$(grep -cE "\"$sid\" NOT resolved" "$LF" 2>/dev/null)+ [ -z "$handle" ] && prob="$prob NO-HANDLE"+ [ "$notres" != "0" ] && prob="$prob NOT-RESOLVED-x$notres"+ [ "$stamped" = "0" ] && prob="$prob NOT-STAMPED"+ [ "$overlay" = "0" ] && prob="$prob NO-OVERLAY"+ [ -z "$jl" ] && prob="$prob NO-JUMPLIST" || { [ "$jl" -lt 5 ] && prob="$prob JUMPLIST-$jl-TASKS"; }+ if [ -n "$handle" ] && [ -n "${allhandles[$handle]}" ] && [ "${allhandles[$handle]}" != "$sid" ]; then prob="$prob COLLISION-with-${allhandles[$handle]}"; fi+ [ -n "$handle" ] && allhandles[$handle]=$sid+ if [ -n "$prob" ]; then echo " [$phase] $sid hwnd=$handle jl=$jl ->$prob"; fail=$((fail+1)); else echo " [$phase] $sid hwnd=$handle jl=$jl stamp=$stamped ovl=$overlay OK"; fi+}++for r in $(seq 1 $ROUNDS); do+ echo "── round $r: concurrent 3-thread mimic ──"+ # kicad: foreground on open, then 2 issue tabs (matches the observed flow incl. the granted reason)+ ( ab pup_open_window "{\"sessionId\":\"mk-kicad-$r\",\"url\":\"https://wiki.adom.inc/adom/kicad-bridge\",\"foreground\":true,\"foregroundReason\":\"ralph mimic of the kicad thread's granted open-foreground\"}" --ai-thread "kicad-bridge dev" >/dev/null 2>&1+ ab pup_open_tab "{\"sessionId\":\"mk-kicad-$r\",\"url\":\"https://wiki.adom.inc/adom/hero-studio/issues/32\"}" --ai-thread "kicad-bridge dev" >/dev/null 2>&1+ ab pup_open_tab "{\"sessionId\":\"mk-kicad-$r\",\"url\":\"https://wiki.adom.inc/adom/adom-bridge/issues/56\"}" --ai-thread "kicad-bridge dev" >/dev/null 2>&1 ) &+ # fusion: background, 4 extra tabs sequentially+ ( ab pup_open_window "{\"sessionId\":\"mk-fusion-$r\",\"url\":\"https://wiki.adom.inc/adom/fusion-bridge\",\"foreground\":false}" --ai-thread "fusion-bridge maintenance" >/dev/null 2>&1+ for u in pup-bridge adom-bridge hero-studio wiki; do+ ab pup_open_tab "{\"sessionId\":\"mk-fusion-$r\",\"url\":\"https://wiki.adom.inc/adom/$u\"}" --ai-thread "fusion-bridge maintenance" >/dev/null 2>&1+ done ) &+ # nb: background open with query-param URL, 5 extra tabs, RAISE after+ ( ab pup_open_window "{\"sessionId\":\"mk-nb-$r\",\"url\":\"https://wiki.adom.inc/adom/nb-bridge?v=puptour\",\"foreground\":false}" --ai-thread "nb bridge build" >/dev/null 2>&1+ for u in hero-studio pup-bridge kicad-bridge adom-bridge definitions; do+ ab pup_open_tab "{\"sessionId\":\"mk-nb-$r\",\"url\":\"https://wiki.adom.inc/adom/$u\"}" --ai-thread "nb bridge build" >/dev/null 2>&1+ done+ ab pup_focus_window "{\"sessionId\":\"mk-nb-$r\",\"reason\":\"ralph mimic of the nb thread's post-open raise\"}" --ai-thread "nb bridge build" >/dev/null 2>&1 ) &+ wait+ sleep 14+ pull_log+ for sid in mk-kicad-$r mk-fusion-$r mk-nb-$r; do assert_win $sid "live"; done+ # register the REAL windows' handles as claims too — theft from them must flag COLLISION+ for sid in kicad-touched3 fusion-wiki-tabs nb-show; do+ h=$(grep -E "\"$sid\".*(resolved OS handle|VALIDATED)" "$LF" 2>/dev/null | tail -1 | grep -oE '[0-9]+' | tail -1)+ [ -n "$h" ] && [ -z "${allhandles[$h]}" ] && allhandles[$h]=$sid+ done++ echo "── round $r: RESPAWN + recovery leg (windows stay open through it) ──"+ ab bridge_install "{\"manifestUrl\":\"$MANIFEST\",\"force\":true}" --ai-thread "pup-bridge build" >/dev/null 2>&1+ sleep 45 # let the NEW process actually boot (old one keeps serving during adopt-before-reap)+ # wake + warm the respawned bridge+ ok=""+ for i in 1 2 3 4 5 6; do+ rr=$(ab pup_readiness '{}' --ai-thread "pup-bridge build" 2>&1 | python3 -c "import sys,json;print(json.load(sys.stdin).get('ready'))" 2>/dev/null)+ [ "$rr" = "True" ] && ok=1 && break; sleep 5+ done+ [ -z "$ok" ] && { echo " [recover] BRIDGE-NOT-READY after respawn"; fail=$((fail+1)); }+ sleep 20 # give recovery + the startup jumplist re-commit sweep time to run+ pull_log+ echo " recovery banner: $(grep -cE 'Recovered session' $LF) recovered / re-commit sweep: $(grep -c 'forcing a re-commit' $LF)"+ # DO NOT reset the collision map: handles survive respawns (Chrome outlives pup), so a recovered+ # window claiming a DIFFERENT session's known handle is THEFT and must flag COLLISION (the exact+ # bug a map reset hid: mk-* windows silently took John's real windows' handles).+ for sid in mk-kicad-$r mk-fusion-$r mk-nb-$r kicad-touched3 fusion-wiki-tabs nb-show; do assert_win $sid "recovered"; done++ # close ONLY the mimic windows, AS THEIR OWNER THREADS (the ownership gate refuses third-party+ # closes — a prior harness closed as "pup-bridge build" and every close silently no-oped)+ ab pup_close_window "{\"sessionId\":\"mk-kicad-$r\"}" --ai-thread "kicad-bridge dev" >/dev/null 2>&1+ ab pup_close_window "{\"sessionId\":\"mk-fusion-$r\"}" --ai-thread "fusion-bridge maintenance" >/dev/null 2>&1+ ab pup_close_window "{\"sessionId\":\"mk-nb-$r\"}" --ai-thread "nb bridge build" >/dev/null 2>&1+ sleep 3+ echo "round $r done (running fails=$fail)"+done+echo "===================="+echo "MIMIC RALPH DONE: $ROUNDS rounds x 3 windows x (live+recovered) | FAILURES: $fail"