Description
## Symptom
On 2026-04-24 (Friday morning), starting the rupelmonde-castle-viewer stream on peppy → boom-pickle showed only the Hydra ExperienceNet branded splash on peppy, never the castle. `/api/v1/stream/status` reported `streaming` the whole time. Manually killing `kioskoverlay.exe` on boom-pickle (`Stop-Process -Name kioskoverlay -Force`) revealed the rendered castle immediately, proving Rupelmonde was rendering correctly and the kiosk overlay was the sole blocker.
The same flow worked on Wed evening (2026-04-22). The difference: today's attempt was a cold boot of boom-pickle.
## Root cause
hydrabody's kiosk overlay has no mechanism to step aside when an experience is streaming. `cmd/kioskoverlay/main.go:1017` places the overlay's main window at `HWND_BOTTOM` with `WS_EX_TRANSPARENT` (click-through), assuming Rupelmonde's new top-level window will naturally draw above it. That assumption holds when an operator interacts with Rupelmonde during a dev session (Wed) but not when Sunshine launches it cold via `CreateProcessAsUserW` in an autologin session — the new SDL window can land in background z-order against the long-lived, already-painted fullscreen overlay.
The stream webhooks `POST /api/v1/stream/started` and `/stream/ended` (`pkg/provider/httpserver.go:54-104`) call `streamProvider.OnStreamStarted / OnStreamEnded` but never touch the kiosk controller — pure design gap.
Verified on boom-pickle:
- `kioskoverlay.exe` was launched without `--debug` (the topmost debug-window path is not involved).
- Rupelmonde processes have `MainWindowHandle=0` from .NET's perspective, but `EnumWindows` from Session 0 is blind to Session 2 — the window exists, just doesn't appear in cross-session queries.
## Secondary bug
`cmd/kioskoverlay/main.go:539 minimizeAllWindows()` runs at overlay startup and SW_HIDEs every visible non-system window. If `KioskController.Tick()` (`pkg/provider/kiosk_windows.go:180-186`) ever relaunches the overlay during a stream — crash, session transition, manual kill — Rupelmonde's window gets SW_HIDE'd with no recovery (`restoreDesktop` only runs on graceful exit via the `HydraBodyKioskStop` named event, not on force-kill).
## Proposed fix
1. Add two named Win32 events symmetric to `HydraBodyKioskStop`: `Global\HydraBodyKioskHide` and `Global\HydraBodyKioskShow`. In `cmd/kioskoverlay/main.go` a goroutine waits on each and calls `ShowWindow(overlayHwnd, SW_HIDE)` / `SetWindowPos(...HWND_BOTTOM...)` + `ShowWindow(SW_SHOWNOACTIVATE)` on the main overlay hwnd. Store the overlay hwnd in a package-level var when `createOverlayWindow` creates it.
2. `KioskController.HideOverlay()` / `ShowOverlay()` in `pkg/provider/kiosk_windows.go` that signal the events. No-op stubs in `kiosk_other.go`.
3. Wire `HideOverlay` from `handleStreamStarted` (`pkg/provider/httpserver.go:65`) and `ShowOverlay` from `handleStreamEnded` (line 97). Mirror the call from the sunshine log-watcher path so both stream-detection paths trigger the same hook.
4. `minimizeAllWindows` skip rule: skip windows whose owning exe is under `experiencesBaseDir` (`C:\experiences\`) — uses the constant already in `httpserver.go:17`.
5. (Optional, preserves loading-screen branding) Don't hide on `stream/started` immediately. Hide only after Rupelmonde's first window appears: poll for non-zero `MainWindowHandle` on the experience process, or watch for Sunshine's first-frame log line. This way users still see the splash during the ~30s Unreal load.
## Validation now possible
With the new `GET /api/v1/debug/screenshot` endpoint on the body (shipped in hydrabody v1.11.21), validation can be done end-to-end. Rerun the same stream-start and screenshot the body before/during/after — expect splash → castle → splash on the body's pixels. Without that endpoint, only peppy-side pixels were observable.
## Open question
Confirm there's a single `OnStreamStarted / OnStreamEnded` hook point inside the sunshine provider so HTTP webhook + log watcher don't both fire `HideOverlay` redundantly.
## Repro
- `node-74f9fbf2` (boom-pickle-38), bxl1/rupelmonde — currently offline (was the reproduction host)
- `node-4c2be4b0` (cosmic-pretzel-98), bxl1/cloud-seven — also affected; reproduces with the same kiosk_mode_enabled=true config (and currently has a stuck Windows Firewall popup for UnrealGame visible via the new screenshot endpoint)
- hydrabody v1.11.20 at the time of incident; v1.11.21 ships the validation endpoint but does NOT fix the underlying overlay bug
- HydraExperienceNet on peppy: bundle from Wed 2026-04-22 22:07 (Info.plist reports 6.1.0 but mtime confirms Wed-evening build)
## Plan file
`~/.claude/plans/enumerated-cuddling-micali.md` (the body screenshot plan that just shipped) contains the full follow-up issue body and the implementation checklist for this fix.