The liveChatId fix (TASK 44) worked on the next Test Stream, but every Mock
Chat Input send returned 'YouTube rejected the message (error 400)'. The
runtime log's body: 400 MISSING_REQUIRED_FIELD, domain
youtube.api.v3.LiveChatMessageInsertResponse.Error.
The liveChat/messages.insert snippet requires type ('textMessageEvent' or
'pollEvent') alongside liveChatId and textMessageDetails.messageText; the
TASK 41 body omitted it, and the Good Dog test asserted only liveChatId +
messageText were present — false-green while real YouTube rejected every
send. Fix body + assert type in the same test so the field can never drop
silently again.
Reference: https://developers.google.com/youtube/v3/live/docs/liveChatMessages/insert
Good Dog: ONE integration test (strengthened TestStream_DockTooling...).
Gate: clean build 0 warnings; full vstest 317/318 (the 1 failure is the
known environmental RealMouseDrag flake — passes 3/3 in isolation).
4.5 KiB
HANDOFF — 2026-09-25 (TASK 45 shipped: TEST-tab insert 400 fix — body was missing snippet.type)
Branch / Commit State
main HEAD = new commit for TASK 45 (chat-insert 400 fix); previous: TASK 44 commit
(TEST-tab chat fix), 94f0150. Repo is ~12 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 (TASK 44 reopened by the live run)
The creator ran the TASK 44 fix and the drawer now worked — but every Mock Chat Input
send returned "YouTube rejected the message (error 400)". The runtime log
(%APPDATA%\ytLlive\startup.log) carried the body: 400 MISSING_REQUIRED_FIELD,
domain: youtube.api.v3.LiveChatMessageInsertResponse.Error.
- Root cause: the
liveChat/messages.insertbody must declaresnippet.type(textMessageEventorpollEvent) alongsideliveChatIdandtextMessageDetails.messageText— the official insert reference liststypeas a required property. The TASK 41 body sent onlyliveChatId+textMessageDetails. - Why the suite didn't catch it: the TASK 41 Good Dog test asserted the POST body
contained
liveChatId+messageTextbut nevertype— false-green while real YouTube rejected every send. The fix assertstypetoo, so a dropped field fails now. - Fix (TASK 45,
TASKS/task-45-chat-insert-type.md):InsertChatMessageAsyncaddstype = "textMessageEvent"to the body; test assertion strengthened; recipe added toMyMistakes.md(MISSING_REQUIRED_FIELD = wrong body shape, not auth).
Good Dog: ONE integration test — the strengthened
TestStream_DockTooling_And_ThreeWayDrawerExclusivity POST-body assertion.
Gate: clean build 0 warnings; full vstest 317/318 — the 1 failure is the known
environmental RealMouseDrag flake (mouse stolen by a fullscreen window mid-suite);
it PASSES in isolation (3/3), confirmed just now, so our change is green.
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 fullscreen windows before full-suite runs; the single test passes in isolation when re-run alone. 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 facts (TASK 44 + 45): a liveChatId fetch at broadcast-insert time (lifecycleStatus
ready) returns nothing by design — missing liveChatId ≈ "broadcast not live yet", not an auth failure, until ~20s post push. And400 MISSING_REQUIRED_FIELDfromliveChat/messages.insert= wrong body shape (missingsnippet.type), NOT auth/scope. - Every committed change needs a close + relaunch of the running app to be seen — running builds are live; verify can't run while the exe is locked.
Next step
- Creator verifies the full TEST-tab chat path on the next run: Test Stream → drawer opens with a liveChatId; Mock Chat Input enabled ~as soon as the broadcast goes live; send a message — it should now insert (200) and round-trip through the ~2s poll onto the overlay. This completes the original creator report ("I still cannot post a chat message in the TEST tab").
- Push decision is the creator's (repo ~12 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 only, never incremental). One runtime model.
- No menus/polls with the creator; decisions asked once then held; do the work, then report.