The previous commit's git add/commit swept in a lot of unrelated,
unreviewed work alongside the intended dead-code deletions: a new Plugin
Composer web UI, new security-audit/CI tooling, a new march-madness
plugin, and 23 local-development-only symlinks under plugin-repos/ (per
scripts/setup_plugin_repos.py's own docstring, these are meant to be
generated locally, never committed -- .gitignore has no entry for them,
which is how they slipped in).
Removed here, split into their own PRs instead (except plugin-repos/*
symlinks and march-madness/ncaa_logos, which are dropped rather than
carried forward -- see PR discussion):
- web_interface/blueprints/composer.py + composer-app.js +
composer-canvas.js + composer.html + manager.py.j2
- scripts/prove_security.py, audit_plugins.py, generate_report.py
- .github/workflows/security-audit.yml, .github/workflows/tests.yml,
bandit.yaml
- All plugin-repos/* symlinks (local dev artifacts, not meant to be
committed at all)
- plugin-repos/march-madness/* and the 4 new assets/sports/ncaa_logos/*
PNGs it needed (left out of every split PR pending a decision on
whether march-madness belongs in the core repo or the plugin monorepo)
This PR now contains only what its title describes: the dead-code
removal from the previous commit, untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
Deletions, each re-verified with a fresh repo-wide grep (core, web,
scripts, docs, plugin monorepo) immediately before removal:
Modules with zero live importers:
- src/background_cache_mixin.py + src/generic_cache_mixin.py (134+150
LOC — referenced only by each other)
- src/font_test_manager.py (134 LOC)
- src/image_utils.py (22 LOC, self-documented deprecated)
- src/layout_manager.py (408 LOC — only its own test imported it) +
test/test_layout_manager.py
- src/common/basketball_plugin_example.py (328 LOC sample)
requirements.txt entries with zero importers in core (pre-plugin-era
manager deps): icalevents, geopy, timezonefinder, unidecode. Plus the
google-auth trio (google-auth-oauthlib, google-auth-httplib2,
google-api-python-client): their only importer is the calendar PLUGIN,
which declares all three in its own requirements.txt (verified in the
monorepo and on an installed copy) — the plugin dependency installer
owns them. Existing venvs are unaffected (removal doesn't uninstall);
fresh installs get them when calendar is installed.
Two stale references cleaned (a comment in test_pillow_compat.py, a
directory listing in HOW_TO_RUN_TESTS.md). Full suite green except the
two documented pre-existing failures (circuit_breaker mock drift, fixed
in #400; clock-simple 64x32 overflow, pre-dates this series); all core
entry modules verified importing cleanly under the emulator.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam
* feat(testing): add cross-size/cross-screen plugin safety harness
Render every plugin across all supported matrix sizes (64x32, 128x32,
128x64, 256x32) and every declared screen, failing on crashes, content
drawn past the panel edge, or visual drift vs committed golden images.
- BoundsCheckingDisplayManager: oversized-canvas overflow detection
- harness.py: multi-size/multi-screen render engine + golden compare
- scripts/check_plugin.py: CLI (functional+bounds, --out-dir, --update-golden,
--freeze-time); render_plugin.py refactored onto shared loading helpers
- test/plugins/test_harness.py + test_plugin_matrix.py (parametrized,
honors per-plugin test/harness.json; skips when no plugins present)
- MockCacheManager.cache_dir so cache-dir-using plugins load headlessly
- .github/workflows/test.yml + docs/plugin-safety-harness.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(testing): address PR review feedback on plugin safety harness
- check_plugin: friendly error for non-numeric --sizes; reject non-object
--config / --mock-data JSON; sanitize plugin mode before using as a
filename; stop --update-golden from masking crash/overflow failures
- bounds_display_manager: pad the canvas out to the largest supported panel
(not a fixed 16px) so far-overshoot coordinates are caught, not clipped
- harness: merge config_schema defaults inside render_plugin_matrix; surface
update() failures as a non-fatal warning + result field instead of a debug
log; sanitize mode in golden_path
- loading: fail fast when harness.json references a missing mock_data fixture
- mocks: clean up the per-instance temp cache dir via weakref.finalize
- test_plugin_matrix: add a discovery guard that fails when
LEDMATRIX_REQUIRE_PLUGINS=1 but none found (still skips locally); type hints
- bound test deps with upper version pins for deterministic CI
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(testing): render plugins across arbitrary panel sizes, not a fixed list
Addresses maintainer feedback that there is no canonical set of supported
panel sizes — a build can be any size/configuration (square, 2x2, 4x4, 8x2,
long strips, tall stacks).
- sizes.py: SUPPORTED_SIZES -> DEFAULT_TEST_SIZES (back-compat alias kept),
reframed as a representative SAMPLE of real panel-grid arrangements rather
than an authoritative list; add parse_size_token / coerce_sizes /
resolve_test_sizes helpers
- sizes are now fully overridable: LEDMATRIX_TEST_SIZES env (global, e.g. test
on your exact hardware) > per-plugin harness.json "sizes" > default sample;
CLI --sizes unchanged
- bounds_display_manager: pad the canvas to the largest panel IN THE CURRENT
RUN (via overflow_extent) instead of a hardcoded max, so cross-size overflow
detection scales to whatever sizes a run uses
- harness: compute per-run extent and thread it into the bounds manager
- tests: arbitrary-shape + size-parsing/precedence coverage
- docs: rewrite "Supported sizes" -> "Sizes: a sample, not a fixed list"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(testing): fail the harness on non-connectivity update() errors
Addresses the remaining review thread: recording every update() exception as a
non-fatal warning still let a real update() regression pass green as long as
display() survived. Now update() failures are classified — a tolerated set of
connectivity errors (ConnectionError/TimeoutError/socket/ssl/urllib/http/
requests) is recorded non-fatally (expected with no network in CI), while any
other exception is treated as a genuine bug and fails that render.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ci(security): pin actions to SHAs and disable checkout credential persistence
Addresses the CodeRabbit/zizmor workflow-hardening finding: pin
actions/checkout and actions/setup-python to full commit SHAs and set
persist-credentials: false on checkout to reduce supply-chain and
token-exposure risk.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(testing): validate positive sizes; narrow requests import except
Two review findings:
- sizes.py: parse_size_token / coerce_sizes now reject non-positive
dimensions (0x32, -64x32) with a clear message instead of passing invalid
sizes downstream (CodeRabbit).
- harness.py: the optional `requests` import now catches ImportError
specifically and logs instead of `except Exception: pass`, clearing the
Codacy medium "Try, Except, Pass" (harness.py L52) and Ruff S110/BLE001.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>