Files
LlamaCasty/HANDOFF.md
T
gramps aea0670723 fix(alerts): wire the frame-rate probe so alert-clip VIDEO paces to real time
The 12:35 live session proved the decoder healthy (frames=240 audioChunks=156
failed=False both clips, alert audio in the mix at peakMix 0.277→0.733) yet the
creator still saw 'no video plays / you lost the video'. Root cause: pacing,
not decode. AlertClipDecoderFor built the decoder with no frameRateProbe, so
RunAsync computed frameDuration = TimeSpan.Zero and RunVideoAsync's pace step
was dead code — all 240 frames of the 10s clip dumped through the pipe in the
first ~1-2s (130MB as fast as ffmpeg read), then the box froze on the LAST
frame while audio chunk-paced its real-time ~7.8s. Reads exactly like a dead
decoder on screen. The media path already wired this seam
(MainViewModel.cs:308); the alert factory never supplied one.

Derivative fix (media/decoder plumbing, cited in broad consensus of players):
pass FfmpegFrameRateProbe into the per-play alert decoder exactly like
MediaVideoSource does. Good Dog:
AlertClipDecoderTests.AlertClipDecoder_PacesVideoFramesToTheProbedFrameRate —
real AlertClipDecoder via the fake-process/fake-probe/delay-recorder seam;
red on the old factory (zero pacing delays), green with the probe (one ~10ms
delay per frame at 100fps). Full suite 321/322, one known-env flake
(RealMouseDrag reorder). Docs in this commit: task-47 addendum,
MyMistakes.md (a decoder that drops data faster than wall-clock looks
identical to a dead one), HANDOFF rewrite.
2026-09-26 12:45:40 -07:00

106 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HANDOFF — 2026-09-26 (TASK 47: crash + silent-void + video-pacing all fixed; 4 local commits, NOT pushed)
## Branch / Commit State
`main` — four local commits ahead of `origin/main` (= `ecb329e`), **NOT pushed** (awaiting the
creator's go / milestone signal):
1. `a11b15e` — **TASK 47** alert box video (built-in/custom clip + read-time fade + message
ticker + unity audio) + `AlertLayerVideoTests` (fake decoder, 0 warnings, 319/319 green).
2. `7b940b6` — **crash fix**: AlertOverlayLayer marshals the chat-poller seam to the UI thread
(regression test red pre-fix / green post-fix). Full suite 320/320.
3. `80038ff` — **silent-decoder fallback + diagnostics** (no-frame grace 1.0s → six-animation;
BeginClip/RunAsync logging). Full suite 321 with the **known env flake** (RealMouseDrag
reorder failing while windows are up).
4. *(uncommitted next — this session)* — **video pacing fix** (below). Scope:
`ViewModels/MainViewModel.Chat.cs`, `ytLive.Tests/AlertClipDecoderTests.cs` (new), docs
(task-47 addendum, `MyMistakes.md`, this file). Test green; build 0 warnings;
`scope-check.sh` pending, then commit.
## ✅ Crash fixed (`7b940b6`)
The creator's first live session: test-tab alerts DID fire (11:21 startup.log: alert-clip audio in
the mix, `peakMix 0.375 → 0.891` with mic+loop at 0.000) but the app crashed at `11:22:01.301` on
the first REAL polled message: `Must create DependencySource on same Thread as the DependencyObject`
in `DataBindEngine.ProcessCrossThreadRequests`. `AlertOverlayLayer.OnMessageReceived` had no
UI-thread marshal (`ChatOverlayLayer` has one); it ran `RefreshAlertPreviews` → `UpdatePreview` on
the MTA poller thread and stamped `alertBox.VideoImageSource` (INPC + WPF-bound) with a
WriteableBitmap created there. Fix + Good Dog regression test
`OnMessageReceived_FromPollerThread_MarshalsPreviewWritesToTheUiThread` (red/green verified).
## ✅ "Nothing in the alert box" void — fixed (`80038ff`)
After the crash fix: On-Air → test → **remove and re-add** the Stream Alerts layer → TEST event →
procs in chat but **nothing in the alert box**. startup.log 11:44 `peakMix 0.105` (no alert audio)
vs 11:21's 0.375–0.891. Every stage of the clip path was silent by design; a decoder whose `Start()`
succeeds but yields **no frames AND no audio** left `_clip != null` with `_latestClipFrame == null`
→ blank box forever. Fix: no-frame grace (1.0s) in `AlertOverlayLayer.Advance` → tear down + same
alert as six-animation; BeginClip/RunAsync diagnostics stop the silence. Good Dog:
`SilentDecoder_FallsBackToTheAnimationAfterTheNoFrameGrace`.
## ✅ "No video plays" — decoder HEALTHY, video UNPACED — fixed (this unit)
Creator repro after `80038ff` (12:35 session): still "no video plays in the web-alert box", "like
you lost the video" — but the new diagnostics PROVED the decoder healthy: `Alert clip start: box …`
→ `Alert clip end: … frames=240 audioChunks=156 failed=False` twice, alert audio in the live mix
(`peakMix 0.277 → 0.733`, mic/loop 0.000). Root cause was **pacing, never decode**:
`AlertClipDecoderFor` (MainViewModel.Chat.cs:113) built the decoder with **no frame-rate probe** →
`RunAsync` computed `frameDuration = TimeSpan.Zero` → `RunVideoAsync`'s pace step
(`if (frameDuration > 0)`) was dead code → all 240 frames of the 10s clip dumped through the pipe
in the first ~1-2s (130MB as fast as ffmpeg read), then the box sat frozen on the LAST frame while
the audio pipeline paced 156 chunks at real-time ~7.8s. Reading exactly like "no video / you lost
it". The media path already had the seam wired (`MediaVideoSource` gets
`frameRateProbe: new FfmpegFrameRateProbe(…)`, MainViewModel.cs:308) — the alert factory just
never passed one.
**Fix (uncommitted):** `AlertClipDecoderFor` passes
`frameRateProbe: new FfmpegFrameRateProbe(new FfmpegLocator(), () => new FfmpegDecodeProcess())` so
video paces at the clip's native ~24fps (10s real-time) in lockstep with audio. Good Dog test
`AlertClipDecoderTests.AlertClipDecoder_PacesVideoFramesToTheProbedFrameRate` drives the REAL
`AlertClipDecoder` through the media-style fake process + fake probe + recorded delay seam
(3 frames at 100fps → one ~10ms delay per frame; red on the old factory, green now). Full suite
321/322 (one known-env flake). `MyMistakes.md`: **a decoder that drops data faster than wall-clock
looks identical to a dead decoder on screen — "plays but you don't see it" is a PACING bug before
it's a decode bug.** Rules: never ship a pipe whose `frameDuration` can be zero; check pacing
(bytes/s vs wall-clock) before re-auditing the codec.
## Follow-ups queued (NOT done in this unit)
- **TASK 3 item 20 persistence half** — canonical `RewardEvents` SQLite table +
`superChatEvents.list` (30-day) backfill + session-report rollup (still in-memory only).
- TASK 3 item 16 (Text source); TASK 40 units A/C/D; TASK 32–36; TASK 12 master limiter — queued.
- If the alert box is STILL "no video" after this fix + relaunch: check the 12:35-style
`Alert clip end: … frames=240` line (now logged) — a healthy decode with no visual means the
painter/render path, not the decoder; the creator's Live scene also has a WebSource
"Web Resource-0" bottom-right — confirm which element they watch when describing the
"web-alert / stream-alerts" box.
- The known pump stall (`render=full-render … totalMs=147`, ~6fps worst) is recorded as a
pre-existing perf item, NOT part of this unit.
## Around the task (carried facts)
- RealMouseDrag tests no-op while a game/fullscreen window steals the mouse — **close fullscreen
windows before full-suite runs**; passes in isolation.
- 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. `MARCOM.md`/
`MONETIZATION.md` gitignored — never commit.
- API facts (TASK 44 + 45): liveChatId unavailable until broadcast live; `400 MISSING_REQUIRED_FIELD`
from `liveChat/messages.insert` = wrong body shape (missing `snippet.type`).
- TASK 47 sticky facts: bgra from ffmpeg is opaque (alpha=255); audio pipe = carry-buffer loop;
default clip = `%TEMP%\ytLive-alert-{id}.mp4`; decoder per-play; DB stays `user_version 10`.
- Every committed change needs a **close + relaunch** of the running app to be seen.
## Next step
1. Commit this unit with the declared scope
`./scripts/scope-check.sh "ViewModels/MainViewModel.Chat.cs" "ytLive.Tests/AlertClipDecoderTests.cs"`.
2. Push the four local commits at the creator's go / milestone signal.
3. Creator to relaunch and re-run the repro — with the probe wired the clip plays real-time
(~10s of motion, not a blur + frozen frame); the `Alert clip end:` line confirms decode health.
## 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. One commit = fix + its memory (task doc, MyMistakes, handoff).
- No menus/polls with the creator; decisions asked once then held; do the work, then report.