Skip to content

feat(cloud-environment): derive the cloud plugin list from the marketplace catalog - #663

Merged
kyle-sexton merged 2 commits into
mainfrom
feat/cloud-plugins-from-catalog
Oct 2, 2026
Merged

kyle-sexton merged 2 commits into
mainfrom
feat/cloud-plugins-from-catalog

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

No related issue: removes the hand-kept cloud plugin list in favor of the catalog's defaultEnabled field (pairs with melodic-software/claude-code-plugins#5897).

Summary

The cloud environment's base plugin set is now every plugin in the melodic-software marketplace catalog whose entry does not set defaultEnabled: false, read at cache build from claude-code-plugins/.claude-plugin/marketplace.json. Repos carry only deltas in their committed enabledPlugins: false to opt out, true to opt in to an off-by-default plugin. The cloud bootstrap already installs a repo's true entries.

Fix

  • setup.sh: new fleet_list_from_catalog writes /opt/melodic-fleet-plugins.json from the catalog with jq, in the same settings shape (extraKnownMarketplaces + enabledPlugins), so cloud-bootstrap.sh and its distributed copies read it unchanged. A failed fetch, unparsable catalog, or empty result leaves no list, as before. SCRIPT_VERSION bumped.
  • Deleted components/cloud-environment/fleet-plugins.json, distribution/check-plugin-baseline.sh and its test, the CI test step, and the plugin-seed-drift job (removed from ci-status needs and results). The list they guarded no longer exists.
  • setup.test.sh: tests the derivation against a fixture catalog (an off entry excluded; an explicit-true entry and an entry without the field included; an all-off or unparsable catalog yields no file) and the catalog URL.
  • READMEs (components/cloud-environment, distribution) describe the catalog-derived list.

With today's catalog on main (10 of 83 off) the derived list has 73 entries; after claude-code-plugins#5897 it drops to about 29.

Verification

  • Derived from the live catalog: 73 entries, equal to the catalog's count of entries not set defaultEnabled: false (both computed by jq).
  • shellcheck, shfmt -d, actionlint, typos, markdownlint-cli2 clean on touched files; lefthook pre-commit passed.
  • cloud-bootstrap.test.sh passes. setup.test.sh passes every new assertion locally; two existing install-census assertions fail on Windows Git Bash on unmodified main too, so CI's Linux run is the check for those.

Related

🤖 Generated with Claude Code

…place catalog

setup.sh now builds /opt/melodic-fleet-plugins.json from the
melodic-software marketplace catalog: every entry whose defaultEnabled is
not false. The file keeps its settings shape, so the cloud bootstrap and
its distributed copies read it unchanged. A failed fetch still leaves no
list.

Delete the hand-kept fleet-plugins.json, check-plugin-baseline.sh and its
test, and the CI step and seed-drift job that compared against the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 2, 2026 20:32
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-02T20:36:07.253808Z 8e90407 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 3m 53s —— View job


Code review complete

  • Gather context (PR diff, CLAUDE.md, REVIEW.md)
  • Review setup.sh changes (fleet_list_from_catalog)
  • Review setup.test.sh changes
  • Review deletions (check-plugin-baseline.sh, CI workflow, fleet-plugins.json)
  • Review README updates
  • Post findings

This repo runs a dedicated security lane (.github/workflows/claude-security-review.yml), so this review is scoped to correctness/design/maintainability per REVIEW.md.

One finding, posted inline on .github/workflows/ci.yml (deleted plugin-seed-drift job, lines 975–1049 on the pre-PR blob):

🔴 Important — deleting plugin-seed-drift (and the check-plugin-baseline.sh --compare-seed-strict path it ran) removes the only automated guard against the cloud fleet set drifting from the dotfiles workstation seed, and this PR makes that drift easier to trigger, not moot. Before this change, a fleet-set change required a PR to standards editing fleet-plugins.json, which is exactly when the job ran. After this change, the fleet set is derived live from claude-code-plugins' defaultEnabled field at every cache build, so a plugin can join or leave the cloud fleet with no PR to this repository at all — there's no longer any trigger point in standards CI where a seed-drift check could run. The PR's Fix section justifies the deletion with "the list they guarded no longer exists," but that's true only of the literal JSON file; the two-list (cloud fleet vs. dotfiles seed) drift problem the job was built for (claude-code-plugins#2654: "four ids went missing") is untouched and now has no backstop anywhere. Worth confirming before merge whether an equivalent check lands on the claude-code-plugins side (e.g. in the paired #5897) or against the dotfiles seed directly — otherwise this is a silent observability regression, not dead-code removal.

Everything else held up well on review:

  • fleet_list_from_catalog (setup.sh:120-134) correctly excludes only explicit defaultEnabled: false, includes entries with the field absent or true, and cleans up its own output on failure (unparsable/empty catalog → no file, verified against the new test fixtures in setup.test.sh).
  • The temp-file handling in the catalog-fetch path (catalog_file via mktemp, unconditional rm -f after the if/else) is correct — no stray files left on a curl failure vs. a fleet_list_from_catalog failure.
  • The now-moot half of the old baseline check (fleet list vs. marketplace catalog coverage) is legitimately resolved by construction, since the fleet list is the catalog filter now.
  • README updates in both components/cloud-environment and distribution accurately describe the new catalog-derived flow and stay in sync with the test assertions that check them.
  • Checked repo-wide for leftover references to the deleted fleet-plugins.json / check-plugin-baseline.sh / plugin-seed-drift — none found outside an unrelated planning doc (docs/topics/standards-sync-audit/PLAN.md) that's out of scope for this diff.

Note: the PR body's verification claims (73-entry count against the live catalog, local test runs, linters) are author-reported and not independently re-verified here.

@claude

claude Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 54s —— View job


Security review

  • Gather context (PR diff, changed files)
  • Review setup.sh (new catalog-fetch/jq logic)
  • Review setup.test.sh (fixture-based tests)
  • Review deleted check-plugin-baseline.sh/test and CI job removal
  • Review README changes
  • Check for instruction-surface deletions (removed guardrails)
  • Post findings

Scope: only the 8 files changed in this PR (git diff origin/main...HEAD), per the lane's rules — GitHub Actions hardening (pins, triggers, permissions:) is zizmor's lane, not re-reported here.

IMPORTANT — components/cloud-environment/setup.sh#L51: the fleet install set's trust boundary moves from a reviewed file in this repo (fleet-plugins.json, PR-gated here) to melodic-software/claude-code-plugins's marketplace.json, fetched live from its unpinned main, with the default inverted to opt-out (select(.defaultEnabled != false)). Any entry landed there without defaultEnabled: false now gets auto-installed at user scope (-y, no confirmation) fleet-wide, with no PR to standards at all — see inline comment for the full reasoning and two possible mitigations (explicit opt-in, or pinning the catalog fetch).

SUGGESTION — components/cloud-environment/setup.sh#L129: .defaultEnabled != false fails open on a non-boolean value (e.g. a stringly-typed "false" typo), silently enabling a plugin meant to be off. See inline comment for a stricter jq filter.

Also considered, not reported as findings:

  • Removal of the plugin-seed-drift CI job / check-plugin-baseline.sh (fleet-vs-dotfiles-seed comparator) under the instruction-surface-deletion lens. This repo has no docs/conventions/instruction-exception-register/ checked out, so the protected-class list wasn't consulted directly; by consequence, the removed job was a report/consistency check (keeping the cloud install set and the developer workstation seed from silently diverging so a slash command still resolves), not an access-control or safety gate, so it falls outside this lane's bar.
  • The claude plugin install ... -y auto-confirm and the unauthenticated curl fetch pattern (no checksum/signature) are unchanged from the prior design (the permission-floor fetch uses the same pattern already) — not new in this diff.
  • No injection path found in how catalog-sourced plugin names flow into the generated settings file and then into claude plugin install "$plugin_id" (properly quoted, no secondary shell evaluation); the hardcoded extraKnownMarketplaces source is a literal, not catalog-derived.
    · Branch

Comment thread components/cloud-environment/setup.sh
Comment thread components/cloud-environment/setup.sh Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review


P2 Badge Retain coverage checking for the dotfiles plugin seed

Removing this job eliminates the only check that the separate dotfiles workstation seed names every cloud-default plugin. The new catalog derivation updates cloud snapshots automatically, but it does not update .chezmoidata/claude.json; therefore a newly default-enabled catalog plugin can again be silently absent from local workstations—the exact drift this deleted job documents as having previously lost four IDs. The gate should derive its expected IDs from the catalog rather than being removed with the old static baseline.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread components/cloud-environment/setup.sh Outdated
Comment thread .github/workflows/ci.yml
Enable a catalog entry only when defaultEnabled is absent or true, so a
mistyped "false" leaves the plugin off. A catalog whose entries are all
off now yields an empty fleet list instead of none, so the repo
bootstrap still overlays a repo's opt-ins; a catalog with no entries
still yields no list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 31s —— View job


Code review complete

  • Gather context (PR diff, CLAUDE.md, REVIEW.md)
  • Review full diff origin/main...HEAD, focused on latest commit (9410e6d)
  • Review setup.sh changes
  • Review setup.test.sh changes
  • Review deletions (CI job, baseline script/test)
  • Post findings

Per REVIEW.md, this repo runs a dedicated security lane, so this pass is scoped to correctness/design/maintainability. No new findings against the current head.

Commit 9410e6d resolves both open items from the prior round on this PR, verified by reading the code and independently re-running the jq filter against the fixture cases (not just re-stating the PR's own claims):

  • setup.sh:129-137 — fleet_list_from_catalog now selects .defaultEnabled == null or .defaultEnabled == true instead of != false, so a stringly-typed "false" (or any other non-boolean) fails closed rather than silently enabling. Confirmed directly: {"name":"off-string","defaultEnabled":"false"} is excluded from the derived enabledPlugins, matching the new setup.test.sh fixture.
  • Same function now emits select((.plugins | length) > 0) | {...} instead of gating on enabledPlugins != {}, so an all-off catalog yields a settings-shaped file with an empty enabledPlugins object rather than no file at all. Confirmed cloud-bootstrap.sh's shape check (components/cloud-bootstrap/cloud-bootstrap.sh:249-256) already treats an object with no/empty enabledPlugins as a valid empty source rather than "list absent" — so this change correctly lets a repo's true opt-ins still apply even when every catalog default is off, whereas before, an all-off catalog skipped the entire plugin stage. Only a catalog with zero .plugins entries now yields no file at all, which is the right distinction (unreadable/empty catalog vs. a catalog that's deliberately all-off).
  • setup.test.sh additions correctly exercise all three cases (non-boolean string, all-off-but-present, genuinely empty) and the assertions match the jq output I independently reproduced.

No new correctness/maintainability issues found in this commit. One item from the previous round on this PR remains open and unaddressed by this commit (not re-filed here as it's already an existing inline thread on this PR): the plugin-seed-drift CI job removal still has no replacement guard against the cloud fleet set drifting from the dotfiles workstation seed, and the catalog-derivation model makes that drift possible with zero PRs to this repo. Worth resolving before merge per the earlier thread.

Note: I was unable to run shellcheck/shfmt/the test harness directly in this sandbox (tool permission denied); I independently verified the jq derivation logic by piping the fixture JSON through the exact filter from setup.sh instead.
· Branch

@claude

claude Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 35s —— View job


Security review

  • Gather context (PR diff at HEAD 9410e6d, prior review findings, instruction-exception register)
  • Review setup.sh changes, including the new "fail closed on non-boolean defaultEnabled" fix
  • Review setup.test.sh changes
  • Review deletions (check-plugin-baseline.sh, CI plugin-seed-drift job) under the instruction-surface-deletion lens
  • Review README updates
  • Post findings

Scope: the 8 files changed in this PR, diffed as 8e90407...9410e6d (the new commit since the last review) against the full PR diff origin/main...9410e6d. GitHub Actions hardening (pins, triggers, permissions:) is zizmor's lane, not re-reported here.

What the new commit (9410e6d) changed, security-relevant:

  • fleet_list_from_catalog (setup.sh#L127-L136) switched from a deny-list filter (.defaultEnabled != false) to an allow-list filter (.defaultEnabled == null or .defaultEnabled == true). This fixes the SUGGESTION from the prior review (8e90407): a mistyped non-boolean defaultEnabled (e.g. the string "false") now fails closed — the entry is excluded — instead of silently installing fleet-wide. setup.test.sh adds an off-string fixture asserting exactly this. Confirmed correct.
  • The same commit also changed the empty-catalog case to emit an empty enabledPlugins: {} rather than deleting the output file when every entry is off, so a repo's own opt-ins still have a fleet list to overlay onto. Functional change, not security-relevant on its own.

Carried forward, still unresolved — posted inline on setup.sh:51:

🔴 IMPORTANT — the trust boundary for the cloud fleet's auto-installed plugin set moved from a reviewed file in this repo (fleet-plugins.json, PR-gated here) to melodic-software/claude-code-plugins's marketplace.json, fetched live from its unpinned main, with the default inverted to opt-out (any entry without defaultEnabled: false is now installed). This was flagged against 8e90407 and is untouched by 9410e6d — FLEET_CATALOG_URL and the opt-out model are unchanged. Any entry landed in that other repo's catalog now gets auto-installed at user scope (-y, no confirmation) fleet-wide, with no PR to standards at all. Worth confirming before merge whether claude-code-plugins's review bar for marketplace.json is at least as strict as a fleet-plugins.json PR here was, or mitigating with explicit defaultEnabled: true opt-in or a pinned catalog ref instead of main.

Also considered, not reported as findings (consistent with the prior review of this PR):

  • Removal of the plugin-seed-drift CI job / check-plugin-baseline.sh (fleet-vs-dotfiles-seed comparator). This repo has no docs/conventions/instruction-exception-register/ checked out, so the protected-class list could not be consulted directly; by consequence, the removed job was a report/consistency check (keeping the cloud install set and the developer workstation seed from silently diverging), not an access-control or safety gate, so it falls outside this lane's bar. (It's a legitimate correctness/observability concern — already raised by both the claude code-review bot and chatgpt-codex-connector on this PR — just not a security finding.)
  • claude plugin install ... -y auto-confirm and the unauthenticated curl fetch (no checksum/signature) are unchanged from the prior design pattern already used for the permission-floor fetch — not new in this diff.
  • No injection path in how catalog-sourced plugin names flow into the generated settings file and into claude plugin install "$plugin_id" — properly quoted, no secondary shell evaluation.
    · Branch

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Replies to the review-body findings (Codex review 5396574274 and the claude[bot] summaries):

Finding Classification Evidence
Codex P2: retain coverage checking for the dotfiles plugin seed UNCERTAIN (escalated to the PR owner) This is the same point as the inline thread on .github/workflows/ci.yml. The drift is real: nothing compares the catalog's default-enabled set with the dotfiles seed any more. Whether to add a catalog-derived check, and where, is a cross-repo decision for the owner. The PR stays unmerged until that decision is made.
claude[bot] security: trust boundary / opt-out default INCORRECT See the inline reply. The plugin code already came from the same unpinned marketplace with autoUpdate: true.
claude[bot] security: non-boolean defaultEnabled fails open VALID (fixed in 9410e6d) See the inline reply.
Codex P2: all-off catalog drops repo opt-ins VALID (fixed in 9410e6d) See the inline reply.

Comment thread components/cloud-environment/setup.sh
@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Codex review 5396574274 (retain seed coverage checking): Disposition (PR owner decision): VALID. It will be resolved by removing the second list, not by checking it. A follow-up PR to melodic-software/dotfiles will stop .chezmoidata/claude.json from listing melodic-software plugins. Workstation defaults will then come from the catalog's defaultEnabled, the same way the cloud base does, so there is no second list left to drift. That dotfiles PR is the next step after this merge. Merging #663 with the plugin-seed-drift job deleted.

@kyle-sexton
kyle-sexton merged commit 39aff7c into main Oct 2, 2026
2 checks passed
@kyle-sexton
kyle-sexton deleted the feat/cloud-plugins-from-catalog branch October 2, 2026 21:14
kyle-sexton added a commit to melodic-software/claude-code-plugins that referenced this pull request Oct 2, 2026
…ed fleet list (#5936)

No related issue: melodic-software/standards#663 deleted the fleet list
this gate fetched, so `lint-2` and `ci-status` fail on every PR and on
main.

## Summary

`scripts/check-plugin-catalog-enablement.sh` downloaded
`components/cloud-environment/fleet-plugins.json` from standards.
standards#663 deleted it and now derives the cloud plugin list from this
repo's catalog: every entry whose `defaultEnabled` is absent or `true`.
The download returns 404, and the gate exits 2.

The user chose to drop per-plugin coverage rather than add `true` keys.
`.claude/settings.json` is project scope, so a `true` key would enable
each off-by-default plugin in every local session here: 5 today, about
54 after #5897, including `miro` and `dometrain-mcp`, whose servers need
credentials. With the list derived from the catalog, every catalogued
plugin is either on by default or recorded off there, so none can go
missing unannounced.

## Fix

- `check-plugin-catalog-enablement.sh`: no download and no UNENABLED
check. It keeps the three checks that still guard something: a key
naming no catalogued plugin (it silently no-ops), keys out of byte
order, and `cloud-bootstrap.sh`'s `marketplace_name` matching the
declared marketplace.
- Its test drops the fleet-list and download cases and adds "catalogued
plugins with no key pass".
- `ci.yml`: step name and comment describe the new check.
- `docs/cloud-sessions.md`: states plainly that a plugin marked
`defaultEnabled: false` is not installed in this repo's cloud sessions
unless the settings block opts it in, and that a `true` key also enables
it locally.

## Verification

- Gate on this tree: exit 0, "Every enabledPlugins key for
'melodic-software' names one of the 84 catalogued plugins; none
orphaned; keys sorted".
- `check-plugin-catalog-enablement.test.sh`: PASS=13 FAIL=0.
- `shellcheck`, `shfmt -d`, `actionlint`, and `markdownlint-cli2` on the
changed files: clean.

## Related

- melodic-software/standards#663 (the change that deleted the list)
- #5897 needs no settings keys under this gate. Its body says the gate
needs no change; that no longer holds once this merges.
- #5933 took the other approach (adding `true` keys) and is closed with
`do-not-merge`.
- Unblocks #5918, #5921, #5923 and #5935.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

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