Skip to content

docs: refresh plugin READMEs and fix manifest bugs#92

Merged
ChuckBuilds merged 15 commits intomainfrom
docs/refresh-2026-04
Apr 8, 2026
Merged

docs: refresh plugin READMEs and fix manifest bugs#92
ChuckBuilds merged 15 commits intomainfrom
docs/refresh-2026-04

Conversation

@ChuckBuilds
Copy link
Copy Markdown
Owner

@ChuckBuilds ChuckBuilds commented Apr 7, 2026

Summary

Walked every plugin against its manifest.json + config_schema.json + manager.py and corrected stale documentation. Two real loader-breaking bugs found and fixed; the rest is README accuracy work. This is the plugins-repo half of a two-PR docs refresh — see the matching LEDMatrix PR for core docs fixes.

Loader-breaking manifest bugs (would AttributeError at load time)

  • plugins/hello-world/manifest.json: class_name was "HelloWorld Plugin" (with a space) but the actual class in manager.py:19 is HelloWorldPlugin. The plugin loader does getattr(module, class_name) (LEDMatrix src/plugin_system/plugin_loader.py:444) so this would have always failed. The "official starter template" was broken. Also bumped the versions[] array to include 1.0.2 to match the version field.
  • plugins/stock-news/manifest.json: missing both entry_point and class_name entirely. Loader requires class_name at loader.py:537-538. Added entry_point: "manager.py" and class_name: "StockNewsTickerPlugin" (verified from manager.py:33). The plugin couldn't load at all before this fix.
  • Verified all 30 plugins via a Python script — these were the only two with loader-critical metadata bugs.

Schema-vs-README mismatches that would silently mislead users

  • ledmatrix-weather: README claimed location was a single string; reality is three keys (location_city/state/country). Added the 5 display modes (was 3) including the previously undocumented radar and almanac modes. Documented the radar customization keys, the new height-≥48px requirements, and the display_format placeholder syntax. Verified against the 167-line config_schema.json.
  • ledmatrix-stocks: README documented top-level keys (display_duration, scroll_speed, font_size, stock_symbols, etc.) but the real schema nests them under display.* / stocks.symbols / customization.*. Also: README said "Future Implementation: API integration with Alpha Vantage / Yahoo Finance" but data_fetcher.py:29 has been using Yahoo Finance directly with no API key for a long time. Full rewrite.
  • hockey-scoreboard: documented update_interval (15-300, default 60) as a top-level key; reality is defaults.update_interval_seconds (default 3600) plus per-league update_intervals.{base,live,recent,upcoming,odds}. Also a "API Calls: 36/hour" calculation that didn't match the actual defaults. Rewrote the Display Settings section.
  • text-display: scroll_speed documented as "1-200 pixels per second" but it's actually a multiplier (default 1). Also missing 4 schema keys: update_interval, target_fps, scroll_loop, scroll_delay.
  • static-image: README only documented the legacy single-image_path form. The current schema and manager.py:79-101 use an images array with image_config.* and rotation_settings.* for built-in multi-image rotation. Added a multi-image example.
  • news: every "Global Settings" key was at the top level in the README but actually nested under global.* in the schema. A user following the old README would set keys the plugin completely ignores. Fixed.
  • ledmatrix-music: Spotify credentials were documented under the legacy "music" key with SPOTIFY_CLIENT_ID (uppercase). Canonical form per spotify_client.py:50-53 is "ledmatrix-music" key with lowercase spotify_client_id/spotify_client_secret/spotify_redirect_uri. Updated to show the canonical form first.
  • calendar: README told users to choose TV and Limited Input Device OAuth client type — and contradicted itself with "Desktop application" one section later. The plugin uses InstalledAppFlow.run_local_server() (manager.py:346-348) which requires Desktop application. Fixed.
  • hockey-scoreboard/manifest.json: last_updated: 2026-02-24 was older than the latest version 1.2.4 released 2026-03-02. Updated.

Stale UI navigation across 11 plugin READMEs

README files referenced a "Plugin Store tab" or "Plugins → Foo → Configuration" UI flow that doesn't exist. Real flow: open Plugin Manager tab → find plugin in Plugin Store section → click Install → open the plugin's tab in the second nav row to configure.

Fixed in: 7-segment-clock, clock-simple, countdown, football-scoreboard, hello-world, hockey-scoreboard, ledmatrix-flights, masters-tournament, olympics, calendar.

Repo-root README

  • Plugin count badge said 27; actually 30. Three plugins were missing from the README index entirely: f1-scoreboard, lacrosse-scoreboard, masters-tournament. Added them.
  • API curl example used port 5050; real port is 5000.
  • python run.py --emulator was wrong (no such flag). Replaced with the real options: scripts/dev_server.py for the dev preview, or EMULATOR=true python3 run.py for the full emulator path.
  • Manual install command was correct (cp -r ... /path/to/LEDMatrix/plugin-repos/) — added a note that the destination directory must match the plugin's id.

Plugin Store docs (docs/PLUGIN_STORE_*.md)

  • 27 occurrences of port 5050 across all three docs. Bulk-fixed to 5000.
  • "Navigate to Plugin Store tab" → "Plugin Manager tab → Plugin Store section".
  • Updated "Install from URL" UI nav to match the real "Install from GitHub" section name.

Submission/verification

  • SUBMISSION.md: same port fix.
  • VERIFICATION.md: extended manifest checklist to require class_name matches the actual class (would have caught the hello-world bug), entry_point file existence, id matches directory name, and last_updated matches the latest version's release date.

Files changed

25 files changed, 565 insertions(+), 521 deletions(-)

Test plan

  • Verify hello-world actually loads now (it would have AttributeError'd before this PR)
  • Verify stock-news actually loads now
  • Spot-check the new ledmatrix-weather README against the real plugin tab in the web UI
  • Spot-check the new ledmatrix-stocks README
  • Verify the Sports section count of 12 in the root README matches expectations

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added F1, UFC, Masters Tournament, and Lacrosse Scoreboard plugins; Sports plugin count increased.
  • Documentation

    • Web UI flow now centers on a Plugin Manager with a Plugin Store section; install flows favor the web UI and note restarting the display service.
    • Plugin Store API examples/ports updated (5050→5000); dev preview workflow introduced with an emulator alternative.
    • Expanded manifest/config validation guidance, many README updates, and added CONTRIBUTING, SECURITY, and Code of Conduct docs.

Chuck and others added 2 commits April 6, 2026 20:46
Walked every plugin against its manifest.json + config_schema.json +
manager.py and corrected stale documentation. Two real loader-breaking
bugs found and fixed; the rest is README accuracy work.

Loader-breaking manifest bugs (would AttributeError at load time)
- plugins/hello-world/manifest.json: class_name was "HelloWorld Plugin"
  (with a space) but the actual class in manager.py:19 is
  HelloWorldPlugin. The plugin loader does
  getattr(module, class_name) (LEDMatrix:src/plugin_system/
  plugin_loader.py:444) so this would have always failed. Also bumped
  the versions[] array to include 1.0.2 to match the version field.
- plugins/stock-news/manifest.json: missing both entry_point and
  class_name entirely. Loader requires class_name (loader.py:537-538).
  Added entry_point: "manager.py" and class_name:
  "StockNewsTickerPlugin" (verified from manager.py:33).

Schema-vs-README mismatches that would silently mislead users
- ledmatrix-weather: README claimed "location" was a single string;
  reality is three keys (location_city/state/country). Added the 5
  display modes (was 3) including the previously undocumented radar
  and almanac modes, plus the radar customization keys. Verified
  against config_schema.json (167 lines).
- ledmatrix-stocks: README documented top-level keys
  (display_duration, scroll_speed, font_size, stock_symbols, etc) but
  the real schema nests them under display.* / stocks.symbols /
  customization.*. Also: README said "Future Implementation: API
  integration with Alpha Vantage / Yahoo Finance" but data_fetcher.py
  has been using Yahoo Finance directly with no API key for a long
  time. Full rewrite.
- hockey-scoreboard: documented update_interval (15-300, default 60)
  as a top-level key; reality is defaults.update_interval_seconds
  (default 3600) plus per-league update_intervals.{base,live,recent,
  upcoming,odds}. Rewrote the Display Settings section to match the
  real config_schema.json structure.
- text-display: scroll_speed documented as "1-200 px/sec" but it's
  a multiplier (default 1). Added 4 missing keys: update_interval,
  target_fps, scroll_loop, scroll_delay.
- static-image: README documented only the legacy single-image_path
  form. The current schema/manager use an images array with
  image_config.* and rotation_settings.* (verified at
  manager.py:79-101). Added multi-image example.
- news: every "Global Settings" key was at the top level in the
  README but actually nested under global.* in the schema. Fixed.
- ledmatrix-music: Spotify credentials were documented under the
  legacy "music" key with SPOTIFY_CLIENT_ID (uppercase). Canonical
  form per spotify_client.py:50-53 is "ledmatrix-music" key with
  lowercase spotify_client_id/spotify_client_secret/spotify_redirect_uri.
- calendar: README told users to choose "TV and Limited Input Device"
  OAuth client type (and contradicted itself with "Desktop application"
  one section later). The plugin uses InstalledAppFlow.run_local_server
  (manager.py:346-348) which requires Desktop application. Fixed.

UI navigation
- README files in 11 plugins referenced a "Plugin Store tab" or
  "Plugins -> Foo -> Configuration" UI that doesn't exist. Real flow:
  open Plugin Manager tab -> find plugin in Plugin Store section ->
  click Install -> open the plugin's tab in the second nav row to
  configure. Fixed in: 7-segment-clock, clock-simple, countdown,
  football-scoreboard, hello-world, hockey-scoreboard,
  ledmatrix-flights, masters-tournament, olympics, calendar
  (and previously hockey/clock-simple).

Repo-root README
- Plugin count badge said 27; actually 30. Three plugins were missing
  from the index entirely: f1-scoreboard, lacrosse-scoreboard,
  masters-tournament. Added them.
- API curl example used port 5050; real port is 5000.
- "python run.py --emulator" was wrong (no such flag). Replaced with
  the real options: scripts/dev_server.py for dev preview, or
  EMULATOR=true python3 run.py for the full emulator path.

Plugin Store docs (docs/PLUGIN_STORE_*.md)
- 27 occurrences of port 5050 across all three docs. Bulk-fixed to
  5000.
- "Navigate to Plugin Store tab" -> real Plugin Manager tab + Plugin
  Store section.
- Updated "Install from URL" UI nav to match the real "Install from
  GitHub" section name.

Submission/verification
- SUBMISSION.md: same port fix.
- VERIFICATION.md: extended manifest checklist to require class_name
  matches the actual class (would have caught the hello-world bug),
  entry_point file existence, id matches directory name, and
  last_updated matches the latest version's release date.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Round 2 of the docs refresh — covering the 13 plugin READMEs that
weren't touched in the first PR commit, plus two plugins that were
shipping without any README at all.

Created from scratch (the plugins had no README)
- plugins/f1-scoreboard/README.md: documents all 8 display modes
  (driver/constructor standings, recent_races, upcoming, qualifying,
  practice, sprint, calendar) and the full nested config from
  config_schema.json. Uses ESPN F1 endpoints; no API key.
- plugins/ufc-scoreboard/README.md: documents the 3 modes (live/recent/
  upcoming), favorite-fighters/weight-classes config, headshot
  auto-download to assets/sports/ufc_fighters/, and the switch/scroll
  display_mode toggle. Credits the original LegoGuy1000 PR #137.

Schema-vs-README bug fixes
- baseball-scoreboard: documented display modes were
  baseball_live/baseball_recent/baseball_upcoming. Real modes per
  manifest are 9 per-league granular modes (mlb_live/mlb_recent/
  mlb_upcoming, milb_*, ncaa_baseball_*). Rewrote.
- baseball-scoreboard: all 3 league config examples used
  display_modes: { live, recent, upcoming } but the real schema uses
  show_live/show_recent/show_upcoming with the show_ prefix. Fixed.
- christmas-countdown: options section listed 3 keys but the schema
  has 10. Added a complete table including transition.{enabled,type,
  speed}, text_color, tree_color, tree_size, and
  high_performance_transitions. Also fixed a malformed code fence
  (orphan ``` with no opening pair).
- mqtt-notifications: heading was "##This plugin is still under heavy
  development" (broken markdown - ## ran into the text). Promoted to
  a clean alpha warning.

UI navigation fixes (real flow: Plugin Manager tab -> Plugin Store
section -> Install -> open the plugin's tab in the second nav row)
- baseball-scoreboard: replaced "Copy to ledmatrix-plugins/plugins/"
- basketball-scoreboard: same
- soccer-scoreboard: same
- lacrosse-scoreboard: was using cp ... plugins/ (LEDMatrix dev-time
  fallback path) instead of the canonical plugin-repos/. Fixed.
- odds-ticker: same
- ledmatrix-leaderboard: same
- mqtt-notifications: replaced "automatically discovered" install
  language with the real Plugin Manager flow
- of-the-day: replaced fictional "Plugins tab -> Configure button"
  with the real per-plugin tab in the second nav row
- web-ui-info: same fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Repository-wide documentation updates: standardized Web UI navigation to “Plugin Manager”, switched Plugin Store API examples to port 5000 and /api/v3, tightened manifest validation and config-schema guidance, added governance/security/contributing docs and PR template, introduced F1 and UFC plugin READMEs, and made small plugin manifest metadata edits.

Changes

Cohort / File(s) Summary
Core docs & API examples
README.md, SUBMISSION.md, VERIFICATION.md, docs/PLUGIN_STORE_IMPLEMENTATION_SUMMARY.md, docs/PLUGIN_STORE_QUICK_REFERENCE.md, docs/PLUGIN_STORE_USER_GUIDE.md
Updated API host/port and endpoint paths (50505000, /api/plugins/.../api/v3/plugins/...), added Content-Type: application/json to example POSTs, and expanded manifest/validation instructions.
Governance & contribution
.github/PULL_REQUEST_TEMPLATE.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md
Added PR template, Contributor Covenant, CONTRIBUTING guide, and SECURITY policy files for repository governance and contributor workflows.
Web UI/navigation across plugins
plugins/*/README.md (examples: 7-segment-clock, countdown, football-scoreboard, ledmatrix-flights, ledmatrix-leaderboard, masters-tournament, odds-ticker, olympics, soccer-scoreboard, web-ui-info, of-the-day, etc.)
Standardized install/navigation text to use LEDMatrix Web UI → Plugin Manager (Plugin Store as a subsection) and updated post-install enable/restart guidance to plugin tabs and Overview restart action.
Config schema & per-plugin docs
plugins/baseball-scoreboard/README.md, plugins/christmas-countdown/README.md, plugins/clock-simple/README.md, plugins/hockey-scoreboard/README.md, plugins/news/README.md, plugins/static-image/README.md, plugins/text-display/README.md, plugins/ledmatrix-weather/README.md, plugins/ledmatrix-stocks/README.md, plugins/ledmatrix-music/README.md
Rewrote configuration docs to reference config_schema.json, introduced/renamed nested keys and defaults, added examples and backward-compatibility notes.
New plugin documentation
plugins/f1-scoreboard/README.md, plugins/ufc-scoreboard/README.md
Added comprehensive READMEs describing features, data sources, display modes, installation, and configuration for F1 and UFC plugins.
Plugin-specific doc edits & starter material
plugins/hello-world/README.md, plugins/hello-world/QUICK_START.md, plugins/calendar/README.md, plugins/mqtt-notifications/README.md, plugins/ledmatrix-music/README.md, plugins/ledmatrix-weather/README.md, plugins/ledmatrix-flights/README.md, plugins/ledmatrix-leaderboard/README.md, plugins/stock-news/*
Refactored starter and plugin docs (simplified hello-world, Quick Start API → port 5000 & /api/v3, calendar OAuth desktop flow, music credential key changes, MQTT alpha notice, etc.).
Manifest & metadata updates
plugins/hello-world/manifest.json, plugins/hockey-scoreboard/manifest.json, plugins/stock-news/manifest.json
Adjusted class_name formatting, added/updated versions and last_updated, and added entry_point/class_name fields to stock-news manifest.
Testing / plugin class name docs
plugins/basketball-scoreboard/STANDALONE.md, plugins/basketball-scoreboard/TESTING.md
Documented plugin class rename to BasketballScoreboardPlugin and updated test expectation text/examples.

Sequence Diagram(s)

(omitted — changes are documentation and metadata updates; no new multi-component control flow introduced)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: documentation updates to plugin READMEs and fixes to manifest bugs, which accurately reflects the bulk of the changeset across 25 files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/refresh-2026-04

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (3)
plugins/hello-world/manifest.json (1)

18-30: ⚠️ Potential issue | 🟠 Major

Manifest version history ordering/sync is currently inconsistent.

On Line 4 the manifest version is 1.0.2, but in versions[] the top entry is still 1.0.1 (Lines 19-23), and Line 30 last_updated still shows the old date. Put 1.0.2 first and sync metadata.

Proposed manifest fix
   "versions": [
     {
-      "released": "2025-10-19",
-      "version": "1.0.1",
+      "released": "2026-04-06",
+      "version": "1.0.2",
       "ledmatrix_min": "2.0.0"
     },
     {
-      "released": "2026-04-06",
-      "version": "1.0.2",
+      "released": "2025-10-19",
+      "version": "1.0.1",
       "ledmatrix_min": "2.0.0"
     }
   ],
-  "last_updated": "2025-10-19",
+  "last_updated": "2026-04-06",

As per coding guidelines: “Add the new version FIRST … Keep the version field in sync with the top entry in the versions array in manifest.json.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/hello-world/manifest.json` around lines 18 - 30, The manifest's
top-level "version" and "last_updated" are out of sync with the newest entry in
the "versions" array: update the "versions" array so the newest release (version
"1.0.2" with "released": "2026-04-06" and existing "ledmatrix_min": "2.0.0") is
the first object, ensure the top-level "version" field equals that first
"versions" entry ("1.0.2"), and set "last_updated" to the same release date
("2026-04-06") so all three fields ("version", versions[0], "last_updated") are
consistent.
plugins/football-scoreboard/README.md (1)

260-392: ⚠️ Potential issue | 🟡 Minor

Remove duplicate "Layout Customization" section.

The "Layout Customization" section appears twice (lines 260-326 and lines 327-392) with identical content. This is likely a copy/paste error during the documentation refresh.

🔧 Proposed fix - remove the duplicate section

Remove lines 327-392 (the second occurrence) to eliminate the duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/football-scoreboard/README.md` around lines 260 - 392, Delete the
duplicated "Layout Customization" block (the second occurrence that repeats the
heading and all subsections: Accessing Layout Settings, Offset Values, Available
Elements, Example Adjustments, Display Size Compatibility) so only the first
instance remains; ensure the examples (JSON snippets) and the "records" field
description are preserved in the kept section and remove the repeated text that
begins with the second "Layout Customization" heading.
docs/PLUGIN_STORE_USER_GUIDE.md (1)

409-419: ⚠️ Potential issue | 🟡 Minor

Example 3 still uses the old UI flow.

This section still says “Plugin Store tab” and “Install from URL,” which conflicts with the updated guide (“Plugin Manager” → “Install from GitHub”). Please update this example to match the new navigation terms.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/PLUGIN_STORE_USER_GUIDE.md` around lines 409 - 419, Update the outdated
UI text in the example snippet: replace occurrences of "Plugin Store" and
"Install from URL" with the new navigation terms "Plugin Manager" and "Install
from GitHub" and adjust the step labels accordingly (e.g., change step 2 to
"Click 'Plugin Manager' tab" and step 4 to "Paste:
https://github.com/yourusername/ledmatrix-awesome-plugin" while step 5 becomes
"Click 'Install from GitHub'") so the example matches the current guide
language.
🧹 Nitpick comments (2)
plugins/text-display/README.md (1)

66-66: Minor wording cleanup in scroll_gap_width note.

“Pixels of empty space” is a bit redundant; “Pixels of space between scroll loops” reads cleaner.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/text-display/README.md` at line 66, Update the README entry for the
scroll_gap_width setting: change the description from "Pixels of empty space
between scroll loops" to "Pixels of space between scroll loops" in the table row
referencing `scroll_gap_width` so the wording is cleaner and not redundant.
plugins/ledmatrix-stocks/README.md (1)

50-50: Optional wording cleanup on Line 50.

“Pixels of space between symbols” reads a bit cleaner than “Pixels of empty space between symbols.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/ledmatrix-stocks/README.md` at line 50, Update the README description
for the display.stock_gap configuration entry to read "Pixels of space between
symbols" instead of "Pixels of empty space between symbols" so the wording is
cleaner; locate the table row for `display.stock_gap` in the README (the entry
showing `32`) and replace the description text accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/hello-world/README.md`:
- Around line 99-103: The fenced log snippet showing the plugin
discovery/initialization messages should include a language hint for
markdownlint and better rendering; update the fenced code block that contains
"Discovered plugin: hello-world v1.0.2" / "Loaded plugin: hello-world" / "Hello
World plugin initialized..." to use a language tag (e.g., add "text" after the
opening ```), so the block becomes ```text ... ```.
- Around line 24-29: The README uses the old install path `~/LEDMatrix/plugins/`
which conflicts with the root README's canonical `plugin-repos/` path; update
the copy instruction and any references in plugins/hello-world/README.md to use
`~/LEDMatrix/plugin-repos/` (or the canonical path documented in the root
README) and keep the restart instruction unchanged, ensuring all instances of
the old `plugins/` path are replaced so the guide is consistent with the root
README.

In `@plugins/hockey-scoreboard/README.md`:
- Around line 244-273: The README still shows legacy flat config keys; update
all examples and troubleshooting references to use the new nested schema keys
instead (e.g. replace update_interval → defaults.update_interval_seconds or
<league>.update_intervals.{base,live,recent,upcoming,odds}; replace
display_duration → defaults.display_duration or
<league>.display_durations.{base,live,recent,upcoming}; replace
prioritize_favorites → <league>.live_priority; replace show_shots_on_goal →
defaults.show_shots_on_goal or <league>.display_options.show_shots_on_goal,
etc.). Search for occurrences of the old keys (update_interval,
display_duration, prioritize_favorites, show_shots_on_goal, show_shots_on_goal,
show_records, show_odds) and change them to the corresponding nested keys used
in config_schema.json so examples and troubleshooting match the schema-driven
section.

In `@plugins/static-image/README.md`:
- Around line 34-35: The README examples for the "images" field use string paths
but the plugin's config_schema.json expects each image entry to be an object
with required fields like id and path; update all README examples (the "images"
examples and other occurrences around the examples) to show object-based entries
(e.g., { "id": "...", "path": "..." } and any optional keys like fit_to_display)
so they match the schema and won't produce invalid configs when validated
against config_schema.json.
- Around line 53-55: The example's image_config is using mode: "single" which
contradicts the "Multiple images with rotation" section; change the
image_config.mode value to "multiple" so the rotation configuration
(rotation_mode: "sequential") applies to multiple images—update the example
where image_config.mode is set and ensure rotation_mode remains as shown.

In `@plugins/stock-news/manifest.json`:
- Around line 7-8: The manifest.json was updated but the top-level "version" and
the "versions" array were not updated; add a new entry at the start of the
"versions" array with keys "released", "version", and "ledmatrix_min" (set
"version" to the new bumped value, e.g., "1.0.3"), and then update the top-level
"version" field to match that new versions[0]. Ensure the new versions entry
precedes existing entries and keeps the version strings identical.

In `@plugins/ufc-scoreboard/README.md`:
- Line 53: Update the README example for ufc.favorite_weight_classes to use the
schema's abbreviation format (e.g., "LW", "HW") instead of full weight class
names; locate the table row for `ufc.favorite_weight_classes` in the README and
replace the example value `["Heavyweight", "Lightweight"]` with an abbreviation
list like `["HW", "LW"]` (or other valid abbrevs per the schema) so the
documentation aligns with the schema guidance.

In `@README.md`:
- Line 212: Update the README sentence that currently reads "its
`manifest.json`. Restart the LEDMatrix display service afterwards" to use US
spelling by replacing "afterwards" with "afterward" so it reads "its
`manifest.json`. Restart the LEDMatrix display service afterward".

---

Outside diff comments:
In `@docs/PLUGIN_STORE_USER_GUIDE.md`:
- Around line 409-419: Update the outdated UI text in the example snippet:
replace occurrences of "Plugin Store" and "Install from URL" with the new
navigation terms "Plugin Manager" and "Install from GitHub" and adjust the step
labels accordingly (e.g., change step 2 to "Click 'Plugin Manager' tab" and step
4 to "Paste: https://github.com/yourusername/ledmatrix-awesome-plugin" while
step 5 becomes "Click 'Install from GitHub'") so the example matches the current
guide language.

In `@plugins/football-scoreboard/README.md`:
- Around line 260-392: Delete the duplicated "Layout Customization" block (the
second occurrence that repeats the heading and all subsections: Accessing Layout
Settings, Offset Values, Available Elements, Example Adjustments, Display Size
Compatibility) so only the first instance remains; ensure the examples (JSON
snippets) and the "records" field description are preserved in the kept section
and remove the repeated text that begins with the second "Layout Customization"
heading.

In `@plugins/hello-world/manifest.json`:
- Around line 18-30: The manifest's top-level "version" and "last_updated" are
out of sync with the newest entry in the "versions" array: update the "versions"
array so the newest release (version "1.0.2" with "released": "2026-04-06" and
existing "ledmatrix_min": "2.0.0") is the first object, ensure the top-level
"version" field equals that first "versions" entry ("1.0.2"), and set
"last_updated" to the same release date ("2026-04-06") so all three fields
("version", versions[0], "last_updated") are consistent.

---

Nitpick comments:
In `@plugins/ledmatrix-stocks/README.md`:
- Line 50: Update the README description for the display.stock_gap configuration
entry to read "Pixels of space between symbols" instead of "Pixels of empty
space between symbols" so the wording is cleaner; locate the table row for
`display.stock_gap` in the README (the entry showing `32`) and replace the
description text accordingly.

In `@plugins/text-display/README.md`:
- Line 66: Update the README entry for the scroll_gap_width setting: change the
description from "Pixels of empty space between scroll loops" to "Pixels of
space between scroll loops" in the table row referencing `scroll_gap_width` so
the wording is cleaner and not redundant.
🪄 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

Run ID: 37d1297a-d808-4c63-b0ea-cf1baec6c6fc

📥 Commits

Reviewing files that changed from the base of the PR and between 64a3ee4 and 5d29ce4.

📒 Files selected for processing (37)
  • README.md
  • SUBMISSION.md
  • VERIFICATION.md
  • docs/PLUGIN_STORE_IMPLEMENTATION_SUMMARY.md
  • docs/PLUGIN_STORE_QUICK_REFERENCE.md
  • docs/PLUGIN_STORE_USER_GUIDE.md
  • plugins/7-segment-clock/README.md
  • plugins/baseball-scoreboard/README.md
  • plugins/basketball-scoreboard/README.md
  • plugins/calendar/README.md
  • plugins/christmas-countdown/README.md
  • plugins/clock-simple/README.md
  • plugins/countdown/README.md
  • plugins/f1-scoreboard/README.md
  • plugins/football-scoreboard/README.md
  • plugins/hello-world/README.md
  • plugins/hello-world/manifest.json
  • plugins/hockey-scoreboard/README.md
  • plugins/hockey-scoreboard/manifest.json
  • plugins/lacrosse-scoreboard/README.md
  • plugins/ledmatrix-flights/README.md
  • plugins/ledmatrix-leaderboard/README.md
  • plugins/ledmatrix-music/README.md
  • plugins/ledmatrix-stocks/README.md
  • plugins/ledmatrix-weather/README.md
  • plugins/masters-tournament/README.md
  • plugins/mqtt-notifications/README.md
  • plugins/news/README.md
  • plugins/odds-ticker/README.md
  • plugins/of-the-day/README.md
  • plugins/olympics/README.md
  • plugins/soccer-scoreboard/README.md
  • plugins/static-image/README.md
  • plugins/stock-news/manifest.json
  • plugins/text-display/README.md
  • plugins/ufc-scoreboard/README.md
  • plugins/web-ui-info/README.md

Comment on lines +7 to +8
"entry_point": "manager.py",
"class_name": "StockNewsTickerPlugin",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bump manifest version and prepend a new versions entry for this manifest change.

This manifest was changed (new loader-critical fields), but version and versions[] were not advanced. Please bump (e.g., 1.0.3) and add a new top versions item with released, version, and ledmatrix_min, keeping version synced to that top entry.

Suggested manifest update
-  "version": "1.0.2",
+  "version": "1.0.3",
@@
   "versions": [
+    {
+      "released": "2026-04-07",
+      "version": "1.0.3",
+      "ledmatrix_min": "2.0.0"
+    },
     {
       "released": "2025-10-19",
       "version": "1.0.2",
       "ledmatrix_min": "2.0.0"
     }
   ],
-  "last_updated": "2025-10-19",
+  "last_updated": "2026-04-07",
As per coding guidelines, “Always bump the `version` field in `manifest.json` … when making any changes to plugin files” and “Add the new version FIRST … to the `versions` array … and keep `version` in sync with the top entry.”

Also applies to: 4-4, 26-31

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/stock-news/manifest.json` around lines 7 - 8, The manifest.json was
updated but the top-level "version" and the "versions" array were not updated;
add a new entry at the start of the "versions" array with keys "released",
"version", and "ledmatrix_min" (set "version" to the new bumped value, e.g.,
"1.0.3"), and then update the top-level "version" field to match that new
versions[0]. Ensure the new versions entry precedes existing entries and keeps
the version strings identical.

These are the non-README docs that ship inside individual plugin
directories. Triaged with grep, deep-audited the ones with hits or
that I knew were likely to have stale class names.

plugins/hello-world/QUICK_START.md
- Port 5001 -> 5000 (×2 in curl examples)
- /api/plugins/* -> /api/v3/plugins/* (×2)
- "Discovered plugin: hello-world v1.0.0" example log -> v1.0.2 to
  match the current manifest version (after the iter 1 fix)
- "python test/test_plugin_system.py" referenced a LEDMatrix-repo
  test file from inside a plugin's QUICK_START — that doesn't make
  sense (the user is on the plugins repo). Removed the section,
  replaced with the simpler journalctl + curl flow that actually
  works from a fresh install.
- "ssh pi@your-pi-ip" -> "ssh ledpi@your-pi-ip" (the documented
  default username throughout the rest of LEDMatrix)
- Broken link to ../../docs/PLUGIN_PHASE_1_SUMMARY.md (file doesn't
  exist anywhere in LEDMatrix). Replaced with an absolute link to
  docs/PLUGIN_DEVELOPMENT_GUIDE.md.
- Added a one-line install-via-Plugin-Manager intro before the
  manual config-editing example, since the Plugin Store is now the
  recommended path.

plugins/basketball-scoreboard/STANDALONE.md
- Class name was wrong throughout: "BasketballPluginManager" vs the
  actual "BasketballScoreboardPlugin" in manager.py:50. The wrong
  name was the same bug pattern as hello-world's manifest issue —
  if anyone copied the name to register the class, it would
  AttributeError at load. Fixed all 4 occurrences via replace_all.
- "Configuration" example used flat top-level keys (nba_enabled,
  nba_favorite_teams, nba_display_modes_live). The real schema
  nests them under nba.{enabled, favorite_teams, display_modes.show_*}
  (verified in manager.py:94 reads config.get("nba", {}).get("enabled")).
  Replaced with the real nested form.

plugins/basketball-scoreboard/TESTING.md
- Same wrong class name in expected test output. Fixed.
- "Multiple inheritance works correctly (BasePlugin + Basketball)" —
  contradicts STANDALONE.md and reality. The plugin only inherits
  from BasePlugin (no Basketball base class). Fixed and pointed at
  STANDALONE.md.

Verified clean (no fixes needed):
- baseball-scoreboard/CHANGELOG.md (historical, leave as-is)
- football-scoreboard/CHANGELOG.md (historical, leave as-is)
- football-scoreboard/DYNAMIC_DURATION.md (uses flat keys early but
  shows real nested examples further down — internally consistent)
- ledmatrix-weather/CHANGELOG.md (historical)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
plugins/basketball-scoreboard/STANDALONE.md (1)

35-35: Optional: make the “Before” snippet explicitly historical.

Using BasketballScoreboardPlugin in the “Before” example can read like current code. Consider using the prior class identifier (or a short note) to make the transition clearer at a glance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/basketball-scoreboard/STANDALONE.md` at line 35, Update the "Before"
snippet to make it explicitly historical by renaming or annotating the class
identifier used there (e.g., change the class line with
BasketballScoreboardPlugin or add a short "(previous implementation)" note) so
readers immediately see it represents prior code; locate the snippet that shows
"class BasketballScoreboardPlugin(BasePlugin, Basketball):" and either use a
prior-class name or append a clear historical marker near BasePlugin/Basketball
to indicate it's the old implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@plugins/basketball-scoreboard/STANDALONE.md`:
- Line 35: Update the "Before" snippet to make it explicitly historical by
renaming or annotating the class identifier used there (e.g., change the class
line with BasketballScoreboardPlugin or add a short "(previous implementation)"
note) so readers immediately see it represents prior code; locate the snippet
that shows "class BasketballScoreboardPlugin(BasePlugin, Basketball):" and
either use a prior-class name or append a clear historical marker near
BasePlugin/Basketball to indicate it's the old implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 17d9c78c-0fe1-4d3f-86d1-1f6ef164089e

📥 Commits

Reviewing files that changed from the base of the PR and between 5d29ce4 and b2f76c1.

📒 Files selected for processing (3)
  • plugins/basketball-scoreboard/STANDALONE.md
  • plugins/basketball-scoreboard/TESTING.md
  • plugins/hello-world/QUICK_START.md
✅ Files skipped from review due to trivial changes (2)
  • plugins/basketball-scoreboard/TESTING.md
  • plugins/hello-world/QUICK_START.md

Tier 1 organizational files mirroring the set added to the LEDMatrix
repo. Additive — no existing content was rewritten. The plugins repo
already has a LICENSE (GPL-3.0), so no LICENSE file is needed here.

CONTRIBUTING.md
- The repository had no CONTRIBUTING file. New plugin authors had to
  reconstruct the dev workflow from SUBMISSION.md, VERIFICATION.md,
  and the LEDMatrix repo's plugin development guide.
- Covers: repo layout, dev environment setup (cloning both repos,
  the symlink + dev_server flow), the per-plugin update workflow
  (especially the version bump requirement that the Plugin Store
  relies on), the new-plugin submission flow, commit message
  convention, and license terms.

CODE_OF_CONDUCT.md
- Contributor Covenant 2.1, the de facto standard for open-source
  projects. Same content as the one added to LEDMatrix.

SECURITY.md
- Plugin-specific framing: routes core LEDMatrix issues to the
  LEDMatrix repo's SECURITY.md and keeps this file scoped to plugin
  code in this repo. Documents the same "no sandboxing" reality that
  LEDMatrix's SECURITY.md flags — plugins run in the same process as
  the display loop with full file/network access.
- Out-of-scope section explicitly routes Python dep vulnerabilities
  to the upstream package maintainer and third-party plugin issues
  to the plugin's own repo.

.github/PULL_REQUEST_TEMPLATE.md
- More plugin-aware than the LEDMatrix template. Calls out the
  exact bugs found earlier in this PR series:
  - "Bumped version in manifest.json" (the Plugin Store only ships
    updates when the version increases, so forgetting this means
    the fix never reaches users)
  - "class_name matches the actual class in manager.py exactly,
    case-sensitive, no spaces" (the hello-world bug)
  - "entry_point matches the real file or is omitted" (the
    stock-news bug)
  - "config_schema.json is the source of truth" (the source of
    drift in many of the plugin READMEs I rewrote)
- Has a separate SUBMISSION checklist block for new-plugin PRs.

README.md
- Added pointers to CONTRIBUTING.md, SECURITY.md, and SUBMISSION.md
  in the Support & Community section. The license section already
  existed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
README.md (1)

212-212: ⚠️ Potential issue | 🟡 Minor

Use US spelling for consistency: "afterward".

Replace "afterwards" with "afterward" to maintain American English style consistent across repository documentation.

✏️ Proposed fix
-its `manifest.json`. Restart the LEDMatrix display service afterwards
+its `manifest.json`. Restart the LEDMatrix display service afterward
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 212, Update the README sentence referencing
"manifest.json" to use US spelling by replacing "afterwards" with "afterward"
(locate the sentence that reads "its `manifest.json`. Restart the LEDMatrix
display service afterwards" and change "afterwards" to "afterward") to maintain
consistent American English across the documentation.
🧹 Nitpick comments (1)
SECURITY.md (1)

67-71: Consider varying sentence structure for better readability.

Three consecutive sentences begin with "Plugins" (lines 67, 69, 70). While grammatically correct, varying the structure improves flow.

♻️ Proposed refactor
-- **Plugins can read and write any file the LEDMatrix process can
-  reach**, including `config_secrets.json`.
-- **Plugins can make arbitrary network requests.**
-- **Plugins are loaded into the same Python process as the display
-  loop**, so a crash in a plugin can affect the whole display.
+- **Plugins can read and write any file the LEDMatrix process can
+  reach**, including `config_secrets.json`.
+- **They can make arbitrary network requests.**
+- **All plugins load into the same Python process as the display
+  loop**, so a crash in one plugin can affect the whole display.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@SECURITY.md` around lines 67 - 71, Rewrite the three bullets so they don't
all start with the word "Plugins": locate the bullets containing the phrases
"Plugins can read and write any file the LEDMatrix process can reach, including
`config_secrets.json`", "Plugins can make arbitrary network requests.", and
"Plugins are loaded into the same Python process as the display loop", and vary
sentence structure (e.g., use passive/agent-first phrasing, combine or reorder
clauses, or start with consequences like "Any plugin can..." or "Because plugins
run in-process...") to improve flow while preserving the original meaning and
warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CONTRIBUTING.md`:
- Around line 21-36: The fenced code block showing the directory tree (starting
with "ledmatrix-plugins/") is missing a language identifier; update the opening
triple-backticks to include a language such as "text" or "plaintext" (e.g.,
change "```" to "```text") so the Markdown linter accepts the example directory
listing in the CONTRIBUTING.md file.

---

Duplicate comments:
In `@README.md`:
- Line 212: Update the README sentence referencing "manifest.json" to use US
spelling by replacing "afterwards" with "afterward" (locate the sentence that
reads "its `manifest.json`. Restart the LEDMatrix display service afterwards"
and change "afterwards" to "afterward") to maintain consistent American English
across the documentation.

---

Nitpick comments:
In `@SECURITY.md`:
- Around line 67-71: Rewrite the three bullets so they don't all start with the
word "Plugins": locate the bullets containing the phrases "Plugins can read and
write any file the LEDMatrix process can reach, including
`config_secrets.json`", "Plugins can make arbitrary network requests.", and
"Plugins are loaded into the same Python process as the display loop", and vary
sentence structure (e.g., use passive/agent-first phrasing, combine or reorder
clauses, or start with consequences like "Any plugin can..." or "Because plugins
run in-process...") to improve flow while preserving the original meaning and
warnings.
🪄 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

Run ID: 1063bcb0-0f3c-4856-9729-ce421e104a1d

📥 Commits

Reviewing files that changed from the base of the PR and between b2f76c1 and 64ce9aa.

📒 Files selected for processing (5)
  • .github/PULL_REQUEST_TEMPLATE.md
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • README.md
  • SECURITY.md
✅ Files skipped from review due to trivial changes (1)
  • .github/PULL_REQUEST_TEMPLATE.md

The plugins repo had no .github/ISSUE_TEMPLATE/ directory at all, so
plugin bug reports landed as freeform issues with whatever info the
reporter happened to include. Triaging required manually asking for
the basics every time.

Added two templates:

bug_report.md
- Plugin-aware bug report. Title prefilled with [<plugin-id>] so
  filed issues are immediately scoped. Prompts for:
  - Plugin id and version (from manifest.json)
  - How the plugin was installed (Plugin Store / GitHub URL / manual)
  - Repro steps and expected vs actual
  - Plugin config snippet with API-key redaction reminder
  - Filtered journalctl excerpt (gives the exact grep command)
  - Pi model, LEDMatrix version, and panel info
- Routes core LEDMatrix bugs (display controller, web interface,
  plugin loader) to the LEDMatrix repo's issue tracker.

plugin_request.md
- For suggesting new plugins or features for existing ones. Prompts
  for:
  - Whether it's a new plugin or extension of an existing one
  - What gets displayed and where the data comes from
  - Whether an API key is needed (and is the free tier enough?)
  - How many display modes
  - Whether the requester is offering to build it (with a pointer to
    SUBMISSION.md for the contributor flow)
- Distinguishes "I'm requesting a plugin" from "I'm contributing a
  plugin" so it doesn't get confused with the SUBMISSION.md flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/ISSUE_TEMPLATE/bug_report.md:
- Around line 60-61: Update the empty fenced code block (the triple backticks
block that currently has no language) to include a language tag such as text or
log; replace ``` with ```text so the markdownlint rule MD040 is satisfied and
the block is explicitly marked as plain text/log output.
🪄 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

Run ID: a82b857e-87f8-4f56-a233-ca785f7a9717

📥 Commits

Reviewing files that changed from the base of the PR and between 64ce9aa and 2f35c88.

📒 Files selected for processing (2)
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/ISSUE_TEMPLATE/plugin_request.md
✅ Files skipped from review due to trivial changes (1)
  • .github/ISSUE_TEMPLATE/plugin_request.md

Comment on lines +60 to +61
```
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a language tag to the empty fenced logs block.

Line 60 uses a fenced code block without a language, which triggers markdownlint MD040. Use text (or log) to keep lint clean.

Suggested patch
-```
-```
+```text
+```
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 60-60: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/ISSUE_TEMPLATE/bug_report.md around lines 60 - 61, Update the empty
fenced code block (the triple backticks block that currently has no language) to
include a language tag such as text or log; replace ``` with ```text so the
markdownlint rule MD040 is satisfied and the block is explicitly marked as plain
text/log output.

Chuck and others added 10 commits April 7, 2026 13:38
Two more spots with the same /api/plugins/* missing /v3 prefix that
I missed in the round 2 sweep:

README.md
- One occurrence in the API curl example (the bulk-fix in earlier
  iterations caught the table form but not this single curl). Fixed
  to /api/v3/plugins/install.

docs/PLUGIN_STORE_IMPLEMENTATION_SUMMARY.md
- 9 entries in the "API Endpoints" reference table all used the
  bare /api/plugins/* form. Bulk-fixed to /api/v3/plugins/* via sed.
  These were missed in the earlier round 2 sweep because that pass
  only fixed the curl examples and not the prose tables.

The api_v3 blueprint mounts at /api/v3 (verified in
LEDMatrix:web_interface/app.py:144). The bare /api/plugins/* paths
returned 404 if anyone copy-pasted them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A doc-vs-code crosscheck of every plugin's manifest.json display_modes
arrays found a real bug: hockey-scoreboard and lacrosse-scoreboard
both register the same six mode names:
- ncaa_mens_recent / ncaa_mens_upcoming / ncaa_mens_live
- ncaa_womens_recent / ncaa_womens_upcoming / ncaa_womens_live

The display controller stores modes in a flat dict keyed by mode name
(LEDMatrix:src/display_controller.py:295):

  self.mode_to_plugin_id[mode] = plugin_id

There is no collision detection. If a user installs both plugins,
whichever loads second silently overrides the first plugin's NCAA
modes. Verified with grep that no collision detection exists in
plugin_manager.py or display_controller.py.

Hockey shipped 2025-10-22, lacrosse shipped 2026-04-06, so the warning
belongs on lacrosse's README — hockey is the established mode owner.

plugins/lacrosse-scoreboard/README.md
- Added a "Known conflict with hockey-scoreboard" callout near the
  top of the README explaining the collision, the underlying cause
  (flat mode-to-plugin dict, no collision detection), and the
  workaround (enable only one plugin at a time).
- Pointed at a future fix (rename lacrosse modes to lax_ncaa_mens_*
  in a version-bumped release) so this isn't lost.

VERIFICATION.md
- Added a Manifest Validation checklist item: "display_modes don't
  collide with any existing plugin's modes". Includes a copy-paste
  Python one-liner that verifies all plugins at once. This would
  have caught the lacrosse bug at submission review time if the
  check had existed.
- Documents the convention going forward: prefix new plugin modes
  with the plugin id or sport (e.g. lax_ncaa_mens_recent) so future
  plugins don't have the same problem.

Confirmed via the same script that the hockey/lacrosse pair is the
ONLY collision across all 30 plugins — no other plugin pairs share
mode names.

Real underlying fix (out of scope for docs PR): rename lacrosse's
display_modes in a 1.x.0 minor version bump, with a migration note
in CHANGELOG.md and the release notes for any user who has the
plugin enabled. Until then this docs warning is the mitigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A doc-vs-code crosscheck of schema defaults vs config.get() defaults
across all 30 plugins found this drift in text-display:

- config_schema.json default for 'text': 'Subscribe to ChuckBuilds'
- manager.py:54 default for 'text':       'Hello, World!'

The schema default is what the auto-generated web UI form prefills,
so anyone who installs the plugin and looks at its tab sees
"Subscribe to ChuckBuilds" — but if they never open the tab and the
plugin runs on its in-memory default, they get "Hello, World!"
instead. Both strings are valid, but the inconsistency means the
plugin doesn't behave the way the form says it should.

The README I wrote earlier in this PR series also documents the
default as "Subscribe to ChuckBuilds" (verified in
plugins/text-display/README.md:35,59,180), so the schema is the
established source of truth.

Aligned the code default to match. Added an inline comment so future
maintainers don't drift it again.

manifest.json
- Bumped version 1.0.1 -> 1.0.2 since this is a user-visible default
  change (the rare user who installed the plugin and never opened
  its config tab will start seeing 'Subscribe to ChuckBuilds'
  instead of 'Hello, World!' after this update reaches them via
  the Plugin Store).
- Added 1.0.2 entry to versions[] dated 2026-04-07.
- Updated last_updated to match.

Audit method (reusable for future verification): the systematic
schema-vs-code default check is too noisy with simple string regex
because nested config keys (e.g. mlb.enabled inside a sports plugin)
share simple key names (enabled) with top-level keys, and grep can't
disambiguate. Targeted spot-checks on individual plugins using
exact key paths is the reliable approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ran the manifest version consistency check from VERIFICATION.md
across all 30 plugins and found 11 with bugs:

Empty versions[] array (4 plugins):
- 7-segment-clock     (current 1.0.0, no history)
- masters-tournament  (current 2.0.0, no history)
- web-ui-info         (current 1.0.0, no history)
- youtube-stats       (current 1.0.1, no history)

Added a single versions[] entry for the current version dated
2026-04-07 with a corresponding last_updated. The historical release
dates are not recoverable, but having an entry for the current
version means the Plugin Store can render *something* in the version
history list. Real release dates can be backfilled later if found.

last_updated drift (6 plugins):
A new version was released and added to versions[] but last_updated
was not bumped along with it. Bumped each to match its latest version's
released date:

- basketball-scoreboard: 2026-03-02 -> 2026-03-30 (1.5.5)
- hello-world:           2025-10-19 -> 2026-04-06 (1.0.2, the manifest
                         entry I added in round 1 of this audit)
- ledmatrix-leaderboard: 2026-02-17 -> 2026-04-03 (1.2.0)
- ledmatrix-stocks:      2026-03-04 -> 2026-03-31 (2.2.0)
- soccer-scoreboard:     2026-02-24 -> 2026-03-31 (1.6.0)
- ufc-scoreboard:        2026-02-24 -> 2026-03-02 (1.2.3)

Missing version entries entirely (1 plugin):
- ledmatrix-weather: version=2.2.2 in the manifest, but versions[]
  topped out at 2.1.2 (released 2026-02-24). Versions 2.2.0, 2.2.1,
  and 2.2.2 had been released without ever being recorded. Added a
  2.2.2 entry to the top of versions[] dated 2026-04-07 with a
  "note" field explicitly acknowledging this is an audit-time
  fixup and that the intermediate 2.2.0/2.2.1 release dates were
  not recorded.

After these fixes, the consistency check from VERIFICATION.md
returns "All clean" across all 30 plugins.

Why this matters:
- The Plugin Store reads versions[] to render the per-plugin update
  history users see in the web UI.
- last_updated is shown next to each plugin in the store browse
  view; stale values mislead users about which plugins are actively
  maintained.
- For ledmatrix-weather specifically, the missing 2.2.x entries
  meant the store couldn't tell that any version newer than 2.1.2
  existed in the registry, so users on 2.1.2 might not have
  received the 2.2.x updates at all.

Audit method: ran the verification script I added to VERIFICATION.md
in the previous iteration. The script and the bug it caught are now
both in this PR — the check is wired into the contributor checklist
so this class of bug should not silently accumulate again.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ields

A second-pass crosscheck of all 30 plugin manifests against the
canonical schema at LEDMatrix:schema/manifest_schema.json found four
distinct categories of bugs spread across the entire plugin ecosystem:

1. compatible_versions missing from 21 manifests
   The schema marks compatible_versions as required, but only 7 of
   30 plugins had it set. The store_manager validator
   (LEDMatrix:src/plugin_system/store_manager.py:213) only does a
   length/type check when present, so the missing field doesn't cause
   runtime errors today — but the store_manager has another check
   (line 221) that flags the deprecated 'ledmatrix_version' field
   with the message "use compatible_versions instead", so the field
   is genuinely meant to be on every plugin.
   Added compatible_versions: [">=2.0.0"] to all 21 missing plugins.
   Chose ">=2.0.0" because every existing ledmatrix_min value across
   all plugins is "2.0.0" (verified before making the change).

2. Deprecated ledmatrix_min field in 167 versions[] entries (every
   single version entry across all 30 plugins, going back through the
   full version history)
   The store_manager validator
   (LEDMatrix:src/plugin_system/store_manager.py:230-231) emits a
   deprecation error for every versions[] entry that uses
   'ledmatrix_min' instead of 'ledmatrix_min_version'. Every single
   plugin uses the old name. Renamed all 167 entries via:

     for v in m.get('versions', []):
         if isinstance(v, dict) and 'ledmatrix_min' in v:
             v['ledmatrix_min_version'] = v.pop('ledmatrix_min')

3. entry_point missing from 2 manifests (ledmatrix-weather, odds-ticker)
   The plugin loader at
   LEDMatrix:src/plugin_system/plugin_loader.py:530 defaults
   entry_point to 'manager.py' if missing, so this isn't a runtime
   bug — but the canonical schema lists it as required and both
   plugins do in fact use manager.py. Added the explicit field to
   both for schema conformance.

4. Deprecated top-level ledmatrix_version field in 4 manifests
   The store_manager validator
   (LEDMatrix:src/plugin_system/store_manager.py:220-221) flags this
   with: "ledmatrix_version is deprecated, use compatible_versions
   instead". Removed the deprecated field from all 4 plugins (their
   compatible_versions field, added in step 1, supersedes it).

Stats from the fix-up script:
  compat_added: 21
  min_renamed: 167
  entry_point_added: 2
  top_level_ledmatrix_version_removed: 4

After all four fix-ups, every plugin manifest:
- Has all 7 required schema fields
- Uses ledmatrix_min_version (current) instead of ledmatrix_min (deprecated)
- Has no top-level deprecated ledmatrix_version field
- Passes the same VERIFICATION.md check that found the previous
  iteration's 11 version-history bugs

These were all latent — the LEDMatrix system has been silently
tolerating them because the validators are permissive — but they
would all have shown up as errors and warnings the first time anyone
ran the canonical validator from store_manager._validate_manifest_*.

No version bumps required: these are metadata-only fixes to the
manifest itself, not changes to plugin behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A doc-vs-code crosscheck of every plugin's manager.py imports against
its requirements.txt found 3 plugins with hard imports that aren't
declared as dependencies. The plugin loader installs requirements.txt
on first plugin load, so missing entries mean fresh installs of these
plugins via the Plugin Store fail with ImportError (or fall back to
degraded behavior, depending on whether the import is wrapped in
try/except).

odds-ticker (most severe — unconditional imports)
- manager.py:35 imports pytz unconditionally
- manager.py:37 imports numpy as np unconditionally
- The existing requirements.txt was essentially empty (just comments
  saying "handled by main LEDMatrix"). On a fresh Pi where the host
  environment is minimal, the plugin would fail to load with
  "ModuleNotFoundError: No module named 'numpy'" or similar.
- Added: numpy>=1.20.0, pytz>=2023.3, plus explicit
  requests>=2.25.0 and Pillow>=8.0.0 (the latter two were assumed
  to come from the host but listing them avoids the assumption).

mqtt-notifications (degraded UX — try/except wrapped)
- manager.py:157 imports freetype inside a try/except for BDF font
  rendering. When the import fails (because freetype-py isn't in
  requirements.txt), the plugin logs a warning and falls back to
  PIL's default font — so users who configured the plugin to use a
  BDF font silently get a different font instead.
- Added: freetype-py>=2.4.0

of-the-day (degraded UX — try/except wrapped)
- manager.py:350 imports freetype inside a try/except, same pattern
  as mqtt-notifications. Same silent fallback to PIL default font
  for users who configured a BDF font.
- Added: freetype-py>=2.4.0

These were latent because:
- Most users have numpy/pytz/freetype-py installed transitively from
  other plugins or from the LEDMatrix host environment, so the
  bugs only surface on minimal installs.
- The freetype try/except specifically masks the failure as a
  default-font fallback rather than a crash, so users may not
  realize they're not getting their configured BDF font.

Out-of-scope finding (flagged but not fixed):

- 11+ plugins (and src/base_odds_manager.py in LEDMatrix core) wrap
  `from web_interface_v2 import increment_api_counter` in a
  try/except with a no-op fallback. The web_interface_v2 module
  doesn't exist in the v3 codebase — so increment_api_counter is
  silently a no-op everywhere. Plugin API call counts that were
  meant to flow into a metrics dashboard are perpetually zero.
  This is a code regression / dead pattern, not a docs bug; it
  needs a separate cleanup PR that either removes the try/except
  blocks entirely or wires up a real implementation.

Audit method: regex-grep imports in plugins/*/manager.py and
plugins/*/*.py against requirements.txt with stdlib + plugin-local
filtering. The script generated noise from stdlib false positives
(uuid, sqlite3, etc.) and plugin-local module false positives, but
manual inspection of the remaining hits found these 3 real bugs.
Could be tightened into a CI check using ast.parse to walk the
import tree properly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rift

A doc-vs-code crosscheck of plugins.json registry entries vs each
plugin's manifest.json found drift in 20 of 30 plugins. The pre-commit
hook (update_registry.py) was only syncing latest_version and
last_updated; everything else (name, description, author, category,
tags, icon) was set when each plugin was first added to the registry
and then drifted independently from the source-of-truth manifest.

This is contrary to the script's own docstring which says "each
plugin's manifest.json is the source of truth" — the script had been
honoring that for version info only.

update_registry.py
- Extended the sync loop to compare and update name, description,
  author, category, tags, and icon from the manifest. Logs which
  fields were synced for each plugin.
- last_updated update now prefers the manifest's last_updated field
  (which I made consistent across all 30 plugins in the previous
  iteration's manifest cleanup) instead of always overwriting with
  today's date. This means the registry's last_updated reflects
  the actual plugin release date, not the day update_registry.py
  happened to be run.

plugins/countdown/manifest.json
- Author was "Charles" — looks like a placeholder from initial
  development that was never updated. The registry already had
  "ChuckBuilds" which is the correct author (matches every other
  plugin in this monorepo). Fixed the manifest.

plugins.json
- Auto-regenerated by running the extended update_registry.py.
  Synced fields across 20 plugins (sample of what changed):
  - hello-world: author "LEDMatrix Team" -> "ChuckBuilds"
  - countdown:   author drift to "ChuckBuilds" + icon sync
  - text-display: name "Scrolling Text Display" -> "Text Display",
                  category "text" -> "display"
  - of-the-day:   name "Of The Day" -> "Of The Day Display",
                  category "content" -> "information"
  - olympics:     name "Olympics Countdown" -> "Olympics"
  - calendar:     category "time" -> "productivity"
  - static-image: category "media" -> "display"
  - several plugins had description drift (registry held marketing
    copy that drifted from the manifest's plain description)
  - hockey/lacrosse/ufc/countdown gained icon fields from their
    manifests (the registry had been omitting them)
- After all syncs, the consistency check returns "All clean" across
  all 30 plugins.

Why this matters:
- Plugin Store filters by category. A plugin filed under the wrong
  category in the registry is hidden from users searching that
  category. text-display being filed as "text" instead of "display"
  was the most obvious example — anyone browsing the "Display"
  category was missing it.
- Plugin Store renders the registry's name and author. A user
  reading "Of The Day" in the store and "Of The Day Display" in
  the plugin's README (which is generated from the manifest) had
  no way to know they're the same plugin.
- Author attribution drift ("LEDMatrix Team" vs the actual
  "ChuckBuilds") is a credit issue.

The pre-commit hook was successfully synced previously by my edits;
this commit just makes future drift impossible by extending what
the hook syncs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A doc-vs-code crosscheck found 11 of 30 plugins missing a per-plugin
LICENSE file. The other 19 plugins each ship a short-form GPL-3.0
notice (copyright header + the recommended "free software / no
warranty / see <gnu.org/licenses>" paragraph) in their plugin
directory. The 11 missing plugins were never created with a LICENSE
file at submission time and have been silently relying on the
repo-root LICENSE under the inbound=outbound rule.

Plugins fixed (11):
- 7-segment-clock
- christmas-countdown
- countdown
- f1-scoreboard
- ledmatrix-flights
- ledmatrix-stocks
- masters-tournament
- olympics
- ufc-scoreboard
- web-ui-info
- youtube-stats

Each got a copy of the same short-form GPL-3.0 LICENSE used by
hello-world, clock-simple, and the other 17 plugins that already had
one. Verified the post-fix state with:

  for d in plugins/*/; do
    ls "$d"LICENSE >/dev/null 2>&1 || echo "MISSING: $d"
  done

All 30 plugins now have a LICENSE file.

Why this matters:
- Per-plugin LICENSE files give downstream users an explicit license
  declaration scoped to the plugin they're installing — particularly
  important for plugins installed via "Install from GitHub URL" since
  those bypass the monorepo's repo-root LICENSE.
- Standard open-source compliance practice: every distributable
  unit of code should have its own LICENSE file.

Other checks in this iteration that came back clean:
- All 30 plugins have id matching directory name (no drift)
- All display_modes follow [a-z0-9_-]+ naming convention (no
  whitespace, no uppercase, no unusual characters)

VERIFICATION.md
- Added a "Per-plugin LICENSE file exists" check to the Documentation
  section, with a copy-paste shell one-liner that verifies all
  plugins at once. This is the same pattern used for the other
  verification checks added earlier in this PR series — checks that
  paid off should be wired into the contributor checklist so they
  catch the same class of bug going forward.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- hello-world/README.md: use canonical plugin-repos/ install path;
  add "text" language hint to log snippet
- hello-world/manifest.json + text-display/manifest.json: reorder
  versions[] newest-first to match the convention used by every
  other plugin (top-level version now equals versions[0])
- hockey-scoreboard/README.md: replace legacy flat keys
  (update_interval, display_duration, prioritize_favorites,
  show_shots_on_goal) with nested schema keys throughout examples
  and troubleshooting
- static-image/README.md: update all image examples to use object
  entries ({id, path}); fix image_config.mode "single" → "multiple"
  in the rotation example; drop stale legacy image_path note
- ufc-scoreboard/README.md: use abbreviation format (HW, LW, ...)
  for ufc.favorite_weight_classes to match config_schema.json
- README.md: "afterwards" → "afterward" (US spelling)
- docs/PLUGIN_STORE_USER_GUIDE.md: update navigation terms in
  example snippet ("Plugin Store"/"Install from URL" →
  "Plugin Manager"/"Install from GitHub")
- football-scoreboard/README.md: delete duplicated
  "Layout Customization" block (kept first instance)
- ledmatrix-stocks/README.md + text-display/README.md: tidy
  "Pixels of empty space" → "Pixels of space" wording

Not changed (verified against current code):
- stock-news/manifest.json: CodeRabbit flagged as out of sync, but
  top-level version (1.0.2) already equals versions[0] (1.0.2).
  No action needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CONTRIBUTING.md: add "text" language hint to repo-layout fenced
  block so markdownlint accepts it
- SECURITY.md: vary sentence structure of the plugin-security-model
  bullets so they don't all start with "Plugins"

Not changed (already fixed in 98d5478):
- README.md line 212 "afterwards" → "afterward" (duplicate comment)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ChuckBuilds ChuckBuilds merged commit 9a27aba into main Apr 8, 2026
1 check passed
@ChuckBuilds ChuckBuilds deleted the docs/refresh-2026-04 branch April 8, 2026 01:24
ChuckBuilds pushed a commit that referenced this pull request Apr 8, 2026
The docs refresh audit on #92 flagged two code bugs affecting the
plugins repo. Neither could be fixed in a docs-only PR, so they
were deferred to this follow-up. The companion LEDMatrix PR
(ChuckBuilds/LEDMatrix#307) handles the six bugs that belong in
the app repo.

Bug 6: lacrosse-scoreboard display-mode collision (BREAKING)
  lacrosse-scoreboard shipped six display modes (ncaa_mens_recent /
  upcoming / live and ncaa_womens_recent / upcoming / live) that
  collide with the same six modes exposed by hockey-scoreboard.
  LEDMatrix's display controller stores modes in a flat dict keyed
  by mode name (src/display_controller.py), so whichever plugin
  loaded second silently overrode the first — with no warning in
  the logs. Renamed all six modes with a plugin-specific lax_
  prefix:

    ncaa_mens_recent      -> lax_ncaa_mens_recent
    ncaa_mens_upcoming    -> lax_ncaa_mens_upcoming
    ncaa_mens_live        -> lax_ncaa_mens_live
    ncaa_womens_recent    -> lax_ncaa_womens_recent
    ncaa_womens_upcoming  -> lax_ncaa_womens_upcoming
    ncaa_womens_live      -> lax_ncaa_womens_live

  Manifest, dispatch logic in manager.py (_get_current_manager,
  _record_dynamic_progress, get_cycle_duration, and the granular
  mode-parser that walks the league registry) all updated to use
  the new names. The split('_', 2)[2] parser has been replaced
  with rsplit('_', 1)[1] where the new four-segment mode strings
  (lax_ncaa_mens_recent) need the last segment. README
  "Known conflict" warning replaced with a breaking-change note
  pointing at the new CHANGELOG.md, which spells out the rename
  table and migration steps for anyone with the old mode names
  hardcoded in their config.json (display_durations, rotation_order,
  etc.). Version bumped 1.0.3 -> 1.1.0 (minor bump per semver —
  breaking config change). No backward-compat aliases.

Bug 5: dead web_interface_v2 import pattern (plugins half)
  Every plugin that used to talk to the v2 API-counter helper
  carried a try/except block importing web_interface_v2 and
  falling back to a no-op stub. The web_interface_v2 module
  doesn't exist anywhere in the v3 codebase — the counter was
  silently a no-op for every plugin, and no API-metrics dashboard
  reads the counter. Deleted the import block, all direct calls,
  one self.increment_api_counter hasattr-guarded call site in
  odds-ticker/data_fetcher.py, and the orphan "# Increment API
  counter" comment lines left behind.

  Touched plugins (14 files):
    - baseball-scoreboard/base_odds_manager.py
    - basketball-scoreboard/base_odds_manager.py
    - football-scoreboard/base_odds_manager.py
    - hockey-scoreboard/base_odds_manager.py
    - lacrosse-scoreboard/base_odds_manager.py
    - soccer-scoreboard/base_odds_manager.py
    - ufc-scoreboard/base_odds_manager.py
    - ledmatrix-leaderboard/data_fetcher.py
    - ledmatrix-music/manager.py
    - ledmatrix-stocks/data_fetcher.py
    - ledmatrix-weather/manager.py
    - odds-ticker/manager.py
    - odds-ticker/data_fetcher.py
    - youtube-stats/manager.py

Verification done locally:
  - ast.parse on all plugin .py files (0 syntax errors)
  - py_compile on every touched file
  - No web_interface_v2 or increment_api_counter references
    remain anywhere under plugins/ (except the unrelated
    src.api_counter mock in lacrosse-scoreboard/test_lacrosse_plugin.py)
  - Lacrosse lax_ prefix applied consistently across manifest,
    default mode fallback, dispatch logic, and mode-string parser

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Apr 8, 2026
…ts) (#93)

The docs refresh audit on #92 flagged two code bugs affecting the
plugins repo. Neither could be fixed in a docs-only PR, so they
were deferred to this follow-up. The companion LEDMatrix PR
(ChuckBuilds/LEDMatrix#307) handles the six bugs that belong in
the app repo.

Bug 6: lacrosse-scoreboard display-mode collision (BREAKING)
  lacrosse-scoreboard shipped six display modes (ncaa_mens_recent /
  upcoming / live and ncaa_womens_recent / upcoming / live) that
  collide with the same six modes exposed by hockey-scoreboard.
  LEDMatrix's display controller stores modes in a flat dict keyed
  by mode name (src/display_controller.py), so whichever plugin
  loaded second silently overrode the first — with no warning in
  the logs. Renamed all six modes with a plugin-specific lax_
  prefix:

    ncaa_mens_recent      -> lax_ncaa_mens_recent
    ncaa_mens_upcoming    -> lax_ncaa_mens_upcoming
    ncaa_mens_live        -> lax_ncaa_mens_live
    ncaa_womens_recent    -> lax_ncaa_womens_recent
    ncaa_womens_upcoming  -> lax_ncaa_womens_upcoming
    ncaa_womens_live      -> lax_ncaa_womens_live

  Manifest, dispatch logic in manager.py (_get_current_manager,
  _record_dynamic_progress, get_cycle_duration, and the granular
  mode-parser that walks the league registry) all updated to use
  the new names. The split('_', 2)[2] parser has been replaced
  with rsplit('_', 1)[1] where the new four-segment mode strings
  (lax_ncaa_mens_recent) need the last segment. README
  "Known conflict" warning replaced with a breaking-change note
  pointing at the new CHANGELOG.md, which spells out the rename
  table and migration steps for anyone with the old mode names
  hardcoded in their config.json (display_durations, rotation_order,
  etc.). Version bumped 1.0.3 -> 1.1.0 (minor bump per semver —
  breaking config change). No backward-compat aliases.

Bug 5: dead web_interface_v2 import pattern (plugins half)
  Every plugin that used to talk to the v2 API-counter helper
  carried a try/except block importing web_interface_v2 and
  falling back to a no-op stub. The web_interface_v2 module
  doesn't exist anywhere in the v3 codebase — the counter was
  silently a no-op for every plugin, and no API-metrics dashboard
  reads the counter. Deleted the import block, all direct calls,
  one self.increment_api_counter hasattr-guarded call site in
  odds-ticker/data_fetcher.py, and the orphan "# Increment API
  counter" comment lines left behind.

  Touched plugins (14 files):
    - baseball-scoreboard/base_odds_manager.py
    - basketball-scoreboard/base_odds_manager.py
    - football-scoreboard/base_odds_manager.py
    - hockey-scoreboard/base_odds_manager.py
    - lacrosse-scoreboard/base_odds_manager.py
    - soccer-scoreboard/base_odds_manager.py
    - ufc-scoreboard/base_odds_manager.py
    - ledmatrix-leaderboard/data_fetcher.py
    - ledmatrix-music/manager.py
    - ledmatrix-stocks/data_fetcher.py
    - ledmatrix-weather/manager.py
    - odds-ticker/manager.py
    - odds-ticker/data_fetcher.py
    - youtube-stats/manager.py

Verification done locally:
  - ast.parse on all plugin .py files (0 syntax errors)
  - py_compile on every touched file
  - No web_interface_v2 or increment_api_counter references
    remain anywhere under plugins/ (except the unrelated
    src.api_counter mock in lacrosse-scoreboard/test_lacrosse_plugin.py)
  - Lacrosse lax_ prefix applied consistently across manifest,
    default mode fallback, dispatch logic, and mode-string parser

Co-authored-by: Chuck <chuck@example.com>
Co-authored-by: Claude Opus 4.6 (1M context) <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.

1 participant