# 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.