7b940b6a5a
Live test session proved the native alert box DID play (the alert ring was the only source in the mix: micLevel/loopLevel 0.000 while peakMix went 0.375->0.733->0.891 after the sim injections) but the app crashed at 11:22:01.301 the moment a REAL message round-tripped through the poller: System.ArgumentException: Must create DependencySource on same Thread as the DependencyObject at ...MS.Internal.Data.DataBindEngine.ProcessCrossThreadRequests() OnMessageReceived ran RefreshAlertPreviews on the MTA poller thread and stamped the WPF-bound VideoImageSource with a WriteableBitmap created there; the binding engine's cross-thread re-bind killed the process. ChatOverlayLayer already marshals this exact seam (Dispatcher.Invoke) - mirror it, guarded for Application.Current null so the pure test seams still run inline. Good Dog test: AlertLayerVideoTests.OnMessageReceived_FromPollerThread_MarshalsPreviewWritesToTheUiThread calls the seam from a raw MTA Thread while the RealApp loop runs, then asserts on the UI thread that the preview bitmap's Dispatcher is the App's (red on the old seam, green on the fix). WPF cross-thread rule recorded in MyMistakes.md. Full suite 320/320, clean build 0 warnings, scope-check green.
82 lines
5.4 KiB
Markdown
82 lines
5.4 KiB
Markdown
# HANDOFF — 2026-09-26 (TASK 47 shipping crash fixed; 2 local commits, NOT pushed)
|
||
|
||
## Branch / Commit State
|
||
|
||
`main` — two local commits ahead of `origin/main` (= `2c9af94`... actually `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, 318/319 with the
|
||
known env flake → final full run 319/319 green).
|
||
2. *(uncommitted next)* — **the 2026-09-26 live-crash fix** (see below), scope = 3 files + docs:
|
||
`Services/AlertOverlayLayer.cs`, `ytLive.Tests/AlertLayerVideoTests.cs`, plus docs
|
||
(`TASKS/task-47-alert-videos.md` addendum, `MyMistakes.md` lesson, this file). Full suite
|
||
**320/320** green, alert tests 3/3, `scope-check.sh` passed.
|
||
|
||
## ⚠️ The live crash + fix (this unit)
|
||
|
||
The creator ran the shipped build in a test session: **test-tab alerts DID fire** (button
|
||
commands run on the UI thread — startup.log `11:21:53` shows the sims and alert-clip audio in the
|
||
live mix: `micLevel=0.000 loopLevel=0.000` yet `peakMix 0.375 → 0.733 → 0.891`, which can only be
|
||
the alert ring; the default clip is materialized at `%TEMP%\ytLive-alert-941785b0-….mp4`, the
|
||
video pipe verified against the pinned ffmpeg), but the app **crashed at `11:22:01.301`** — the
|
||
first REAL message that round-tripped through the chat poller:
|
||
|
||
System.ArgumentException: Must create DependencySource on same Thread as the DependencyObject.
|
||
at ...MS.Internal.Data.DataBindEngine.ProcessCrossThreadRequests()
|
||
|
||
Root cause: `AlertOverlayLayer.OnMessageReceived` had NO UI-thread marshal while
|
||
`ChatOverlayLayer.OnMessageReceived` has one. A polled message ran `RefreshAlertPreviews` →
|
||
`UpdatePreview` on the MTA poller thread and stamped `alertBox.VideoImageSource` (INPC-raised +
|
||
WPF-bound) with a WriteableBitmap created there → binding engine cross-thread re-bind → crash.
|
||
Fix: marshal the whole ingest via `Application.Current.Dispatcher.Invoke` (null-guarded so pure
|
||
seams still run inline). Good Dog regression test
|
||
`AlertLayerVideoTests.OnMessageReceived_FromPollerThread_MarshalsPreviewWritesToTheUiThread`
|
||
(calls the seam from a raw MTA `Thread`, returns to the message loop, asserts on the UI thread
|
||
that `VideoImageSource.Dispatcher` is the App's dispatcher — **red pre-fix, green post-fix,
|
||
verified both ways**). Rule written into `MyMistakes.md`: every INPC-raising seam fed by the chat
|
||
poller must marshal WPF-object writes to the UI thread.
|
||
|
||
## 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 — all
|
||
queued unchanged.
|
||
- If the creator still reports "nothing in the box" after this fix (their Live scene's "Stream
|
||
Alerts" AlertBox is at 612,45 680×200; there is also a WebSource "Web Resource-0" bottom-right —
|
||
confirm which element they're watching), investigate the video-render path live — but note all
|
||
evidence (audio in the mix, materialized clip, verified pipe) says the native box played.
|
||
|
||
## 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**; 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. `$99/yr` Polar
|
||
must become one-time before launch. `MARCOM.md`/`MONETIZATION.md` gitignored — never commit.
|
||
- **API facts (TASK 44 + 45):** liveChatId unavailable before the broadcast is live (poll until
|
||
active); `400 MISSING_REQUIRED_FIELD` from `liveChat/messages.insert` = wrong body shape
|
||
(missing `snippet.type`), NOT auth/scope.
|
||
- **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, never the refcounted
|
||
`MediaVideoSourceManager`. DB stays `user_version 10` — TASK 47's Source columns are additive
|
||
guarded ALTERs. Recipe in `MyMistakes.md`.
|
||
- ChatOverlayLayer.OnMessageReceived marshals the poller seam; AlertOverlayLayer now mirrors it.
|
||
- Every committed change needs a **close + relaunch** of the running app to be seen.
|
||
|
||
## Next step
|
||
|
||
1. Commit the crash fix (declared scope + same-commit docs), then run
|
||
`./scripts/scope-check.sh "Services/AlertOverlayLayer.cs" "ytLive.Tests/AlertLayerVideoTests.cs"`.
|
||
2. Full clean build (0 warnings, Windows dotnet host) + full vstest **320/320**.
|
||
3. Push `a11b15e` + the fix commit at the creator's go / milestone signal.
|
||
4. Creator to relaunch and re-run the test-tab alerts (one of each) + post a real chat message —
|
||
the crash is fixed; then confirm which visual element they call the "web-alert box".
|
||
|
||
## 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. |