Skip to content

fix(store): gate install_from_url, the last ungated route in - #510

Merged
ChuckBuilds merged 1 commit into
fix/gate-git-pull-updatesfrom
fix/gate-install-from-url
Sep 1, 2026
Merged

fix(store): gate install_from_url, the last ungated route in#510
ChuckBuilds merged 1 commit into
fix/gate-git-pull-updatesfrom
fix/gate-install-from-url

Conversation

@ChuckBuilds

Copy link
Copy Markdown
Owner

Stacked on #508 — base it on main and retarget once that lands. The diff against #508 is just this.

Sideloading a plugin from a URL validated required manifest fields, warned on version-field and schema problems, and then installed whatever it found — never once comparing the core version. With this, all three routes in refuse on the same rule: install_plugin (#431/#433), the git-pull update path (#508), and now sideloading.

Why gate it at all

The argument is genuinely different from the other two, and worth saying out loud. An automatic store update is something that happens to a user; a sideload is something they chose, so "they didn't ask for this" doesn't apply.

But the floor is not advice about intent — it is a statement that the plugin cannot run on this core. Letting it through produces exactly the silent PluginState.ERROR at load that the store gate exists to prevent, and someone who pasted a URL is no better placed to diagnose that than someone who pressed Update.

Placement

Checked after the download, like the others, because the manifest is the only place the floor is written. Placed before the move into plugins/, so the existing finally removes the temp tree and nothing half-installed survives a refusal.

The reason string is returned to the caller rather than only logged, so the web UI shows the same actionable message the store path already gives ("…requires LEDMatrix X, but this system is running Y. Update LEDMatrix first, then install it.").

Tests

TestSideloadGate pins the same three cases the other routes pin:

  • refuses a floor above this core, and leaves nothing installed behind
  • still allows a compatible plugin — the guard against a gate that refuses everything, which would pass the first test and break sideloading entirely
  • does not block a 2.0.0 floor on a core reporting an untrustworthy version — the v3.1.0-reports-1.0.0 population, who must not be locked out

TestInstallGate's docstring said this route was still ungated. It no longer is, so it now points at TestSideloadGate rather than describing a gap.

Verified the tests bite: removing the check fails the refusal case with assert True is False.

Full suite 3723 passed, 6 skipped.

🤖 Generated with Claude Code

https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9

Sideloading a plugin from a URL validated required manifest fields, warned on
version-field and schema problems, and then installed whatever it had found --
never once comparing the core version. With this, all three routes in refuse on
the same rule: install_plugin (#431/#433), the git-pull update path (the commit
before this one), and now sideloading.

The argument for gating it is not the same as for the other two, and worth
stating. An automatic store update is something that happens TO a user; a
sideload is something they chose. But the floor is not advice about intent --
it is a statement that the plugin cannot run on this core. Letting it through
produces exactly the silent PluginState.ERROR at load that the store gate
exists to prevent, and someone who pasted a URL is no better placed to diagnose
that than someone who pressed Update.

Checked after the download, like the others, because the manifest is the only
place the floor is written. Placed before the move into plugins/, so the
existing `finally` removes the temp tree and nothing half-installed survives a
refusal. The reason string is returned to the caller, so the web UI shows the
same actionable message the store path already gives.

TestSideloadGate pins the three cases the other routes pin: refuses a floor
above this core and leaves nothing behind, still allows a compatible plugin
(the guard against a gate that refuses everything and passes the first test),
and does not block a 2.0.0 floor on a core reporting an untrustworthy version
-- the v3.1.0-reports-1.0.0 population, who must not be locked out.

TestInstallGate's docstring said this route was still ungated. It no longer is,
so it now points at TestSideloadGate rather than describing a gap.

Verified the tests bite: removing the check fails the refusal case.
Full suite 3723 passed, 6 skipped.

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

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: bd424197-7126-4494-abfc-474dff0c3ef3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@ChuckBuilds
ChuckBuilds merged commit 2ef70bc into fix/gate-git-pull-updates Sep 1, 2026
5 checks passed
@ChuckBuilds
ChuckBuilds deleted the fix/gate-install-from-url branch September 1, 2026 13:54
ChuckBuilds added a commit that referenced this pull request Sep 2, 2026
This PR was opened to say B6 was deliberately held. It has since run, so the
document said the opposite of the truth -- which is the exact failure it was
written to fix: "a plan that misreports which phase it is in is worse than no
plan".

Merges current main first (53 commits), which brings in #508's corrections to
this same file, then replaces the hold with what happened.

**Why the hold lifted is worth recording, because the stated gate was never
met.** It asked for evidence of 3.2.0 uptake, and that evidence could not
arrive: the core updates by `git pull --rebase`, so release-asset counts cannot
measure it, and no store-side telemetry exists. What changed is that the risk
the gate protected against was closed directly -- the store now refuses a
plugin whose floor exceeds the running core on all three routes in:
install_plugin (#431/#433), update_plugin's git branch (#508), and
install_from_url (#510). A pre-3.2.0 user cannot receive a sunset plugin at
all, so they keep the version they run. Refusal replaced the bundled copy,
which is what the copy stood in for.

Records what shipped (eight plugins, ~5,800 lines, plugins #346/#349/#350/#351)
and the two findings worth carrying to the next module: baseball's fallback was
the only one holding orchestration logic the core lacked, and two tests had
been leaning on the guard -- soccer's stubbed `src` in a way that shadowed the
core, so it had been exercising the frozen copy rather than the shipping class
since B5.

The remaining-work list is replaced too. Its first item was "nothing on the
critical path, B6 is waiting on calendar time", which is no longer true. What
remains: hardware soaks (with a note to check the rig's display_mode first, or
a board in switch mode tells you nothing about the scroll code), cutting 3.3.0
-- not required by B6, whose floors are 3.2.0, but calendar 1.2.3 floors at
3.3.0 and is un-installable until it exists -- and reconsidering the modules
held back during the sunset.

Keeps the pre-B6 sections as history. The reasoning still applies to the next
module; it is just no longer in force for this one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9
ChuckBuilds added a commit that referenced this pull request Sep 2, 2026
…511)

#510 shows as merged, but into fix/gate-git-pull-updates -- #508's branch --
rather than main. #508 reached main first, so the sideload gate was left behind
on a branch. Same failure as plugins #350/#351, which merged into each other's
bases; worth knowing the pattern, because GitHub reports these as MERGED and
`gh pr list` shows nothing outstanding.

main today has two of the three routes gated: install_plugin (#431/#433) and
update_plugin's git branch (#508). install_from_url validates required manifest
fields and then installs whatever it found, never comparing the core version.

Cherry-picked unchanged from the orphaned branch -- it applies to main with no
conflict. TestSideloadGate pins the three cases the other routes pin: refuses a
floor above this core leaving nothing behind, still allows a compatible plugin
(the guard against a gate that refuses everything), and does not block a 2.0.0
floor on a core reporting an untrustworthy version.

Full suite 3725 passed, 6 skipped.


Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9

Co-authored-by: Claude Opus 5 (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