Skip to content

docs(plugins): README↔schema parity + missing titles — 5 plugins (audit phase 2)#175

Merged
ChuckBuilds merged 11 commits into
mainfrom
claude/plugin-audit-phase2-docs
Jul 10, 2026
Merged

docs(plugins): README↔schema parity + missing titles — 5 plugins (audit phase 2)#175
ChuckBuilds merged 11 commits into
mainfrom
claude/plugin-audit-phase2-docs

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Audit phase 2: realign five plugins' READMEs with their config_schema.json (the source of truth for the web-UI form) and fill in missing web-UI titles. Several documented options that don't exist, omitted ones that do, or were prose-only. Docs + schema title/changelog metadata only — no config behavior changes.

Stacked on #173. Based on the Phase 1 branch because both touch on-air and the generated plugins.json; targets that branch to keep the diff focused and auto-retarget to main once #173 merges. Merge #173 first. (CodeRabbit skips review until the base is the default branch — it'll run after retarget, or on @coderabbitai review.)

Plugin(s) affected

7-segment-clock, on-air, static-image, calendar, countdown

What changed per plugin

  • 7-segment-clock (1.0.2→1.0.3) — README documented a whole sunrise/sunset color-transition feature (location.lat/lng/locality, color_daytime/nighttime, min_fade_elevation, an astral dep) that no longer exists in the schema or manager.py, and omitted the real color/digit_spacing. Rewrote the config table to match; fixed the placeholder license → GPL-3.0.
  • on-air (1.2.1→1.2.2) — replaced the removed pulsing/glow/tally-dot options with the actual text color, background color, font, and Home-Assistant discovery fields; corrected the per-message color/bg MQTT payload docs; dropped the broken preview.png reference.
  • static-image (1.0.3→1.0.4) — documented all four rotation_mode values (was missing time_based/date_based), described images as scheduled image entries, and added a Per-Image Scheduling section.
  • calendar (1.1.2→1.1.3) — converted the prose config bullets to a per-key table incl. the previously-undocumented customization fonts (kept credentials_file/token_file, which are real config-only keys the manager reads); added titles to six untitled options.
  • countdown (3.1.0→3.1.1) — replaced the stale prose config section with per-key tables covering the v3 keys (target_time, mode, layout_preset, text_align, per-countdown style/layout); added a title to the top-level enabled.

Type of change

  • Documentation only
  • Bug fix in an existing plugin (docs correctness)

Test plan

  • Verified each rewritten README against its config_schema.json and manager.py by hand (incl. confirming token_file is a live config key and 7-segment-clock's sunrise feature is truly gone).
  • Local: python update_registry.py (5 bumps), python scripts/check_module_collisions.py (clean), JSON validity of all touched manifests + schemas.
  • CI renders every changed plugin at all four sizes and validates each manifest against the core schema.

Notes for reviewer

Each touched manifest also had its versions[] key normalized to ledmatrix_min (some mixed the old ledmatrix_min_version) to pre-empt core manifest-validation failures. Still queued in this audit: an optional per-field title bulk-pass for the remaining zero-title schemas and enumNames on cryptic dropdowns — both verify-first, since the schema renderer lives in the core repo and I can't confirm here whether the UI humanizes raw keys or renders enumNames.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB

claude added 4 commits July 8, 2026 20:51
…:// retry mount

Neglected-plugin hygiene pass (plugin audit, phase 1):

- Add LICENSE files matching each manifest's declared license: MIT for
  geochron, nfl-draft, on-air, tide-display; GPL-3.0 for march-madness
  (previously undeclared — license field added to its manifest).
- march-madness: add a README (it was the only plugin shipping without one)
  documenting features, display mode, per-key config, and data source; and
  mount the HTTP retry adapter on http:// as well as https:// so redirected
  ESPN requests keep the retry/backoff policy.
- Bump manifest versions + add changelog entries; sync plugins.json.

ledmatrix-elections (third-party author) LICENSE intentionally deferred
pending the contributor's licensing choice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
The safety CI validates each changed plugin's manifest against the core
manifest_schema.json. Two pre-existing issues surfaced once these plugins
became "changed" in this PR:

- march-madness: add the required `compatible_versions` field ([">=2.0.0"]).
- on-air: remove `update_interval: 0`, which violates the schema's
  minimum of 1. on-air is MQTT push-driven and does not poll; the sibling
  mqtt-notifications plugin omits the field entirely.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
The core manifest_schema.json requires `dependencies` to be an array;
march-madness declared it as an empty object `{}`. It has no plugin
dependencies, so `[]` is correct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
… static-image)

Plugin audit phase 2a — realign these READMEs with their config_schema.json
(the source of truth for the web UI form):

- 7-segment-clock: remove the documented-but-removed sunrise/sunset color
  transition feature (location lat/lng/locality, color_daytime/nighttime,
  min_fade_elevation, astral dependency) — it no longer exists in the schema
  or manager; document the real color and digit_spacing options; fix the
  placeholder license section (GPL-3.0, matching the LICENSE file).
- on-air: replace the removed pulsing/glow/tally-dot options with the actual
  text color, background color, font, and Home Assistant discovery fields;
  correct the per-message color/bg MQTT payload docs; drop the broken
  preview.png reference.
- static-image: document all rotation_mode values (add time_based/date_based),
  describe images as scheduled image entries rather than plain paths, and add
  a Per-Image Scheduling section.

Also normalizes each manifest's versions[] changelog key to ledmatrix_min
(some mixed the old ledmatrix_min_version). Version bumps + plugins.json sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

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 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@ChuckBuilds, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 12 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cbb82752-419b-4819-bcf5-4b74cbaf0653

📥 Commits

Reviewing files that changed from the base of the PR and between 25780e7 and 1352470.

📒 Files selected for processing (13)
  • plugins.json
  • plugins/7-segment-clock/README.md
  • plugins/7-segment-clock/manifest.json
  • plugins/calendar/README.md
  • plugins/calendar/config_schema.json
  • plugins/calendar/manifest.json
  • plugins/countdown/README.md
  • plugins/countdown/config_schema.json
  • plugins/countdown/manifest.json
  • plugins/on-air/README.md
  • plugins/on-air/manifest.json
  • plugins/static-image/README.md
  • plugins/static-image/manifest.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/plugin-audit-phase2-docs

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.

Audit phase 2 (README parity, continued):

- calendar: convert the prose config bullets to a per-key table that also
  documents the previously-undocumented customization fonts. Kept
  credentials_file/token_file — both are real config-only keys the manager
  reads (manager.py:72-73), not schema drift. Added web-UI titles to the
  untitled options (Enabled, Max Events, Show All-Day Events, Event Rotation
  Interval, Display Duration, Update Interval).
- countdown: replace the stale prose config section with per-key tables
  covering the v3 keys (target_time, mode until/since, layout_preset,
  text_align, and the per-countdown style/layout overrides); add a title to
  the top-level enabled option.

Normalizes both manifests' versions[] key to ledmatrix_min. Version bumps +
plugins.json sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
@ChuckBuilds ChuckBuilds changed the title docs(plugins): fix stale README config docs — 7-segment-clock, on-air, static-image (audit phase 2a) docs(plugins): README↔schema parity + missing titles — 5 plugins (audit phase 2) Jul 9, 2026
claude added 3 commits July 9, 2026 18:11
Per project policy the whole repo is GPL-3.0. This PR originally added MIT
LICENSE files (matching those plugins' declared MIT manifests); switch them
to GPL-3.0 instead so nothing ships as MIT:

- geochron, nfl-draft, on-air, tide-display: LICENSE file + manifest
  "license" field MIT -> GPL-3.0. All are first-party (ChuckBuilds), so the
  relicense is the copyright holder's call; MIT is GPL-compatible.
- ledmatrix-elections: add a GPL-3.0 LICENSE (it shipped without one) and
  declare it in the manifest; bump 1.1.0 -> 1.1.1. This plugin is a
  third-party contribution (rpierce99) — under the repo's inbound=outbound
  GPL policy (CONTRIBUTING.md) it is already GPL-licensed to the project.

No code changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
countdown's LICENSE file was already GPL-3.0 but its manifest declared MIT;
align the manifest to the file (project standard). Folded into the pending
3.1.1 changelog entry — no extra version bump.

Rides on a merge of the #173 branch, which flips geochron/nfl-draft/on-air/
tide-display to GPL-3.0 and adds ledmatrix-elections' LICENSE, keeping this
stacked branch consistent with #173's licensing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
The README license line still said "MIT License"; align it with the
GPL-3.0 manifest + LICENSE (repo-wide MIT -> GPL-3.0 standardization).
Docs only; countdown is already version-bumped in this PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
@ChuckBuilds ChuckBuilds changed the base branch from claude/plugin-audit-phase1-neglected to main July 9, 2026 18:29
claude and others added 2 commits July 10, 2026 13:40
Resolves conflicts after #173 (phase-1) landed on main:
- on-air/manifest.json: the branch's versions[] (1.2.2 -> 1.2.1 -> ...)
  is a clean superset of main's (1.2.1 -> ...), so kept the branch's
  manifest.
- plugins.json: regenerated from manifests (deterministic).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
This PR's changelog normalization went the wrong direction: it converted
each touched plugin's versions[] entries from ledmatrix_min_version to
ledmatrix_min. The core's own validator (store_manager.py,
_validate_manifest_version_fields) explicitly flags ledmatrix_min as
deprecated and expects ledmatrix_min_version, so every real device
install/update of these 5 plugins would start logging a fresh
"deprecated field" warning that wasn't there before. Revert the field
name direction (and the changelog notes describing the change) so it
actually matches the core's documented, non-deprecated field.
@ChuckBuilds ChuckBuilds merged commit 13b5af8 into main Jul 10, 2026
3 checks passed
@ChuckBuilds ChuckBuilds deleted the claude/plugin-audit-phase2-docs branch July 10, 2026 16:57
ChuckBuilds pushed a commit that referenced this pull request Jul 10, 2026
Follows the maintainer's correction on #175: the core's manifest
validator flags `ledmatrix_min` as deprecated and expects
`ledmatrix_min_version`. Switch the music versions[] entries (1.0.8,
1.0.7, 1.0.6) to `ledmatrix_min_version` so device installs don't log a
deprecated-field warning. Also merges the latest main (#174, #175) and
regenerates plugins.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
ChuckBuilds pushed a commit that referenced this pull request Jul 10, 2026
Follows the maintainer's correction on #175: the core's manifest
validator flags `ledmatrix_min` as deprecated and expects
`ledmatrix_min_version`. Normalize every versions[] entry in the flights
and stocks manifests to `ledmatrix_min_version` so device installs don't
log deprecated-field warnings. (Latest main, incl. #174/#175, was merged
in the preceding commit.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9Saiat9CQmNi3DeDdvacB
ChuckBuilds added a commit that referenced this pull request Jul 10, 2026
* chore(license): standardize flights, music, stocks to GPL-3.0

Completes the repo-wide MIT -> GPL-3.0 pass for the plugins not covered by
the other audit PRs. All first-party:

- ledmatrix-flights (1.10.0 -> 1.10.1): manifest "license" + README MIT ->
  GPL-3.0 (its LICENSE file was already GPL).
- ledmatrix-music (1.0.6 -> 1.0.7): LICENSE file + manifest "license" +
  README MIT -> GPL-3.0.
- ledmatrix-stocks (2.3.0 -> 2.3.1): manifest "license" MIT -> GPL-3.0 (its
  LICENSE file was already GPL).

No code changes.

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

* fix(ledmatrix-stocks): guard process_deferred_updates for harness compatibility

The plugin-safety harness renders each changed plugin, and its
BoundsCheckingDisplayManager does not implement process_deferred_updates(),
so stocks crashed at every size once the license bump made it a "changed"
plugin. Guard the call with hasattr — production DisplayManager still runs it;
harness/other managers that lack it are skipped. No production behavior change.

Note: leaderboard, odds-ticker, news, and stock-news call the same method and
would hit this too when next touched — the core harness double is missing it.

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

* chore: defer ledmatrix-music relicense (pre-existing 64px overflow)

Relicensing made music a "changed" plugin, so the safety harness rendered it
and caught a PRE-EXISTING overflow on the 64px-wide panels
([FAIL] 64x32 / 64x64 now_playing overflow bbox=(64,...)) — album-art + text
don't fit a 64px width. That's a narrow-panel layout bug, not a licensing
issue, and needs a real rendering fix (best verified against the core
harness). Revert music to its current state so this PR (flights + stocks ->
GPL-3.0) can land; music's relicense will follow once the overflow is fixed.

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

* Use non-deprecated ledmatrix_min_version in flights + stocks manifests

Follows the maintainer's correction on #175: the core's manifest
validator flags `ledmatrix_min` as deprecated and expects
`ledmatrix_min_version`. Normalize every versions[] entry in the flights
and stocks manifests to `ledmatrix_min_version` so device installs don't
log deprecated-field warnings. (Latest main, incl. #174/#175, was merged
in the preceding commit.)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Jul 10, 2026
…wing (+ GPL-3.0) (#179)

* fix(ledmatrix-music): clip scrolling text to viewport (fix 64px overflow) + GPL-3.0

The now-playing title/artist/album marquee drew the full (rotated) string at
the text-area x with no right-edge clip, so long text ran past the panel's
right edge. The safety harness flagged this as an overflow on the 64px-wide
panels (64x32 / 64x64), where the text area is only ~29px wide.

Add _clip_text_to_width() and apply it to all four text draws so the marquee
stays inside its viewport: long text still scrolls (the char-based rotation is
unchanged) — it's just clipped on the right instead of overflowing the panel.

Also relicense to GPL-3.0 (LICENSE + manifest + README). music was the plugin
deferred from the repo-wide MIT -> GPL-3.0 pass because of this overflow.

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

* fix(ledmatrix-music): draw no text when the panel has no text room (square panels)

Follow-up to the viewport clip: on square panels (e.g. 64x64) the album art
is sized to the panel height, so it fills the full width and text_area_width
goes negative. The clip helper's `max_width <= 0` guard returned the text
UNCLIPPED, so it was still drawn off-panel (overflow bbox=(64,28,91,36) at
64x64). Now return an empty string when there's no room, and skip the progress
bar too. Album art fills the panel; no text is drawn rather than overflowing.

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

* fix(ledmatrix-music): clip the "Nothing Playing" screen to the panel width

That was the real overflow the harness caught (CI has no auth -> no track ->
the "Nothing Playing" idle screen renders). The text is centered via
x = (width - text_width)//2, which goes negative when the text is wider than
the panel (64px), spilling off both edges — matching the failing bbox exactly
(y = height//2 - 4 = 12 at 64x32, 28 at 64x64). Clip the text to the panel
width and clamp x to >= 0. (The earlier marquee viewport clip is still correct
for the now-playing view with a real track.)

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

* fix(ledmatrix-music): keep the ellipsis when truncating non-scrolling text

When scrolling is disabled, the title/artist/album were truncated with a
rough char-count estimate (`text[:width//6] + "..."`) and then passed
through the draw-time viewport clip, which could trim the trailing "..."
back off — leaving no truncation indicator. Add
`_truncate_text_with_ellipsis`, which reserves room for the ellipsis
before clipping so the "..." always survives and the result fits the
text-area viewport.

Bumps ledmatrix-music to 1.0.8.

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

* Merge main + use non-deprecated ledmatrix_min_version in music manifest

Follows the maintainer's correction on #175: the core's manifest
validator flags `ledmatrix_min` as deprecated and expects
`ledmatrix_min_version`. Switch the music versions[] entries (1.0.8,
1.0.7, 1.0.6) to `ledmatrix_min_version` so device installs don't log a
deprecated-field warning. Also merges the latest main (#174, #175) and
regenerates plugins.json.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Jul 10, 2026
Same recurring issue as PRs #175/#177/#178: the core's store_manager.py
validator flags ledmatrix_min as deprecated and expects
ledmatrix_min_version. This PR's new 1.19.0 changelog entry used the
deprecated name; corrected to match the non-deprecated field (older
entries below 1.6.2 already have this same pre-existing issue but are
untouched by this PR, so left as-is -- out of scope here).
ChuckBuilds added a commit that referenced this pull request Jul 10, 2026
…ayer card (v1.19.0) (#180)

* feat(baseball-scoreboard): team-color grid, clearer at-bat labels, player card (v1.19.0)

Three enhancements to the baseball plugin:

1. Traditional scoreboard: subtle team-color row tint (~12% brightness
   wash) plus a solid left-edge accent strip per team, reusing the
   already-fetched away/home ESPN colors. Gated by a new
   show_team_color_backgrounds toggle (default on; requires
   use_team_colors). Text stays legible via its black outline.

2. Pitcher/Batter screen: labels spelled out ("Pitcher:" / "Batter:")
   so "B" is no longer ambiguous with the grid's Balls indicator.

3. New Player Card screen (show_player_card, MLB & NCAA): rotates in a
   masters-style card for the current batter/pitcher with headshot,
   jersey number, position, bat/throw, and season stats (AVG/HR/RBI for
   hitters, ERA/W-L/K for pitchers). Bio + headshot fetched from ESPN's
   athlete API and cached (memory + disk); the headshot hides on tiny
   panels and the card degrades to text-only when unavailable. Skipped
   for MiLB (no ESPN player data).

All new code lives in existing files (no new bare-name modules, per the
cross-plugin collision rule). Adds config schema (grid toggle, per-league
show_player_card, player_card customization block), manager plumbing,
docs, and tests (test_player_card.py + grid-tint tests).

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

* fix(baseball-scoreboard): harden headshot cache path + download URL

Address a Codacy medium security finding on the new player-card headshot
loader:
- Sanitize the ESPN-provided player_id and league before using them as
  filesystem path segments (_safe_filename strips to [A-Za-z0-9_-]) so an
  unexpected id can't escape the headshot cache directory via '..' or path
  separators.
- Only fetch headshots over http(s), refusing file://, ftp://, etc.

Adds a unit test for the filename sanitization.

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

* fix(baseball-scoreboard): log player-bio cache errors instead of silent pass

Resolve the Codacy/Bandit B110 (try/except/pass) medium finding in
_fetch_player_bio: the two cache_manager get/set guards swallowed
exceptions with a bare `pass`. Log them at debug level instead so
cache issues are diagnosable, matching how the rest of the plugin
handles non-fatal cache errors.

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

* fix(baseball-scoreboard): harden + de-block headshot fetch (CodeRabbit review)

Address two CodeRabbit review findings on the player-card headshot loader:

1. SSRF + unbounded response (security): the ESPN-supplied headshot URL was
   fetched with only a scheme check. Now restrict the host to ESPN domains
   (*.espncdn.com / *.espn.com) via an allowlist, and stream the download
   with a hard 5 MB size cap (Content-Length check + bounded iter_content)
   to prevent memory exhaustion.

2. Render-path blocking (stability): load_headshot() previously did a
   synchronous requests.get on a cache miss straight from
   _draw_player_card_screen, freezing the display up to 5s when a new
   batter/pitcher rotated in. The render path is now cache-only
   (allow_download=False); BaseballLive.update() prefetches headshot bytes
   off-thread (daemon thread, throttled) to warm the disk cache, mirroring
   the existing _fetch_player_bio pattern. A cold cache degrades to a
   text-only card until the prefetch completes.

Adds tests for the host allowlist (incl. metadata-IP and look-alike-host
rejection) and for the render path never downloading.

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

* fix(baseball-scoreboard): use ledmatrix_min_version in new 1.19.0 entry

Same recurring issue as PRs #175/#177/#178: the core's store_manager.py
validator flags ledmatrix_min as deprecated and expects
ledmatrix_min_version. This PR's new 1.19.0 changelog entry used the
deprecated name; corrected to match the non-deprecated field (older
entries below 1.6.2 already have this same pre-existing issue but are
untouched by this PR, so left as-is -- out of scope here).

---------

Co-authored-by: Claude <noreply@anthropic.com>
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