fix(alerts): AlertOverlayLayer marshals the chat-poller seam to the UI thread
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.
This commit is contained in:
@@ -129,6 +129,25 @@ class (physical-mouse drag no-ops when the desktop/session isn't interactive; it
|
||||
in isolation on the third re-run of this unit; the two sibling reorder tests — identical
|
||||
persistence path — pass). `scripts/scope-check.sh` green.
|
||||
|
||||
## Post-ship crash fix (2026-09-26)
|
||||
|
||||
First live run found a crash the sims never could: test-tab alerts work (button commands run on
|
||||
the UI thread — startup.log shows the alert clip's audio in the live mix, `peakMix 0.375 → 0.891`
|
||||
with mic and loop at 0.000), but the first **real** message through the chat poller killed the app
|
||||
at `11:22:01.301`:
|
||||
|
||||
System.ArgumentException: Must create DependencySource on same Thread as the DependencyObject.
|
||||
at ...MS.Internal.Data.DataBindEngine.ProcessCrossThreadRequests()
|
||||
|
||||
`AlertOverlayLayer.OnMessageReceived` lacked the UI-thread marshal `ChatOverlayLayer.OnMessageReceived`
|
||||
has: a polled message ran `RefreshAlertPreviews` → `UpdatePreview` on the MTA poller thread and
|
||||
stamped `alertBox.VideoImageSource` (INPC-raised and WPF-bound) with a `WriteableBitmap` created
|
||||
there — the binding engine's cross-thread re-bind crashed. Fix: marshal the whole ingest
|
||||
(`Dispatcher.Invoke`, `Application.Current`-null guard) so enqueue, ticker, and preview writes stay
|
||||
on the UI thread in lockstep. Good Dog test
|
||||
`OnMessageReceived_FromPollerThread_MarshalsPreviewWritesToTheUiThread` (red on the old seam: the
|
||||
WriteableBitmap's `Dispatcher` was the poller's) — committed with the fix.
|
||||
|
||||
## Open follow-ups (NOT this unit)
|
||||
|
||||
- TASK 3 item 20 (RewardEvent SQLite persistence half) and item 16 (Text source) remain
|
||||
|
||||
Reference in New Issue
Block a user