Live status header icon + redesigned videos page - #758
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new end-to-end “live status” feature to aspire.dev: StaticHost exposes live state + SSE + Twitch/YouTube webhooks, and the frontend consumes it to drive a header live icon, a redesigned /community/videos/ live page, and a site-global Document Picture-in-Picture experience.
Changes:
- Implemented StaticHost live-status backend (JSON + SSE, Twitch EventSub + YouTube WebSub, background reconciliation/polling, dev-only override endpoint, Scalar OpenAPI in dev).
- Added frontend live-status client + header integration + PiP controller, and rewrote
/community/videos/into a two-tab live embeds page. - Added comprehensive unit/e2e tests plus a build script to emit Astro output directly into StaticHost
wwwroot.
Reviewed changes
Copilot reviewed 50 out of 51 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/StaticHost.Tests/StaticHost.Tests.csproj | Adds new StaticHost test project and dependencies. |
| tests/StaticHost.Tests/Live/YouTubeWebSubServiceTests.cs | Unit tests for YouTube worker tick behavior. |
| tests/StaticHost.Tests/Live/YouTubeWebhookHandlerTests.cs | Unit tests for YouTube signature + Atom parsing helpers. |
| tests/StaticHost.Tests/Live/YouTubeClientTests.cs | Unit tests for YouTube Data API + PubSubHubbub client. |
| tests/StaticHost.Tests/Live/TwitchWebhookHandlerTests.cs | Unit tests for Twitch signature verification and webhook handler behavior. |
| tests/StaticHost.Tests/Live/TwitchEventSubServiceTests.cs | Unit tests for Twitch reconcile logic (create/delete subs). |
| tests/StaticHost.Tests/Live/TwitchClientTests.cs | Unit tests for Twitch Helix client behavior. |
| tests/StaticHost.Tests/Live/TwitchAppTokenProviderTests.cs | Unit tests for token acquisition + refresh behavior. |
| tests/StaticHost.Tests/Live/LiveTestHelpers.cs | Shared test helpers (fake options monitor, HTTP handler recording, etc.). |
| tests/StaticHost.Tests/Live/LiveStatusJsonTests.cs | Ensures YouTube property name serialization is correct. |
| tests/StaticHost.Tests/Live/LiveStatusBroadcasterTests.cs | Unit tests for broadcaster aggregation + coalescing + subscriptions. |
| tests/StaticHost.Tests/GlobalUsings.cs | Global usings for the new test project. |
| src/statichost/StaticHost/wwwroot/scalar/aspire-theme.css | Adds Aspire-themed Scalar CSS for dev API reference. |
| src/statichost/StaticHost/wwwroot/.gitignore | Prevents committing locally-built Astro output while preserving Scalar assets. |
| src/statichost/StaticHost/StaticHost.csproj | Adds resilience/OpenAPI/Scalar packages and debug content trimming for wwwroot. |
| src/statichost/StaticHost/Program.cs | Wires live-status + OpenAPI/Scalar in dev and adjusts static assets serving behavior. |
| src/statichost/StaticHost/Live/YouTube/YouTubeWebSubService.cs | Implements YouTube background worker (subscribe + polling). |
| src/statichost/StaticHost/Live/YouTube/YouTubeWebhookHandler.cs | Adds pure helper methods for YouTube webhook signature + payload parsing. |
| src/statichost/StaticHost/Live/YouTube/YouTubeClient.cs | Implements YouTube API + PubSubHubbub client. |
| src/statichost/StaticHost/Live/YouTube/IYouTubeClient.cs | Defines YouTube client abstraction + result types. |
| src/statichost/StaticHost/Live/Twitch/TwitchWebhookHandler.cs | Adds pure helper methods for Twitch EventSub signature + state updates. |
| src/statichost/StaticHost/Live/Twitch/TwitchEventSubService.cs | Implements Twitch background worker reconcile loop + state seeding. |
| src/statichost/StaticHost/Live/Twitch/TwitchClient.cs | Implements Twitch Helix client for user/stream/subscription operations. |
| src/statichost/StaticHost/Live/Twitch/TwitchAppTokenProvider.cs | Implements cached Twitch app token provider for Helix requests. |
| src/statichost/StaticHost/Live/Twitch/ITwitchClient.cs | Defines Twitch client abstraction + record types. |
| src/statichost/StaticHost/Live/README.md | Documents architecture, endpoints, configuration, and testing strategy. |
| src/statichost/StaticHost/Live/LiveStatusServiceCollectionExtensions.cs | Adds DI registration for live-status feature + named resilient HttpClients. |
| src/statichost/StaticHost/Live/LiveStatusOptions.cs | Adds strongly-typed configuration options for live-status feature. |
| src/statichost/StaticHost/Live/LiveStatusBroadcaster.cs | Implements live snapshot aggregation + coalesced broadcasts + subscriptions. |
| src/statichost/StaticHost/Live/LiveStatus.cs | Defines live snapshot schema + source-gen JSON context. |
| src/statichost/StaticHost/Live/LiveEndpoints.cs | Maps /api/live JSON, SSE stream, webhooks, and dev endpoint. |
| src/statichost/StaticHost/GlobalUsings.cs | Updates global usings to include live-status namespace. |
| src/statichost/StaticHost/appsettings.json | Adds Live configuration section defaults. |
| src/frontend/tests/unit/live-status.vitest.test.ts | Adds unit tests for the live-status client public API. |
| src/frontend/tests/e2e/ui-regressions.spec.ts | Updates header action ordering assertions and adds navigation regression coverage. |
| src/frontend/tests/e2e/live-status.spec.ts | Adds extensive e2e coverage for SSE behavior, header state, PiP, and videos page behavior. |
| src/frontend/src/content/docs/community/videos.mdx | Replaces curated videos list with “Aspire Live” page embedding YouTube/Twitch. |
| src/frontend/src/content/docs/community/index.mdx | Renames community CTA to “Watch live streams”. |
| src/frontend/src/components/YouTubeEmbed.astro | Supports channel live-stream embed + adjusts iframe labeling. |
| src/frontend/src/components/TwitchEmbed.astro | Adjusts iframe labeling and props typing. |
| src/frontend/src/components/starlight/Header.astro | Adds header live icon + loads live-status client + adds PiP controller. |
| src/frontend/src/components/starlight/Head.astro | Enables Astro ClientRouter for client-side transitions. |
| src/frontend/src/components/LiveVideosTabs.astro | Adds two-tab live embeds component wired to live-status snapshots. |
| src/frontend/src/components/LivePip.astro | Implements site-global Document Picture-in-Picture controller UI + logic. |
| src/frontend/src/components/live-status.ts | Adds singleton SSE client, DOM wiring, dismissal logic, and snapshot pub/sub. |
| src/frontend/src/assets/icons/live.svg | Adds the “live” header icon SVG. |
| src/frontend/scripts/build-static-host.mjs | Adds script to build Astro directly into StaticHost wwwroot while preserving Scalar assets. |
| src/frontend/package.json | Adds build:statichost and build:statichost:skip-search scripts. |
| src/frontend/config/sidebar/community.topics.ts | Renames “Videos” navigation entry to “Live Streams” (with translations). |
| src/frontend/astro.config.mjs | Supports configurable outDir via ASTRO_OUT_DIR. |
| src/apphost/Aspire.Dev.AppHost/AppHost.cs | Adds local dev dashboard commands + per-run secrets for live-status simulation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Two follow-ups from @eerhardt's review on PR #807: * Move InternalsVisibleTo into StaticHost.csproj using Include="$(AssemblyName).Tests" to match the convention established in PR #758 and the existing src/tools/*.csproj files. Delete the now-unnecessary Properties/AssemblyInfo.cs. * Mirror MarkdownNegotiationMiddleware's positive ShouldHandle pattern in LinkHeaderMiddleware. The two were inverted: one returned "should I handle?" while the other returned "should I skip?". Both now use the same shape — guard with !ShouldHandle, then check MarkdownPathMapper.IsInfrastructurePath separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
51c937a to
d8e6b72
Compare
* PR feedback * fixup
Two follow-ups from @eerhardt's review on PR #807: * Move InternalsVisibleTo into StaticHost.csproj using Include="$(AssemblyName).Tests" to match the convention established in PR #758 and the existing src/tools/*.csproj files. Delete the now-unnecessary Properties/AssemblyInfo.cs. * Mirror MarkdownNegotiationMiddleware's positive ShouldHandle pattern in LinkHeaderMiddleware. The two were inverted: one returned "should I handle?" while the other returned "should I skip?". Both now use the same shape — guard with !ShouldHandle, then check MarkdownPathMapper.IsInfrastructurePath separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… Content-Signal, agent-skills, WebMCP) (#807) * Add agent-readiness improvements (Link headers, markdown negotiation, robots Content-Signal, agent-skills, WebMCP) Bring aspire.dev up to spec for the checks at https://isitagentready.com: * RFC 8288 Link headers on HTML responses - LinkHeaderMiddleware advertises </llms.txt>; rel="llms", </.well-known/agent-skills/index.json>; rel="agent-skills", </sitemap-index.xml>; rel="sitemap", and a per-page rel="alternate" type="text/markdown" link when a .md companion exists. - Header attached via Response.OnStarting on 2xx text/html responses only; redirects, JSON, static assets, and well-known JSON are skipped. * Cloudflare-style "Markdown for Agents" content negotiation - MarkdownNegotiationMiddleware handles Accept: text/markdown by streaming the .md companion (emitted by starlight-page-actions) directly via IFileProvider.SendFileAsync. No path rewrite, so no interaction with UseRouting / MapStaticAssets endpoint selection. - Cache-Control: private, max-age=0, must-revalidate ensures Front Door does NOT cache, avoiding Vary: Accept cache-key explosion. - 406 when markdown preferred but no companion AND no HTML acceptable. - HEAD parity, Vary: Accept on negotiated responses, infrastructure paths (.well-known, _astro, healthz, install., pagefind) bypass negotiation. * Both new middlewares run BEFORE UseDefaultFiles + UseRouting (UseDefaultFiles rewrites /foo/ -> /foo/index.html, breaking companion mapping; MapStaticAssets registers endpoints during UseRouting, so post-routing path rewrites do not re-trigger endpoint selection). * robots.txt declares Content-Signal: ai-train=yes, search=yes, ai-input=yes inside the User-agent: * group (per draft-romm-aipref-contentsignals). * /.well-known/agent-skills/index.json (Agent Skills Discovery RFC v0.2.0) with a getting-started-with-aspire SKILL.md and a digest field of the form sha256:<lowerhex>. compute-skill-digests.mjs recomputes / verifies on every build; pnpm lint runs verify-skill-digests in --check mode. .gitattributes pins LF for the agent-skills artifacts so digests are byte-stable across Windows / Linux checkouts. * WebMCP integration on the Astro side - src/scripts/webmcp.ts feature-detects navigator.modelContext.registerTool and registers a single search-aspire-docs tool with a JSON Schema input. - Backed by src/scripts/search/* (SearchProvider abstraction with Pagefind today and a Typesense stub for the upcoming migration). The WebMCP tool surface is engine-agnostic so the Pagefind -> Typesense swap is a one-line change in src/scripts/search/index.ts. - Hooked into Head.astro via a single import line. * New host-level tests in tests/StaticHost.Tests/ - In-process TestServer with a temp wwwroot fixture (no frontend build required; PrivateAssets="all" on the frontend.esproj reference prevents the dist/ directory from leaking into test compilations). - 51 tests covering markdown negotiation (incl. HEAD, 406, fallback, Vary behavior, infrastructure-path skip), Link header content + skip rules, AcceptHeaderParser q-value handling, and the well-known artifacts. * New Playwright spec tests/e2e/webmcp.spec.ts asserts that the homepage registers exactly one WebMCP tool (search-aspire-docs) when the runtime exposes navigator.modelContext, and that the absence of the API is non-fatal. Out of scope (intentionally not advertised, would mislead agents): * /.well-known/openid-configuration / oauth-authorization-server (no protected APIs). * /.well-known/oauth-protected-resource (no protected resource). * /.well-known/mcp/server-card.json (aspire.dev does not host an MCP server). * /.well-known/api-catalog (RFC 9727 requires real API endpoints; aspire.dev exposes documentation, an LLM corpus, a sitemap, and an RSS feed - none of which are APIs in the RFC's sense). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rewrite getting-started-with-aspire skill to match official Aspire skill conventions The first cut was inaccurate — it told agents to `mkdir my-aspire-app && cd my-aspire-app && aspire new` and then non-interactively pick a template. In reality `aspire new` is fully interactive and creates its own project folder, so the mkdir+cd pattern is wrong and the fabricated template flags would mislead agents. Rewrite the skill to align with the conventions used by the official skill at github.com/microsoft/aspire/tree/main/.agents/skills/aspire while keeping this one short and focused on getting started: * Frontmatter description is now a long when-to-use / when-not-to-use sentence in the same shape as the official skill. * Body: install, `aspire new` (interactive, no fabricated flags), `aspire start` (called out as the agent-friendly path vs. `aspire run` which blocks the terminal), and a concise list of authoritative references. * Explicit pointer to the official `aspire` skill for the operate-an-existing- app workflow, so an agent that has both available picks the right one. * Updated index.json description to match the new framing. * compute-skill-digests.mjs refreshed the sha256 to reflect the new bytes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Correct Aspire framing in skill: polyglot, not .NET-only Aspire is a polyglot stack — the AppHost can be authored in C# or TypeScript today, with additional languages (Java, Go, Python, Rust, …) on the roadmap. Calling it "the .NET cloud-native stack" was both inaccurate and misleading to agents who would then assume C#-only tooling and dismiss the TypeScript AppHost path. Re-runs compute-skill-digests.mjs to update the index.json digest to match the corrected SKILL.md bytes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Tighten markdown-companion mapping and centralize agent-readiness DRY Concerns flagged by the user: 1. LinkHeaderMiddleware.ShouldSkip duplicated the infrastructure path list maintained on MarkdownPathMapper.IsInfrastructurePath. Fixed by routing the path-skip check through the helper so both middlewares stay in lock-step. 2. Not every page on aspire.dev produces a `.md` companion (DocFX-rendered /reference/api/**, the search route, Lunaria stats, redirects, the 404 page). The previous mapper accepted any `.md` that happened to exist on disk, so a stray markdown file with no real HTML page would have been advertised via the `Link: rel="alternate"; type="text/markdown"` header and served by the negotiation middleware on `Accept: text/markdown`. Fixed by requiring BOTH the `.md` AND the corresponding HTML page to exist before declaring a companion. Adds new xUnit cases pinning the stray-md scenario for both middlewares. Additional cleanup along the way: * AcceptHeaderParser.PrefersMarkdown: removed a dead `htmlQ` assignment and hoisted `HighestExplicitQuality` to a private static method so markdown and html lookups go through the same helper. * Extracted shared sample HTML/Markdown bodies and seed helpers used by LinkHeaderTests + MarkdownNegotiationTests into SamplePages so the on-disk Starlight layout is described in one place. Tests: 57 passing (was 51), 0 warnings, 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR #807 review comments * MarkdownNegotiationMiddleware: drop the stale reference to the `HasMarkdownCompanion` API (which never existed in this PR's final form); point readers at `MarkdownPathMapper.TryGetMarkdownCompanion` instead so the comment matches the live code. * compute-skill-digests.mjs: harden `resolvePublicPath` against `..` traversal. Switches from `path.join` to `path.resolve` and asserts the result stays under `publicRoot` so a malicious or malformed `url` in index.json (e.g. `/.well-known/agent-skills/../../../../etc/passwd`) cannot read bytes outside the published public/ tree in dev/CI. * WellKnownArtifactTests: rename `Agent_skills_files_are_LF_only` to `AgentSkills_files_are_LF_only` to match the surrounding `AgentSkills_*` PascalCase prefix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review nits: InternalsVisibleTo in csproj, symmetrical helper Two follow-ups from @eerhardt's review on PR #807: * Move InternalsVisibleTo into StaticHost.csproj using Include="$(AssemblyName).Tests" to match the convention established in PR #758 and the existing src/tools/*.csproj files. Delete the now-unnecessary Properties/AssemblyInfo.cs. * Mirror MarkdownNegotiationMiddleware's positive ShouldHandle pattern in LinkHeaderMiddleware. The two were inverted: one returned "should I handle?" while the other returned "should I skip?". Both now use the same shape — guard with !ShouldHandle, then check MarkdownPathMapper.IsInfrastructurePath separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove Typesense stubs and flatten search abstraction The original PR shipped a SearchProvider interface plus a Typesense stub in src/frontend/src/scripts/search/ so the Pagefind -> Typesense swap would be a one-line change. We're no longer doing that migration, so the abstraction layer has no remaining justification. * Delete src/frontend/src/scripts/search/typesense-provider.ts * Delete src/frontend/src/scripts/search/SearchProvider.ts * Delete src/frontend/src/scripts/search/index.ts * Delete src/frontend/src/scripts/search/pagefind-provider.ts * Inline the Pagefind logic and types into a single src/frontend/src/scripts/search.ts that exports searchAspireDocs(query, limit). * webmcp.ts now imports searchAspireDocs directly; drop the stale "Pagefind today, Typesense later" comment and the provider-selector indirection. No behavior change for the WebMCP search-aspire-docs tool. Pagefind is the only backend now, and the unavailable-fallback contract is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: David Pine <7679720+IEvangelist@users.noreply.github.com>
Adam Ratzman (adamint)
left a comment
There was a problem hiding this comment.
I left inline comments for the must-fix local-dev, webhook validation/caching, broadcaster ordering, and frontend state/test issues. These all look fixable in place, and CI is green at this head. I could not run the site or browser paths inside the isolated worker because the dependencies and local server were unavailable there, so this pass relies on CI plus direct source inspection for that proof.
Frontend HTML artifact readyThe latest frontend build uploaded the This comment updates automatically when a new frontend build artifact is uploaded. |
Keep non-sensitive live-status settings as deployment parameters while retaining provider credentials and webhook signing keys as read-only Key Vault references. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use Microsoft.AspNetCore.OpenApi 10.0.12 so StaticHost resolves a current, non-vulnerable Microsoft.OpenApi dependency. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep the public StaticHost on Aspire's normal per-site worker ceiling and proxy live-status traffic to a dedicated single-worker coordinator. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Eric Erhardt (eerhardt)
left a comment
There was a problem hiding this comment.
review the usage of key vault and redis
Use direct Redis optimistic concurrency for shared records, require distributed production services, clarify resource names and startup behavior, and share frontend brand tokens with Scalar. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Review findingsReviewed commit High
Medium
Test coverageLocal runs passed: 33 focused live-infrastructure tests and four AppHost tests after restore/build, plus 46 provider tests using the resulting binaries with The frontend reproductions were dependency-free simulations, not browser runs. Frontend dependencies were absent, so local Vitest, browser tests, and typechecking were not run. No local frontend build was run. |
Preserve consent and API search ownership across navigation, fence YouTube observations, make WebSub retries idempotent, and refresh rejected Twitch tokens. Add mapped HTTP, Redis, and accessibility coverage and run backend suites in CI. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Mitch Denny (@mitchdenny) Addressed the agreed feedback in cc7b010.
Also added mapped HTTP endpoint tests and live videos/open-picker axe and keyboard coverage. Redis tests use an explicit disposable instance and exact-key cleanup, never a shared/default database or database-wide flush. Local results: 236 StaticHost tests (including 18 against Redis 8.6), 4 AppHost tests, and 107 focused frontend unit tests passed. Consent/live/accessibility coverage passed 62 browser cases across desktop/tablet/mobile, with 7 existing device-specific skips. All 12 API navigation cases passed using native transitions on desktop and the supported ClientRouter swap fallback on touch projects. Native touch emulation surfaced Chromium The repository-wide TypeScript check has existing failures: baseline 187 diagnostics, current 185, with zero newly introduced diagnostics. No local frontend production build was run; the updated commit has triggered CI. |
Create the Redis-only application with DistributedApplicationTestingBuilder and the same Azure Managed Redis hosting integration as the AppHost. Remove the separate CI image pin, external connection-string contract, and shared-instance ownership guards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve Twitch parent from the browser hostname before loading, correct the shared Aspire YouTube channel, and switch between the non-autoplaying uploads playlist and exact live video. Cover source transitions, hidden tabs, and client navigation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Live status header icon + redesigned videos page
Adds a real-time live indicator to the aspire.dev site header, immediately left of cookie preferences, and replaces the legacy
/community/videos/curated list with a focused two-tab live page for YouTube and Twitch.When a stream is live, state is pushed to connected clients over Server-Sent Events from StaticHost. The header icon strobes everywhere except
/community/videos/and while native Picture-in-Picture is already open. Clicking the live header icon opens a native Document Picture-in-Picture window when supported, and falls back to the videos page otherwise. The PiP window is tracked as site-global state so it survives Astro client-side navigations without recreating the embed or interrupting playback; closing native PiP only closes PiP and does not redirect.What's included
primarySourceand PiP-open state shared across Astro navigations.aspiredotdevchannel live-stream embed and Twitch loads theaspiredotdevchannel player even when no live event is active.GET /api/live,GET /api/live/stream, Twitch EventSub webhook, YouTube WebSub webhook, and a dev-only override endpoint.HttpClients, webhook signature validation, reconciling polls/subscription renewal, and safe idle behavior when provider credentials are missing.X-Aspire-Live-Dev-Command-Keyand validated by StaticHost.wwwroot/.gitignoreguard so local copies of the built frontend output cannot be accidentally added to source control.build:statichostandbuild:statichost:skip-searchthat build Astro directly intosrc/statichost/StaticHost/wwwrootwhile preserving the Scalar assets and ignore guard.Local testing
Start the AppHost from the worktree root with
aspire start --isolated --apphost .\src\apphost\Aspire.Dev.AppHost\Aspire.Dev.AppHost.csproj, open the Aspire dashboard, select theaspiredevresource, and run the live-status commands from the Actions context menu.To refresh the local StaticHost static files without search indexing, run from
src/frontend:pnpm build:statichost:skip-searchIn AppHost run mode, StaticHost gets
Live__EnableDevEndpoint=trueand per-run secret parameters for the dev command key and fake webhook signing keys. Without real provider API credentials, Twitch and YouTube workers stay idle, so the feature is effectively off until a dashboard command triggers it.Useful endpoints on the
aspiredevresource:/api/live,/api/live/stream,/api/live/twitch/webhook,/api/live/youtube/webhook,/api/live/_dev/set, and/scalar/v1. The Twitch webhook is POST-only for notifications and the YouTube webhook supports GET verification plus POST notifications; plain GETs now return descriptive text instead of an unhelpful 404.Tests
tests/StaticHost.Testscovers webhook HMAC verification, YouTube Atom parsing, JSON shape, live-state aggregation, coalescing, subscribe/unsubscribe, and timing behavior.src/frontend/tests/unit/live-status.vitest.test.tscovers the live-status client public API.src/frontend/tests/e2e/live-status.spec.tscovers mocked/api/live+ SSE behavior, header strobing, videos-page no-strobe behavior, PiP-open strobe suppression, native PiP click interception, PiP state surviving Astro client-side navigation, and idle channel embeds.Configuration
User-secrets in dev, env vars / Key Vault in prod. Missing provider secrets degrade safely: the workers log and remain idle, while the API/SSE endpoints continue serving the non-live state.