0981a72eea
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).
74 lines
4.5 KiB
Markdown
74 lines
4.5 KiB
Markdown
# 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. |