Kiosks at venues with no online body hang forever on Loading experiences…. Hit today on cheeky-cactus-86 after we updated its venue to bxl1/ad6 (no body enrolled there). v6.1.25 kiosk runs in correct fullscreen game-mode, but the spinner never resolves.
The agent's GET /api/v1/experiences (hydraheadflatscreen/pkg/client/localapi.go:66–103) does:
discoverBody(district) — query hydracluster /api/v1/bodies/eligible, probe LAN+WGfetchSunshineApps(body) — poll the body's Sunshine :47990/api/apps[]SunshineAppWhen step 1 fails, returns HTTP 503 {"error":"no bodies available"}. Qt KioskView.qml:105 checks data.length, which is undefined on the error object, so loading=true is never cleared.
This is wrong by design. Catalog should come from hydraexperiencelibrary keyed by venue, not from live bodies. Bodies are runtime substrate; they are not the source of truth for which experiences are available at a venue.
~/.claude/plans/partitioned-launching-liskov.md)Two-part change:
GET /api/v1/heads/{id}/experiences in a new pkg/api/handlers_head_experiences.go:
hydraexperiencelibrary GET /api/v1/experiences/live?district=X&venue=Y with the existing library tokenNode.AllowedExperiences (pkg/store/store.go:49) if non-empty[{name,label,orientation,stream}]Centralizes "what experiences should this kiosk see" logic. No new credentials on Mac Minis.
pkg/client/catalog.go with fetchVenueCatalog() calling the new hydracluster endpoint~/.hydraheadflatscreen/catalog_cache.json for offline resiliencehandleExperiences always returns HTTP 200 + JSON array ([] when empty), never {error}. Empty grid is distinguishable from broken kiosk.discoverBody, fetchSunshineApps) stays unchanged — used only by /api/v1/stream/startNo code change needed. KioskView.qml:101–112 already iterates experiences[index].name; new response shape is backward compatible.
bxl1/ad6 (likely empty until experiences planted)bxl1/ad6 in library, re-curl, see it