8.2 KiB
title, category, date, created, severity, component, tags
| title | category | date | created | severity | component | tags | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Manual release-please with GitHub Releases for multi-component plugin and marketplace releases | workflow | 2026-03-17 | 2026-03-17 | process | release-automation |
|
Manual release-please with GitHub Releases for multi-component plugin and marketplace releases
Problem
The repo had one automated release path for the npm CLI, but the actual release model was fragmented across:
- root-only
semantic-release - a local maintainer workflow via
release-docs - multiple version-bearing metadata files
- inconsistent release-note ownership
That made it hard to batch merges on main, hard for multiple maintainers to share release responsibility, and easy for release notes, plugin manifests, marketplace metadata, and computed counts to drift out of sync.
Root Cause
Release intent, component ownership, release-note ownership, and metadata synchronization were split across different systems:
- PRs merged to
mainwere too close to an actual publish event - only the root CLI had a real CI-owned release path
- plugin and marketplace releases depended on local knowledge and stale docs
- the repo had multiple release surfaces (
cli,compound-engineering,coding-tutor,marketplace) but no single release authority
An adjacent contributor-guidance problem made this worse: root CLAUDE.md had become a large, stale, partially duplicated instruction file, while AGENTS.md was the better canonical repo guidance surface.
Solution
Move the repo to a manual release-please model with one standing release PR and explicit component ownership.
Key decisions:
- Use
release-pleasemanifest mode for five release components:clicompound-engineeringcoding-tutormarketplace(Claude marketplace,.claude-plugin/)cursor-marketplace(Cursor marketplace,.cursor-plugin/)
- Keep release timing manual: the actual release happens when the generated release PR is merged.
- Keep release PR maintenance automatic on pushes to
main. - Use GitHub release PRs and GitHub Releases as the canonical release-notes surface for new releases.
- Replace
release-docswith repo-owned scripts for preview, metadata sync, and validation. - Keep PR title scopes optional; use file paths to determine affected components.
- Make
AGENTS.mdcanonical and reduce rootCLAUDE.mdto a compatibility shim.
Critical Constraint Discovered
release-please does not allow package changelog paths that traverse upward with ...
The failed first live run exposed this directly:
release-please failed: illegal pathing characters in path: plugins/compound-engineering/../../CHANGELOG.md
That means a multi-component repo cannot force subpackage release entries back into one shared root changelog file using changelog-path values like:
../../CHANGELOG.md../CHANGELOG.md
The practical fix was:
- set
skip-changelog: truefor all components in.github/release-please-config.json - treat GitHub Releases as the canonical release-notes surface
- reduce
CHANGELOG.mdto a simple pointer file - add repo validation to catch illegal upward changelog paths before merge
Resulting Release Process
After the migration:
- Normal feature PRs merge to
main. - The
Release PRworkflow updates one standing release PR for the repo. - Additional releasable merges accumulate into that same release PR.
- Maintainers can inspect the standing release PR or run the manual preview flow.
- The actual release happens only when the generated release PR is merged.
- npm publish runs only when the
clicomponent is part of that release. - Component-specific release notes are published via GitHub releases such as
cli-vX.Y.Zandcompound-engineering-vX.Y.Z.
Component Rules
- PR title determines release intent:
feat=> minorfix/perf/refactor/revert=> patch!=> major
- File paths determine component ownership:
src/**,package.json,bun.lock,tests/cli.test.ts=>cliplugins/compound-engineering/**=>compound-engineeringplugins/coding-tutor/**=>coding-tutor.claude-plugin/marketplace.json=>marketplace.cursor-plugin/marketplace.json=>cursor-marketplace
- Optional title scopes are advisory only.
This keeps titles simple while still letting the release system decide the correct component bump.
Examples
One merge lands, but no release is cut yet
- A
fix:PR merges tomain - The standing release PR updates
- Nothing is published yet
More work lands before release
- A later
feat:PR merges tomain - The same open release PR updates to include both changes
- The pending bump can increase based on total unreleased work
Plugin-only release
- A change lands only under
plugins/coding-tutor/** - Only
coding-tutorshould bump compound-engineering,marketplace, andclishould remain untouched- npm publish should not run unless
cliis also part of that release
Marketplace-only release
- A new plugin is added to the catalog or marketplace metadata changes
marketplacebumps- Existing plugin versions do not need to bump just because the catalog changed
Exceptional manual bump
- Maintainers decide the inferred bump is too small
- They use the preview/release override path instead of making fake commits
- The release still goes through the same CI-owned process
Release Notes Model
- Pending release state is visible in one standing release PR.
- Published release history is canonical in GitHub Releases.
- Component identity is carried by component-specific tags such as:
cli-vX.Y.Zcompound-engineering-vX.Y.Zcoding-tutor-vX.Y.Zmarketplace-vX.Y.Zcursor-marketplace-vX.Y.Z
- Root
CHANGELOG.mdis only a pointer to GitHub Releases and is not the canonical source for new releases.
Key Files
.github/release-please-config.json.github/.release-please-manifest.json.github/workflows/release-pr.yml.github/workflows/release-preview.yml.github/workflows/ci.ymlsrc/release/components.tssrc/release/metadata.tsscripts/release/preview.tsscripts/release/sync-metadata.tsscripts/release/validate.tsAGENTS.mdCLAUDE.md
Prevention
- Keep release authority in CI only.
- Do not reintroduce local maintainer-only release flows or hand-managed version bumps.
- Keep
AGENTS.mdcanonical. If a tool still needsCLAUDE.md, use it only as a compatibility shim. - Do not try to force multi-component release notes back into one committed changelog file if the tool does not support it natively.
- Validate
.github/release-please-config.jsonin CI so unsupported changelog-path values fail before the workflow reaches GitHub Actions. - Run
bun run release:validatewhenever plugin inventories, release-owned descriptions, or marketplace entries may have changed. - Prefer maintained CI actions over custom validation when a generic concern does not need repo-specific logic.
Validation Checklist
Before merge:
- Confirm PR title passes semantic validation.
- Run
bun test. - Run
bun run release:validate. - Run
bun run release:preview ...for representative changed files.
After merging release-system changes to main:
- Verify exactly one standing release PR is created or updated.
- Confirm ordinary merges to
maindo not publish npm directly. - Inspect the release PR for correct component selection, versions, and metadata updates.
Before merging a generated release PR:
- Verify untouched components are unchanged.
- Verify
marketplaceonly bumps for marketplace-level changes. - Verify plugin-only changes do not imply
cliunlesssrc/also changed.
After merging a generated release PR:
- Confirm npm publish runs only when
cliis part of the release. - Confirm no recursive follow-up release PR appears containing only generated churn.
- Confirm the expected component GitHub releases were created and that release-owned metadata matches the released components.
Related Docs
docs/solutions/plugin-versioning-requirements.mddocs/solutions/adding-converter-target-providers.mdAGENTS.mdplugins/compound-engineering/AGENTS.mddocs/specs/kiro.md