chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:20:01 +08:00
commit e65605f012
669 changed files with 128771 additions and 0 deletions
@@ -0,0 +1,158 @@
---
title: "Manual release-please with GitHub Releases for plugin and marketplace releases"
category: workflow
date: 2026-03-17
last_refreshed: 2026-06-23
created: 2026-03-17
severity: process
component: release-automation
tags:
- release-please
- github-releases
- marketplace
- plugin-versioning
- ci
- automation
- release-process
---
# Manual release-please with GitHub Releases for 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 package metadata, plugin manifests, marketplace catalogs, and release-note surfaces. That made it easy for plugin manifests, marketplace metadata, and computed counts to drift out of sync.
## Solution
Use release-please manifest mode with one standing release PR and explicit component ownership.
Current components:
- `compound-engineering` package/plugin at root package path `.`
- `marketplace` for `.claude-plugin/marketplace.json`
- `cursor-marketplace` for `.cursor-plugin/marketplace.json`
The root `compound-engineering` package is now the plugin package. It owns the CLI/tooling code, root plugin manifests, native harness metadata, and `skills/`.
Key decisions:
- 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.
- Keep PR title scopes optional; use file paths to determine affected components.
- Keep `AGENTS.md` canonical and `CLAUDE.md`/`GEMINI.md` as compatibility shims.
## Critical constraint discovered
Release-please does not allow package changelog paths that traverse upward with `..`. A multi-component repo cannot force subpackage release entries back into one shared root changelog file using `../../CHANGELOG.md` or `../CHANGELOG.md`.
The practical fix:
- Treat GitHub Releases as the canonical release-notes surface.
- Keep root `CHANGELOG.md` as a pointer to GitHub Releases.
- Validate `.github/release-please-config.json` in CI so unsupported changelog paths fail before the workflow reaches GitHub Actions.
## Resulting release process
1. Normal feature PRs merge to `main`.
2. The `Release PR` workflow updates one standing release PR for the repo.
3. Additional releasable merges accumulate into that release PR.
4. Maintainers can inspect the standing release PR or run the manual preview flow.
5. The actual release happens only when the generated release PR is merged.
6. Component-specific release notes are published via GitHub Releases such as `compound-engineering-vX.Y.Z`, `marketplace-vX.Y.Z`, and `cursor-marketplace-vX.Y.Z`.
## Component rules
PR title determines release intent:
- `feat` -> minor
- `fix`, `perf`, `revert` -> patch
- `refactor` -> visible in release notes under `Refactoring`, but not release-driving unless breaking or explicitly overridden
- `!` -> major; do not use without explicit maintainer confirmation
File paths determine component ownership:
| Component | Paths |
|---|---|
| `compound-engineering` | `skills/`, `src/`, `tests/`, `package.json`, root plugin manifests, `.opencode/`, `.pi/`, `.agy/plugin.json`, `README.md`, instruction shims |
| `marketplace` | `.claude-plugin/marketplace.json` |
| `cursor-marketplace` | `.cursor-plugin/marketplace.json` |
Docs-only, CI-only, and build-only changes are non-releasable unless their conventional type says otherwise and a releasable component path changed.
## Examples
### Plugin-only release
- A `fix:` PR changes `skills/ce-plan/SKILL.md`
- `compound-engineering` bumps
- marketplace versions remain untouched
### Root packaging release
- A `fix:` PR changes `.codex-plugin/plugin.json` or `.agy/plugin.json`
- `compound-engineering` bumps because those files are root package/plugin extra-files
- `bun run release:validate` must pass so all root package/plugin versions remain aligned
### Marketplace-only release
- A marketplace catalog entry changes in `.claude-plugin/marketplace.json`
- `marketplace` bumps
- plugin versions do not need to bump just because the catalog changed
## Release notes model
- Pending release state is visible in one standing release PR.
- Published release history is canonical in GitHub Releases.
- Root `CHANGELOG.md` is 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.yml`
- `src/release/components.ts`
- `src/release/metadata.ts`
- `scripts/release/preview.ts`
- `scripts/release/sync-metadata.ts`
- `scripts/release/validate.ts`
- `AGENTS.md`
- `CLAUDE.md`
- `GEMINI.md`
## Prevention
- Keep release authority in CI only.
- Do not reintroduce local maintainer-only release flows or hand-managed version bumps.
- Keep root package/plugin manifests aligned through release-please extra-files, not manual edits.
- Do not try to force multi-component release notes back into one committed changelog file.
- Run `bun run release:validate` whenever plugin inventories, release-owned descriptions, marketplace entries, or root plugin manifests 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 when release-component selection is non-obvious.
Before merging a generated release PR:
- Verify untouched components are unchanged.
- Verify marketplace components only bump for marketplace-level changes.
- Verify root package/plugin extra-files share the same version.
After merging a generated release PR:
- Confirm no recursive follow-up release PR appears containing only generated churn.
- Confirm the expected component GitHub Releases were created and release-owned metadata matches the released components.
## Related docs
- `docs/solutions/plugin-versioning-requirements.md`
- `docs/solutions/adding-converter-target-providers.md`
- `AGENTS.md`
@@ -0,0 +1,153 @@
---
title: "Release-please version drift recovery"
category: workflow
date: 2026-04-24
last_refreshed: 2026-06-20
created: 2026-04-24
severity: high
component: release-automation
problem_type: workflow_issue
tags:
- release-please
- version-drift
- plugin-versioning
- recovery-playbook
- extra-files
---
# Release-please version drift recovery
## Problem
Manual edits to a release-managed version field cause drift that:
- Breaks `bun run release:validate` on PR CI.
- Can cause version regression on the next release-please run if left uncorrected.
- Is easy to introduce accidentally during a feature commit.
- Has multiple recovery paths with different user-impact trade-offs.
This doc is the playbook when drift is detected. It exists because investigating from scratch takes significant effort and the wrong choice can make things worse.
## File relationship map
The current root-native repo has three release components. Release-please reads `.github/.release-please-manifest.json` and writes each package's configured `extra-files`.
```text
.github/.release-please-manifest.json
├── "." -> compound-engineering package/plugin (v = X.Y.Z)
├── ".claude-plugin" -> Claude marketplace (v = M.N.O)
└── ".cursor-plugin" -> Cursor marketplace (v = P.Q.R)
.github/release-please-config.json
└── packages
├── "." extra-files
│ ├── package.json
│ ├── .claude-plugin/plugin.json
│ ├── .cursor-plugin/plugin.json
│ ├── .codex-plugin/plugin.json
│ └── gemini-extension.json
├── ".claude-plugin" extra-files
│ └── marketplace.json ($.metadata.version)
└── ".cursor-plugin" extra-files
└── marketplace.json ($.metadata.version)
```
Key invariants:
- Every extra-file inside the root `.` component must share the same plugin version.
- Marketplace components are independent; their metadata versions do not move with every plugin release.
- `bun run release:validate` enforces root package/plugin version parity, marketplace parity, Codex manifest shape, and description sync.
- The repo no longer has separate `cli`, `plugins/compound-engineering`, or `coding-tutor` release components.
## How release-please tracks versions
Release-please treats the manifest as the source of truth for "last released version per component." Extra-files are outputs that release-please writes during a release PR. Under normal operation, humans do not hand-edit the manifest or extra-files. Drift is the state where that guarantee has been violated.
## Drift detection
`bun run release:validate` runs on PRs and pushes to `main`. It fails when:
- Root package/plugin versions disagree across `package.json`, `.claude-plugin/plugin.json`, `.cursor-plugin/plugin.json`, `.codex-plugin/plugin.json`, and `gemini-extension.json`.
- Marketplace plugin lists diverge across Claude, Cursor, and Codex marketplace metadata.
- A Codex manifest is missing required fields or points at a missing `skills/` directory.
- Release-owned descriptions drift across plugin manifests or marketplace entries.
- `release-as` pins become stale relative to the base-branch manifest.
Important: a state where all extra-files agree at X.Y.Z but the manifest still says W.X.Y can pass some local checks and still cause the next release PR to regress versions. Check the manifest when investigating drift.
## Recovery decision tree
```text
release:validate reports drift
|
v
1. Identify the affected component:
- "." root package/plugin
- ".claude-plugin" marketplace
- ".cursor-plugin" marketplace
|
v
2. Compare:
- extra-files vs each other within that component
- extra-files vs .github/.release-please-manifest.json
- any active release-as pins in .github/release-please-config.json
|
v
3. Is anyone installed at the drifted higher version?
- Yes or unknown -> forward-sync
- Verified no -> backward-revert
```
### Path A: Forward-sync
Use when any user may have installed the drifted version locally. For the root `.` component, update every root extra-file to the drifted higher version:
- `package.json`
- `.claude-plugin/plugin.json`
- `.cursor-plugin/plugin.json`
- `.codex-plugin/plugin.json`
- `gemini-extension.json`
- `.github/.release-please-manifest.json` entry for `.`
For marketplace drift, sync the affected marketplace `marketplace.json` metadata version and matching manifest entry.
Why the manifest edit is necessary: without it, the next release-please run reads the stale last-released value and may write a lower next version to extra-files, regressing users at the forward-synced version.
### Path B: Backward-revert
Use only when you can verify no user is installed at the drifted version. Revert the drifted extra-file(s) down to the manifest value, leaving the manifest unchanged.
This is fewer files, but it risks user regression if verification was wrong. Default to Path A when in doubt.
### Path C: `release-as` pin
Use when you want release-please itself to drive the recovery via a normal release PR. Forward-sync extra-files up to the drifted version, add a temporary `"release-as": "<drifted+1>"` pin for the affected package, and let the release PR bump above the drifted value.
This has cleanup overhead: remove the pin after the release PR lands. Prefer Path A unless there is a specific reason the release PR should own the bump.
## Summary
| Path | Files changed | When to use | Risk |
|---|---|---|---|
| A -- forward-sync | Extra-files + manifest | Anyone might be at drifted version | Low if executed completely |
| B -- backward-revert | Drifted extra-file(s) only | Verified no one has drifted version | User regression if wrong |
| C -- `release-as` pin | Extra-files + config pin + later cleanup | Want release-please to drive recovery | Stale pin risk |
## Prevention
Direct-to-main merges are the root cause. They bypass PR CI, release validation, tests, and semantic title checks.
Branch protection on `main` is the enforcement. The `test` status check must be required before merge, and admin bypass should be reserved for true emergencies.
Optional guards:
- Dedicated CI detecting manual version bumps by non-release PRs.
- A pre-commit or pre-push hook running `bun run release:validate`.
## Related docs
- `docs/solutions/workflow/manual-release-please-github-releases.md` -- big-picture release model.
- `docs/solutions/plugin-versioning-requirements.md` -- plugin-scoped contributor rules.
- `AGENTS.md` -- repo-level release versioning rules.
- `.github/release-please-config.json` -- package and extra-file configuration.
- `src/release/metadata.ts` -- metadata sync and validation implementation.
@@ -0,0 +1,98 @@
---
title: "Stale local base contamination in multi-session branch creation"
category: workflow
date: 2026-04-27
created: 2026-04-27
severity: medium
component: ce-commit-push-pr
problem_type: workflow_issue
tags:
- branching
- multi-agent
- multi-session
- pre-push
- stacked-prs
- contamination
---
# Stale local base contamination in multi-session branch creation
## Problem
When multiple agent sessions (Claude Code, Cursor, Codex, plus any humans) share one local clone, local `<default-branch>` can drift relative to its remote counterpart. Two specific drifts cause downstream pain:
1. **Local default behind remote.** Another session pushed and merged work; this session's local `main` doesn't know yet.
2. **Local default ahead of remote with unpushed work.** Another session committed locally to `main`, or merged a feature branch into local `main`, before pushing — and never pushed those commits to `origin/main`.
When a session creates a feature branch from local `main` while drift type 2 holds, the new branch silently inherits the unpushed work. The eventual PR opens looking clean to the originating session but appears contaminated on GitHub. Resolving it requires force-push surgery during PR review.
This came in as [issue #707](https://github.com/EveryInc/compound-engineering-plugin/issues/707).
## Why post-facto detection is the wrong tool
The intuitive fix is to detect the contamination before pushing or before opening a PR. Two detection approaches were considered and rejected:
### Approach A: surface foreign commit authors
Read `git log <base>..HEAD --pretty=format:'%h %ae %s'` and warn when any commit's author email differs from `git config user.email`.
Catches the cross-author case (cherry-picks, teammate-authored work) but misses the dominant scenario: multi-agent setups where every session uses the same `user.email`. The check fires on intentional cherry-picks and stays silent on the actual contamination pattern.
### Approach B: cross-branch reachability
For each commit in `<base>..HEAD`, check whether it is reachable from any other `origin/*` ref. If yes, treat as suspect.
Authorship-agnostic, so it catches same-user contamination. But the signal it measures — "this commit is on another remote branch" — is the **defining characteristic** of stacked-PR workflows, where parent commits in the stack are intentionally shared with sibling branches. Tools like Graphite and git-spice rely on this. With GitHub-native stacked PRs moving toward general availability and likely broad adoption, the false-positive rate moves from "narrow population" to "majority of pushes for sophisticated users." The check would invert from useful signal to default noise.
You can patch around it (parse stack metadata from PR base refs) but the patches multiply with every adjacent workflow (first push before PR exists, multi-level stacks, fork-based stacks). Each patch is a heuristic that will be wrong somewhere.
## Solution
Prevent at branch creation rather than detect at push or PR time.
`ce-commit-push-pr` Step 4 — the branch-creation path used when the user invokes the skill while on the default branch with working-tree changes — was changed from:
```bash
git checkout -b <branch-name>
```
to:
```bash
git fetch --no-tags origin <base>
git checkout -b <branch-name> origin/<base>
```
with a graceful fallback to the local-base form when the fetch fails (offline, restricted network, expired auth). The fallback is documented to the user so they know base freshness was not verified.
This makes the skill's branch-creation path safe by construction:
- Drift type 1 (local behind remote): the new branch starts at fresh remote `<base>`, not stale local `<base>`.
- Drift type 2 (local ahead of remote with unpushed work): unpushed local commits stay on local `<base>` (recoverable via reflog or branch ref); the new feature branch starts clean.
The principle generalizes cleanly to stacked PRs: when a user wants to stack on top of an open PR, the same `git fetch && git checkout -b <name> origin/<parent>` pattern works — `<parent>` is just a different ref. Nothing about prevention depends on detecting "is this commit suspicious."
## What this does not cover
- **Branches created outside the skill.** Users who run `git checkout -b` manually, or whose IDE creates branches without fetching, can still produce contaminated branches. The skill's path becomes safe; the user's general workflow is not. A pre-push hook (which the original reporter installed) covers this case — opt-in hooks remain a reasonable user-side mitigation.
- **Already-contaminated branches.** Once a branch carries foreign commits, this change does nothing for it. Recovery is still manual: identify the foreign commits, drop them via interactive rebase or `git reset` to a clean base, force-push.
- **Step 1 branch-creation paths with different semantics.** When the user is on the default branch with unpushed commits and asks to create a feature branch to "rescue" those commits, the desired behavior is to carry the local commits onto the new branch — opposite of the Step 4 case. Step 1's behavior is unchanged.
## User-side mitigations
For workflows where branch creation happens outside the skill, recommend:
- `git switch -c <name> origin/<base>` instead of `git checkout -b <name> <base>`
- A `git config --global alias.nb '!f() { git fetch origin "${2:-main}" && git switch -c "$1" "origin/${2:-main}"; }; f'` style alias
- An opt-in pre-push hook that compares HEAD's parent chain against `origin/<base>` for unexpected commits — useful for individual users, but not shipped from this plugin because the cost of getting stacked-PR semantics right in a hook outweighs the benefit at the plugin level
## Why we did not ship a detection check at all
The reporter framed their issue as "a pattern, not a request for a merge." Taking that at its word and acting on the structural signal — a real failure mode worth a permanent fix — produced this outcome:
- One small preventive change in the skill that is safe by construction
- A documented pattern with rationale for future readers
- No behavioral prompt added to a heavy-traffic skill
- No detection heuristic that risks being obsoleted by stacked PRs
A detection check at push or PR time was not free even when scoped tightly: it adds a prompt to a frictionless workflow, false-positives on legitimate workflows that share commits across branches, and would require ongoing tuning as stacked-PR conventions evolve. Prevention at the right layer avoids all of that.