Skip to content

fix(sports): auto-heal the stale "timezone": "UTC" left in saved configs - #237

Merged
ChuckBuilds merged 4 commits into
mainfrom
claude/baseball-plugin-timezone-nx7cka
Aug 2, 2026
Merged

fix(sports): auto-heal the stale "timezone": "UTC" left in saved configs#237
ChuckBuilds merged 4 commits into
mainfrom
claude/baseball-plugin-timezone-nx7cka

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Pull Request

Summary

Follow-up to #228. Users who updated to those versions still saw UTC start times until they hand-edited config.json, because the pre-fix write-back bug had already persisted "timezone": "UTC" into their saved plugin config — and an explicit plugin-level value outranks everything else. #228 stopped the plugin writing that value but did nothing about the copies already on disk.

That stale value is now detected and ignored automatically. No manual edit, and no config write either — this is a runtime interpretation, so the plugin never mutates the dict the core owns. That mutation is what created the mess in the first place.

Also fixes a real gap in #228 affecting all ten plugins (see below) — the system-zone fallback it added could never actually be reached.

Type of change

  • Bug fix in an existing plugin
  • New plugin (also fill out the SUBMISSION.md checklist below)
  • New feature for an existing plugin
  • Documentation only
  • Repo-wide change (registry script, hook, top-level docs)

Plugin(s) affected

All ten from #228, patch bumps: baseball 1.20.1, football 2.9.1, hockey 1.5.1, basketball 1.8.1, soccer 2.4.1, ufc 1.3.1, lacrosse 1.5.1, nrl 1.1.1, afl 1.1.1, f1 1.6.1.

Related issues

Follow-up to #228 — user reports of the leftover "UTC" after updating.

What changed

1. The stale-artifact heuristic, scoped to the two plugins that can have one

A bare "UTC" is honored only when nothing downstream disagrees. If the global or system timezone says otherwise, it's treated as the artifact it almost certainly is — it only ever got written on resolution failure — and the downstream value wins, with a warning naming both.

Only baseball-scoreboard and football-scoreboard ever wrote a timezone back. In the other eight a plugin-level "UTC" can only have come from the user, so overriding it would be plain wrong; there it's honored verbatim. _HAD_WRITEBACK_BUG records this per plugin, and the two groups' test suites assert opposite behavior for the same input.

Etc/UTC is the unambiguous opt-in for genuinely wanting UTC — a spelling the old bug could never have produced, so it's always honored. It's in the schema description, the README, and named in the warning itself.

2. ConfigManager.get_timezone()'s own default was masking a missing global setting

Verified against the shipping core (ChuckBuilds/LEDMatrix @ e2acbfb):

def get_timezone(self) -> str:
    """Get the configured timezone."""
    return self.config.get('timezone', 'UTC')

It returns "UTC" for a config with no timezone key at all, indistinguishable from a deliberate choice. #228 took that at face value, so resolution latched onto it and could never reach the host system zone — the very backstop #228 added. Resolution now reads the raw config dict (get_config() / load_config(), both cheap — get_config returns the in-memory dict and load_config has an mtime fast path) and treats an absent key as absent. get_timezone() is consulted only for cores exposing no raw config.

This one applies to all ten plugins and is arguably the more consequential of the two fixes.

Test plan

  • Loaded the plugin in LEDMatrix on real hardware
  • Loaded the plugin in LEDMatrix emulator mode (EMULATOR=true python3 run.py)
  • Rendered the plugin in the dev preview server (scripts/dev_server.py)
  • Verified the web UI configuration form against the schema
  • Other — see below

17 tests per plugin, 170 total, all passing. Six are new, including a _RealCoreConfigManager double that reproduces the shipping core's get_timezone() defaulting behavior rather than an idealized version:

  • stale "UTC" ignored when the global config disagrees / when only the system zone disagrees
  • "UTC" kept when the global and system zone agree — a genuinely-UTC device is never dragged off UTC
  • Etc/UTC honored even against a disagreeing global
  • a global config with no timezone key falls through to the system zone (the fix(sports): stop rendering event start times in UTC (baseball + 9 plugins) #228 gap)
  • an explicit global timezone still outranks the system zone
  • in the eight plugins without the write-back bug: a user-set "UTC" is never overridden, and beats the system zone

Also run: check_module_collisions.py OK across 42 plugins; every test_*.py in all ten plugins before and after — the nine that fail need the core src package and font assets absent here and fail identically on main.

Not verified on hardware or in the emulator.

Required for plugin changes

  • Bumped version in plugins/<id>/manifest.json — all ten, each with a versions[] entry
  • class_name matches the actual class in manager.py (unchanged)
  • entry_point matches the real file (unchanged)
  • Updated the plugin's README.md if config keys changed — baseball and football document the leftover-UTC behavior and the Etc/UTC opt-in
  • config_schema.json is the source of truth — no new keys; the timezone description now covers the Etc/UTC opt-in on the two affected plugins
  • Pre-commit hook ran successfully (auto-synced plugins.json)

Checklist

  • My commits follow the message convention in CONTRIBUTING.md
  • I read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • I've not committed any secrets

Notes for reviewer

The heuristic is the deliberate trade-off here. A bare "UTC" genuinely is ambiguous, and I said as much in #228 when I chose not to auto-heal. What changed is evidence: real users are hitting it, and the artifact only ever appeared on resolution failure, which makes "UTC while your global says Chicago" overwhelmingly likely to be the bug rather than intent. The heuristic is gated three ways to keep the blast radius tight — only the two plugins that could produce it, only when something downstream actively disagrees, and never against Etc/UTC.

Why not a config migration that deletes the line. A plugin can't durably remove it: BasePlugin has no save-config hook, and the web UI's save path replaces each plugin block wholesale from the schema-generated form, so an in-memory delete gets overwritten on the next save. Deleting it properly belongs in the core's ConfigManager as a one-shot migration with a persisted marker — cleaner, and it would tidy config.json for real, but it needs a core release and reaches users only when they update the core. This ships through the plugin store today. The two aren't exclusive; the core migration would still be worth doing.

A note on #228's root-cause story. Reading the core confirmed CacheManager does construct its own ConfigManager, so cache_manager.config_manager normally exists — meaning the plugin-manager lookup #228 added may not have been the operative fix for every reporter. The get_timezone() defaulting bug fixed here is a likelier explanation for some of them: any install whose global config lacks a timezone key got "UTC" from the core and had no path to the system zone. Both fixes are correct and independent; I'm flagging it so the #228 changelog entry isn't read as the whole story.


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved timezone detection across scoreboard plugins when global settings are missing.
    • Scoreboards now fall back to the device’s local timezone instead of incorrectly using UTC.
    • Stale saved UTC values are ignored when conflicting with current settings, while intentional UTC and Etc/UTC choices remain supported.
    • Invalid timezone values are handled safely.
  • Documentation

    • Updated timezone guidance and plugin release notes.
  • Updates

    • Updated scoreboard plugin versions and release metadata.

Users who updated to the previous release still saw UTC start times until they
hand-edited config.json, because the pre-fix write-back bug had already
persisted "timezone": "UTC" into their saved plugin config, where an explicit
plugin-level value outranks everything else.

That stale value is now detected and ignored automatically whenever the global
or system timezone disagrees, with a warning naming the source and zone it used
instead. No config edit, and no config *write* either -- this is a runtime
interpretation, so the plugin never mutates the dict the core owns (that
mutation is what created the mess in the first place).

The heuristic is scoped to the two plugins that could actually produce the
artifact. Only baseball-scoreboard and football-scoreboard ever wrote a
timezone back; in the other eight a plugin-level "UTC" can only have come from
the user, so it is honored verbatim. _HAD_WRITEBACK_BUG records this per plugin
and their tests assert the opposite behaviors.

Etc/UTC is the unambiguous opt-in for genuinely wanting UTC -- a spelling the
old bug could never have written, so it is always honored. Documented in the
schema description and README of the two affected plugins, and named in the
warning itself.

Also fixes a real gap in the previous release, in all ten plugins: the core's
ConfigManager.get_timezone() is self.config.get('timezone', 'UTC'), so it hands
back "UTC" for a global config that has no timezone key at all. Resolution took
that at face value and so could never reach the host system zone -- the very
backstop that release added. It now reads the raw config dict and treats an
absent key as absent.

Verified against the shipping core (ChuckBuilds/LEDMatrix @ e2acbfb): a new
_RealCoreConfigManager test double reproduces get_timezone()'s defaulting
behavior. 17 tests per plugin, 170 total. Module-collision check passes; the
nine pre-existing failures in these plugins are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSnspNZceRCdpUtJh2Co6e
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Updated timezone resolution across ten scoreboard plugins. The changes distinguish missing global timezone keys from explicit UTC, validate timezone names, handle stale UTC artifacts, add regression coverage, and publish patch releases dated 2026-08-02.

Changes

Timezone resolution updates

Layer / File(s) Summary
Resolver source handling and precedence
plugins/*-scoreboard/*_timezone.py
Resolvers read raw configuration, validate IANA zones, evaluate downstream sources lazily, and handle plugin-level UTC values according to plugin write-back metadata.
Timezone precedence regression coverage
plugins/*-scoreboard/test_timezone_resolution.py
Tests cover ConfigManager defaults, UTC and Etc/UTC handling, global-versus-system precedence, fallback behavior, conversion, and registered test execution.
Plugin release metadata and documentation
plugins.json, plugins/*-scoreboard/manifest.json, plugins/*-scoreboard/CHANGELOG.md, plugins/*-scoreboard/README.md, plugins/*-scoreboard/config_schema.json
Patch versions, release dates, changelogs, and timezone configuration guidance were updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PluginResolver
  participant GlobalConfig
  participant SystemTimezone
  PluginResolver->>GlobalConfig: Read raw timezone key
  GlobalConfig-->>PluginResolver: Explicit value or missing key
  PluginResolver->>SystemTimezone: Resolve downstream fallback
  SystemTimezone-->>PluginResolver: IANA timezone
  PluginResolver-->>PluginResolver: Validate and select first usable source
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.34% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: automatically correcting stale saved "timezone": "UTC" values across sports plugins.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/baseball-plugin-timezone-nx7cka

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Jul 30, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 270 complexity

Metric Results
Complexity 270

View in Codacy

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
plugins/afl-scoreboard/afl_timezone.py (1)

134-147: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Narrow the blind except Exception in _validated().

Static analysis flags this as BLE001. pytz.timezone() raises pytz.UnknownTimeZoneError for invalid names; catching bare Exception here can silently swallow unrelated bugs (e.g. issues inside pytz internals) as "invalid timezone" instead of surfacing them.

♻️ Proposed fix
     try:
         pytz.timezone(name)
-    except Exception:
+    except pytz.UnknownTimeZoneError:
         log.warning("Ignoring invalid timezone %r from %s", name, source)
         return None

Since this pattern is duplicated in the sibling _timezone.py modules for the other nine plugins (per the shared architecture), the same narrowing would apply there too if in scope.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/afl-scoreboard/afl_timezone.py` around lines 134 - 147, Replace the
broad exception handler in _validated() with a catch for
pytz.UnknownTimeZoneError only, preserving the existing warning and None return
for invalid timezone names while allowing unrelated pytz errors to propagate.
Apply the change only to this function unless sibling modules are explicitly in
scope.

Source: Linters/SAST tools

plugins/basketball-scoreboard/basketball_timezone.py (1)

56-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead _WRITEBACK_FIXED_IN constant in the seven plugins where the heuristic is disabled.

These modules set _HAD_WRITEBACK_BUG = False, so _WRITEBACK_FIXED_IN is never read, and its comment ("Release that fixed the write-back") contradicts the module docstring stating the plugin never wrote back. Either drop the constant or reword the comment to say it is unused while the heuristic is off. Same copied-header issue: the "ESPN/MLB" phrasing at L3-6 in these non-baseball modules should name the actual feed.

  • plugins/basketball-scoreboard/basketball_timezone.py#L56-L58: remove the unused constant (or note it is inert) and fix "ESPN/MLB" at L3-6.
  • plugins/f1-scoreboard/f1_timezone.py#L56-L58: same; F1 sessions do not come from MLB.
  • plugins/hockey-scoreboard/hockey_timezone.py#L56-L58: same.
  • plugins/lacrosse-scoreboard/lacrosse_timezone.py#L56-L58: same.
  • plugins/nrl-scoreboard/nrl_timezone.py#L56-L58: same.
  • plugins/soccer-scoreboard/soccer_timezone.py#L56-L58: same.
  • plugins/ufc-scoreboard/ufc_timezone.py#L56-L58: same.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/basketball-scoreboard/basketball_timezone.py` around lines 56 - 58,
Remove the unused _WRITEBACK_FIXED_IN constant and its misleading write-back
comment from the timezone modules, while preserving _HAD_WRITEBACK_BUG = False.
Apply this in plugins/basketball-scoreboard/basketball_timezone.py,
plugins/f1-scoreboard/f1_timezone.py,
plugins/hockey-scoreboard/hockey_timezone.py,
plugins/lacrosse-scoreboard/lacrosse_timezone.py,
plugins/nrl-scoreboard/nrl_timezone.py,
plugins/soccer-scoreboard/soccer_timezone.py, and
plugins/ufc-scoreboard/ufc_timezone.py; also replace the copied “ESPN/MLB”
header wording in each module with the actual feed or sport.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/baseball-scoreboard/README.md`:
- Around line 40-45: Update the stale-“UTC” troubleshooting guidance in the
README to state that saved "timezone": "UTC" values are automatically ignored
when they conflict with the global or system timezone, while Etc/UTC remains
honored; remove the outdated claim that these values override everything or
require manual clearing.

---

Nitpick comments:
In `@plugins/afl-scoreboard/afl_timezone.py`:
- Around line 134-147: Replace the broad exception handler in _validated() with
a catch for pytz.UnknownTimeZoneError only, preserving the existing warning and
None return for invalid timezone names while allowing unrelated pytz errors to
propagate. Apply the change only to this function unless sibling modules are
explicitly in scope.

In `@plugins/basketball-scoreboard/basketball_timezone.py`:
- Around line 56-58: Remove the unused _WRITEBACK_FIXED_IN constant and its
misleading write-back comment from the timezone modules, while preserving
_HAD_WRITEBACK_BUG = False. Apply this in
plugins/basketball-scoreboard/basketball_timezone.py,
plugins/f1-scoreboard/f1_timezone.py,
plugins/hockey-scoreboard/hockey_timezone.py,
plugins/lacrosse-scoreboard/lacrosse_timezone.py,
plugins/nrl-scoreboard/nrl_timezone.py,
plugins/soccer-scoreboard/soccer_timezone.py, and
plugins/ufc-scoreboard/ufc_timezone.py; also replace the copied “ESPN/MLB”
header wording in each module with the actual feed or sport.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 09330a16-5fea-42db-86b2-060b1c64c84e

📥 Commits

Reviewing files that changed from the base of the PR and between 8d33894 and d9dde8e.

📒 Files selected for processing (38)
  • plugins.json
  • plugins/afl-scoreboard/afl_timezone.py
  • plugins/afl-scoreboard/manifest.json
  • plugins/afl-scoreboard/test_timezone_resolution.py
  • plugins/baseball-scoreboard/CHANGELOG.md
  • plugins/baseball-scoreboard/README.md
  • plugins/baseball-scoreboard/baseball_timezone.py
  • plugins/baseball-scoreboard/config_schema.json
  • plugins/baseball-scoreboard/manifest.json
  • plugins/baseball-scoreboard/test_timezone_resolution.py
  • plugins/basketball-scoreboard/basketball_timezone.py
  • plugins/basketball-scoreboard/manifest.json
  • plugins/basketball-scoreboard/test_timezone_resolution.py
  • plugins/f1-scoreboard/f1_timezone.py
  • plugins/f1-scoreboard/manifest.json
  • plugins/f1-scoreboard/test_timezone_resolution.py
  • plugins/football-scoreboard/CHANGELOG.md
  • plugins/football-scoreboard/README.md
  • plugins/football-scoreboard/config_schema.json
  • plugins/football-scoreboard/football_timezone.py
  • plugins/football-scoreboard/manifest.json
  • plugins/football-scoreboard/test_timezone_resolution.py
  • plugins/hockey-scoreboard/hockey_timezone.py
  • plugins/hockey-scoreboard/manifest.json
  • plugins/hockey-scoreboard/test_timezone_resolution.py
  • plugins/lacrosse-scoreboard/CHANGELOG.md
  • plugins/lacrosse-scoreboard/lacrosse_timezone.py
  • plugins/lacrosse-scoreboard/manifest.json
  • plugins/lacrosse-scoreboard/test_timezone_resolution.py
  • plugins/nrl-scoreboard/manifest.json
  • plugins/nrl-scoreboard/nrl_timezone.py
  • plugins/nrl-scoreboard/test_timezone_resolution.py
  • plugins/soccer-scoreboard/manifest.json
  • plugins/soccer-scoreboard/soccer_timezone.py
  • plugins/soccer-scoreboard/test_timezone_resolution.py
  • plugins/ufc-scoreboard/manifest.json
  • plugins/ufc-scoreboard/test_timezone_resolution.py
  • plugins/ufc-scoreboard/ufc_timezone.py

Comment thread plugins/baseball-scoreboard/README.md
…EADME

Review feedback from CodeRabbit on #237, all four valid:

- _validated() caught bare Exception around pytz.timezone(). Now catches
  UnknownTimeZoneError for the expected case; anything else is logged with
  exc_info instead of being silently reclassified as "invalid timezone". Kept
  non-propagating rather than letting it escape: this runs in the render path,
  and a mislabelled zone beats taking the display down.

- _WRITEBACK_FIXED_IN is inert in the eight plugins with _HAD_WRITEBACK_BUG =
  False, where its old comment ("release that fixed the write-back") also
  contradicted the docstring saying the plugin never wrote back. Reworded to
  say it is inert and why it stays -- removing it would leave the shared
  resolver body referencing an undefined name if the flag were ever flipped.

- The nine non-baseball modules claimed start times "arrive from ESPN/MLB",
  copied from baseball; they are ESPN-only. F1 and UFC additionally described
  a scroll-mode game_renderer.py neither plugin ships, and called their
  sessions/fights "games".

- baseball README troubleshooting still told users to clear a stuck
  "timezone": "UTC" by hand and said an explicit value "overrides everything
  else" -- both untrue as of 1.20.1, and directly contradicting the note added
  earlier in the same file.

No version bump: these plugins are already bumped against main in this PR.
170 tests still pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSnspNZceRCdpUtJh2Co6e

Copy link
Copy Markdown
Owner Author

All four review points were valid — fixed in ddb811a.

README troubleshooting (the actionable one). Correct, and worse than a stale doc: the section told users to clear a stuck "timezone": "UTC" by hand and stated an explicit value "overrides everything else", which directly contradicted the note added a few lines earlier in the same file. Both are untrue as of 1.20.1. Rewritten, with the Etc/UTC opt-in noted.

Blind except Exception in _validated(). Narrowed to pytz.UnknownTimeZoneError for the expected case. I kept a broad fallback rather than letting other errors propagate — this runs in the render path on a Pi, and a mislabelled timezone beats taking the whole display down — but it now logs with exc_info instead of silently reclassifying the error as "invalid timezone", which was the substance of the concern. Verified both branches: invalid names still return None with the same warning, and an injected non-UnknownTimeZoneError is contained and logged.

Inert _WRITEBACK_FIXED_IN. Right, and the contradiction was real — the comment said "release that fixed the write-back" in modules whose docstring says the plugin never wrote back. Reworded to state it's inert and why it stays. I did not delete it: the _HAD_WRITEBACK_BUG branch that reads it is still present in every module (that's what keeps the resolver body identical across all ten), so removing the constant would leave a latent NameError if the flag were ever flipped.

Copied "ESPN/MLB" header. Good catch — all nine non-baseball modules inherited baseball's feed description; they're ESPN-only. F1 and UFC also described a scroll-mode game_renderer.py that neither plugin ships, and called their sessions/fights "games". All corrected.

Not addressing the Docstring Coverage 47.34% pre-merge warning. The bulk of the diff is test functions whose names are self-describing and which already carry docstrings where the intent isn't obvious; adding boilerplate to reach an 80% threshold would add noise, not information. Happy to revisit if that threshold reflects a repo standard rather than a bot default.

No version bump for this commit — these ten plugins are already bumped against main within this PR. 170 tests still pass.


Generated by Claude Code

claude added 2 commits August 2, 2026 12:44
Resolved version-mechanics conflicts: manifests and plugins.json taken from
main (versions corrected in the follow-up commit); the two scoreboard
CHANGELOGs keep main's history with the timezone-fix note re-headed to the new
patch version. The *_timezone.py fix content applied cleanly.
Corrects the stale version bumps from #237's original commits, which targeted
numbers main has since passed (via #234/#239) — 4 were collisions and 2 were
downgrades. Each is now one patch above main's current version:

  baseball 1.20.2->1.20.3, football 2.9.2->2.9.3, hockey 1.5.2->1.5.3,
  basketball 1.8.2->1.8.3, soccer 2.5.1->2.5.2, ufc 1.3.1->1.3.2,
  lacrosse 1.5.1->1.5.2, nrl 1.1.1->1.1.2, afl 1.1.1->1.1.2, f1 1.7.1->1.7.2

Patch, per CONTRIBUTING.md: a bug fix with no schema additions. plugins.json
regenerated; verified every version is strictly above main and the registry
matches all ten manifests. The 10 test_timezone_resolution.py suites pass
(17 each).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/afl-scoreboard/manifest.json (1)

56-56: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the manifest freshness date for the new releases.

manifest.json last_updated must match the latest version release date. These scoreboard manifests release 2026-08-02, but still record last_updated dates from July.

  • plugins/afl-scoreboard/manifest.json#L56-L56: Set last_updated to 2026-08-02.
  • plugins/baseball-scoreboard/manifest.json#L296-L296: Set last_updated to 2026-08-02.
  • plugins/basketball-scoreboard/manifest.json#L124-L124: Set last_updated to 2026-08-02.
  • plugins/f1-scoreboard/manifest.json#L241-L241: Set last_updated to 2026-08-02.
  • plugins/football-scoreboard/manifest.json#L166-L166: Set last_updated to 2026-08-02.
  • plugins/hockey-scoreboard/manifest.json#L178-L178: Set last_updated to 2026-08-02.
  • plugins/lacrosse-scoreboard/manifest.json#L118-L118: Set last_updated to 2026-08-02.
  • plugins/nrl-scoreboard/manifest.json#L68-L68: Set last_updated to 2026-08-02.
  • plugins/soccer-scoreboard/manifest.json#L161-L161: Set last_updated to 2026-08-02.
  • plugins/ufc-scoreboard/manifest.json#L98-L98: Set last_updated to 2026-08-02.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/afl-scoreboard/manifest.json` at line 56, Update the last_updated
manifest field to the latest release date, 2026-08-02, in
plugins/afl-scoreboard/manifest.json:56-56,
plugins/baseball-scoreboard/manifest.json:296-296,
plugins/basketball-scoreboard/manifest.json:124-124,
plugins/f1-scoreboard/manifest.json:241-241,
plugins/football-scoreboard/manifest.json:166-166,
plugins/hockey-scoreboard/manifest.json:178-178,
plugins/lacrosse-scoreboard/manifest.json:118-118,
plugins/nrl-scoreboard/manifest.json:68-68,
plugins/soccer-scoreboard/manifest.json:161-161, and
plugins/ufc-scoreboard/manifest.json:98-98.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@plugins/afl-scoreboard/manifest.json`:
- Line 56: Update the last_updated manifest field to the latest release date,
2026-08-02, in plugins/afl-scoreboard/manifest.json:56-56,
plugins/baseball-scoreboard/manifest.json:296-296,
plugins/basketball-scoreboard/manifest.json:124-124,
plugins/f1-scoreboard/manifest.json:241-241,
plugins/football-scoreboard/manifest.json:166-166,
plugins/hockey-scoreboard/manifest.json:178-178,
plugins/lacrosse-scoreboard/manifest.json:118-118,
plugins/nrl-scoreboard/manifest.json:68-68,
plugins/soccer-scoreboard/manifest.json:161-161, and
plugins/ufc-scoreboard/manifest.json:98-98.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 77288237-4ea5-4367-a08f-583e011b2470

📥 Commits

Reviewing files that changed from the base of the PR and between d9dde8e and 33babc6.

📒 Files selected for processing (26)
  • plugins.json
  • plugins/afl-scoreboard/afl_timezone.py
  • plugins/afl-scoreboard/manifest.json
  • plugins/baseball-scoreboard/CHANGELOG.md
  • plugins/baseball-scoreboard/README.md
  • plugins/baseball-scoreboard/baseball_timezone.py
  • plugins/baseball-scoreboard/config_schema.json
  • plugins/baseball-scoreboard/manifest.json
  • plugins/basketball-scoreboard/basketball_timezone.py
  • plugins/basketball-scoreboard/manifest.json
  • plugins/f1-scoreboard/f1_timezone.py
  • plugins/f1-scoreboard/manifest.json
  • plugins/football-scoreboard/CHANGELOG.md
  • plugins/football-scoreboard/config_schema.json
  • plugins/football-scoreboard/football_timezone.py
  • plugins/football-scoreboard/manifest.json
  • plugins/hockey-scoreboard/hockey_timezone.py
  • plugins/hockey-scoreboard/manifest.json
  • plugins/lacrosse-scoreboard/lacrosse_timezone.py
  • plugins/lacrosse-scoreboard/manifest.json
  • plugins/nrl-scoreboard/manifest.json
  • plugins/nrl-scoreboard/nrl_timezone.py
  • plugins/soccer-scoreboard/manifest.json
  • plugins/soccer-scoreboard/soccer_timezone.py
  • plugins/ufc-scoreboard/manifest.json
  • plugins/ufc-scoreboard/ufc_timezone.py
🚧 Files skipped from review as they are similar to previous changes (12)
  • plugins/baseball-scoreboard/config_schema.json
  • plugins/football-scoreboard/manifest.json
  • plugins/baseball-scoreboard/README.md
  • plugins/football-scoreboard/config_schema.json
  • plugins/baseball-scoreboard/baseball_timezone.py
  • plugins.json
  • plugins/afl-scoreboard/afl_timezone.py
  • plugins/nrl-scoreboard/nrl_timezone.py
  • plugins/football-scoreboard/football_timezone.py
  • plugins/lacrosse-scoreboard/lacrosse_timezone.py
  • plugins/f1-scoreboard/f1_timezone.py
  • plugins/ufc-scoreboard/ufc_timezone.py

@ChuckBuilds
ChuckBuilds merged commit f163182 into main Aug 2, 2026
4 checks passed
ChuckBuilds pushed a commit that referenced this pull request Aug 2, 2026
#237/#239

Feature files (*_favorite_check.py, managers) applied cleanly. Resolved the
version-mechanics conflicts against current main; the config_schema/odds-ticker
changes from #234 that #235 still carried are now no-ops (already in main).
Versions corrected in the follow-up commit.
ChuckBuilds pushed a commit that referenced this pull request Aug 2, 2026
Minor bumps (new user-facing feature) for the seven plugins #235 actually
changes, each above main's post-#237 version:

  afl 1.2.0, baseball 1.21.0, basketball 1.9.0, football 2.10.0,
  hockey 1.6.0, lacrosse 1.6.0, nrl 1.2.0

odds-ticker is intentionally NOT bumped: its only change in #235 was the NHL
picker correction (UTA->UTAH, +Seattle) that came from #234 and is already in
main, so it has no net change here. The original bump list also targeted
numbers main has since passed via #234/#236/#237/#239; corrected. plugins.json
regenerated; every changed plugin is strictly above main.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
ChuckBuilds added a commit that referenced this pull request Aug 3, 2026
* Fix wrong ESPN team codes in pickers and help text

Several plugins documented — or in one case offered in a picker — team
abbreviations that ESPN does not use, so copying them matched no team and
the plugin silently showed nothing.

odds-ticker's NHL picker was the only one where the user could not work
around it: it listed UTA (a retired code, labelled with the club's former
name "Utah Hockey Club" rather than "Utah Mammoth") and omitted the
Seattle Kraken entirely, so that team could not be selected at all. The
enum and labels are now generated from ESPN's team endpoint and match it
exactly at 32 teams.

The rest are description-only corrections to the favorite_teams examples:

  basketball  NBA    GSW   -> GS     (Golden State Warriors)
  basketball  WNBA   NYL   -> NY     (New York Liberty)
  basketball  WNBA   LAS   -> LA     (Los Angeles Sparks)
  basketball  NCAAW  UCONN -> CONN   (UConn Huskies)
  basketball  NCAAW  SCAR  -> SC     (South Carolina Gamecocks)
  football    NCAAFB BAMA  -> ALA    (Alabama Crimson Tide)
  hockey      NCAAWH WISC  -> WIS    (Wisconsin Badgers)

Each description now also says these are ESPN's codes and are not always
the ones you would guess, since that is the underlying trap.

Every code here was verified against
site.api.espn.com/apis/site/v2/sports/{sport}/{league}/teams?limit=1000.
The limit matters: without it the default page size truncates the NCAA
responses (362 of 755 teams) and makes valid codes look wrong.

Left alone deliberately: lacrosse-scoreboard's WISC/MINN/OSU and
BU/BC/MICH examples, and baseball-scoreboard's MiLB DUR/SWB/NOR, because
ESPN's lacrosse team endpoints return zero teams and the MiLB one 404s.
Unverifiable, so not guessed at.

No rendering code changed; the safety harness passes for all four plugins
at every size.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ

* Explain an empty scoreboard instead of leaving the user guessing

Favorite teams are matched by exact ESPN abbreviation, so a code that is
not real matches no game and the plugin shows nothing — with no hint that
the code is the problem. Out of season, a perfectly correct code produces
the identical empty screen. The two were indistinguishable from the logs,
which is how a user ends up asking whether their config is broken when it
is only July.

Each of these plugins now says which case it is:

  WARNING  NFL favorite team 'GBP' is not a NFL team code. Closest match
           is 'GB' (Green Bay Packers). Every code this league accepts is
           listed at https://site.api.espn.com/.../nfl/teams?limit=1000.

  INFO     NFL favorite teams TB look correct, but the league has nothing
           on until 06 August 2026. An empty display until then is
           expected, not a configuration problem.

  INFO     NCAA Baseball favorite teams UGA look correct, but the season
           has finished and the next one's fixtures are not published yet.

Suggestions rank word-initial matches first, because string similarity is
useless at three characters: 'MUN' scores identically against 'MAN' and
'SUN', so Manchester United and Sunderland tie and the answer is a coin
flip. Fragments are handled too ('BAMA' is inside 'Alabama' but
abbreviates nothing in it), and a code that only differs in case is told
so rather than guessed at.

Reading the schedule turned out to be the subtle part, and both traps are
real ESPN behaviour confirmed against live endpoints:

- An out-of-season league does not return an empty scoreboard. ESPN rolls
  forward to the next day with fixtures, so in July the NHL endpoint
  returns seven September games. Emptiness cannot be the signal.
- A *finished* season rolls nowhere and returns its last game instead,
  months in the past — so dates must be filtered before the soonest one
  means anything. Filtering on "later than now" then wrongly drops games
  that started earlier today and reports a live slate as a dead season,
  so the window is the last 24 hours.

Verified against every league these plugins cover: MLB, AFL, NRL and WNBA
correctly stay quiet; NFL, NCAA football, NHL, NBA and NCAA men's
basketball report their start dates; NCAA baseball and NCAA women's
hockey report finished seasons.

Safety, since this runs inside update():
- It runs on a daemon thread, so it never delays a frame.
- Once per league per process, re-armed only when the config changes.
- Every failure path is swallowed to a debug line. A plugin whose ESPN
  endpoint returns no teams at all (college lacrosse) draws no conclusion
  rather than calling a valid code wrong.

Each plugin ships its own copy of the module, since the loader gives
plugins no shared library to import from, under a plugin-unique name per
the module-collision rule. A test asserts the copies stay byte-identical
while they live in one checkout.

Tested: 30 unit tests; safety harness 24/24 PASS per plugin (168 renders,
zero failures); module-collision check clean. Validated end-to-end on real
hardware, where all four message paths appeared as intended with no errors.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ

* chore: version the favorite-team diagnostics above current main

Minor bumps (new user-facing feature) for the seven plugins #235 actually
changes, each above main's post-#237 version:

  afl 1.2.0, baseball 1.21.0, basketball 1.9.0, football 2.10.0,
  hockey 1.6.0, lacrosse 1.6.0, nrl 1.2.0

odds-ticker is intentionally NOT bumped: its only change in #235 was the NHL
picker correction (UTA->UTAH, +Seattle) that came from #234 and is already in
main, so it has no net change here. The original bump list also targeted
numbers main has since passed via #234/#236/#237/#239; corrected. plugins.json
regenerated; every changed plugin is strictly above main.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
@ChuckBuilds
ChuckBuilds deleted the claude/baseball-plugin-timezone-nx7cka branch August 5, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants