Fix wrong ESPN team codes in pickers and help text - #234
Conversation
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
|
Warning Review limit reached
Next review available in: 19 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (10)
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
|
Two issues flagged by Codacy on the previous commit: - Dropped an unused 're' import left over from an earlier draft. - Pinned the URL scheme to https before opening it. The league path is interpolated into the URL, and urlopen would honour file:// or a custom scheme if a path ever arrived from somewhere less trustworthy than the hardcoded table. Bandit's B310 is a syntactic blacklist rule so it still fires on the call itself; annotated with the reason rather than left to look unexamined. Verified: bandit clean, the guard rejects a file:// URL, and the checker still fetches all 32 NHL teams and still exits 1 against main (which is correct until #234 lands). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ MSG
hockey 1.5.1->1.6.0, basketball 1.8.1->1.9.0; odds-ticker moves to 1.1.9 (main took 1.1.8). plugins.json regenerated via update_registry.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgbA8SMutQQpXkMG8LMmC4
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
…igs (#237) * fix(sports): auto-heal the stale "timezone": "UTC" left in saved configs 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 * fix(sports): address review — narrow except, fix copied docstrings, README 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 * chore: bump the ten scoreboards for the timezone auto-heal, above main 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 --------- Co-authored-by: Claude <noreply@anthropic.com>
…ort (#236) * Catch team-picker drift against ESPN instead of waiting for a bug report A favorite_teams checkbox-group is a hand-maintained copy of a league's roster, and rosters change — clubs get renamed, relocated, or added. When the copy drifts nothing errors. The picker simply fails to offer a team that exists, or offers a code that matches nothing, and the user gets an empty screen. odds-ticker's NHL list had both faults at once, and had had them long enough for a user to hit it: it still listed UTA, a retired code, under the club's former name, and omitted the Seattle Kraken entirely so that team could not be selected at all. Nothing in CI could have noticed. scripts/check_team_pickers.py compares every picker in the repo against ESPN and separates the two kinds of difference, because they are not equally serious: enum which codes exist. A mismatch is a bug, so it fails the check and names the teams that cannot be selected. labels the display names. ESPN's own text is sometimes worse than the hand-written label — it calls the Clippers "LA Clippers" where the schema says "Los Angeles Clippers" — so this only warns, and --apply never overwrites an existing label unless asked. --apply regenerates the enums, fills in labels only where one is missing, and preserves the file's existing escaping style so a three-line fix does not arrive as hundreds of lines of reformatting. Verified by reintroducing the exact pre-fix NHL state, which the checker catches and reports precisely: odds-ticker: nhl - offers 1 which ESPN does not have: UTA odds-ticker: nhl - cannot select 2 real team(s): SEA (Seattle Kraken), UTAH (Utah Mammoth) and confirming --apply restores it with the hand-written NBA label intact. All four current pickers (NFL, NBA, MLB, NHL) pass. Unreachable ESPN warns rather than fails, so an outage cannot turn into a red build. A picker keyed by an unknown league is reported rather than skipped, so adding a league cannot quietly opt out of the check. Intended to run on a weekly schedule rather than per-PR, since the check needs the network and a roster does not change because someone opened a pull request. The workflow file is not included here: pushing .github/workflows requires an OAuth token with the workflow scope, which this one does not have. It is ready to add separately. No plugin code or schema changes here — tooling only. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ * Address static-analysis findings on the picker checker Two issues flagged by Codacy on the previous commit: - Dropped an unused 're' import left over from an earlier draft. - Pinned the URL scheme to https before opening it. The league path is interpolated into the URL, and urlopen would honour file:// or a custom scheme if a path ever arrived from somewhere less trustworthy than the hardcoded table. Bandit's B310 is a syntactic blacklist rule so it still fires on the call itself; annotated with the reason rather than left to look unexamined. Verified: bandit clean, the guard rejects a file:// URL, and the checker still fetches all 32 NHL teams and still exits 1 against main (which is correct until #234 lands). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ MSG * Fix two CodeRabbit-flagged gaps in check_team_pickers.py An ESPN fetch that succeeds but returns an empty roster was silently skipped and counted toward the final "OK" tally with no warning. Warn instead, distinguishing it from the existing connectivity-failure warning. --apply returned 0 immediately after rewriting fixable schemas, without ever printing warnings or problems that never made it into `pending` — an unknown-league entry is the only such case, and it was silently dropped from --apply's output entirely, exiting 0 on a real unresolved config problem. Track those as `unresolved` separately so --apply always surfaces and fails on them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ --------- Co-authored-by: Claude <noreply@anthropic.com>
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
* 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>
What
Several plugins documented — or in one case offered in a picker — team abbreviations that ESPN does not actually use. Because favorite-team matching is by abbreviation, copying a documented example matched no team and the plugin silently showed nothing, with no indication the code was the problem.
Found while investigating a user report that
MUNfor Manchester United wasn't matching in the soccer plugin (fixed separately in #233); I audited every other plugin that matches teams by abbreviation.The one real bug: odds-ticker's NHL picker
This is the only case a user could not work around, since it's a
checkbox-grouppicker rather than free text:UTAwas listed — a retired code, and labelled with the club's former name "Utah Hockey Club" instead of "Utah Mammoth". ESPN usesUTAH.The enum and labels are now generated from ESPN's team endpoint and match it exactly at 32 teams (
enumkeys ==labelskeys).Description-only corrections
GSWGSNYLNYLASLAUCONNCONNSCARSCBAMAALAWISCWISEach description now also notes that these are ESPN's codes and aren't always the ones you'd guess, since that's the actual trap —
BAMAandGSWare what a fan would reach for first.Verification
Every code was checked against
site.api.espn.com/apis/site/v2/sports/{sport}/{league}/teams?limit=1000.The
limit=1000matters. Without it the default page size truncates the NCAA responses (362 of 755 teams), which made several valid codes look wrong — an earlier pass of this audit produced false positives that way.Deliberately not changed
lacrosse-scoreboard—WISC/MINN/OSUandBU/BC/MICH: ESPN'smens-college-lacrosseandwomens-college-lacrosseteam endpoints both return 0 teams.baseball-scoreboard— MiLBDUR/SWB/NOR: that endpoint 404s.Unverifiable either way, so flagged rather than guessed at.
Testing
No rendering code changed. Still ran the full safety harness for all four plugins — all sizes PASS.
check_module_collisions.pyclean across 42 plugins. Thefill warn: extent 0%x0%notes in harness output are offseason no-data and are present onmaintoo.