Bring the release runbook up to date, protect release branches, and fix main's preview version - #568
Draft
ChrisonSimtian wants to merge 4 commits into
Conversation
Two runbook gaps found while cutting v10.4.0-rc.4.
Prerelease numbers on a release branch are now a literal value in version.json
rather than {height}, so bumping them is a manual step someone has to remember —
and forgetting means tagging republishes an existing version, which
`--skip-duplicate` swallows silently. Both consequences are written down.
Release notes: always use --generate-notes, and anchor --notes-start-tag on the
last GA tag rather than letting gh default to the most recent one. Defaulting
makes an rc diff against the rc before it, which collapses the notes to whatever
landed in between; anchoring on the last GA shows the full set of changes since
the last real release, which is what someone evaluating an rc needs. rc.4 was
first published with hand-written notes for this reason — the fix was the start
tag, not abandoning generation.
Also records the one real gap in generated notes: cherry-picked promotions get
new SHAs, so GitHub attributes them to the promotion PR rather than the original
ones.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
release/v10.4 ran unprotected from its cut on 2026-07-21 until today: the on-demand cut in ADR-0007 has no protection step attached, and the runbook's step 3 pointed at scripts/release-branch-protection.json — a file that never existed. Anything could be pushed straight to the branch that fires production releases. Replaced with a pattern-based ruleset on refs/heads/release/**, so every release line is protected the moment the branch exists — release/v10.4 now, a release/2027 CalVer cut later — with no per-branch step to forget. It mirrors main's profile: no deletion, no force-push, linear history, PRs with CODEOWNERS review and conversation resolution, and the ubuntu-latest check. Repo admins bypass, matching the tag ruleset. The payload is committed under .github/ so the config is reviewable in the repo rather than only in settings, with the PUT command to re-apply it after an edit. Also corrects two things the protection docs asserted that were not true: stale approvals are not dismissed on new commits (dismiss_stale_reviews is false), and squash merge is not disabled by repo setting — only plain merge commits are, so the no-squash rule on release branches rests on convention. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The runbook described a CalVer production line that does not exist yet. Every
example named release/2026 and v2026.1.X, while the line actually shipping is
release/v10.4 at 10.4.0-rc.N — so none of the commands could be copy-pasted, and
the channel table claimed previews were 2026.1.0-preview.<height> when main's
version.json says 10.0.0-preview.{height}.
Documents the release/vMAJOR.MINOR line shape alongside release/YYYY (the
pipeline recognises both, via publicReleaseRefSpec and validate-ref), adds a
"lines live right now" block as the single place to keep current, and retargets
the examples at the live line. The CalVer intent from ADR-0004/0007 is unchanged
— a year cut substitutes the branch and tag and works identically.
Adds two things learned cutting rc.4 that the runbook did not warn about:
--ref on a workflow_dispatch selects the *workflow definition*, not just the
source to pack. Dispatching against the default branch runs main's copy of the
pipeline, which differs from the release branch's whenever a fix has not been
forward-ported — and still pushes the result to nuget.org.
A green release run is not evidence that packages shipped. Every publish job is
conditional, so a bad condition skips it while the run reports success; that was
true of all three jobs on the dispatch path. Adds the two commands that check
what actually ran and whether the version resolves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main advertised 10.0.0-preview.{height} while the line actually shipping is
10.4.0 — so the trunk, which is ahead of release/v10.4 in content, published
previews numbered below the branch cut from it. 10.0.0 was never a target
anyone intends to release; it was left behind when the 10.4 line was cut.
Sets the preview core to 10.4.0. Consequences worth knowing:
Changing the version field resets the git height, so previews restart at
10.4.0-preview.1 rather than continuing from 10.0.0-preview.34. Nothing
regresses — the 10.4.0 core sorts above 10.0.0, so the new previews still
supersede every previously published one.
10.4.0-preview.N sorts below 10.4.0-rc.N, which is correct while main is the
trunk that 10.4.0 stabilises from. Once 10.4.0 goes GA, main will need moving to
the next core (10.5.0, or the CalVer major) or its previews will start reading as
older than the released package despite carrying newer code — the same roll-
forward step the yearly-cut runbook already describes.
Docs updated to match: main keeps {height} for previews, only the core changes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Brings the release runbook in line with what the project actually ships, and captures the release-branch protection config in the repo. Everything here came out of cutting
v10.4.0-rc.4. Docs + one config file; no product code.1. Release branches are protected by a ruleset
release/v10.4ran unprotected from its cut on 2026-07-21 until today — anything could be pushed straight to the branch that fires production releases. The on-demand cut (ADR-0007) has no protection step attached, and the runbook's step 3 pointed atscripts/release-branch-protection.json— a file that never existed.Now a pattern-based ruleset on
refs/heads/release/**(19766406), so every release line is protected the moment the branch exists —release/v10.4now, arelease/2027cut later, with no per-branch step to forget. Mirrorsmain's profile; repo admins bypass, matching the tag ruleset. Payload committed at.github/release-branch-ruleset.json(in.github/, notscripts/— it's GitHub config, not tooling) with thePUTcommand to re-apply after an edit.2. The runbook described a line that doesn't exist
Every example named
release/2026andv2026.1.X, while the line actually shipping isrelease/v10.4at10.4.0-rc.N— so no command was copy-pasteable. The channel table also claimed previews were2026.1.0-preview.<height>whenmain'sversion.jsonsays10.0.0-preview.{height}.Documents
release/vMAJOR.MINORalongsiderelease/YYYY(the pipeline matches both), adds a "lines live right now" block as the one place to keep current, and retargets the examples. ADR-0004/0007 CalVer intent is unchanged — a year cut substitutes branch and tag and works identically.3. Two traps that cost time on rc.4
--refon aworkflow_dispatchselects the workflow definition, not just the source to pack. Dispatching against the default branch runsmain's pipeline — which differs from the release branch's whenever a fix isn't forward-ported yet — and still pushes to nuget.org.4. Prerelease numbers are a manual counter
The rc number is pinned literally in
version.jsonrather than derived from{height}, which would have jumpedrc.3→rc.23. That makes bumping manual, and forgetting means republishing an existing version — which--skip-duplicateswallows silently.5.
main's preview lane moves onto the 10.4 coremainadvertised10.0.0-preview.{height}while the line actually shipping is10.4.0— so the trunk published previews numbered below the branch cut from it.10.0.0was never a target anyone intends to release; it was left behind when the 10.4 line was cut. Now10.4.0-preview.{height}.Two consequences, both verified:
10.4.0-preview.1rather than continuing from10.0.0-preview.34. Nothing regresses —10.4.0sorts above10.0.0, so the new previews still supersede every published one.10.4.0-preview.Nsorts below10.4.0-rc.N, which is right whilemainis the trunk10.4.0stabilises from. Once10.4.0goes GA,mainneeds rolling to the next core (10.5.0, or the CalVer major) or its previews start reading as older than the released package despite carrying newer code — the same roll-forward the yearly-cut runbook already describes.Ordering checked against SemVer 2.0 precedence:
10.0.0-preview.34<10.4.0-preview.1<10.4.0-rc.4<10.4.0, and10.3.47<10.4.0-preview.1.Corrections to existing claims
Two things the protection docs asserted that aren't true:
dismiss_stale_reviews: false).allow_squash_merge: true). So "rebase only" rests on convention, not enforcement. Worth closing separately if you want it enforced; squashing a promotion would collapse reviewed commits into one opaque commit.