chore(release): version each plugin from its own commits - #481
Merged
Merged
Conversation
release-please tracked one package at the repo root and stamped that one version into 17 plugin.json files. Any commit anywhere therefore moved every plugin's version, and a plugin's cache directory is named cache/<marketplace>/<plugin>/<version> — so a plugin whose content was byte-identical still got a new directory on every release. Sessions already running resolve plugin paths at startup and re-stat CLAUDE_PLUGIN_ROOT per hook invocation, so that churn broke them. Three plugins were absent from extra-files and never moved (ai-cli-harness-better-practices, homelab-ops, openbao), which is what identified the mechanism. Each plugin directory is now its own release-please package with its own component, so its version tracks only its own commits. Shared settings move to root-level defaults rather than being repeated per package. separate-pull-requests stays false, so this still produces a single aggregated release PR and the org workflow's auto-merge step, which reads one PR, is unaffected. The caller now passes include-component-in-tag so components tag as <plugin>-v<version>; the root package keeps v<version>. Each component stamps its own plugin.json, plus its own entry in the root marketplace.json via a filtered JSONPath. That also fixes a pre-existing inconsistency: marketplace.json listed all three hand-versioned plugins at 5.11.0 while their plugin.json did not. Verified: config validates against release-please's published schema; all 41 extra-file targets resolve to real files under release-please's own addPath rules (a leading slash is repo-root-relative), and the filtered JSONPath matches exactly one plugin entry and leaves the rest untouched. Assisted-by: Claude:claude-opus-5 Claude-Session: https://claude.ai/code/session_011KxaQ6Hu73ymuCLtGFJyD7
release-please tracked one package at the repo root and stamped that one version into the plugin.json of most plugins. Any commit anywhere therefore moved every plugin's version, and a plugin's cache directory is named cache/<marketplace>/<plugin>/<version> — so a plugin whose content was byte-identical still got a new directory on every release. Sessions already running resolve plugin paths at startup and re-stat CLAUDE_PLUGIN_ROOT per hook invocation, so that churn broke them. The plugins absent from extra-files never moved (ai-cli-harness-better-practices, homelab-ops, openbao, slack), which is what identified the mechanism. Each plugin directory is now its own release-please package with its own component, so its version tracks only its own commits. Shared settings move to root-level defaults rather than being repeated per package. separate-pull-requests stays false, so this still produces a single aggregated release PR and the org workflow's auto-merge step, which reads one PR, is unaffected. The caller now passes include-component-in-tag so components tag as <plugin>-v<version>; the root package keeps v<version>. Each component stamps its own plugin.json, plus its own entry in the root marketplace.json via a filtered JSONPath. That also fixes a pre-existing inconsistency: marketplace.json listed every hand-versioned plugin at the repo-wide version while its plugin.json did not. Verified: config validates against release-please's published schema; all 43 extra-file targets resolve to real files under release-please's own addPath rules (a leading slash is repo-root-relative), and the filtered JSONPath matches exactly one plugin entry and leaves the rest untouched. Assisted-by: Claude:claude-opus-5 Claude-Session: https://claude.ai/code/session_011KxaQ6Hu73ymuCLtGFJyD7
The first attempt was built against 5.11.0. main has since cut 5.12.0 and 5.13.0 and gained a 21st plugin (slack), so the generated config and manifest are regenerated from the current plugin.json versions rather than conflict-resolved. Content is the regeneration; this merge only records the superseded tip.
Replaces the single repo-wide release-please package with one component per plugin directory, so a plugin's version tracks only its own commits. - Each plugin package stamps its own plugin.json and its own entry in the root marketplace manifest, replacing the wildcard that stamped every entry. - The root package keeps the repo-wide version and the manifest metadata version. - Corrects the marketplace entries for the plugins that were absent from the old extra-files list and so carried the repo-wide version rather than their own. - Tags carry the component name. Assisted-by: Claude:claude-opus-5 Claude-Session: https://claude.ai/code/session_011KxaQ6Hu73ymuCLtGFJyD7
The previous generation was made against an older main. This regenerates it against current main; the superseded tip is recorded as a parent so the push is a fast-forward.
Without an anchor, each new component walks its whole history and re-reads breaking markers from months ago. A dry run against this branch proposed seven unintended major bumps on the first release. last-release-sha starts every component from the tree whose versions the manifest already records, so a plugin bumps only on commits that land after this change. Assisted-by: Claude:claude-opus-5 Claude-Session: https://claude.ai/code/session_011KxaQ6Hu73ymuCLtGFJyD7
Manifest and marketplace entries take each plugin's current version from its own plugin.json on main; last-release-sha moves to the current main head. Assisted-by: Claude:claude-opus-5 Claude-Session: https://claude.ai/code/session_01U32ZwmRFZtdzUH5LZBqR3Q
Assisted-by: Claude:claude-opus-5 Claude-Session: https://claude.ai/code/session_01U32ZwmRFZtdzUH5LZBqR3Q
JacobPEvans-personal
deleted the
chore/per-plugin-release-components
branch
September 12, 2026 15:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
release-please tracked a single package at the repo root and stamped that one version into the
plugin.jsonof most plugins viaextra-files. Any commit anywhere in the repo therefore moved every plugin's version.A plugin's installed cache directory is named
cache/<marketplace>/<plugin>/<version>, so a plugin whose content was byte-identical still got a brand-new directory on every release. Sessions already running resolve plugin paths at startup and re-stat${CLAUDE_PLUGIN_ROOT}on every hook invocation, so that churn broke them:The plugins absent from
extra-filesnever moved —ai-cli-harness-better-practices(4.24.0),homelab-ops(1.0.0),openbao(4.21.0),slack(5.10.1) — while the repo went 5.11.0 → 5.13.0. That control group is what identified the mechanism.Change
Each of the 21 plugin directories becomes its own release-please package with its own
component, so its version tracks only its own commits.release-type,include-v-in-tag,changelog-sections) move to root-level defaults instead of being repeated per package — the config ends up smaller than before, not 21x larger.separate-pull-requestsstaysfalse, so this still produces one aggregated release PR. The org workflow's auto-merge step reads a singleproutput and is unaffected.include-component-in-tag: true, so plugins tag as<plugin>-v<version>while the root package keepsv<version>.plugin.jsonplus its own entry in the root.claude-plugin/marketplace.json, addressed by a filtered JSONPath.Also corrects a pre-existing inconsistency:
marketplace.jsonlisted every hand-versioned plugin at the repo-wide version rather than its real one.Merge with main (2026-09-12)
Main released 6.2.1 while this PR was open. The manifest and
marketplace.jsonnow record each plugin at the version its ownplugin.jsoncarries onmain(6.2.1, with the hand-versioned plugins at 4.25.0 / 1.0.0 / 4.21.0), andlast-release-shais re-anchored to the currentmainhead, so the first per-component run bumps nothing that has not changed since 6.2.1.Verification done
extra-filetargets resolve to real files under release-please's ownaddPathrules — a leading/is repo-root-relative, and none trip its illegal-pathing check.marketplace.jsonwith the samejsonpath-plusversion release-please bundles: it matches exactly one plugin entry and leaves the others untouched.Verification that CI cannot do
CI does not run release-please, so the first run on
mainafter this merges is the real test. What to check on that run:<plugin>-v<version>, with the root stayingv<version>.Reverting is the config + manifest + workflow input; any component tags created would need deleting separately.
🤖 Generated with Claude Code
https://claude.ai/code/session_011KxaQ6Hu73ymuCLtGFJyD7