← Commit history

Update 2 file(s)

John Lauer ·d2821fe4db ·2mo ago ·parent f308e17
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"