# ECP-0123: Instant Station Guide and Player Warmup Status: Draft ## Problem statement The hosted watch page can feel broken when `/api/public-streams` is slow, empty, or temporarily unreachable: the channel rail waits on the network before showing stations. Even after stations appear, the first channel tap pays the `@moq/watch` module import cost before the player element can mount. That increases time to first playing frame and makes channel selection feel unreliable. ## Constraints - Keep the first screen a usable TV/player interface, not a marketing page. - Preserve manual tuning, share links, DVR mode, public station refresh, and existing relay fields. - Keep rollback simple: the live API remains authoritative after it answers. - Avoid Chrome-only transport assumptions and keep live playback failure messages actionable. - Keep the change static-site compatible so Cloudflare Worker asset deployment stays simple. ## Decision Embed the current starter LA station guide in `index.html` and render it immediately on first load. Also keep a short-lived local guide cache, merge cached entries with the HTML seed, and refresh `/api/public-streams` in the background with a bounded timeout. A slow or empty refresh no longer clears already visible channels. Change the web client, publisher CLI/module defaults, runbook examples, and remote watch E2E default relay to `https://relay.every.channel/anon`. Default `wt-publish` and `nbc-wt-publish` to fMP4 passthrough so hotpatch-launched publishers emit the `timescale` and `trackId` CMAF catalog metadata expected by the hosted `@moq/watch` element. Preload/preconnect the primary player dependencies and warm the `@moq/watch` custom element after first paint. Let `@moq/watch` use its available live transport fallback instead of forcing WebTransport-only playback. Add client performance marks for guide first render, guide fetch, watch request, player module readiness, player mount, catalog/live status, and first observable canvas frame when the browser exposes it. ## Alternatives considered - Wait only for `/api/public-streams`. Rejected because the UI becomes blank when the directory is slow and because children should be able to tap visible channels immediately. - Make the API response cacheable at the CDN. Rejected as the only fix because active streams are short TTL and stale-but-visible fallback belongs in the client. - Bundle `@moq/watch` into the static site. Deferred because CDN import fallback is already in use; warming and modulepreload reduce tap latency without changing the build graph. ## Rollout / teardown plan Ship the static web change with the existing Worker asset deploy and roll the publisher hotpatch binary to the LA nodes so their catalogs match the current watcher schema. Validate with clean-cache desktop/mobile browser loads and check the app's `window.__ecPerf` marks plus a live tune through the public relay. The Forgejo workflows use the locally registered `namespace-profile-linux-medium` runner label and ecp-forge runs a persistent `forgejo-runner-agent` service with a normal shell tool PATH so the Cloudflare deploy can actually leave the queue on the self-hosted forge. Teardown is removing the HTML seed/cache/warmup path, returning to live-API-only station rendering, and explicitly passing `--passthrough=false` only if an older watcher path is restored. Forgejo CI and deploy jobs run inside the repository Nix dev shell instead of downloading generic Linux Rust, Trunk, age, or Node binaries. This keeps self-hosted NixOS runners reproducible and prevents dynamic-linker failures from blocking the Cloudflare asset rollout.