Files
LEDMatrix/docs
ChuckandClaude Opus 5 9b522d412c fix(store): gate the git-pull update path (#508)
* fix(store): gate the git-pull update path

`install_plugin` gates every route that re-downloads, `_reinstall_with_rollback`
included. `update_plugin` has one branch that re-downloads nothing: a git
checkout pulls in place, installs dependencies, and returns True. A pull could
therefore deliver a manifest flooring above this core and nothing would notice
until the plugin failed to load — which surfaces as one line in the journal and
a display that silently stopped appearing.

Checked after the pull rather than before it, for the same reason
`_install_plugin_impl` checks after the download: the registry carries no
compatibility field, so the incoming floor is only knowable once the new commit
is on disk.

Undone with `git reset --hard` to the pre-pull commit rather than by removing
the directory. This is a live checkout, the old commit is still in the object
store, and the reset leaves the user on the exact version they were already
running — the same promise `_reinstall_with_rollback` makes, reached by the
means this path actually has, with no window where the plugin directory does
not exist. An unreadable manifest allows: it is not evidence of a floor.

Scope, stated plainly: monorepo plugins install as archives and update through
`_reinstall_with_rollback`, so they were already gated. Only registry entries
with no `plugin_path` reach this branch. It is closed anyway because the sunset
rule in the plugins repo's `08-shared-sports-code.md` names, as condition 3,
that the core enforces the floor "at install/update time" — and B6 rests on
that being true rather than merely written down. `install_from_url` is still
ungated; the tests say so rather than letting the next reader assume otherwise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9

* fix(store): do not pull what the gate cannot un-pull

Review of the gate found a data-loss path it had introduced, plus two smaller
scope errors. All three from CodeRabbit on #508.

**The stash failure was load-bearing and was not treated as one.** update_plugin
stashes local changes before pulling; when that stash failed or timed out it
logged a warning and pulled anyway. That was harmless while nothing ever undid
a pull. It is not harmless now: the gate's rollback is `git reset --hard`, which
discards uncommitted tracked edits -- exactly the edits the stash existed to
protect. A pull does not refuse on a dirty tree as long as the incoming commit
touches other files, so the sequence completed silently: pull succeeds, gate
refuses, reset takes the user's work with it.

update_plugin now returns before pulling unless the tree was already clean or
was successfully stashed. Refusing costs an update in a case that had already
gone wrong; the alternative costs data. That also makes `--hard` safe by
construction in _gate_pulled_commit, and its comment now says so rather than
observing it in passing.

Pinned by test_a_failed_stash_stops_the_update_before_pulling, which writes a
local edit, forces the stash to fail, and asserts both that HEAD did not move
and that the edit is still on disk. Verified it bites: with the new guard
removed the file comes back as `class P: pass`, the edit gone.

**_HAS_GIT could take the module down instead of skipping it.** With no git on
PATH, subprocess.run raises FileNotFoundError, and this runs at import time --
before skipif can act, so the whole file errors rather than skipping. Now
catches OSError.

**The doc overclaimed the gate's reach.** It said the floor is enforced on
"every route that installs or updates" while the same passage notes
install_from_url is ungated. Both spots now scope the claim to registry-managed
installs and the two supported update paths, and name the sideload exception.

Full suite 3720 passed, 6 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 17:35:00 -04:00
..
2025-12-27 14:15:49 -05:00
2025-12-27 14:15:49 -05:00

LEDMatrix Documentation

This directory contains guides, references, and architectural notes for the LEDMatrix project. If you are setting up a Pi for the first time, start with the project root README — it covers hardware, OS imaging, and the one-shot installer. The pages here go deeper.

I'm a new user

  1. GETTING_STARTED.md — first-time setup walkthrough
  2. WEB_INTERFACE_GUIDE.md — using the web UI
  3. PLUGIN_STORE_GUIDE.md — installing and managing plugins
  4. WIFI_NETWORK_SETUP.md — WiFi and AP-mode setup
  5. TROUBLESHOOTING.md — common issues and fixes
  6. SSH_UNAVAILABLE_AFTER_INSTALL.md — recovering SSH after install
  7. CONFIG_DEBUGGING.md — diagnosing config problems
  8. LOW_MEMORY_BOARDS.md — Pi Zero 2 W / 3B+ / 1GB Pi 4 memory limits

I want to write a plugin

Start here:

  1. PLUGIN_DEVELOPMENT_GUIDE.md — end-to-end workflow
  2. PLUGIN_QUICK_REFERENCE.md — cheat sheet
  3. PLUGIN_API_REFERENCE.md — display, cache, and plugin-manager APIs
  4. PLUGIN_ERROR_HANDLING.md — error-handling patterns
  5. DEV_PREVIEW.md — preview plugins on your dev machine without a Pi
  6. EMULATOR_SETUP_GUIDE.md — running the matrix emulator

Going deeper:

Configuring plugins

Advanced features

Reference

Contributing to LEDMatrix itself

Archive

docs/archive/ holds older guides that have been superseded or describe features that have been removed. They are kept for historical context and git history but should not be relied on.

Contributing to the docs

  • Markdown only, professional tone, minimal emoji.
  • Prefer adding to an existing page over creating a new one. If you add a new page, link it from this index in the section it belongs to.
  • If a page becomes obsolete, move it to docs/archive/ rather than deleting it, so links don't rot.
  • Keep examples runnable — paths, commands, and config keys here should match what's actually in the repo.