Repository navigation
feat(common): sports consolidation stage 4 -- the identical sweep (plugin host, live scroll, display rules, font path) - #705
Conversation
…rules and sports_font_path, promoted from the scoreboards (sports consolidation stage 4) The identical sweep: method families every scoreboard carrying them has as an identical copy (executable AST, docstrings stripped, decorators compared), re-measured at ledmatrix-plugins 56c4f15. Four new hardware-free modules; the bodies are the plugins', with type annotations and type comments for the mypy ratchet. - src/common/sports_plugin_host.py: SportsPluginHostMixin, ten helpers of the scoreboard plugin class (manager.py) identical in all nine: _dispatch_switch_refresh (with _SWITCH_REFRESH_MIN_GAP_SECONDS), get_vegas_priority_weight, _favorite_team_is_live, _favorite_scan_targets, _favorite_scan_games, _game_involves, get_vegas_content_type, _dynamic_feature_enabled, _get_total_games_for_manager, _build_manager_key. Listed before BasePlugin, whose defaults two of them override. - src/common/sports_live_scroll.py: SportsLiveScrollMixin, the eight manager.py methods that rebuild a live scroll strip mid-cycle (all eight scoreboards with a strip; ufc has none), with the two rebuild-rate constants. LIVE_VOLATILE_FIELDS differs (afl/nrl/soccer add period_text) and stays in each plugin as host contract. - src/common/sports_display_rules.py: SportsCardOptionsMixin (_card_option, _recent_date_text; the eight team scoreboards; must precede SportsCoreSharedMixin, whose _card_option it wraps) and SportsGameRulesMixin (_filtered_or_all, all but football; _effective_live_duration, all but ufc). Split by carrier so ufc does not gain a _card_option override it never had. - src/common/sports_font_path.py: resolve_font_path, what the 17 _resolve_font_path copies (nine sports.py, eight game_renderer.py) return on a core that ships it: the cwd first, then resolve_asset_path. Not resolve_asset_path alone, which skips the cwd. Left in the plugins: _get_timezone, _extract_game_details and _fetch_data (per-plugin import, abstract contract), _schema_font_size and _resolve_font_size (each plugin's _SCHEMA_PATH), and the families carried by seven plugins or fewer. Tests: behaviour ported from the plugins' own tests against stub hosts with exactly the documented contract, a host-contract test per mixin, and test_sports_stage4_parity.py, which with LEDMATRIX_PLUGINS set compares every promoted method and constant with every plugin copy using scripts/sports_drift_report.py's normalisation plus decorators and values (27 pass against 56c4f15; mutation-checked: a changed body, a swapped decorator, a changed constant, a dropped @contextmanager and an unlisted new method each fail it). test_sports_font_path.py runs both on the same paths from a temporary cwd. All four modules are on the mypy ratchet, in src/common/README.md, the CHANGELOG's Unreleased section (module entries for the sunset floor; the version is not bumped) and SPORTS_UNIFICATION. Nothing in core uses them yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… weighting exists The scoreboards' manager.py introduced get_vegas_priority_weight with a section comment; the adoption drops it with the copies, so it moves here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 39 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (14)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 119 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
pyflakes (Codacy) does not read "# type:" comments, so it flagged the Optional and List imports they use as unused (F401). Inline annotations say the same to mypy. The drift-report normaliser, which the stage 4 parity test shares, now treats "x: T = v" as "x = v" -- it already dropped annotations elsewhere -- so the core copies still match the plugins' plain assignments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#703, #705, #706 and #693 merged after this branch was cut; their CHANGELOG entries now sit under 3.8.0. The summary and New modules list name them (sports consolidation stage 4's four modules, src/ipc, field_model), stage 4's section says to floor on 3.8.0, and src/common/README.md marks its four modules 3.8.0 instead of Unreleased. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore(deprecation): remove the 35 APIs deprecated for 3.8.0 The usage scan (docs/DEPRECATIONS_3.8.md, regenerated 2026-10-01 and committed here) finds no call or override of any of them in the 46 monorepo plugins or the 8 third-party plugins plugins.json lists; the only core callers were other deprecated methods removed alongside. - CacheManager: 13 methods, plus the private helpers only has_data_changed used (_has_*_changed, _is_market_open). - DisplayManager: 7 methods, plus WEATHER_COLORS and the private _draw_sun/_cloud/_rain/_snow/_storm helpers only the icon methods used. - FontManager: 14 methods, plus size_tokens, _save_overrides and _clear_plugin_font_cache. font_overrides and _load_overrides stay: resolve_font() still applies config/font_overrides.json. performance_stats stays: get_font() keeps it and tests read it. - PluginManager.get_enabled_plugins. test_deprecation.py pins only the two 3.9.0 markers now; the scanner tests run against a stand-in core instead of the real markers. The memory-tier tests read stats through log_memory_cache_stats() and the component, and the test of the removed _clear_plugin_font_cache goes. Docs drop the removed methods' reference entries; the Deprecated APIs table becomes "Removed in 3.8.0". CHANGELOG gains a Removed section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(deprecation): drop the test harness's copies of the removed icon methods VisualTestDisplayManager still drew weather icons that DisplayManager no longer has, so a plugin's visual tests could pass on calls that raise AttributeError on the real display. Its draw_sun/draw_cloud/draw_rain/ draw_snow/draw_weather_icon/draw_text_with_icons, WEATHER_COLORS and the private helpers go, with the tests that exercised them. The CHANGELOG's Deprecations entries no longer say nothing is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: prepare the 3.8.0 release Bumps src.__version__ to 3.8.0 and turns Unreleased into ## 3.8.0, with a summary and a New modules list (vegas_elements, testing.vegas, sports_vegas; display_watchdog, plugin_catalog, plugin_runtime) for plugins flooring on 3.8.0. Adds the CHANGELOG line #701's second commit lacked (blocks laid out off the render thread). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: 3.8.0 also ships what landed on main since the prep #703, #705, #706 and #693 merged after this branch was cut; their CHANGELOG entries now sit under 3.8.0. The summary and New modules list name them (sports consolidation stage 4's four modules, src/ipc, field_model), stage 4's section says to floor on 3.8.0, and src/common/README.md marks its four modules 3.8.0 instead of Unreleased. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sports consolidation stage 4, the identical sweep (docs/SPORTS_UNIFICATION.md, roadmap row 4): the method families that every scoreboard carrying them still has as an identical copy move into four new hardware-free
src/commonmodules. Nothing to reconcile and no behaviour change; nothing in core uses them yet. The version is not bumped: the modules are recorded under Unreleased, and the plugins adopt them once a release containing this PR exists (the sunset rule).What moved
Re-measured with
scripts/sports_drift_report.pyagainst ledmatrix-plugins56c4f15(origin/main, 2026-09-30; the scoreboards are unchanged at today'sbb1db84): 58 families identical in every copy, as at4327c2e. This PR takes the ones identical across the nine, or across eight with the ninth lacking the method.sports_plugin_hostSportsPluginHostMixin--manager.py:_dispatch_switch_refresh(+_SWITCH_REFRESH_MIN_GAP_SECONDS),get_vegas_priority_weight,_favorite_team_is_live,_favorite_scan_targets,_favorite_scan_games,_game_involves,get_vegas_content_type,_dynamic_feature_enabled,_get_total_games_for_manager,_build_manager_keysports_live_scrollSportsLiveScrollMixin--manager.py:_live_scroll_managers,_refresh_live_scroll_managers,_live_scroll_fields,_fingerprint_games,_live_scroll_fingerprint,_live_scroll_needs_rebuild,_note_live_scroll_built,_preserving_scroll_position(+ the twoLIVE_SCROLL_REBUILD_*constants)sports_display_rulesSportsCardOptionsMixin(_card_option,_recent_date_text) andSportsGameRulesMixin(_filtered_or_all,_effective_live_duration) --sports.py_filtered_or_all: no football)sports_font_pathresolve_font_path-- the module-level_resolve_font_pathin everysports.pyandgame_renderer.pyAbout 4,000 lines across the copies; one copy of each is about 470 lines of core.
Design points:
SportsCardOptionsMixin(whose_card_optionwould otherwise replace theSportsCoreSharedMixin._card_optionufc uses today).SportsGameRulesMixingives football an uncalled_filtered_or_alland ufc an uncalled_effective_live_duration; nothing in either plugin or in core calls them.BasePlugin(two of its methods overrideBasePlugindefaults), andSportsCardOptionsMixinbeforeSportsCoreSharedMixin(it wraps that mixin's_card_optionby name).LIVE_VOLATILE_FIELDSdiffers (afl, nrl and soccer addperiod_text) and stays in each plugin as host contract.__init__, no class attributes beyond the moved constants; the host contract is annotations only, and each module docstring lists it. The parity-safe way to satisfy mypy was annotations on parameters and type comments on locals, since a local annotation changes the AST the drift report compares._resolve_font_pathis not replaced byfont_layout.resolve_asset_path, as the roadmap row planned: that one never looks in the cwd and the plugins' copy does first, so the swap would change which font a process started from another checkout loads.resolve_font_pathis what the copy does on any core that ships it (cwd, thenresolve_asset_path); its body differs from the copy (the copy's probe for older cores is dead there), so it is checked by behaviour, path for path, against all 17 copies.Not promoted, though identical
_get_timezone,_extract_game_details,_fetch_data(nine): a per-plugin import and the abstract contract, as in stage 3._schema_font_size,_resolve_font_size(eight renderers): they read each plugin's own_SCHEMA_PATH, as in stage 3._swrr_advance,_refresh_switch_mode_managers,_initialize_logo_dir, ...), the multi-league plugins' (_resolve_managers_for_mode(5),_extract_mode_type(7),needs_high_fps(5)),_read_bdf_native_size(6),has_active_celebrationand_score_to_int(5), the rest of the three- and four-plugin helpers, and eleven two-plugin ones. Each is one lineage's code; most go when family 13 or 14 reconciles the code around them._odds_color(seven renderers) is already core's, inSportsHelpersMixin.None of the promoted families turned out to differ: decorators (which the report's normalisation drops) and the moved constants' values agree in every copy too.
Tests
test/test_sports_stage4_parity.py: withLEDMATRIX_PLUGINSset, every promoted method and constant against every plugin copy, usingscripts/sports_drift_report.py's own normalisation, plus decorators and constant values; a copy that is gone counts as adopted; a plugin growing a copy it never had fails; a mixin method with no parity row fails; and the drift report must still call each family identical. 27 pass against the plugins; skipped without the variable (core CI). Mutation-checked: a changed body,@staticmethod->@classmethod, a changed constant, a dropped@contextmanagerand an unlisted new method each fail it.test/test_sports_font_path.py:resolve_font_pathand each of the 17 plugin copies (read fromLEDMATRIX_PLUGINS, else a transcription) return the same string for cwd-only, both, install-root-only, absolute, missing and empty paths, from a temporary cwd.test_sports_plugin_host.py,test_sports_live_scroll.py,test_sports_display_rules.py: behaviour ported from the plugins' own tests (Vegas weight across every plugin's data shape, the off-thread refresh and its rate limit, what counts as a strip change, the duty-cycle floor, the marquee position, the date/time rescue and its base-order requirement, the fail-open filter, the non-favourite dwell), against stub hosts carrying exactly the documented contract, plus a host-contract test per mixin that fails on an undocumentedself.read.scripts/check_types.py: no issues in 82 modules.origin/main(596809ac), Pillow 12.3.0 before and after: the same 61 failed / 6 errors (Windows-only, pre-existing), no failing test ID only on either side; 97 more passing, 26 more skipped (the parity rows without a plugins checkout).Adoption (not in this PR)
Prepared on ledmatrix-plugins branch
claude/sports-stage4-adoption, no PR: it floors the nine scoreboards on the release that ships these modules, so it waits for that release. There, all nine inherit the mixins (the base orders above), delete their copies (the scoreboards' tracked files lose 4,726 lines and gain 81; ufc'sfight_renderer.pyandheadshot_downloader.pycopies of_resolve_font_pathgo too), bump minor versions with aversions[]entry flooring on 3.8.0 (compatible_versionstoo), and gainscripts/test_stage4_mixin_copies.py, a sunset guard (mutation-checked) in the shape of stage 3's. Checked against this branch as core:origin/mainbb1db84) and after, alternating before/after/before: 399 frames, 0 differ (control before vs before2: 0 differ), compared pixel by pixel, Pillow 12.3.0 throughout. The after tree also passes its committed card and celebration goldens.run_plugin_tests.pyon the nine, before and after: 321 pass on both sides, and the same two pre-existing failures (baseballtest_odds_placement.py, football's interactivetest_football_plugin.py). Everyscripts/test_*.pyguard gives the same result on both sides;check_sports_drift.pyand the version-bump, manifest and registry checks pass.check_min_core_version.pyreports every new import as "in no released core yet": that is its first commit listing the modules as unreleased, and it is the gate doing its job until the release is tagged._card_optionstill tosports_shared, as before).Owner decision: the release. The adoption can merge only after a core release containing this PR is tagged; its floor says 3.8.0 and must be changed if that release is numbered otherwise. The alternative, adopting now behind guarded imports that fall back to bundled copies, keeps every copy (moved into a fallback module), gives users nothing (there is no behaviour change to deliver), and is the B5 shape the doc warns against: the fallback protects nobody from a break on the modern path while doubling what has to stay in step. Recommended: release, then merge the adoption as is.
One coupling to know about: once this PR is on core
main, the monorepo'sscripts/test_check_min_core_version.py(run against coremainin its CI) fails on four core modules missing fromMODULE_FIRST_VERSIONuntil they are added there asNone(unreleased). That four-line change is the first commit of the adoption branch and can go in on its own as soon as this merges.CHANGELOG has the module entries under Unreleased;
src/common/README.mdand SPORTS_UNIFICATION (module table, a "Stage 4" section, roadmap row 4) are updated.🤖 Generated with Claude Code