← Commit history

mode-ralph: pixel gate (teal classifier) + UIA-rect red-box crops per round; new dev skill pup-mode-ralph (names are not pixels; red-box proof in shotlog is mandatory)

John Lauer ·4691ba2f36 ·2mo ago ·parent 78726e7
3 files changed +120−8
dev-skills/pup-mode-ralph/SKILL.mdadded+69
@@ -0,0 +1,69 @@+---+name: pup-mode-ralph+description: "DEVELOPER-only (user-invocable:false). THE ralph loop for taskbar identity state switching: tools/mode-ralph.sh drives the real Settings endpoint through tiles/aumid/plain plus RAPID double-click race rounds, then verifies the GLASS three ways per round (UIA button names, UIA-rect red-box screenshot crops, a teal-fraction pixel classifier on the button under test). Born 2026-08-16 after TWO name-only 'GREEN' runs shipped while the Chrome-for-Testing group button still wore pup's teal icon. Trigger words: mode ralph, taskbar state test, plain mode broken, aumid not removed, red box screenshot, pixel gate, ralph the mode switching."+---++# pup-mode-ralph — ralph the taskbar identity states until the GLASS is right++**The law this skill exists to enforce (John, verbatim): "you should be drawing a red box around+the icon you are screenshotting in the windows task bar to always narrow in on what you are+testing. prove that in shotlog." And: "loop forever until fixed."**++## The run++```+tools/mode-ralph.sh [TARGET] [FINAL_MODE] [FINAL_OV]     # default AdomLapper plain on+```++Eight rounds: tiles, aumid, plain, RAPID(aumid then plain at 1.2s), tiles, RAPID(tiles then+plain), aumid, then FINAL_MODE. Every switch goes through the SAME `/dashboard/action+set-setting` endpoint the Settings dialog rows call (via pup_eval on cn-dash), then waits on+`/dashboard/apply-progress` — so the run also validates the user-visible progress tracker.+Exit code = failed rounds. Shots land in `$SHOTDIR` (default /tmp/mode-ralph-shots).++## The three gates per round — names, rects, PIXELS++1. **UIA names**: plain = zero `Adom Pup`/thread-name buttons and one `Google Chrome for+   Testing - N` group; aumid = one `Adom Pup · category · thread` button per window; tiles =+   thread-name buttons.+2. **Red-box crop**: UIA gives each button's BoundingRectangle in PHYSICAL px. Scale by+   `captureWidth / 2560`, draw a red box on the tray shot around THE button under test+   (`<label>-redbox.png`), and save a 10x crop of just that button (`<label>-btn10x.png`).+   The red box is not decoration: it forces the analysis to name exactly which button was+   judged, so a pass can never be "the bar looked fine overall".+3. **Pixel classifier**: teal fraction (g>110, b>100, r<90, |g-b|<70) over the button crop.+   Plain must be < 0.02 (measured 0.000-0.001 healthy, 0.235 when broken). Tiles/aumid must+   be > 0.04 on a pup button. **Names are NOT pixels**: two runs passed on names while the+   CfT button wore pup's teal icon.++After the script, EYEBALL the final `-btn10x.png` yourself and inject the red-box shots into+shotlog (channel of the day) BEFORE claiming anything to John. A claim without the red-box+shot in shotlog does not count.++## The two bugs this loop caught (do not re-learn them)++- **The in-flight-stamp race (fixed 2.0.216, `_identityGen`)**: a mode click launches parallel+  per-window stamp chains that pass the settings gate instantly; a plain click seconds later+  strips, then the in-flight stamps land ON TOP. Every mode change / new sweep bumps the+  generation; stamps re-check it right before `desktop_set_window_identity` and abort if stale.+  The plain apply also runs a VERIFY re-strip after the settle. The RAPID rounds are this+  race's permanent regression test.+- **The leftover-art strip hole (fixed 2.0.217)**: the property-store wipe alone leaves (a) the+  WM_SETICON window icon pup applied (the shell builds the CfT GROUP button's art from it) and+  (b) every historical `Adom.Pup.*` AUMID registry key (bookkeeping's id list was already+  nulled, so the targeted delete ran on nothing). Truly plain = VT_EMPTY property wipe + null+  both WM_SETICON slots + sweep-delete ALL `Adom.Pup.*` keys + nudge.++## Traps++- The tray hwnd goes stale on Explorer restart (re-resolve `Shell_TrayWnd`; cached in+  /tmp/tray_hwnd). Screenshot scale = captureWidth/2560 — never fixed pixels.+- Grep patterns must key on the `Adom Pup` prefix: ab brands OTHER apps' windows+  "Adom · thread · description" (nb windows), and Hydrogen/ab embedded windows carry teal+  icons legitimately. They are not pup's and must not fail the plain gate.+- In plain+overlay the CfT group button legitimately wears ONE favicon badge (the most+  recently updated window's). A badge is not branding; only teal pup ART fails the gate.+- The run flips the USER's live taskbar. Land on the state the user last chose (FINAL_MODE),+  and say so when reporting.+- Run this after ANY change to stamping, stripping, sweeps, nudges, or the mode handlers —+  in addition to tools/selftest.sh, not instead of it.
dev-skills/pup-selftest/SKILL.md+1−1
@@ -26,7 +26,7 @@ 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+After ANY identity/stamp/strip/mode change: `tools/mode-ralph.sh` (full doctrine: dev-skills/pup-mode-ralph) — 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.
tools/mode-ralph.sh+50−7
@@ -37,11 +37,52 @@ wait_done() { # poll apply-progress until done (max ~40s)   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}'+uia_buttons() { # prints name lines; also writes x|y|w|h|name rows to $SHOTDIR/rects.txt for the pixel gate+  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){$r=$b.Current.BoundingRectangle; Write-Output ("{0}|{1}|{2}|{3}|{4}" -f [int]$r.X,[int]$r.Y,[int]$r.Width,[int]$r.Height,$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 '')"+  timeout 90 $CLI shell_execute "{\"command\":\"powershell -NoProfile -EncodedCommand $B64\",\"reason\":\"mode-ralph: UIA taskbar button names + rects\"}" 2>&1 \+    | python3 -c "+import sys,json+raw=sys.stdin.read();i=raw.find('{');d=json.loads(raw[i:]) if i>=0 else {}+out=d.get('stdout') or ''+open('$SHOTDIR/rects.txt','w').write(out)+for l in out.splitlines():+    p=l.split('|',4)+    print(p[4] if len(p)==5 else l)"+}++pixel_gate() { # pixel_gate <mode> <label> — crop the button under test by its UIA rect, red-box the+  # tray shot, and CLASSIFY the art: plain must show NO pup teal on the CfT group button; tiles and+  # aumid must show pup teal on a pup button. Names are not pixels (John's red-box rule).+  local mode="$1" label="$2"+  python3 - "$mode" "$label" <<'PYEOF'+import sys, re+from PIL import Image, ImageDraw+mode, label = sys.argv[1], sys.argv[2]+import os+SHOTDIR = os.environ.get("SHOTDIR","/tmp/mode-ralph-shots")+rows=[l.split('|',4) for l in open(f'{SHOTDIR}/rects.txt').read().splitlines() if l.count('|')>=4]+if mode=='plain':+    tgt=[r for r in rows if 'Chrome for Testing' in r[4]]+else:+    tgt=[r for r in rows if r[4].startswith('Adom Pup') or re.match(r'^(ti-crawl|mouser-run|digikey-run|wiki-in|wiki-out|dash-app|reclaimed|pup bridge)', r[4])]+if not tgt: print('PIXELGATE FAIL: no target button found'); sys.exit(1)+x,y,w,h = (int(tgt[0][i]) for i in range(4))+im=Image.open(f'{SHOTDIR}/{label}.png'); sc=im.width/2560.0+cx0,cx1=int(x*sc),int((x+w)*sc)+crop=im.crop((cx0,0,cx1,im.height)).convert('RGB')+px=list(crop.getdata()); teal=0+for r,g,b in px:+    if g>110 and b>100 and r<90 and abs(int(g)-int(b))<70: teal+=1+frac=teal/max(1,len(px))+ann=im.convert('RGB').copy(); d=ImageDraw.Draw(ann)+d.rectangle([cx0-2,0,cx1+2,im.height-1],outline=(255,0,0),width=2)+ann.resize((ann.width*2,ann.height*2),Image.LANCZOS).save(f'{SHOTDIR}/{label}-redbox.png')+crop.resize((crop.width*10,crop.height*10),Image.LANCZOS).save(f'{SHOTDIR}/{label}-btn10x.png')+ok = (frac < 0.02) if mode=='plain' else (frac > 0.04)+print(f"PIXELGATE {'PASS' if ok else 'FAIL'}: teal fraction {frac:.3f} on '{tgt[0][4][:50]}' ({'must be ~0 in plain' if mode=='plain' else 'pup art expected'})")+sys.exit(0 if ok else 1)+PYEOF }  shot_tray() { # shot_tray <label>@@ -59,15 +100,17 @@ verify_state() { # verify_state <mode> <label>   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"+  local PG; PG=$(SHOTDIR="$SHOTDIR" pixel_gate "$mode" "$label"); local PGOK=$?+  echo "  $PG"   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+      if [ "$N_PUP" -eq 0 ] && [ "$N_THREAD" -eq 0 ] && [ $PGOK -eq 0 ]; then PASS=$((PASS+1)); echo "  PASS [$label] plain: 0 branded buttons (CfT groups: $N_CFT) + pixel gate"; 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+      if [ "$N_PUP" -ge 5 ] && [ $PGOK -eq 0 ]; then PASS=$((PASS+1)); echo "  PASS [$label] aumid: $N_PUP 'Adom Pup' per-window buttons + pixel gate"; 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+      if [ "$N_THREAD" -ge 4 ] && [ $PGOK -eq 0 ]; then PASS=$((PASS+1)); echo "  PASS [$label] tiles: $N_THREAD thread-name buttons + pixel gate"; return 0       else FAIL=$((FAIL+1)); echo "  FAIL [$label] tiles: only $N_THREAD thread-name buttons"; return 1; fi ;;   esac }