Files
LlamaCasty/HANDOFF.md
T
gramps bb5dcb4ba2 feat(stream): TASK 41 Test Stream mode — private test broadcast + TEST drawer
Test button next to Start runs the real private-only go-live pipeline as a
session variant (IsTestStream): BeginTestStream -> extracted
StartStreamingSession(alsoRecord:false) shared with the dialog path — no Go
Live dialog, no 'last live' stamp, no recording. Gold top bar + TEST badge +
'End Test' button face; TEST drawer (third right-rail pull-out, three-way
exclusivity) auto-opens once the liveChatId resolves.

Chat tooling: Mock Chat Input sends a REAL liveChat/messages.insert
(YouTubeStreamService.InsertChatMessageAsync) that round-trips the real ~2s
poll and renders through the live overlay; simulated Subscriber/New
Member/Super Chat events inject through the poller's MessageReceived seam
(YouTubeChatService.InjectSimulatedMessage, ChatMessage.IsSimulated) — the
insert API only creates textMessageEvent, so non-text events are local-only
by design (ref: developers.google.com/youtube/v3/live/docs/liveChatMessages/insert).
Scopes youtube + youtube.force-ssl already cover insert; no re-consent.

Good Dog: ytLive.Tests/TestStreamTests.cs — coordinator protocol with fake
HTTP + real-window drawer exclusivity/gate. 0 warnings; 314/314 pass (clean
run; intermittent host abort is the pre-existing WinRT-webcam flake).

Docs ride in the same change: TASKS.md row 41, TASKS/task-41-test-stream-mode.md,
ai.md (YouTube Live API constraints), HANDOFF rewrite.
2026-09-22 09:31:00 -07:00

4.3 KiB

HANDOFF — 2026-09-22 (TASK 41 Test Stream mode shipped; TASK 40 plan queued)

Branch / Commit State

main HEAD = 4d9188a — docs(plan): TASK 40 App Settings round — saved plan. Pushed state at 197ee81. Working tree DIRTY — TASK 41 implementation in progress (code + docs staged for a single scope-checked commit; HANDOFF rewrite rides in a final docs commit):

?? HANDOFF.md                      (this rewrite)
 M Models/YouTube.cs
 M Services/YouTubeStreamService.cs
 M Services/YouTubeChatService.cs
 ?? Services/TestSessionViewModel.cs
 M ViewModels/MainViewModel.cs
 M ViewModels/MainViewModel.Ypp.cs
 M ViewModels/MainViewModel.Account.cs
 M ViewModels/MainViewModel.Streaming.cs
 M ViewModels/MainViewModel.Streaming.Operations.cs
 M Controls/TopBar.xaml
 M Controls/PreviewPane.xaml
 ?? ytLive.Tests/TestStreamTests.cs
 M TASKS.md
 ?? TASKS/task-41-test-stream-mode.md
 M ai.md

Build impact: main + tests build clean, 0 warnings; 314/314 tests pass (313 prior + 1 new Good Dog). A flaky real-camera startup test aborted one earlier full-suite run (hardware-dependent, unrelated to this change); the clean rerun passes fully.

What shipped — TASK 41: Test Stream mode (2026-09-22)

Full record: TASKS/task-41-test-stream-mode.md. Decisions locked with the creator: chat-overlay-events-only (no stinger/TTS — stays TASK 3 16/17/20), TEST drawer in the right rail, hybrid simulation (real text insert + local synthetic events). Key design: since every broadcast is already private-only (CreateBroadcast hardcodes privacyStatus=private), "Test" is a session variant: BeginTestStream → extracted-shared StartStreamingSession(alsoRecord:false) (no dialog, no "last live" stamp, no recording). IsTestStream = gold top bar (#9c6f1c) + TEST badge

  • "End Test" button face. TestSession.BeginTest(broadcastId, liveChatId) opens the TEST drawer once the liveChatId resolves. Mock Chat Input = real liveChat/messages.insert round-trip; simulated events inject through the poller's MessageReceived seam marked IsSimulated (hard API fact: insert only creates text — non-text events are local-only by design). Three-way drawer exclusivity (Stream Settings / YPP / TEST) in MainViewModel.Ypp.cs.

Good Dog — ytLive.Tests/TestStreamTests.cs (one [Fact]): coordinator protocol w/ fake HTTP (insert POST body asserted; simulated shapes verified) + real-window exclusivity/gate. Note: the coordinator section runs on the test thread directly (pure services, no WPF), the window section via RealAppHost.Run — deviates from YppPullOutTests' all-in-one-Run shape for debuggable stacks.

⚠️ Around the task

  • Manual verification still owed (needs a real run, Windows): Test button → gold face + badge + auto-open TEST drawer; send a message → appears on a ChatBox overlay after ~2s; simulated rows render text/member/SuperChat styled; End Test closes it all out cleanly (transition complete).
  • The app may be RUNNING — in-place build fails MSB3021/MSB3027 until closed (temp-OutDir workaround in ai.md). Close it before the manual pass.
  • The full-suite flake earlier: real-camera startup test under the test host (hardware, pre-existing behavior; unrelated to TASK 41).
  • LayerReorderPersistenceTests.RealMouseDrag flaked once pre-TASK-38 (fullscreen game); unrelated.
  • subscriberCount rounded to 3 sig figs (YPP); carried. YPP slice 2 needs re-consent — do NOT merge with other units.
  • Polar product d105dfa1… still $99/yr — must become one-time before launch (carried). MARCOM.md/MONETIZATION.md gitignored — never commit.

Next step

Commit TASK 41 (scope-check the declared list first), then TASK 40 App Settings round is queued (TASKS/task-40-app-settings-round.md, units A → B → C → D, one Good Dog each). Unit A's SharedReadOnly control-write question is the one genuinely uncertain technical point — run its mitigation ladder, spin-guard research before a third guess. Push only on the user's say-so.

Critical working rules (unchanged, still binding)

  • Good Dog = ONE integration test per change.
  • Scope lock: declare the file list before editing; ./scripts/scope-check.sh before commit.
  • WSL builds use the Windows dotnet host (/mnt/c/Program Files/dotnet/dotnet.exe, quoted paths).