Files
LlamaCasty/HANDOFF.md
T
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

4.3 KiB
Raw Blame History

HANDOFF — 2026-09-25 (TASK 44 shipped: TEST-tab chat fix — liveChatId now resolves)

Branch / Commit State

main HEAD = new commit for TASK 44 (TEST-tab chat fix); previous: TASK 43 commit (native events & alerts), ac6e67a (end close-out). Repo is ~11 commits ahead of pushed 197ee81 — push is the creator's call (sub-milestone decision, not per-commit).

Working tree CLEAN after this unit's commit.

⚠️ The unit that just shipped (the open TEST-tab chat report, diagnosed + fixed)

The HANDOFF-open creator report — "I still cannot post a chat message in the TEST tab" — is RESOLVED (one cause, one scoped unit, TASK 44 in TASKS/task-44-test-chat-fix.md):

  • The feedback line "Chat polling couldn't start — Mock Chat Input is disabled" is TestSessionViewModel.BeginTest running with a null liveChatId.
  • Root cause (two API facts, cited in commit + MyMistakes): (1) liveChatId lives in snippet.liveChatId — contentDetails has no such property, and the old code read part=contentDetails (can NEVER resolve); (2) YouTube only populates it once the broadcast is live (official GetLiveChatId.java lists broadcastStatus=active), and the fetch ran before the frame pump pushed RTMP (enableAutoStart flips ready→live).
  • Fix: GetBroadcastLiveChatIdAsync(broadcastId, maxAttempts=10, delayMs=2000) now reads part=snippet and polls with a bounded retry; PrepareAndStartLiveAsync starts the frame pump FIRST, then resolves the id. Chat stays non-fatal. TEST drawer wiring unchanged (BeginTest opens the dock once the id resolves → Mock Chat Input becomes sendable).

Good Dog: ONE integration test in YouTubeStreamServiceTests.cs (GetBroadcastLiveChatIdAsync_Polls_Snippet_Until_Id_Appears) — a fake broadcast that starts without a liveChatId and gains it on a later poll; asserts the retry resolves it, polled ≥3x, and every request asked for snippet (never contentDetails). Gate: clean build 0 warnings, full vstest 318/318 passed (the LayerReorder real-mouse-drag test passed this run too).

Follow-ups queued (NOT done in this unit)

  • TASK 3 item 20 persistence half — the canonical RewardEvents SQLite table + superChatEvents.list (30-day) backfill + session-report rollup (events still parse to ChatMessage in memory only).
  • TASK 3 item 16 (Text source) still open; TASK 40 units A/C/D queued; TASK 32–36 queued.

Around the task (carried facts)

  • RealMouseDrag tests no-op while a game/fullscreen window steals the mouse (POE 2 seen 2026-09-22) — close games before full-suite runs. AudioPipeline timing flake similar.
  • Test-env trap: saved OAuth session loads synchronously → force vm.IsConnected in signed-in tests.
  • subscriberCount YPP slice 2 needs re-consent — do not merge with other units. $99/yr Polar must become one-time before launch. MARCOM.md/MONETIZATION.md gitignored — never commit.
  • Latent bug the YPP 403 masked: statistics are JSON strings — always the ValueKind-first ReadInt64 (MyMistakes recipe), never GetInt64.
  • API fact (TASK 44): a liveChatId fetch at insert time (lifecycleStatus ready) returns nothing by design — missing liveChatId ≈ "broadcast not live yet", not an auth failure, until ~20s post push. Debug with that lens.
  • Every committed change needs a close + relaunch of the running app to be seen — running builds are live; verify.sh can't run while the exe is locked.

Next step

  1. Creator verifies the TEST-tab chat fix on the next run: Test Stream → the drawer should open with a liveChatId line and Mock Chat Input enabled ~as soon as the broadcast goes live; send a message and confirm it round-trips through the ~2s poll and renders on the overlay.
  2. Push decision is the creator's (repo ~11 ahead). If pushed, note the commit id in this file.
  3. When scoped: TASK 3 item 20 (RewardEvents persistence half) draws next.

Critical working rules (unchanged, still binding)

  • Good Dog = ONE integration test per change. Scope lock + ./scripts/scope-check.sh before commit.
  • Windows dotnet host for all WSL builds (/mnt/c/Program Files/dotnet/dotnet.exe, quoted paths).
  • 0 warnings on real clean builds (verify.sh only, never incremental). One runtime model.
  • No menus/polls with the creator; decisions asked once then held; do the work, then report.