- Stable releases use versions like
0.4.24and publish to the npmlatestdist-tag. - Prereleases use versions like
0.4.24-alpha.1and publish to the npm dist-tag named by the prerelease suffix, such asalpha.
Branch policy
Use branch separation to decide what code is eligible for each release channel. Dist-tags only control npm install defaults; they do not remove code from a package.mainis stable/releasable. Anything merged tomainis eligible forlatest.release/v*branches are for stable patch releases and hotfixes.next,alpha,beta,rc,canary, andprerelease/*branches are for prerelease integration.
main.
Stable release
Stable releases publish only when a reviewedrelease/vX.Y.Z PR merges into protected main.
Prepare and review release notes before creating the release commit:
release:prepare drafts missing changelog artifacts and exits non-zero for review so chained release commands stop before tagging. After the generated TODO summary is rewritten, rerun the same command to create the release commit and tag.
See Changelog process for the full workflow. For stable releases, bun run set-version <version> still enforces this checkpoint when maintainers run the lower-level release command directly.
Push the release branch without its local tag, open a PR to main, and merge it after approval and CI. The publish workflow checks out the exact PR merge SHA, verifies it before publishing, creates the stable tag at that commit, publishes npm packages, and creates the GitHub release.
If that workflow needs recovery, rerun its original merged-PR event. Stable tag pushes and manual dispatches do not publish, so recovery cannot select a different commit.
For hotfixes, branch from the last stable tag, cherry-pick only the fix, publish the patch release, then merge or cherry-pick the same fix back into the prerelease branch.
Alpha release
Alpha releases must be reachable from a prerelease branch such asorigin/next or origin/alpha.
Use the same changelog draft workflow when the prerelease contains changes that users should know about.
CI guardrails
The publish workflow validates release channel boundaries before publishing:- Stable versions must publish with
latest. - Prerelease versions must publish with the prerelease dist-tag, such as
alpha. - Stable releases must come from a merged, reviewed
release/vX.Y.ZPR. - Prerelease tags must be reachable from a prerelease branch.
- Merged
release/vX.Y.ZPRs publish stable releases only. - Every publish job verifies that its checkout matches the immutable event SHA.
Stable guard maintenance
Stable publishing requires the repository secretRELEASE_GUARD_TOKEN. Its named owner must
provision and rotate a dedicated fine-grained personal access token with an explicit expiry and only
these repository permissions: Administration (read-only) and Pull requests (read-only). Metadata read
access is added automatically by GitHub. Scope the token to this repository only; do not grant
Contents, Packages, Workflows, or organization write access. The publish workflow passes this
credential only to the read-only stable guard; checkout, tag creation, npm provenance, and GitHub
Release writes continue to use their existing credentials.
After provisioning or rotation, an administrator must run the read-only capability check from a
protected environment that injects the secret without putting its value on the command line:
RELEASE_GUARD_TOKEN. The check reads effective main
rules, repository rule suites, reviews for the probe PR, and check runs for the probe SHA. Public
check-run reads may not expose a separate fine-grained permission in GitHub’s PAT UI, so this
same-client capability proof is authoritative. It reports only endpoint/status/request-ID
classification on failure and never prints response bodies, headers, or the credential. Record a
successful check before merge. If the secret is missing, expired, incorrectly scoped, or the
capability check does not pass, this PR must not merge and the stable workflow will fail before
creating a tag.
The separate release-guard credential health workflow repeats these reads every Monday and supports
a safe manual run after provisioning or rotation. It discovers a recent merged main pull request
and verifies effective rules, its exact month-window rule suite, reviews, and check runs. It has only
contents: read on the built-in token, passes RELEASE_GUARD_TOKEN only to the health step, and has
no release, tag, package, or publish path.
Required checks remain dynamically sourced from effective rules, but each must identify a concrete
GitHub App integration so the guard can reduce actual check runs. A new required context without a
positive integration ID is unsupported and fails closed; the guard never falls back to legacy commit
statuses. Configured stable-guard timeouts must be 15–40 minutes, with a 25-minute default.
The stable guard intentionally fails closed when GitHub adds a rule type it does not recognize.
If a release reports Unsupported effective repository rule, inspect the effective rules for
main and decide whether the new rule needs an explicit check or is already enforced by the
exact update rule-suite result. Add a rule to NON_CHECK_RULES only when a pass rule suite
fully covers it; never soften the unknown-rule failure into a warning.
After updating and reviewing the guard, rerun the original merged-PR workflow for recovery.
Do not push a stable tag, select a different SHA, or introduce a manual publish path.
Bypass rejection is intentional: a ruleset result of bypass permanently blocks that merge SHA
from publishing. Release PRs must merge normally with their required approval and checks. If a
release PR was bypass-merged, prepare a fresh release PR and merge it normally; rerunning the
bypassed event cannot turn its historical rule-suite result into pass.