Follow-up review findings on the promoted base-class methods.
_is_game_really_over:
- `period` present-but-None raised TypeError on `None >= FINAL_PERIOD`,
taking down the whole live-update pass (_detect_stale_games has no
try/except). Same failure shape as the null `period_text` already fixed.
- An expired clock spelled "00:00" normalizes to "0000", which matched
none of the hand-listed literals, so a finished game with a two-digit
minute clock stayed on the scoreboard forever. Compare numerically.
SportsCore:
- _load_fonts kept the cwd-relative "assets/fonts/..." literals the
_font_root() seam exists to remove, so every scoreboard font degraded
to PIL's default face outside the install root.
- _should_log read self._last_warning_time unguarded while only an
unrelated method initialized it lazily; the first warning of a run
raised AttributeError. Initialize it in __init__.
Also documents that game_update_timestamps is written by subclasses, not
by the base class, so the staleness branch is inert until B5 adoption.
14 new tests. Gates: 463 core unit, 60 plugin safety.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FgbA8SMutQQpXkMG8LMmC4