Files
LlamaCasty/HANDOFF.md
T
gramps 0981a72eea fix(stream): chat insert 400 — insert body must declare snippet.type
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).
2026-09-25 08:12:05 -07:00

4.5 KiB
Raw Blame History

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.insert body must declare snippet.type (textMessageEvent or pollEvent) alongside liveChatId and textMessageDetails.messageText — the official insert reference lists type as a required property. The TASK 41 body sent only liveChatId + textMessageDetails.
  • Why the suite didn't catch it: the TASK 41 Good Dog test asserted the POST body contained liveChatId + messageText but never type — false-green while real YouTube rejected every send. The fix asserts type too, so a dropped field fails now.
  • Fix (TASK 45, TASKS/task-45-chat-insert-type.md): InsertChatMessageAsync adds type = "textMessageEvent" to the body; test assertion strengthened; recipe added to MyMistakes.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 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 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.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 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. And 400 MISSING_REQUIRED_FIELD from liveChat/messages.insert = wrong body shape (missing snippet.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

  1. 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").
  2. Push decision is the creator's (repo ~12 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 only, never incremental). One runtime model.
  • No menus/polls with the creator; decisions asked once then held; do the work, then report.