← Commit history

mode-ralph: taskbar-state switching ralph with UIA glass verification + rapid-click race rounds; selftest skill: run it after any identity change

John Lauer ·eba280e748 ·2mo ago ·parent 5c1a339
2 files changed +109
dev-skills/pup-selftest/SKILL.md+4
@@ -26,6 +26,10 @@ roundtrip · T9 **no test windows left open** (John's hard rule) + cleanup.  Also run before/after risky work: `tools/churn-ralph.sh` (the #46 destructive-kill invariant) and `tests/check_dashboard_script.js` alone for any dashboard edit.+After ANY identity/stamp/strip/mode change: `tools/mode-ralph.sh` — walks all three taskbar+modes plus RAPID double-click rounds (the in-flight-stamp race that shipped branded buttons in+plain mode, caught by John 2026-08-16), UIA-verifying the GLASS each round. Bookkeeping said the+strip worked while nine buttons stayed branded; only button names + pixels count.  ## The pixel gates (manual, non-negotiable before claiming anything to John) 
tools/mode-ralph.shadded+105
@@ -0,0 +1,105 @@+#!/usr/bin/env bash+# mode-ralph.sh — the taskbar-state switching ralph (John, 2026-08-16: "loop forever until you have+# it fixed. figure out how to take screenshots to fix yourself and analyze").+#+# Drives taskbarMode/overlayBadges through the SAME /dashboard/action endpoint the Settings dialog+# uses (via pup_eval on the cn-dash window), waits for the apply-progress tracker to report done,+# then verifies the GLASS with UIA button names and captures the tray. Includes RAPID double-click+# rounds that reproduce the in-flight-stamp race (the 2.0.216 generation-token fix's regression+# test). Exit code = number of failed rounds.+#+# Usage: tools/mode-ralph.sh [TARGET] [FINAL_MODE] [FINAL_OV]+set -u+TARGET="${1:-AdomLapper}"+FINAL_MODE="${2:-plain}"+FINAL_OV="${3:-on}"+export ADOM_AI_THREAD="${ADOM_AI_THREAD:-pup bridge maintenance}"+export ADOM_CONTAINER_NAME="${ADOM_CONTAINER_NAME:-galliaApril}"+CLI="adom-bridge --target $TARGET"+PASS=0; FAIL=0+SHOTDIR="${SHOTDIR:-/tmp/mode-ralph-shots}"; mkdir -p "$SHOTDIR"++evaljs() { # evaljs <expression> -> prints result+  timeout 60 $CLI pup_eval "{\"sessionId\":\"cn-dash\",\"expression\":$(python3 -c 'import json,sys;print(json.dumps(sys.argv[1]))' "$1")}" 2>&1 \+    | python3 -c "import sys,json;raw=sys.stdin.read();i=raw.find('{');print(json.loads(raw[i:]).get('result',''))" 2>/dev/null+}++switch_state() { # switch_state <mode> <ov on|off>+  evaljs "(fetch('/dashboard/action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'set-setting',key:'taskbarMode',value:'$1'})}).then(()=>fetch('/dashboard/action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'set-setting',key:'overlayBadges',value:'$2'})})), 'sent')" >/dev/null+}++wait_done() { # poll apply-progress until done (max ~40s)+  for i in $(seq 1 40); do+    D=$(evaljs "fetch('/dashboard/apply-progress').then(r=>r.json()).then(j=>String(j&&j.apply&&j.apply.done))")+    [ "$D" = "true" ] && return 0+    sleep 1+  done+  return 1+}++uia_buttons() {+  local PS='Add-Type -AssemblyName UIAutomationClient; $tray=[System.Windows.Automation.AutomationElement]::RootElement.FindFirst([System.Windows.Automation.TreeScope]::Children,(New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ClassNameProperty,"Shell_TrayWnd"))); $btns=$tray.FindAll([System.Windows.Automation.TreeScope]::Descendants,(New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ControlTypeProperty,[System.Windows.Automation.ControlType]::Button))); foreach($b in $btns){Write-Output $b.Current.Name}'+  local B64; B64=$(python3 -c "import base64,sys;print(base64.b64encode(sys.stdin.read().encode('utf-16-le')).decode())" <<< "$PS")+  timeout 90 $CLI shell_execute "{\"command\":\"powershell -NoProfile -EncodedCommand $B64\",\"reason\":\"mode-ralph: UIA taskbar button names\"}" 2>&1 \+    | python3 -c "import sys,json;raw=sys.stdin.read();i=raw.find('{');d=json.loads(raw[i:]) if i>=0 else {};print(d.get('stdout') or '')"+}++shot_tray() { # shot_tray <label>+  local HW; HW=$(cat /tmp/tray_hwnd 2>/dev/null || echo 65952)+  timeout 60 $CLI desktop_screenshot_window "{\"hwnd\":$HW}" >/dev/null 2>&1+  local P; P=$(ls -t ~/project/screenshots/adom-bridge/window/*-${HW}*.full.png 2>/dev/null | head -1)+  [ -n "$P" ] && cp "$P" "$SHOTDIR/$1.png"+}++verify_state() { # verify_state <mode> <label>+  local mode="$1" label="$2"+  local B; B=$(uia_buttons)+  local N_PUP N_THREAD N_CFT+  N_PUP=$(echo "$B" | grep -c "Adom Pup" || true)+  N_THREAD=$(echo "$B" | grep -Ec "^(ti-crawl|mouser-run|digikey-run|wiki-in|wiki-out|dash-app|reclaimed|pup bridge maintenance|pup-dashboard)" || true)+  N_CFT=$(echo "$B" | grep -c "Chrome for Testing" || true)+  shot_tray "$label"+  case "$mode" in+    plain)+      if [ "$N_PUP" -eq 0 ] && [ "$N_THREAD" -eq 0 ]; then PASS=$((PASS+1)); echo "  PASS [$label] plain: 0 branded buttons (CfT groups: $N_CFT)"; return 0+      else FAIL=$((FAIL+1)); echo "  FAIL [$label] plain: $N_PUP 'Adom Pup' + $N_THREAD thread buttons still on the bar"; echo "$B" | grep -E "Adom Pup|^(ti-crawl|mouser-run|digikey-run|wiki-in|wiki-out|dash-app|reclaimed|pup bridge)" | sed 's/^/    /'; return 1; fi ;;+    aumid)+      if [ "$N_PUP" -ge 5 ]; then PASS=$((PASS+1)); echo "  PASS [$label] aumid: $N_PUP 'Adom Pup' per-window buttons"; return 0+      else FAIL=$((FAIL+1)); echo "  FAIL [$label] aumid: only $N_PUP 'Adom Pup' buttons"; return 1; fi ;;+    tiles)+      if [ "$N_THREAD" -ge 4 ]; then PASS=$((PASS+1)); echo "  PASS [$label] tiles: $N_THREAD thread-name buttons"; return 0+      else FAIL=$((FAIL+1)); echo "  FAIL [$label] tiles: only $N_THREAD thread-name buttons"; return 1; fi ;;+  esac+}++round() { # round <mode> <ov> <label> — clean switch, wait, verify+  echo "== ROUND $3: switch to $1 + overlay $2 =="+  switch_state "$1" "$2"+  wait_done || echo "  (apply-progress never reported done — verifying anyway)"+  sleep 3+  verify_state "$1" "$3"+}++rapid_round() { # rapid_round <modeA> <modeB> <label> — the race: click A, then B ~1.2s later; B must win+  echo "== RAPID $3: $1 then $2 after 1.2s (the in-flight-stamp race) =="+  switch_state "$1" "on"+  sleep 1.2+  switch_state "$2" "on"+  wait_done || echo "  (apply-progress never reported done — verifying anyway)"+  sleep 5+  verify_state "$2" "$3"+}++round tiles on  r1-tiles+round aumid on  r2-aumid+round plain on  r3-plain+rapid_round aumid plain r4-rapid-aumid-then-plain+round tiles on  r5-tiles+rapid_round tiles plain r6-rapid-tiles-then-plain+round aumid on  r7-aumid+round "$FINAL_MODE" "$FINAL_OV" r8-final-$FINAL_MODE++echo+echo "== MODE-RALPH RESULT: $PASS passed, $FAIL failed (shots in $SHOTDIR) =="+[ "$FAIL" -eq 0 ] && echo "GREEN" || echo "RED"+exit $FAIL