Files
LlamaCasty/TASKS/task-44-test-chat-fix.md
gramps 94f015044a fix(stream): TEST-tab chat — resolve liveChatId from snippet, poll until the broadcast is live
The open creator report ('I still cannot post a chat message in the TEST tab',
feedback 'Chat polling couldn't start — Mock Chat Input is disabled') was a single
cause: the liveChatId never resolved. Two YouTube API facts:
1. The id lives in snippet.liveChatId — contentDetails has no such property
   (TASK 44 read part=contentDetails: could never resolve).
   https://developers.google.com/youtube/v3/live/docs/liveBroadcasts
2. It only exists once the broadcast is LIVE — the official sample lists
   broadcastStatus=active, and our fetch ran before the frame pump pushed RTMP
   (enableAutoStart flips ready→live). A ready-state list legitimately returns
   no id.
   https://github.com/youtube/api-samples/blob/master/java/src/main/java/com/google/api/services/samples/youtube/cmdline/live/GetLiveChatId.java

Fix: GetBroadcastLiveChatIdAsync reads part=snippet and polls with a bounded
retry (10x/2s); PrepareAndStartLiveAsync starts the frame pump FIRST, then
resolves the id. Chat stays non-fatal. Docs ai.md/TASKS.md/HANDOFF.md +
MyMistakes recipe updated same commit. TASK 44.

Good Dog: ONE integration test (GetBroadcastLiveChatIdAsync_Polls_Snippet_...
) drives a broadcast that gains its id mid-retry and asserts every request used
part=snippet. Gate: clean build 0 warnings, vstest 318/318.
2026-09-25 08:06:02 -07:00

3.4 KiB

TASK 44 — TEST-tab chat fix: resolve the liveChatId (snippet + live-timing)

Catalog: TASKS.md. Status: ✅ SHIPPED 2026-09-25 — the open creator report from HANDOFF ("I still cannot post a chat message in the TEST tab") diagnosed and fixed as its own scoped unit.

Provenance

  • 2026-09-24, HANDOFF filed the creator report as open: "I still cannot post a chat message in the TEST tab." The TEST drawer's Mock Chat Input posts a real liveChat/messages.insert; the drawer only enables it when a liveChatId is resolved.
  • 2026-09-25 diagnosis: the reported feedback line "Chat polling couldn't start — Mock Chat Input is disabled" is TestSessionViewModel.BeginTest running with a null liveChatId. That id comes from YouTubeStreamService.GetBroadcastLiveChatIdAsync, and two API facts (from the official liveBroadcasts reference + the GetLiveChatId.java sample) pin why it never resolved:
    1. It lives under snippet.liveChatId — contentDetails has no such property at all. The code requested part=contentDetails and read contentDetails.liveChatId, a path that can NEVER resolve.
    2. YouTube only populates it once the broadcast is live (the official sample lists broadcastStatus=active). The fetch ran right after broadcast insert (lifecycleStatus ready) AND before _framePump.StartAsync() pushed the RTMP stream that enableAutoStart needs to flip the broadcast to live — so even the correct property would have been empty at fetch time.

What shipped

  • Services/YouTubeStreamService.cs:
    • GetBroadcastLiveChatIdAsync(broadcastId, maxAttempts = 10, delayMs = 2000) now requests part=snippet and reads snippet.liveChatId, and polls with a bounded retry until the id appears or the attempts exhaust (never throws). The default retry (~20s) gives the encoder time to connect and the broadcast to go live.
    • New private FetchLiveChatIdAsync holds the single read; the public method owns the retry loop.
  • ViewModels/MainViewModel.Streaming.Operations.cs:
    • PrepareAndStartLiveAsync starts the frame pump FIRST, then resolves the liveChatId — the reorder is required for the id to ever exist. Chat remains non-fatal: failure logs and the stream continues.
    • TEST drawer wiring preserved: TestSession.BeginTest(broadcastId, liveChatId) still opens the dock once the id resolves → Mock Chat Input becomes sendable.
  • Docs: ai.md (Test Stream section), TASKS.md (row 44), HANDOFF.md rewritten.

Validation (Good Dog: ONE integration test)

ytLive.Tests/YouTubeStreamServiceTests.cs: GetBroadcastLiveChatIdAsync_Polls_Snippet_Until_Id_Appears — a fake broadcast that starts WITHOUT a liveChatId (not yet live) and gains it on a later poll; asserts the retry eventually resolves Cg0LIVECHAT42, that it polled ≥3 times, and that every request asked for snippet (never contentDetails) — the exact seam that was broken.

Gate: clean build 0 warnings; full vstest 318/318 passed.

Reference