Releases go through a soak: we snapshot main into a release branch, validate that
branch, and only then cut a stable release — all without blocking work on main. Automated
with release-please.
- Cut — Actions ▸ Cut release branch ▸ Run workflow. This creates
release/<date>frommainand opens a release PR on it. That branch is now frozen: nothing merged tomainafterwards affects it. - Soak — deploy/validate the
release/<date>branch. Meanwhilemainstays open for everyone else. - Patch — found a problem? Land the fix on
mainfirst (a normalfix:PR), then cherry-pick it onto the release branch via a backport PR. Backports must be PRs (the release branches enforce it), so the fix shows up in the release notes. - Publish — when soak passes, merge the release PR. That cuts the
vX.Y.Ztag and a GitHub Release with notes grouped by type. Then squash-merge the auto-opened "merge back into main" PR — do this promptly; it's what advancesmainto the released version. Merging it also deletes the release branch (the tag is the permanent snapshot).
- Title PRs with Conventional Commits —
feat:,fix:,docs:, … A check enforces it and adds a matchingtype:label.feat→ minor,fix→ patch;feat!:/BREAKING CHANGE:→ minor (pre-1.0). - Everything is squash-merged, so the PR title becomes the commit release-please reads.
After a release, every PR it included is labeled released: X.Y.Z with a comment linking
the release, and is listed under its type (Features, Bug Fixes, …) in the release notes.
The release branch is gone after merge-back, but the tag is the exact snapshot. To patch a
shipped version: Cut release branch with from: vX.Y.Z to recreate it, then open a backport
PR onto the recreated branch for the X.Y.Z+1 patch — nothing from main comes along.
A fix you backport appears in both the release it shipped in and the next release's notes — it's two commits (the
mainoriginal and the backport). That double-listing is expected.