Pup - Puppeteer Bridge
Public Made by Adomby adom
pup is the AI's own browser: a real, full Chrome on the user's desktop that the AI fully controls (a sandbox, not the user's signed-in browser). Rides Bridge; pup_* verbs open windows and tabs, navigate, screenshot, and eval JS.
name: pup-icon-consistency description: Why the pup DASHBOARD taskbar strip keeps "not matching" the real Windows taskbar, and the rules that actually keep them consistent. This is the single most-repeated complaint from John ("how many times are we going to go through this same issue, over and over"). READ THIS before touching the dashboard's taskbarButtons/baseIcon logic, the overlay-match LED, or anything that claims the dashboard mirrors the taskbar.
pup dashboard icons vs the real Windows taskbar
John has caught this same class of mismatch a dozen times. The dashboard shows one thing, his real taskbar shows another, and every time it's a slightly different cause. This file is the map so nobody re-derives it and nobody ships another "I measured it" claim off a blurry screenshot.
THE root truth (internalize this)
pup CANNOT read what Windows actually renders on the taskbar. There is no API to read back a button's base icon or its overlay. So the dashboard's "TASKBAR (pup)" strip is a model pup builds from its own bookkeeping, and any drift between that bookkeeping and the OS reality shows up as a mismatch John will catch. Treat the model as a claim that must be kept honest, never as ground truth.
The recurring causes (each one bit us at least once)
- Stamping inconsistency (the current + most common one). With
aumidIconsON, a window's real taskbar icon is the teal AUMID tile ONLY if it actually stamped. The dashboard keys its base icon onw._curAppId: set → tealstampicon, null → Chrome-for-Testingbaseicon. So if some windows stamped and some didn't, the dashboard shows a MIX of teal and Chrome. If the OS shows them all teal (or all Chrome), that's the mismatch. A window fails to stamp when its stamp can't resolve a handle - i.e. the title-lookup bug (seepup-window-targeting). Fix = make EVERY window stamp (all teal) or none (all Chrome); never a mix. Verify with_curAppIdper window in the snapshot. - AD and HD are NOT pup. Bridge and Hydrogen Desktop are Tauri apps (
class=Tauri Window) with their OWN teal Adom-branded taskbar tiles. They are not pup windows and not ghosts; pup correctly excludes them. Do not mistake them for pup icons (I did, and restarted Explorer chasing "ghosts" that were AD+HD). EnumerateChrome_WidgetWin_1only to find pup windows. - Ghost taskbar buttons. A pup window that was AUMID-stamped and then died can leave a teal ghost
button.
hwndBelongsToPup+ enumeration only see LIVE processes, so pup reports 0 untracked while the taskbar still shows the ghost. Clearing a dead-window button needs an Explorer refresh (restartexplorer.exeon HIS desktop - his machine, not the container; it redraws in ~1s). No new ghosts form whileaumidIconsis off. - Wrong base icon in the dashboard. History: I once forced the dashboard base to teal for ALL
windows off a blurry crop (1.9.298), then had to revert it (1.9.306) when a FLASH proved the live
windows actually wore the Chrome-for-Testing icon and the teal tiles were AD/HD. Rule: the base
icon is
stampiconiffw._curAppId, else the realbaseicon. Don't "simplify" it to always-teal. - The overlay-match LED lies if you let it. pup can't read the button back, so the LED can only assert its own bookkeeping. Red is transient right after a respawn (before repaint) - John's rule: the LED should never sit red, so treat a just-adopted/not-yet-painted window as pending, not red.
The favicon-overlay compositor bug (v1.9.326 — John: "icons are STILL wrong")
Even with the model consistent, a pup tile can wear the WRONG or a BARE badge because the composite dropped the favicon. Two root causes, both fixed in 1.9.326, both worth knowing:
- Format judged by URL, not bytes. The compositor set
isIcofromsrc.href.endsWith('.ico'). ButresolveFaviconRich→analyzeIconByteshas ALREADY re-encoded the favicon to PNG, so a site whose favicon URL ends in.ico(Google Docs'docs-favicon-2026-v2.ico, many others) arrived as PNG, went down the ICO branch,icoLargestPngreturned null on PNG bytes, the favicon layer was dropped, and the badge rendered as the bare Adom mark. FIX: sniff the real magic bytes (00 00 01 00= ICO; else decode as a normal image). NEVER branch on the href extension. - The generic-glyph fallback set
srcbut noticonPng. The composite draws ONLYiconPng; the "no usable favicon → generic globe" fallback assigned a new SVG tosrcand lefticonPngnull, so undecodable favicons ALSO went mark-only. FIX: render the glyph straight intoiconPng, and setsrc.href='generic:page'so_overlayIsGeneric(hence the LED) is honest (amber, not a lying green).
Debug method that actually works: pull %TEMP%\pup-badge-debug\<session>.png — the EXACT bytes pup
handed Windows — and look. If the composed PNG is bare/wrong, it's the compositor (above); if the PNG
is right but the taskbar is wrong, it's delivery/targeting (pup-window-targeting). Do NOT diagnose
this from a downscaled taskbar screenshot; crop the native-res Shell_TrayWnd capture per-tile.
The overlay GUARANTEE — why a window ends up BARE, and the three layers that fix it
John, repeatedly: "ensure you ALWAYS have an overlay icon working consistently... every verb that loads tabs / opens windows / changes pup must trigger a correct overlay AND main-tile analysis." And with the notch shipped, a bare window is now a VISIBLE HOLE, so this got sharper.
ROOT CAUSE of a bare window (found by tracing, not guessing): applyAppOverlay REFUSES to paint when
it can't confirm the window's hwnd belongs to pup — the "Edge guard" (never paint a badge onto a
non-pup taskbar button). So a missing overlay is almost always an UNRESOLVED HWND, not a compositor
bug. Anything that churns adoption — a bridge respawn (e.g. the user rebuilding AD in a loop), a
dragged-out tab, sleep/wake — forces hwnd re-resolution and leaves the window transiently bare.
The guarantee is THREE layers (all must stay intact):
- Per-event triggers —
framenavigated→scheduleOverlayRefresh(any nav from any source),MUTATING_VERBS→ refresh, open/close tab+window → refresh. These fire the recompute immediately on a real change. - The enforcer backstop — a 4s sweep (NOT gated on minimalTouch) that repaints ANY bare-or-stuck
window regardless of which verb did or didn't fire. Bare cooldown 4s. It re-resolves the hwnd each
attempt (via
applyAppOverlay→resolveSessionHwndByBounds). This is the real guarantee: even if a verb forgets to trigger, the window is healed within ~4s. Do NOT slow it back down, and do NOT let itcontinuepast the overlay paint when a base-tile re-stamp is pending (they are independent AD calls). - The baked notch fallback — the static category tile has a small Adom mark baked INTO the notch, so a window that is momentarily bare shows a tidy branded corner instead of a raw transparent hole. The live favicon overlay covers it when present. This makes a transient bare state look intentional, not broken — critical because the notch made bareness visible.
If overlays are STILL unreliable after this: the fault is hwnd resolution (pup-window-targeting), not
the overlay code. Chase why resolveSessionHwndByBounds / the birth-hwnd / persisted-hwnd path fails to
re-confirm the window after adoption — that is the thing that starves the overlay.
The Adom mark on the overlay is AUMID-CONDITIONAL (John, 2026-08-05)
The composed favicon overlay carries the little Adom mark in its lower-right ONLY when aumidIcons is OFF. Rationale (John): the mark exists to say "this is Adom-controlled." When AUMID is ON, the base taskbar tile IS the teal Adom category tile — that already says it — so a second mark on the favicon is redundant. AUMID on → overlay is the CLEAN site favicon, nothing added. AUMID off → favicon + Adom mark (the base is plain Chrome and needs the branding). The badge composite cache key includes the AUMID state so it recomposes on toggle. (Related: the baked Adom-mark notch fallback only shows when there is NO favicon at all, a transient placeholder the live favicon covers — keep it small/central so an opaque favicon fully hides it and it can't read as a redundant second mark.)
Rules that keep it honest
- Never claim the dashboard "matches the taskbar" from a screenshot you took. pup's screen grabs
downscale (~1568px) and blur the icons. To actually SEE the taskbar: native-res capture via a
desktop-side
.NET CopyFromScreen(watch multi-monitor -PrimaryScreenmay be the wrong display), or map an icon to a window by flashing it (pup_alert_window) and seeing which button highlights. EnumerateChrome_WidgetWin_1windows (hwnd+pid+title) as the authoritative window list. - Keep stamping all-or-nothing. The mismatch is almost always "1 of 3 stamped." Chase the window
that failed to stamp; it's a handle-resolution failure (
pup-window-targeting), not a dashboard bug. - The dashboard renders what the window ACTUALLY wears, derived from real state (
_curAppId, the composed badge PNG pup last handed the OS), never an idealized guess. - Real screenshots for the settings examples, captured under a real pointer - see the
MOUSE-TAKEOVER section in
pup-bridge-dev(presence-gated notify; never a mockup).
When John says "they don't match" again
- Pull
_curAppId+overlayLedper window from the snapshot. A mix of set/null_curAppIdunderaumidIcons:onIS the mismatch - find the unstamped window and fix its handle resolution. - Enumerate live
Chrome_WidgetWin_1windows; confirm the count equals pup's window count (extras = ghosts → Explorer refresh; AD/HD Tauri tiles are not pup). - Only then look at the dashboard render. It is almost never the dashboard; it's the state feeding it.
---
name: pup-icon-consistency
description: Why the pup DASHBOARD taskbar strip keeps "not matching" the real Windows taskbar, and the rules that actually keep them consistent. This is the single most-repeated complaint from John ("how many times are we going to go through this same issue, over and over"). READ THIS before touching the dashboard's taskbarButtons/baseIcon logic, the overlay-match LED, or anything that claims the dashboard mirrors the taskbar.
---
# pup dashboard icons vs the real Windows taskbar
John has caught this same class of mismatch a dozen times. The dashboard shows one thing, his real
taskbar shows another, and every time it's a slightly different cause. This file is the map so nobody
re-derives it and nobody ships another "I measured it" claim off a blurry screenshot.
## THE root truth (internalize this)
**pup CANNOT read what Windows actually renders on the taskbar. There is no API to read back a
button's base icon or its overlay.** So the dashboard's "TASKBAR (pup)" strip is a *model* pup builds
from its own bookkeeping, and any drift between that bookkeeping and the OS reality shows up as a
mismatch John will catch. Treat the model as a claim that must be kept honest, never as ground truth.
## The recurring causes (each one bit us at least once)
1. **Stamping inconsistency (the current + most common one).** With `aumidIcons` ON, a window's real
taskbar icon is the teal AUMID tile ONLY if it actually stamped. The dashboard keys its base icon
on `w._curAppId`: set → teal `stampicon`, null → Chrome-for-Testing `baseicon`. So if some windows
stamped and some didn't, the dashboard shows a MIX of teal and Chrome. If the OS shows them all
teal (or all Chrome), that's the mismatch. **A window fails to stamp when its stamp can't resolve a
handle** - i.e. the title-lookup bug (see `pup-window-targeting`). Fix = make EVERY window stamp
(all teal) or none (all Chrome); never a mix. Verify with `_curAppId` per window in the snapshot.
2. **AD and HD are NOT pup.** Bridge and Hydrogen Desktop are Tauri apps (`class=Tauri Window`)
with their OWN teal Adom-branded taskbar tiles. They are not pup windows and not ghosts; pup
correctly excludes them. Do not mistake them for pup icons (I did, and restarted Explorer chasing
"ghosts" that were AD+HD). Enumerate `Chrome_WidgetWin_1` only to find pup windows.
3. **Ghost taskbar buttons.** A pup window that was AUMID-stamped and then died can leave a teal ghost
button. `hwndBelongsToPup` + enumeration only see LIVE processes, so pup reports 0 untracked while
the taskbar still shows the ghost. Clearing a dead-window button needs an Explorer refresh (restart
`explorer.exe` on HIS desktop - his machine, not the container; it redraws in ~1s). No new ghosts
form while `aumidIcons` is off.
4. **Wrong base icon in the dashboard.** History: I once *forced* the dashboard base to teal for ALL
windows off a blurry crop (1.9.298), then had to revert it (1.9.306) when a FLASH proved the live
windows actually wore the Chrome-for-Testing icon and the teal tiles were AD/HD. Rule: the base
icon is `stampicon` iff `w._curAppId`, else the real `baseicon`. Don't "simplify" it to always-teal.
5. **The overlay-match LED lies if you let it.** pup can't read the button back, so the LED can only
assert its own bookkeeping. Red is transient right after a respawn (before repaint) - John's rule:
the LED should never sit red, so treat a just-adopted/not-yet-painted window as pending, not red.
## The favicon-overlay compositor bug (v1.9.326 — John: "icons are STILL wrong")
Even with the model consistent, a pup tile can wear the WRONG or a BARE badge because the composite
dropped the favicon. Two root causes, both fixed in 1.9.326, both worth knowing:
1. **Format judged by URL, not bytes.** The compositor set `isIco` from `src.href.endsWith('.ico')`.
But `resolveFaviconRich` → `analyzeIconBytes` has ALREADY re-encoded the favicon to PNG, so a site
whose favicon URL ends in `.ico` (Google Docs' `docs-favicon-2026-v2.ico`, many others) arrived as
PNG, went down the ICO branch, `icoLargestPng` returned null on PNG bytes, the favicon layer was
dropped, and the badge rendered as the **bare Adom mark**. FIX: sniff the real magic bytes
(`00 00 01 00` = ICO; else decode as a normal image). NEVER branch on the href extension.
2. **The generic-glyph fallback set `src` but not `iconPng`.** The composite draws ONLY `iconPng`;
the "no usable favicon → generic globe" fallback assigned a new SVG to `src` and left `iconPng`
null, so undecodable favicons ALSO went mark-only. FIX: render the glyph straight into `iconPng`,
and set `src.href='generic:page'` so `_overlayIsGeneric` (hence the LED) is honest (amber, not a
lying green).
Debug method that actually works: pull `%TEMP%\pup-badge-debug\<session>.png` — the EXACT bytes pup
handed Windows — and look. If the composed PNG is bare/wrong, it's the compositor (above); if the PNG
is right but the taskbar is wrong, it's delivery/targeting (`pup-window-targeting`). Do NOT diagnose
this from a downscaled taskbar screenshot; crop the native-res `Shell_TrayWnd` capture per-tile.
## The overlay GUARANTEE — why a window ends up BARE, and the three layers that fix it
John, repeatedly: "ensure you ALWAYS have an overlay icon working consistently... every verb that
loads tabs / opens windows / changes pup must trigger a correct overlay AND main-tile analysis." And
with the notch shipped, a bare window is now a VISIBLE HOLE, so this got sharper.
ROOT CAUSE of a bare window (found by tracing, not guessing): `applyAppOverlay` REFUSES to paint when
it can't confirm the window's hwnd belongs to pup — the "Edge guard" (never paint a badge onto a
non-pup taskbar button). So **a missing overlay is almost always an UNRESOLVED HWND**, not a compositor
bug. Anything that churns adoption — a bridge respawn (e.g. the user rebuilding AD in a loop), a
dragged-out tab, sleep/wake — forces hwnd re-resolution and leaves the window transiently bare.
The guarantee is THREE layers (all must stay intact):
1. **Per-event triggers** — `framenavigated` → `scheduleOverlayRefresh` (any nav from any source),
`MUTATING_VERBS` → refresh, open/close tab+window → refresh. These fire the recompute immediately on
a real change.
2. **The enforcer backstop** — a 4s sweep (NOT gated on minimalTouch) that repaints ANY bare-or-stuck
window regardless of which verb did or didn't fire. Bare cooldown 4s. It re-resolves the hwnd each
attempt (via `applyAppOverlay` → `resolveSessionHwndByBounds`). This is the real guarantee: even if a
verb forgets to trigger, the window is healed within ~4s. Do NOT slow it back down, and do NOT let it
`continue` past the overlay paint when a base-tile re-stamp is pending (they are independent AD calls).
3. **The baked notch fallback** — the static category tile has a small Adom mark baked INTO the notch, so
a window that is momentarily bare shows a tidy branded corner instead of a raw transparent hole. The
live favicon overlay covers it when present. This makes a transient bare state look intentional, not
broken — critical because the notch made bareness visible.
If overlays are STILL unreliable after this: the fault is hwnd resolution (`pup-window-targeting`), not
the overlay code. Chase why `resolveSessionHwndByBounds` / the birth-hwnd / persisted-hwnd path fails to
re-confirm the window after adoption — that is the thing that starves the overlay.
## The Adom mark on the overlay is AUMID-CONDITIONAL (John, 2026-08-05)
The composed favicon overlay carries the little Adom mark in its lower-right ONLY when **aumidIcons is
OFF**. Rationale (John): the mark exists to say "this is Adom-controlled." When AUMID is ON, the base
taskbar tile IS the teal Adom category tile — that already says it — so a second mark on the favicon is
redundant. AUMID on → overlay is the CLEAN site favicon, nothing added. AUMID off → favicon + Adom mark
(the base is plain Chrome and needs the branding). The badge composite cache key includes the AUMID
state so it recomposes on toggle. (Related: the baked Adom-mark notch fallback only shows when there is
NO favicon at all, a transient placeholder the live favicon covers — keep it small/central so an opaque
favicon fully hides it and it can't read as a redundant second mark.)
## Rules that keep it honest
- **Never claim the dashboard "matches the taskbar" from a screenshot you took.** pup's screen grabs
downscale (~1568px) and blur the icons. To actually SEE the taskbar: native-res capture via a
desktop-side `.NET CopyFromScreen` (watch multi-monitor - `PrimaryScreen` may be the wrong display),
or map an icon to a window by **flashing** it (`pup_alert_window`) and seeing which button
highlights. Enumerate `Chrome_WidgetWin_1` windows (hwnd+pid+title) as the authoritative window list.
- **Keep stamping all-or-nothing.** The mismatch is almost always "1 of 3 stamped." Chase the window
that failed to stamp; it's a handle-resolution failure (`pup-window-targeting`), not a dashboard bug.
- **The dashboard renders what the window ACTUALLY wears**, derived from real state (`_curAppId`,
the composed badge PNG pup last handed the OS), never an idealized guess.
- **Real screenshots for the settings examples**, captured under a real pointer - see the
MOUSE-TAKEOVER section in `pup-bridge-dev` (presence-gated notify; never a mockup).
## When John says "they don't match" again
1. Pull `_curAppId` + `overlayLed` per window from the snapshot. A mix of set/null `_curAppId` under
`aumidIcons:on` IS the mismatch - find the unstamped window and fix its handle resolution.
2. Enumerate live `Chrome_WidgetWin_1` windows; confirm the count equals pup's window count (extras =
ghosts → Explorer refresh; AD/HD Tauri tiles are not pup).
3. Only then look at the dashboard render. It is almost never the dashboard; it's the state feeding it.