You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
No hotfix path — once main moves to v12-prep, there's no clean way to ship a v11 patch. The only escape hatch is reverting main to a v11 base, fixing, releasing, reapplying v12 work — a non-starter.
Proposal
Branching model
main — integration trunk. All PRs merge here. Merging does not publish to public channels.
release/vN — release channel for major N (e.g. release/v11). Cut from main when major N goes live; N.x.y patches release from this branch.
release/vN.M — optional minor channel, cut from release/vN when a minor line needs independent patching. Deferred until demand-driven.
Old release branches stay alive as long as we support that major, then get archived (not deleted).
Release trigger
Tag-triggered, on a release branch. Pushing a vX.Y.Z tag at a commit in release/vX (or release/vX.Y) fires the publish workflow.
Merging to release/v11 only stages commits; nothing publishes.
The maintainer cuts the tag when ready — this is the quality gate.
gh release create vX.Y.Z --target release/vX --generate-notes makes the tag and release in one step.
Hotfix flow
Always cherry-pick from main. Fix lands on main first via normal PR, then:
Forward-compat is guaranteed: any fix in release/v11 is also in main (and in release/v12 once cut). Exceptions (security incident, prod-down with main on a divergent v12 architecture) need a hotfix-direct label and maintainer sign-off — not the default.
Multi-channel CD via GitHub Environments
Three environments keyed by channel, not by major:
One tag-push workflow → three deployment records (one per environment) → three publish steps. Per-major granularity lives in the deployment ref (the tag), so no v11-nuget-org / v12-nuget-org fan-out. Failed publishes retry independently per environment.
Versioning (Nerdbank.GitVersioning)
version.json lives on each branch:
main carries the next major in development (e.g. 12.0 once v11 ships).
release/v11 stays at 11.0. Patch height is computed within the branch — cherry-picks add to the height.
When v12 ships:
Tag the last v11 release from release/v11.
Cut release/v12 from main.
Bump main's version.json to 13.0.
This is the .NET runtime's pattern (release/8.0, release/9.0, main = next).
Documentation deliverables
CONTRIBUTING.md — contributor flow: where to branch from, where PRs merge, how releases happen.
docs/agents/release-and-versioning.md — extend with the new model. PR-creation flow stays (PRs still target main); release-pipeline section gets rewritten.
New docs/branching-and-release.md (or absorbed above) — full maintainer reference for cutting releases.
Open implementation questions
1. When do we cut release/v11? Chris confirmed: now, off current main. But main has unreleased v11 breaking-change work in flight (the [Unreleased] — 11.0 CHANGELOG section is substantial):
1a. Cut release/v11 now; treat unreleased v11 work on main as the v11 line until v12-prep. main and release/v11 stay roughly in sync until then.
1b. Finish v11 first (release v11.0.0 from main under the current model), cut release/v11 after the tag, then flip main to v12-prep.
Leaning 1b — avoids mid-major model switches; gives one clean "v11.0.0 ships from main; v11.0.1+ ships from release/v11" transition.
2. Branch protection on release/vN — required: linear history, status checks (ubuntu-latest), CODEOWNER review. Forbidden: direct pushes by anyone but the release tagger (or restrict tag creation via tag protection). Cherry-picks land via PR, not direct push.
3. gh release --generate-notes template — move to auto-generated notes from PR titles + labels. Needs .github/release.yml (don't have it yet — see #263). Resolving #263 unblocks part of this.
4. [GitHubActions] generator implications — should the consumer-facing generator output reflect a similar release-branch model? Out of scope here; file a follow-up.
5. Backfill / migration — existing 11.0.x tags stay as-is. The new model takes effect from the first tag after cut. No retroactive renaming.
Work breakdown (filed as sub-issues once RFC settles)
Branching policy + branch protection on release/vN (settings + docs)
Cut release/v11 at the agreed point
Refactor .github/workflows/release.yml from push: main to push: tags: v* on release branches
Create GitHub Environments (nuget-org with approval, github-packages, github-releases)
Wire NUGET_API_KEY to the nuget-org environment (currently a repo secret)
Validate Nerdbank.GitVersioning on release branches (commit-height boundary at branch point)
Document in CONTRIBUTING.md
Document in docs/agents/release-and-versioning.md and/or new docs/branching-and-release.md
Design RFC for Milestone #13.
Problem
The CD pipeline auto-releases on every push to
main: Nerdbank.GitVersioning bumps the patch, the tag fires, publish-to-NuGet runs. Two problems:vN.0.Xrelease. The license-header strip (chore: strip per-file license headers; LICENSE is single source of truth #260) alone bumped11.0.12→11.0.13for zero consumer-facing change.mainmoves to v12-prep, there's no clean way to ship a v11 patch. The only escape hatch is revertingmainto a v11 base, fixing, releasing, reapplying v12 work — a non-starter.Proposal
Branching model
main— integration trunk. All PRs merge here. Merging does not publish to public channels.release/vN— release channel for major N (e.g.release/v11). Cut frommainwhen major N goes live; N.x.y patches release from this branch.release/vN.M— optional minor channel, cut fromrelease/vNwhen a minor line needs independent patching. Deferred until demand-driven.Release trigger
Tag-triggered, on a release branch. Pushing a
vX.Y.Ztag at a commit inrelease/vX(orrelease/vX.Y) fires the publish workflow.release/v11only stages commits; nothing publishes.gh release create vX.Y.Z --target release/vX --generate-notesmakes the tag and release in one step.Hotfix flow
Always cherry-pick from
main. Fix lands onmainfirst via normal PR, then:Forward-compat is guaranteed: any fix in
release/v11is also inmain(and inrelease/v12once cut). Exceptions (security incident, prod-down with main on a divergent v12 architecture) need ahotfix-directlabel and maintainer sign-off — not the default.Multi-channel CD via GitHub Environments
Three environments keyed by channel, not by major:
nuget-orggithub-packagesgithub-releasesOne tag-push workflow → three
deploymentrecords (one per environment) → three publish steps. Per-major granularity lives in the deploymentref(the tag), so nov11-nuget-org/v12-nuget-orgfan-out. Failed publishes retry independently per environment.Versioning (Nerdbank.GitVersioning)
version.jsonlives on each branch:maincarries the next major in development (e.g.12.0once v11 ships).release/v11stays at11.0. Patch height is computed within the branch — cherry-picks add to the height.When v12 ships:
release/v11.release/v12frommain.main'sversion.jsonto13.0.This is the .NET runtime's pattern (release/8.0, release/9.0, main = next).
Documentation deliverables
CONTRIBUTING.md— contributor flow: where to branch from, where PRs merge, how releases happen.docs/agents/release-and-versioning.md— extend with the new model. PR-creation flow stays (PRs still targetmain); release-pipeline section gets rewritten.docs/branching-and-release.md(or absorbed above) — full maintainer reference for cutting releases.Open implementation questions
1. When do we cut
release/v11? Chris confirmed: now, off currentmain. Butmainhas unreleased v11 breaking-change work in flight (the[Unreleased] — 11.0CHANGELOG section is substantial):release/v11now; treat unreleased v11 work onmainas the v11 line until v12-prep.mainandrelease/v11stay roughly in sync until then.mainunder the current model), cutrelease/v11after the tag, then flipmainto v12-prep.Leaning 1b — avoids mid-major model switches; gives one clean "v11.0.0 ships from main; v11.0.1+ ships from release/v11" transition.
2. Branch protection on
release/vN— required: linear history, status checks (ubuntu-latest), CODEOWNER review. Forbidden: direct pushes by anyone but the release tagger (or restrict tag creation via tag protection). Cherry-picks land via PR, not direct push.3.
gh release --generate-notestemplate — move to auto-generated notes from PR titles + labels. Needs.github/release.yml(don't have it yet — see #263). Resolving #263 unblocks part of this.4.
[GitHubActions]generator implications — should the consumer-facing generator output reflect a similar release-branch model? Out of scope here; file a follow-up.5. Backfill / migration — existing
11.0.xtags stay as-is. The new model takes effect from the first tag after cut. No retroactive renaming.Work breakdown (filed as sub-issues once RFC settles)
release/vN(settings + docs)release/v11at the agreed point.github/workflows/release.ymlfrompush: maintopush: tags: v*on release branchesnuget-orgwith approval,github-packages,github-releases)NUGET_API_KEYto thenuget-orgenvironment (currently a repo secret)CONTRIBUTING.mddocs/agents/release-and-versioning.mdand/or newdocs/branching-and-release.mdOut of scope
[GitHubActions]generator changes — separate follow-up.Nuke.*transition shims — tracked in Add backwards-compatibility principle to AGENTS.md AI brief #262.Discussion
Comment with feedback. The RFC label means consensus shapes the implementation — sub-issues land only once the design here is signed off.