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.
4.3 KiB
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.BeginTestrunning with a null liveChatId. - Root cause (two API facts, cited in commit + MyMistakes): (1)
liveChatIdlives insnippet.liveChatId—contentDetailshas no such property, and the old code readpart=contentDetails(can NEVER resolve); (2) YouTube only populates it once the broadcast is live (officialGetLiveChatId.javalistsbroadcastStatus=active), and the fetch ran before the frame pump pushed RTMP (enableAutoStart flips ready→live). - Fix:
GetBroadcastLiveChatIdAsync(broadcastId, maxAttempts=10, delayMs=2000)now readspart=snippetand polls with a bounded retry;PrepareAndStartLiveAsyncstarts the frame pump FIRST, then resolves the id. Chat stays non-fatal. TEST drawer wiring unchanged (BeginTestopens 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
RewardEventsSQLite table +superChatEvents.list(30-day) backfill + session-report rollup (events still parse toChatMessagein 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.IsConnectedin signed-in tests. subscriberCountYPP slice 2 needs re-consent — do not merge with other units.$99/yrPolar must become one-time before launch.MARCOM.md/MONETIZATION.mdgitignored — never commit.- Latent bug the YPP 403 masked: statistics are JSON strings — always the ValueKind-first
ReadInt64(MyMistakes recipe), neverGetInt64. - 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
- 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.
- Push decision is the creator's (repo ~11 ahead). If pushed, note the commit id in this file.
- 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.shbefore 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.