Files
LlamaCasty/HANDOFF.md
T
gramps d4aa588da0 feat(alerts): TASK 43 — native events & alerts (six unique animations) + chat parity
Replace the external StreamElements feed with NATIVE YouTube events per the creator's
2026-09-23 question.

Chat parity half:
- YouTubeChatService decodes ALL SIX liveChat/messages event types into
  ChatMessage.Kind (superChat/superSticker/newSponsor/membershipGifting/
  giftMembershipReceived/memberMilestoneChat) — the four formerly-empty overlay
  rows are real now — and re-arms its one-shot poll on the server's
  pollingIntervalMillis (streamList connection semantics; clamp 1000-6000ms,
  maxResults=2000). ParsePage internal static seam + ChatPage record for
  deterministic tests; optional HttpClient ctor seam kept; InjectSimulatedMessage
  (TASK 41) preserved.

Alerts half (creator rulings: one celebration zone, six UNIQUE animations, no
menus/polls, sub mention = chat row only, NO viewer count):
- New SourceType.AlertBox 'Stream Alerts' (one per layout, CanAddAlerts gate
  mirroring chat; idle = transparent).
- AlertRenderer: six distinct branded animations (SuperChat slide-up/shine/
  count-up, SuperSticker scale-pop, NewMember drop-in/flash, MemberGift slide-left/
  chip-fan, GiftReceived confetti, MemberMilestone rise/growth-bar), every card
  drawing the 'made with LlamaCasty!' brand line.
- AlertOverlayLayer: true component (Commit-G pattern) — queue (cap 10, drop tail
  never stall), 33ms UI ticker, cache-first RenderFrame + UpdatePreview,
  Advance(double) as the deterministic test clock; ChatEventKind.None rows never
  enqueue.
- Wired: resolver RenderAlertBox, _alertLayer ctor + dispose, LoadLayout previews,
  Add menu item (Controls/LeftPanel.xaml), TestSessionViewModel sims tagged
  (member->NewMember, superchat->SuperChat).

Good Dog ONE integration test: AlertLayerTests (RealApp STA, real WPF raster) —
ParsePage classifies all six kinds + cadence fields; None rows enqueue nothing;
six events play pairwise-distinct moving frames then drain to null.

Gate: clean build 0 warnings; full suite 316/317, the one failure
(LayerReorderPersistenceTests.RealMouseDrag) repros on the clean tree — the
known environmental class (real-mouse-drag no-ops with a game/fullscreen window
focused).

References (OBS/overlay ecosystem):
- streamList semantics: https://developers.google.com/youtube/v3/live/docs/liveChatMessages/streamList
- OBS alert-box pattern (designated celebration zone, idle transparent): creator-chosen model
2026-09-24 08:27:00 -07:00

5.4 KiB
Raw Blame History

HANDOFF — 2026-09-24 (TASK 43 shipped: native events & alerts — creator said "go")

Branch / Commit State

main HEAD = new commit for TASK 43 (native events & six-animation alert box); previous: 3a4e17c (account zone world-independent), cb75066 (YPP 403), d72949e (top-bar feedback), ac6e67a (end close-out). Repo is ~10 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 (this turn, creator said "go")

The creator asked (2026-09-23) whether the app could use native YouTube events instead of StreamElements. Research pinned the API facts; creator rulings locked the product shape. One combined work unit ("go"), task filed as TASK 43 (TASKS/task-43-native-alerts.md):

  1. Chat parity half — YouTubeChatService now decodes all six liveChat/messages event types into ChatMessage.Kind (the four previously-empty event rows are real now) and re-arms its poll on the server's pollingIntervalMillis (streamList semantics; clamp 1000–6000ms, maxResults=2000).
  2. Alerts half — new SourceType.AlertBox ("Stream Alerts", one per layout, CanAddAlerts gate mirroring chat): an OBS-style celebration zone, idle = transparent. Services/AlertRenderer.cs draws six DISTINCT branded animations (SuperChat slide-up/ shine/count-up; SuperSticker scale-pop; NewMember drop-in/flash; MemberGift slide-left/chip-fan; GiftReceived confetti; MemberMilestone rise/growth-bar), each card carrying the "made with LlamaCasty!" brand line (free product placement per broadcast). Services/AlertOverlayLayer.cs is a true component (Commit-G pattern): queue (cap 10) + 33ms ticker + cache-first RenderFrame + UpdatePreview; Advance(double) = deterministic test clock.
  3. Creator rulings baked in: no menus/polls (asked once, held); free-sub mention = chat row only, no sub alert (YouTube emits none); no viewer count (vetoed — demotivating, weaponizable); six events = six unique animations, not a recolored template.

Good Dog: ytLive.Tests/AlertLayerTests.cs (RealApp STA, real WPF raster) — ParsePage classifies all six kinds + cadence fields; playback never enqueues a None (sub-mention) row, plays six pairwise-distinct moving frames, drains to idle. Gate: clean build 0 warnings, 316/317 — the ONE failure (LayerReorderPersistenceTests.RealMouseDrag…) repros on the CLEAN tree (proved via git stash + rebuild + isolated run): a real-mouse-drag test that no-ops while a game/fullscreen window steals the mouse (the HANDOFF-documented environmental class). NOT a regression — do not chase it in-app; close games and rerun if in doubt.

Follow-ups queued (NOT done in this unit)

  • Creator report still open — not yet diagnosed: "I still cannot post a chat message in the TEST tab." The TEST drawer's Mock Chat Input posts a REAL liveChat/messages.insert; the fix needs its own scoped unit.
  • TASK 3 item 20 persistence half — the canonical RewardEvents SQLite table + superChatEvents.list (30-day) backfill + session-report rollup (the shipped alert path parses events to ChatMessage in memory; the report reads that table once it exists).
  • 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 games before full-suite runs. 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.
  • New facts recorded: the chat feed IS the event feed (six types, no sub event); streamList is a connection-semantics description of that same endpoint (pollingIntervalMillis drives cadence).
  • Every committed change needs a close + relaunch of the running app to be seen — running builds are live; verify.sh can't run while the exe is locked.

Next step

  1. Creator verifies the alerts unit on the next run: Add → Stream Alerts, then (Test Stream drawer) Simulated Super Chat / New Member — confirm the zone stays transparent at idle and each event plays its own animation on the canvas and preview; a real Super Chat during the next live run confirms the real path.
  2. Push decision is the creator's (repo ~10 ahead). If pushed, note the commit id in this file.
  3. When scoped: diagnose the TEST-tab chat posting report ("I still cannot post a chat message in the TEST tab") as its own Good-Dog unit.

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.sh only, never incremental). One runtime model.
  • No menus/polls with the creator; decisions asked once then held; do the work, then report.