A game that was live in the evening was still being drawn as live the next
morning. Two faults combined to freeze plugin visuals indefinitely.
PR #291 added a call to plugin_adapter.invalidate_plugin_scroll_cache() so
a plugin's own cached scroll image would be rebuilt from fresh data. That
method was never implemented. hot_swap_content() wraps the call in a broad
except, so every hot swap has raised AttributeError and been swallowed
silently ever since — which is why the visuals it was meant to keep fresh
never were.
Continuous scrolling then removed the only path that reached it at all:
should_recompose() and hot_swap_content() are called from the
non-continuous branch of run_frame(), and continuous_scroll defaults to
True. So on a default install the pending-update flags were set by the
update tick, never consumed, and grew without bound.
Together these froze content completely, because refetching is not enough
on its own: the sports plugins' get_vegas_content() regenerates only "if
the cache is empty", so take_next_group() kept receiving the same picture
however often it asked.
Fixed by:
- Implementing invalidate_plugin_scroll_cache(). It covers both layouts —
a helper directly on the plugin (stocks, news, odds-ticker) and one
owned by a scroll-display manager (the sports scoreboards, which is the
shape that produced this bug) — and clears cached_image and
cached_array together, since the array is the image's numpy mirror.
- Adding StreamManager.invalidate_pending_updates() and calling it from
the continuous branch. It only drops the caches; the plugin recomposes
when it next comes round in the rotation. process_updates() is wrong
here: it refetches synchronously and merges into the active buffer that
continuous mode bypasses, and hot_swap_content() rebuilds and
repositions the whole strip, which is the freeze-and-jump this mode
exists to avoid.
Tests assert the fix rather than the implementation: 14 of the 17 new
tests fail without it. Includes the wiring itself, since the regression
was a call that was simply absent, and a check that the scroll position is
untouched so this cannot regress into the swap's visible jump.
All Vegas suites pass (355 tests). test_display_controller_vegas_tick.py
still cannot be collected off-device for want of rgbmatrix, identically
with and without this change.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ