From fab39383f3c2b2a6cdd7c2bd74b4da4cc9dd3294 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 17:36:32 -0400 Subject: [PATCH 01/19] Add RFC: move off the release train to a simpler release model Propose retiring Ember's calendar-driven release train (fixed six-week minor cadence, rotating release-manager ceremony, hand-maintained canary/beta/release channels) in favor of an automated, PR-driven model powered by release-plan. Work integrates on `develop`; every merge can release from `main`, with the npm publish gated behind a protected GitHub deployment environment requiring manual approval. Stable releases go through the same gated release-plan publish. SemVer and existing compatibility guarantees are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 text/0000-move-off-the-release-train-to-simpler-release-model.md diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md new file mode 100644 index 0000000000..376c22a7e8 --- /dev/null +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -0,0 +1,310 @@ +--- +stage: accepted +start-date: 2026-06-05T00:00:00.000Z +release-date: # In format YYYY-MM-DDT00:00:00.000Z +release-versions: +teams: # delete teams that aren't relevant + - cli + - framework + - steering +prs: + accepted: # Fill this in with the URL for the Proposal RFC PR +project-link: +suite: +--- + +# Move off the release train to a simpler, automated release model + +## Summary + +Retire Ember's calendar-driven "release train" — the fixed six-week minor +cadence, the rotating release-manager ceremony, and the hand-maintained +`canary` / `beta` / `release` channels — and replace it with the same +automated, PR-driven release model the rest of the ecosystem already runs: +[`release-plan`][release-plan]. + +Concretely: + +- Work integrates on a long-lived `develop` branch. Every pull request that + changes shipped code carries a SemVer impact label and a changelog entry. +- `release-plan` derives the next version and the changelog from that per-PR + metadata. There is no longer a human deciding "what goes in this release" by + hand. +- Promoting `develop` to `main` cuts a release. **Every merge can release**, + but the actual `npm publish` runs inside a GitHub Actions job bound to a + protected [Deployment environment][gh-environments]. A required reviewer + approves the deployment before the publish runs. That approval is the only + manual gate, and it replaces the entire release-manager checklist. +- Official, stable releases go through that same gated `release-plan publish`. + Pre-release / bleeding-edge builds (today's `canary`) come straight off + `develop`. + +SemVer is unchanged. Ember keeps the same compatibility promises it has always +made; only the *mechanics and cadence* of cutting a release change. + +[release-plan]: https://github.com/embroider-build/release-plan +[gh-environments]: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment + +## Motivation + +Ember's release train has served the project well for over a decade. But the +train is *expensive to run*, and almost all of that expense is recurring human +effort that has to be staffed forever. + +### The train is a standing labor cost + +A minor ships every six weeks. Each cycle a release manager (RM) works through a +long, mostly-manual checklist: cut `beta` from `canary`, promote `beta` to +`release`, publish `ember-source`, `ember-cli`, and (historically) `ember-data` +in lockstep, regenerate and proofread the changelog, coordinate the release blog +post, and smoke-test the result. RFC [#0830][rfc-830] layered an analogous +*major* train on top, with deprecation-freeze deadlines pinned to `M.10` and a +new major every `M.12`. + +This produces a few chronic problems: + +- **It needs a volunteer, on a clock, indefinitely.** When no RM is available + the release simply slips. The cadence is only as reliable as the rota behind + it, and that rota is a recurring source of burnout and bus-factor risk. +- **The work is manual and therefore error-prone.** Version bumps, changelog + assembly, and multi-package lockstep publishing are exactly the kind of + mechanical, repetitive steps that tooling does more reliably than a human at + the end of a long checklist. +- **Three live channels cost branch-management and CI overhead.** Maintaining + `canary`, `beta`, and `release` (plus LTS branches) means cherry-picks, + back-merges, and CI matrices that exist only to service the train's shape. + +### The cadence couples "ready" to "the calendar" + +A fixed six-week beat means a finished, reviewed change waits for the next +departure, while an unfinished change feels pressure to make the train. The +calendar, not the readiness of the work, decides when users get it. + +### The ecosystem already solved this + +Nearly every modern Ember addon — including the ones maintained by the same +people who staff the train — releases with [`release-plan`][release-plan]. It +derives the SemVer bump and the changelog from merged PRs, publishes to npm with +provenance, and needs no scheduled human ceremony. It is battle-tested across +hundreds of packages. + +The framework does not need a bespoke, hand-run process that is strictly more +work than the tooling everyone else trusts. **We can get away with +`release-plan`**, and we should. + +[rfc-830]: https://github.com/emberjs/rfcs/blob/master/text/0830-evolving-embers-major-version-process.md + +## Detailed design + +### Branching model + +Two long-lived branches: + +- **`develop`** — the integration branch. All pull requests merge here. This is + the source of pre-release (`canary`-equivalent) builds. +- **`main`** — the released line. Whatever is on `main` corresponds to the + latest published stable version. + +A release is "promote `develop` to `main`." In practice this is `release-plan` +opening (and later merging) a release PR; the promotion is what triggers the +publish job. + +> The names `develop` / `main` are illustrative. A `main` (integration) / +> `release` (published) split works identically. See +> [Unresolved questions](#unresolved-questions). + +### Per-PR release metadata + +Every PR that changes shipped code declares: + +1. **A SemVer impact** — `patch`, `minor`, or `breaking` (major) — via a label + (or a changeset file, whichever the team prefers; `release-plan` supports the + label-driven flow out of the box). +2. **A changelog entry** — the human-readable "what changed," authored as part + of review rather than reconstructed afterward. + +`release-plan` aggregates these across everything merged since the last release +to compute the next version number and assemble the changelog. The decision a +release manager used to make by reading the diff is now made, deterministically, +from metadata that was reviewed when the change landed. + +Breaking changes (`breaking` label) still require the same approval they require +today: a `major` is a steering-level decision, not something an automated bump +performs silently. The label only records the *impact*; the gate (below) still +governers *when* it ships. + +### Releasing: every merge, gated by a GitHub deployment + +The publish itself runs in CI, not on a maintainer's laptop: + +1. A merge to `develop` lets `release-plan prepare` compute the pending release + (version + changelog) and surface it as a release PR. +2. Promoting that to `main` triggers the publish workflow. +3. The workflow's publish job targets a **protected GitHub Environment** (e.g. + `npm-publish`). The environment has a *required reviewers* protection rule. + The npm token / trusted-publishing identity is scoped to that environment, so + nothing can publish until a required reviewer clicks **Approve** on the + pending deployment. +4. On approval, the job runs `release-plan publish`: it tags, pushes, and + publishes to npm (with provenance via OIDC trusted publishing). + +So "every merge can release" is true *and* safe: the cadence is no longer the +calendar, but a human still consciously approves each stable publish. That +single click is the entire residual ceremony — there is no checklist, no manual +version edit, no manual changelog, no manual `npm publish`. + +Because the gate is a GitHub Environment, the existing GitHub permission and +audit model applies: who may approve, the record of who approved what, and +required-reviewer rotation are all standard repo configuration rather than tribal +release-manager knowledge. + +### Channels + +- **Stable** — published from `main` through the gated environment, as above. +- **Pre-release (`canary`)** — published automatically and unattended from + `develop` (e.g. `X.Y.Z-canary.N` / `--tag canary`). Users who want the + bleeding edge keep a continuous stream, now produced by CI on every merge + rather than by a nightly job against a special branch. +- **`beta`** — the dedicated beta channel goes away as a *standing* concept. + Its purpose (a soak period before stable) is served by the `canary` stream + plus the deliberate approval gate. A specific change that warrants extended + baking can still be shipped under a pre-release tag before promotion; it just + isn't a permanent, separately-managed branch. + +### Deprecations, majors, and LTS + +The train coupled three things that this proposal decouples: + +- **Deprecations.** Still introduced freely; still SemVer-minor. A deprecation + may be *removed* in any subsequent major. What changes is that removal is no + longer pinned to a calendar major (RFC #0830's `M.10` freeze / `M.12` major). + Removal lands behind a `breaking` label and ships in the next major that + steering approves. +- **Majors.** No longer calendar-bound. A major ships when breaking work is + ready *and* steering approves the publish — the gate is the control, not a + date. "Majors should be rare" remains a *policy* the approvers uphold, not a + property enforced by an 18-month timer. This supersedes the cadence mechanics + of RFC #0830 while keeping its intent (rare, predictable-in-spirit majors). +- **LTS.** Decoupled from "every fourth release." LTS becomes a *designation* + applied to chosen stable releases (and an associated support window), rather + than a slot mechanically derived from the train's position. Enterprises still + get a marked, supported line to target; it just isn't generated by the + cadence. + +### Lockstep across packages + +Historically `ember-source`, `ember-cli`, and `ember-data` released in lockstep. +`release-plan` operates per repository. This proposal does **not** mandate +dropping lockstep, but it makes lockstep an explicit, opt-in coordination step +rather than a side effect of the train: + +- If lockstep is kept, the release workflow coordinates the version across the + packages at promotion time. +- If lockstep is relaxed, normal SemVer ranges already express cross-package + compatibility, and each package releases on its own merges. + +This is called out as a design consideration rather than decided here; see +[Unresolved questions](#unresolved-questions). + +### What is removed + +- The six-week minor cadence and the major cadence/`M.10` freeze mechanics. +- The release-manager rotation and its checklist. +- The standing `beta` channel and dedicated channel branches. +- Manual version bumps, manual changelog assembly, and manual `npm publish`. + +### What is kept + +- SemVer and every existing compatibility guarantee. +- The `canary` stream (now CI-produced on merge). +- A deliberate human gate before each stable publish. +- Steering control over majors and over what ships. +- An LTS line for consumers who need one. + +## How we teach this + +**Contributors** learn one new habit: every PR that changes shipped code gets a +SemVer-impact label and a changelog entry, authored during review. This is the +same workflow they already follow in virtually every addon, so for most +contributors it is *less* new process than the train, not more. + +**Release approvers** (a documented, rotating group with access to the protected +environment) learn that their job is to review the pending release PR — version +and changelog are pre-computed — and approve the deployment. The role shrinks +from "run the checklist" to "review and click approve." + +**App and addon authors** need to know that Ember now releases continuously and +that versions remain strict SemVer. The Releases page is rewritten to explain: +releases come out as work lands (not on a six-week clock), "what changed" lives +in the generated changelog, `canary` is still there for early adopters, and +there is still a marked LTS line to target. The "Ember keeps its version number +low / majors are rare" messaging is reframed around *policy and the gate* rather +than the train. + +Documentation work: + +- Rewrite the website Releases page. +- Update the contributor guide with the label/changelog workflow. +- Document the environment, the approver group, and the approval procedure. +- A migration/announcement blog post explaining the change and the new LTS + designation. + +## Drawbacks + +- **Loss of a predictable calendar.** Downstream teams that plan upgrades around + the six-week beat (and addon authors who time releases against it) lose that + shared rhythm. The LTS line mitigates this for teams that need predictability. +- **More version numbers.** Releasing per-merge produces more versions than a + batched six-week train. RFC #0830 deliberately valued *rare* version-number + growth (especially for majors); a continuous model trades some of that for + immediacy. Majors stay rare by policy, but minors/patches will be more + numerous and changelog volume per version drops. +- **Per-PR labeling discipline.** A wrong impact label yields a wrong bump. + `release-plan` makes the bump deterministic, but the input is still + human-supplied; mislabeled PRs are a new failure mode the train didn't have. +- **Concentrated publish authority.** Publish power moves to whoever can approve + the protected environment. That group's security and rotation matter, and a + too-small group reintroduces a bus-factor problem in a different place. +- **Tooling dependency.** The framework's release process becomes coupled to + `release-plan`. That is a small, well-understood, community-owned tool, but it + is a dependency the bespoke process did not have. +- **Cultural change.** The release train is a visible, load-bearing part of + Ember's identity and contributor culture. Retiring it is not only a mechanical + change. + +## Alternatives + +- **Keep the train (status quo).** Pays the recurring labor cost and the + bus-factor risk indefinitely. This is the thing the RFC argues against. +- **Automate the train without changing cadence.** Script the RM checklist but + keep the six-week beat, the channels, and the rotation. This removes some toil + but keeps calendar coupling, channel-branch overhead, and a standing rota — + i.e. it keeps the parts that are most expensive to staff. +- **Use `changesets` instead of `release-plan`.** Functionally similar + (PR-derived versions + changelog). `release-plan` is preferred here because it + is what the Ember ecosystem has already standardized on, so contributors and + maintainers reuse existing muscle memory and infrastructure. +- **Time-based automation (e.g. a weekly cron release).** A middle ground: + automated like this proposal, but batched on a timer rather than per-merge. + Cheaper changelog/version churn, but reintroduces calendar coupling and is + arguably just a faster, automated train. The gated per-merge model is + preferred for immediacy; a timer could be layered on later if churn proves a + problem. + +## Unresolved questions + +- **Branch names.** `develop` / `main` vs `main` / `release` — pick the pair + that best fits existing automation and contributor expectations. +- **Lockstep.** Do `ember-source`, `ember-cli`, `ember-data`/WarpDrive keep + lockstep versioning, or release independently? If lockstep, where does the + coordination live in the workflow? +- **LTS policy.** Concrete criteria for designating an LTS release and the + support window attached to it, now that it is not "every fourth release." +- **Relationship to RFC #0830.** Confirm exactly which parts of the major + process RFC are superseded (the cadence/freeze mechanics) versus retained (the + intent that majors stay rare and well-communicated). +- **Approver group.** Who can approve the protected environment, how membership + rotates, and the security posture (2FA, OIDC trusted publishing, audit). +- **Pre-release naming.** Confirm the tag scheme for the continuous channel + (`canary` vs `alpha`/`next`) and whether any soak/beta tag is retained for + specific high-risk changes. From 95b8c2bea1ceacedb47d0a523de8dfbb5476d919 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:13:29 -0400 Subject: [PATCH 02/19] Drop the develop branch; release from a single `main` PRs merge to `main`, canary builds come off `main`, and the gated release-plan publish runs from `main`. Removes the develop/main split and the "promote develop to main" framing throughout. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 376c22a7e8..4bfe55840f 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -25,19 +25,19 @@ automated, PR-driven release model the rest of the ecosystem already runs: Concretely: -- Work integrates on a long-lived `develop` branch. Every pull request that - changes shipped code carries a SemVer impact label and a changelog entry. +- Work integrates on `main` — the single long-lived branch. Every pull request + that changes shipped code carries a SemVer impact label and a changelog entry. - `release-plan` derives the next version and the changelog from that per-PR metadata. There is no longer a human deciding "what goes in this release" by hand. -- Promoting `develop` to `main` cuts a release. **Every merge can release**, - but the actual `npm publish` runs inside a GitHub Actions job bound to a - protected [Deployment environment][gh-environments]. A required reviewer - approves the deployment before the publish runs. That approval is the only - manual gate, and it replaces the entire release-manager checklist. +- **Every merge to `main` can release.** The actual `npm publish` runs inside a + GitHub Actions job bound to a protected [Deployment environment][gh-environments]. + A required reviewer approves the deployment before the publish runs. That + approval is the only manual gate, and it replaces the entire release-manager + checklist. - Official, stable releases go through that same gated `release-plan publish`. Pre-release / bleeding-edge builds (today's `canary`) come straight off - `develop`. + `main`. SemVer is unchanged. Ember keeps the same compatibility promises it has always made; only the *mechanics and cadence* of cutting a release change. @@ -98,20 +98,14 @@ work than the tooling everyone else trusts. **We can get away with ### Branching model -Two long-lived branches: +A single long-lived branch: **`main`**. All pull requests merge here, and +`main` is both where work integrates and what the latest published version +corresponds to. There is no `develop`, no `beta`, and no `release` branch — none +of the branch-per-channel machinery the train requires. -- **`develop`** — the integration branch. All pull requests merge here. This is - the source of pre-release (`canary`-equivalent) builds. -- **`main`** — the released line. Whatever is on `main` corresponds to the - latest published stable version. - -A release is "promote `develop` to `main`." In practice this is `release-plan` -opening (and later merging) a release PR; the promotion is what triggers the -publish job. - -> The names `develop` / `main` are illustrative. A `main` (integration) / -> `release` (published) split works identically. See -> [Unresolved questions](#unresolved-questions). +A release is simply "publish the current state of `main`," driven by +`release-plan` and gated by the deployment approval described below. Pre-release +(`canary`-equivalent) builds also come off `main`. ### Per-PR release metadata @@ -137,9 +131,10 @@ governers *when* it ships. The publish itself runs in CI, not on a maintainer's laptop: -1. A merge to `develop` lets `release-plan prepare` compute the pending release - (version + changelog) and surface it as a release PR. -2. Promoting that to `main` triggers the publish workflow. +1. A merge to `main` lets `release-plan prepare` compute the pending release + (version + changelog) from the metadata of everything merged since the last + release, and surface it as a release PR. +2. Merging that release PR triggers the publish workflow. 3. The workflow's publish job targets a **protected GitHub Environment** (e.g. `npm-publish`). The environment has a *required reviewers* protection rule. The npm token / trusted-publishing identity is scoped to that environment, so @@ -162,14 +157,14 @@ release-manager knowledge. - **Stable** — published from `main` through the gated environment, as above. - **Pre-release (`canary`)** — published automatically and unattended from - `develop` (e.g. `X.Y.Z-canary.N` / `--tag canary`). Users who want the - bleeding edge keep a continuous stream, now produced by CI on every merge + `main` (e.g. `X.Y.Z-canary.N` / `--tag canary`) on each merge. Users who want + the bleeding edge keep a continuous stream, now produced by CI off `main` rather than by a nightly job against a special branch. - **`beta`** — the dedicated beta channel goes away as a *standing* concept. Its purpose (a soak period before stable) is served by the `canary` stream plus the deliberate approval gate. A specific change that warrants extended - baking can still be shipped under a pre-release tag before promotion; it just - isn't a permanent, separately-managed branch. + baking can still be shipped under a pre-release tag before the stable publish; + it just isn't a permanent, separately-managed branch. ### Deprecations, majors, and LTS @@ -199,7 +194,7 @@ dropping lockstep, but it makes lockstep an explicit, opt-in coordination step rather than a side effect of the train: - If lockstep is kept, the release workflow coordinates the version across the - packages at promotion time. + packages at release time. - If lockstep is relaxed, normal SemVer ranges already express cross-package compatibility, and each package releases on its own merges. @@ -293,8 +288,6 @@ Documentation work: ## Unresolved questions -- **Branch names.** `develop` / `main` vs `main` / `release` — pick the pair - that best fits existing automation and contributor expectations. - **Lockstep.** Do `ember-source`, `ember-cli`, `ember-data`/WarpDrive keep lockstep versioning, or release independently? If lockstep, where does the coordination live in the workflow? From 9e1543a46d804483252f7817f45c322a7a7da86f Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:15:37 -0400 Subject: [PATCH 03/19] Keep the six-week cadence; drop only the canary/beta channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reframe: the proposal retains the six-week release schedule and SemVer/ deprecation/major (RFC 830)/LTS policies unchanged. What changes is the branch-and-channel machinery — a single `main`, stable cut from it via release-plan on the existing cadence (gated by a GitHub deployment env), and the published canary/beta channels removed in favor of tracking `main` via git. Updates motivation, channels, deprecation/major/LTS, drawbacks, alternatives, and unresolved questions accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 368 +++++++++--------- 1 file changed, 178 insertions(+), 190 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 4bfe55840f..9fd74b8fcb 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -13,99 +13,94 @@ project-link: suite: --- -# Move off the release train to a simpler, automated release model +# Move off the release train to a simpler release model ## Summary -Retire Ember's calendar-driven "release train" — the fixed six-week minor -cadence, the rotating release-manager ceremony, and the hand-maintained -`canary` / `beta` / `release` channels — and replace it with the same -automated, PR-driven release model the rest of the ecosystem already runs: -[`release-plan`][release-plan]. +Ember's "release train" today bundles two separate things: + +1. **A six-week release cadence.** A new minor ships on a steady, predictable + clock. +2. **A multi-channel promotion pipeline.** Code flows through dedicated + `canary` → `beta` → `release` branches, cut and promoted by a rotating + release manager working a mostly-manual checklist each cycle. + +**This RFC keeps (1) and replaces (2).** The six-week cadence is the part that +works, and it stays exactly as it is. What goes away is the multi-channel branch +machinery and the hand-run release ceremony. Concretely: -- Work integrates on `main` — the single long-lived branch. Every pull request - that changes shipped code carries a SemVer impact label and a changelog entry. -- `release-plan` derives the next version and the changelog from that per-PR - metadata. There is no longer a human deciding "what goes in this release" by - hand. -- **Every merge to `main` can release.** The actual `npm publish` runs inside a - GitHub Actions job bound to a protected [Deployment environment][gh-environments]. - A required reviewer approves the deployment before the publish runs. That - approval is the only manual gate, and it replaces the entire release-manager - checklist. -- Official, stable releases go through that same gated `release-plan publish`. - Pre-release / bleeding-edge builds (today's `canary`) come straight off - `main`. - -SemVer is unchanged. Ember keeps the same compatibility promises it has always -made; only the *mechanics and cadence* of cutting a release change. +- A single long-lived branch: **`main`**. No `canary`, `beta`, or `release` + branches. +- Stable releases are cut from `main` by [`release-plan`][release-plan] on the + same six-week schedule, with the `npm publish` gated behind a protected + [GitHub deployment environment][gh-environments] — a required reviewer + approves before it publishes. +- The separate, published **`canary` and `beta` channels go away**. "Bleeding + edge" becomes *track `main` via git*: consumers who want unreleased code point + at the git ref rather than a special npm dist-tag maintained off a special + branch. `main` itself is the canary/alpha. + +SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version +process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch +structure and the publishing mechanics change. [release-plan]: https://github.com/embroider-build/release-plan [gh-environments]: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment +[rfc-830]: https://github.com/emberjs/rfcs/blob/master/text/0830-evolving-embers-major-version-process.md ## Motivation -Ember's release train has served the project well for over a decade. But the -train is *expensive to run*, and almost all of that expense is recurring human -effort that has to be staffed forever. +The expensive part of the release train is not the cadence — it's the machinery +around it. -### The train is a standing labor cost +### Three live channels are a standing branch-management cost -A minor ships every six weeks. Each cycle a release manager (RM) works through a -long, mostly-manual checklist: cut `beta` from `canary`, promote `beta` to -`release`, publish `ember-source`, `ember-cli`, and (historically) `ember-data` -in lockstep, regenerate and proofread the changelog, coordinate the release blog -post, and smoke-test the result. RFC [#0830][rfc-830] layered an analogous -*major* train on top, with deprecation-freeze deadlines pinned to `M.10` and a -new major every `M.12`. +Maintaining `canary`, `beta`, and `release` as separate, simultaneously-live +lines (plus LTS) means ongoing cherry-picks, back-merges, and branch bookkeeping +that exist only to service the shape of the train. Every cycle a release manager +(RM) works a long, mostly-manual checklist: cut `beta` from `canary`, promote +`beta` to `release`, hand-publish `ember-source`, `ember-cli`, and (historically) +`ember-data` in lockstep, regenerate and proofread the changelog, and coordinate +the release. That work: -This produces a few chronic problems: +- **Needs a volunteer on a clock, indefinitely**, and slips when no RM is + available. It is a recurring source of burnout and bus-factor risk. +- **Is manual and therefore error-prone** — version bumps, changelog assembly, + and multi-package lockstep publishing are exactly the mechanical steps tooling + does more reliably than a person at the end of a checklist. -- **It needs a volunteer, on a clock, indefinitely.** When no RM is available - the release simply slips. The cadence is only as reliable as the rota behind - it, and that rota is a recurring source of burnout and bus-factor risk. -- **The work is manual and therefore error-prone.** Version bumps, changelog - assembly, and multi-package lockstep publishing are exactly the kind of - mechanical, repetitive steps that tooling does more reliably than a human at - the end of a long checklist. -- **Three live channels cost branch-management and CI overhead.** Maintaining - `canary`, `beta`, and `release` (plus LTS branches) means cherry-picks, - back-merges, and CI matrices that exist only to service the train's shape. +### The ecosystem already automated this -### The cadence couples "ready" to "the calendar" +Nearly every modern Ember addon releases with [`release-plan`][release-plan]. It +derives the SemVer bump and changelog from merged PRs and publishes to npm with +provenance, no scheduled human ceremony required. The framework does not need a +bespoke, hand-run process that is strictly more work than the tooling everyone +else already trusts. **We can get away with `release-plan`.** -A fixed six-week beat means a finished, reviewed change waits for the next -departure, while an unfinished change feels pressure to make the train. The -calendar, not the readiness of the work, decides when users get it. +### `canary`/`beta` as published channels mostly duplicate "the git repo" -### The ecosystem already solved this +The reason `canary` exists is so people can run unreleased Ember. But that is +exactly what the default branch *is*. Publishing it as a separate, branch-backed +npm channel is extra plumbing to approximate "whatever is on `main` right now." +Letting consumers track `main` via git directly removes the plumbing without +removing the capability. -Nearly every modern Ember addon — including the ones maintained by the same -people who staff the train — releases with [`release-plan`][release-plan]. It -derives the SemVer bump and the changelog from merged PRs, publishes to npm with -provenance, and needs no scheduled human ceremony. It is battle-tested across -hundreds of packages. +### Keep the cadence — it works -The framework does not need a bespoke, hand-run process that is strictly more -work than the tooling everyone else trusts. **We can get away with -`release-plan`**, and we should. - -[rfc-830]: https://github.com/emberjs/rfcs/blob/master/text/0830-evolving-embers-major-version-process.md +The six-week cadence is predictable, well-understood, and well-loved. It is *not* +what makes the train expensive, so this proposal deliberately keeps it. We are +moving off the *channel-and-ceremony* model, not the calendar. ## Detailed design ### Branching model -A single long-lived branch: **`main`**. All pull requests merge here, and -`main` is both where work integrates and what the latest published version -corresponds to. There is no `develop`, no `beta`, and no `release` branch — none -of the branch-per-channel machinery the train requires. - -A release is simply "publish the current state of `main`," driven by -`release-plan` and gated by the deployment approval described below. Pre-release -(`canary`-equivalent) builds also come off `main`. +A single long-lived branch: **`main`**. All pull requests merge here. `main` is +both where work integrates and the source of every release. There are no +`canary`, `beta`, or `release` branches, and none of the cherry-pick / +back-merge machinery that keeping three live channels requires. ### Per-PR release metadata @@ -123,181 +118,174 @@ release manager used to make by reading the diff is now made, deterministically, from metadata that was reviewed when the change landed. Breaking changes (`breaking` label) still require the same approval they require -today: a `major` is a steering-level decision, not something an automated bump -performs silently. The label only records the *impact*; the gate (below) still -governers *when* it ships. - -### Releasing: every merge, gated by a GitHub deployment - -The publish itself runs in CI, not on a maintainer's laptop: - -1. A merge to `main` lets `release-plan prepare` compute the pending release - (version + changelog) from the metadata of everything merged since the last - release, and surface it as a release PR. -2. Merging that release PR triggers the publish workflow. -3. The workflow's publish job targets a **protected GitHub Environment** (e.g. - `npm-publish`). The environment has a *required reviewers* protection rule. - The npm token / trusted-publishing identity is scoped to that environment, so - nothing can publish until a required reviewer clicks **Approve** on the - pending deployment. +today: a `major` is a steering-level decision, sequenced by the major-version +process (RFC #0830), not something an automated bump performs silently. + +### Releasing: the six-week schedule, automated and gated + +The cadence is unchanged; the *mechanics* are automated: + +1. `release-plan prepare` keeps a release PR continuously up to date — it + computes the pending version and changelog from the metadata of everything + merged since the last release. +2. On the scheduled six-week release date, that release PR is merged, which + triggers the publish workflow. (The schedule is the rhythm; nothing forces a + release between scheduled dates, and a date can still be held or moved by the + same people who manage the calendar today.) +3. The publish job targets a **protected GitHub Environment** (e.g. + `npm-publish`) with a *required reviewers* protection rule. The npm token / + trusted-publishing identity is scoped to that environment, so nothing can + publish until a required reviewer clicks **Approve** on the pending + deployment. 4. On approval, the job runs `release-plan publish`: it tags, pushes, and publishes to npm (with provenance via OIDC trusted publishing). -So "every merge can release" is true *and* safe: the cadence is no longer the -calendar, but a human still consciously approves each stable publish. That -single click is the entire residual ceremony — there is no checklist, no manual -version edit, no manual changelog, no manual `npm publish`. - -Because the gate is a GitHub Environment, the existing GitHub permission and -audit model applies: who may approve, the record of who approved what, and -required-reviewer rotation are all standard repo configuration rather than tribal -release-manager knowledge. +The deliberate approval click is the entire residual ceremony — there is no +checklist, no manual version edit, no manual changelog, no manual `npm publish`, +and no channel to cut or promote. Because the gate is a GitHub Environment, the +existing GitHub permission and audit model applies: who may approve, the record +of who approved what, and required-reviewer rotation are all standard repo +configuration rather than tribal release-manager knowledge. ### Channels -- **Stable** — published from `main` through the gated environment, as above. -- **Pre-release (`canary`)** — published automatically and unattended from - `main` (e.g. `X.Y.Z-canary.N` / `--tag canary`) on each merge. Users who want - the bleeding edge keep a continuous stream, now produced by CI off `main` - rather than by a nightly job against a special branch. -- **`beta`** — the dedicated beta channel goes away as a *standing* concept. - Its purpose (a soak period before stable) is served by the `canary` stream - plus the deliberate approval gate. A specific change that warrants extended - baking can still be shipped under a pre-release tag before the stable publish; - it just isn't a permanent, separately-managed branch. - -### Deprecations, majors, and LTS - -The train coupled three things that this proposal decouples: - -- **Deprecations.** Still introduced freely; still SemVer-minor. A deprecation - may be *removed* in any subsequent major. What changes is that removal is no - longer pinned to a calendar major (RFC #0830's `M.10` freeze / `M.12` major). - Removal lands behind a `breaking` label and ships in the next major that - steering approves. -- **Majors.** No longer calendar-bound. A major ships when breaking work is - ready *and* steering approves the publish — the gate is the control, not a - date. "Majors should be rare" remains a *policy* the approvers uphold, not a - property enforced by an 18-month timer. This supersedes the cadence mechanics - of RFC #0830 while keeping its intent (rare, predictable-in-spirit majors). -- **LTS.** Decoupled from "every fourth release." LTS becomes a *designation* - applied to chosen stable releases (and an associated support window), rather - than a slot mechanically derived from the train's position. Enterprises still - get a marked, supported line to target; it just isn't generated by the - cadence. +- **Stable (`latest`)** — published from `main` through the gated environment, + on the six-week cadence, as above. +- **Canary / alpha** — **is `main`.** To run unreleased Ember, depend on the git + ref (e.g. `main` or a specific commit) rather than a published `canary` + dist-tag. There is no longer a separately-managed canary branch or its nightly + publish job; the latest in-progress code is just the head of `main`. +- **`beta`** — removed. The dedicated beta channel and its branch go away. A + specific change that warrants extended baking can still be merged early and + exercised via the git-tracked `main` before the next scheduled stable release; + it just isn't a permanent, separately-published channel. + +> How `ember-source` consumers consume `main` from git — since the published +> package is normally a build artifact — is the main detail to settle. See +> [Unresolved questions](#unresolved-questions). + +### Deprecations, majors, and LTS — unchanged + +Because the six-week cadence is retained, the policies layered on top of it are +**unaffected**: + +- **Deprecations** are still introduced as SemVer-minor and removed in majors, + on the same deprecation-freeze schedule. +- **Majors** still follow the major-version process of RFC #0830 (its `M.10` + deprecation freeze and `M.12` → `(M+1).0` train). That process is sequenced by + the minor cadence, which this proposal keeps; it simply *executes* via + `release-plan` from `main` instead of via the channel pipeline. +- **LTS** continues as today. + +In other words, this RFC changes *how* a release is cut and *which branches/ +channels exist*, not *when* releases happen or *what compatibility they +guarantee*. ### Lockstep across packages Historically `ember-source`, `ember-cli`, and `ember-data` released in lockstep. `release-plan` operates per repository. This proposal does **not** mandate dropping lockstep, but it makes lockstep an explicit, opt-in coordination step -rather than a side effect of the train: +rather than a side effect of the channel pipeline: - If lockstep is kept, the release workflow coordinates the version across the packages at release time. - If lockstep is relaxed, normal SemVer ranges already express cross-package - compatibility, and each package releases on its own merges. + compatibility, and each package releases on its own schedule. This is called out as a design consideration rather than decided here; see [Unresolved questions](#unresolved-questions). ### What is removed -- The six-week minor cadence and the major cadence/`M.10` freeze mechanics. -- The release-manager rotation and its checklist. -- The standing `beta` channel and dedicated channel branches. -- Manual version bumps, manual changelog assembly, and manual `npm publish`. +- The published `canary` and `beta` channels and their dedicated branches. +- The cherry-pick / back-merge machinery for keeping three live channels. +- The release-manager checklist: manual version bumps, manual changelog + assembly, manual lockstep `npm publish`, and channel cut/promote steps. ### What is kept +- **The six-week release cadence**, unchanged. - SemVer and every existing compatibility guarantee. -- The `canary` stream (now CI-produced on merge). -- A deliberate human gate before each stable publish. +- The deprecation policy, LTS, and the major-version process (RFC #0830). - Steering control over majors and over what ships. -- An LTS line for consumers who need one. +- A deliberate human gate before each publish. ## How we teach this -**Contributors** learn one new habit: every PR that changes shipped code gets a +**Contributors** learn one habit: every PR that changes shipped code gets a SemVer-impact label and a changelog entry, authored during review. This is the -same workflow they already follow in virtually every addon, so for most -contributors it is *less* new process than the train, not more. +same workflow they already follow in virtually every addon. **Release approvers** (a documented, rotating group with access to the protected -environment) learn that their job is to review the pending release PR — version -and changelog are pre-computed — and approve the deployment. The role shrinks -from "run the checklist" to "review and click approve." - -**App and addon authors** need to know that Ember now releases continuously and -that versions remain strict SemVer. The Releases page is rewritten to explain: -releases come out as work lands (not on a six-week clock), "what changed" lives -in the generated changelog, `canary` is still there for early adopters, and -there is still a marked LTS line to target. The "Ember keeps its version number -low / majors are rare" messaging is reframed around *policy and the gate* rather -than the train. +environment) review the pre-computed release PR and approve the deployment. The +role shrinks from "run the channel/checklist ceremony" to "review and click +approve." + +**Consumers of `canary`/`beta`** are the most important audience for this +change, because their workflow moves. Anyone who today depends on +`ember-source@canary` or `ember-source@beta` — including `ember-try` scenarios, +addon CI matrices that test against upcoming Ember, and people bisecting +regressions — needs to switch to tracking `main` via git. The migration guide +must spell out exactly how to do that (see Unresolved questions), and the +default `ember-try` / blueprint scenarios should be updated to the new approach. Documentation work: -- Rewrite the website Releases page. +- Rewrite the website Releases page: `main` is the release line, the six-week + cadence is unchanged, and there are no longer published `canary`/`beta` + channels — track git for the edge. - Update the contributor guide with the label/changelog workflow. - Document the environment, the approver group, and the approval procedure. -- A migration/announcement blog post explaining the change and the new LTS - designation. +- A migration/announcement blog post focused on the `canary`/`beta` → git change. ## Drawbacks -- **Loss of a predictable calendar.** Downstream teams that plan upgrades around - the six-week beat (and addon authors who time releases against it) lose that - shared rhythm. The LTS line mitigates this for teams that need predictability. -- **More version numbers.** Releasing per-merge produces more versions than a - batched six-week train. RFC #0830 deliberately valued *rare* version-number - growth (especially for majors); a continuous model trades some of that for - immediacy. Majors stay rare by policy, but minors/patches will be more - numerous and changelog volume per version drops. +- **Losing published `canary`/`beta` builds is a real regression for some + workflows.** `npm install ember-source@beta` is lower-friction than depending + on a git ref, and a lot of ecosystem CI (`ember-try`, addon test matrices) is + built around those dist-tags. Tracking `main` from git must be made genuinely + easy or this trades maintainer effort for downstream effort. +- **`ember-source` is a build artifact**, so "just depend on the git ref" is not + as turnkey as it is for a typical source-published addon. The consumption story + for `main` needs to be concrete and supported, not hand-waved. - **Per-PR labeling discipline.** A wrong impact label yields a wrong bump. - `release-plan` makes the bump deterministic, but the input is still - human-supplied; mislabeled PRs are a new failure mode the train didn't have. + `release-plan` makes the bump deterministic, but the input is human-supplied. - **Concentrated publish authority.** Publish power moves to whoever can approve - the protected environment. That group's security and rotation matter, and a - too-small group reintroduces a bus-factor problem in a different place. + the protected environment; that group's security and rotation matter. - **Tooling dependency.** The framework's release process becomes coupled to - `release-plan`. That is a small, well-understood, community-owned tool, but it - is a dependency the bespoke process did not have. -- **Cultural change.** The release train is a visible, load-bearing part of - Ember's identity and contributor culture. Retiring it is not only a mechanical - change. + `release-plan` — a small, community-owned tool, but a new dependency. +- **Cultural change.** `canary` and `beta` are long-standing, load-bearing parts + of Ember's testing culture and infrastructure; removing them as channels is not + only a mechanical change. ## Alternatives -- **Keep the train (status quo).** Pays the recurring labor cost and the - bus-factor risk indefinitely. This is the thing the RFC argues against. -- **Automate the train without changing cadence.** Script the RM checklist but - keep the six-week beat, the channels, and the rotation. This removes some toil - but keeps calendar coupling, channel-branch overhead, and a standing rota — - i.e. it keeps the parts that are most expensive to staff. -- **Use `changesets` instead of `release-plan`.** Functionally similar - (PR-derived versions + changelog). `release-plan` is preferred here because it - is what the Ember ecosystem has already standardized on, so contributors and - maintainers reuse existing muscle memory and infrastructure. -- **Time-based automation (e.g. a weekly cron release).** A middle ground: - automated like this proposal, but batched on a timer rather than per-merge. - Cheaper changelog/version churn, but reintroduces calendar coupling and is - arguably just a faster, automated train. The gated per-merge model is - preferred for immediacy; a timer could be layered on later if churn proves a - problem. +- **Keep the train as-is (status quo).** Pays the recurring branch-management and + RM-ceremony cost, and the bus-factor risk, indefinitely. +- **Keep a published `canary` (or `beta`), but automate it.** A middle ground: + drop only `beta`, and have `release-plan` publish a `canary` prerelease + dist-tag off `main` automatically so existing `@canary` consumers don't have to + move to git. This preserves the convenient dist-tag at the cost of keeping one + prerelease channel. Worth considering if the git-tracking story proves too + rough for downstream CI. +- **Use `changesets` instead of `release-plan`.** Functionally similar; + `release-plan` is preferred because the Ember ecosystem has standardized on it. +- **Also drop the cadence (fully continuous releases).** A more radical model + where every merge can release. Explicitly **not** proposed here — the six-week + cadence is retained deliberately because it works. ## Unresolved questions +- **How consumers track `main` via git.** Given `ember-source` is a built + artifact: is it a git dependency on a build branch, an automated prerelease tag + cut from `main`, published nightly git snapshots, or documented local-build + instructions? This is the crux of the proposal and must be settled. +- **Whether any prerelease dist-tag is retained at all** (the "automate canary" + alternative) versus purely git-tracked `main`. +- **`ember-try` and ecosystem CI migration** off `@canary`/`@beta` scenarios. - **Lockstep.** Do `ember-source`, `ember-cli`, `ember-data`/WarpDrive keep lockstep versioning, or release independently? If lockstep, where does the coordination live in the workflow? -- **LTS policy.** Concrete criteria for designating an LTS release and the - support window attached to it, now that it is not "every fourth release." -- **Relationship to RFC #0830.** Confirm exactly which parts of the major - process RFC are superseded (the cadence/freeze mechanics) versus retained (the - intent that majors stay rare and well-communicated). - **Approver group.** Who can approve the protected environment, how membership rotates, and the security posture (2FA, OIDC trusted publishing, audit). -- **Pre-release naming.** Confirm the tag scheme for the continuous channel - (`canary` vs `alpha`/`next`) and whether any soak/beta tag is retained for - specific high-risk changes. From 94aff164affa344a17786b8549e76f46cc337389 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:20:07 -0400 Subject: [PATCH 04/19] Correct canary/beta channel facts; alpha auto-published from main Canary is consumed from git, not an ember-source@canary npm dist-tag; only @beta is a published prerelease tag today. Reframe the prerelease story: publish an `@alpha` dist-tag automatically from `main` (like Embroider's prereleases) while keeping main's git-ref workflow, drop the `@beta` tag, and resolve the former "build artifact consumption" open question. Updates motivation, channels, teaching, drawbacks, alternatives, and unresolved questions to match. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 109 +++++++++--------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 9fd74b8fcb..8bea172c5e 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -37,10 +37,11 @@ Concretely: same six-week schedule, with the `npm publish` gated behind a protected [GitHub deployment environment][gh-environments] — a required reviewer approves before it publishes. -- The separate, published **`canary` and `beta` channels go away**. "Bleeding - edge" becomes *track `main` via git*: consumers who want unreleased code point - at the git ref rather than a special npm dist-tag maintained off a special - branch. `main` itself is the canary/alpha. +- The **`canary` and `beta` channels collapse into a single `alpha`**, published + automatically from `main` (the way Embroider published its prereleases). + Because `ember-source` lives in git, `main` is also directly consumable as a + git ref. Either way, "bleeding edge" is just "whatever is on `main`" — there is + no separate channel branch or nightly promotion job behind it. SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch @@ -79,13 +80,15 @@ provenance, no scheduled human ceremony required. The framework does not need a bespoke, hand-run process that is strictly more work than the tooling everyone else already trusts. **We can get away with `release-plan`.** -### `canary`/`beta` as published channels mostly duplicate "the git repo" +### A separate `canary` branch mostly duplicates "the git repo" -The reason `canary` exists is so people can run unreleased Ember. But that is -exactly what the default branch *is*. Publishing it as a separate, branch-backed -npm channel is extra plumbing to approximate "whatever is on `main` right now." -Letting consumers track `main` via git directly removes the plumbing without -removing the capability. +The reason `canary` exists is so people can run unreleased Ember — and it is +already consumed straight from git, not from an `ember-source@canary` npm +dist-tag. But "the latest unreleased code" is exactly what the default branch +*is*. A separate `canary` branch plus its nightly promotion job is extra +plumbing to approximate "whatever is on `main` right now." Publishing an `alpha` +prerelease automatically from `main` (and keeping the existing git-ref workflow) +delivers the same capability with none of the branch machinery. ### Keep the cadence — it works @@ -151,18 +154,17 @@ configuration rather than tribal release-manager knowledge. - **Stable (`latest`)** — published from `main` through the gated environment, on the six-week cadence, as above. -- **Canary / alpha** — **is `main`.** To run unreleased Ember, depend on the git - ref (e.g. `main` or a specific commit) rather than a published `canary` - dist-tag. There is no longer a separately-managed canary branch or its nightly - publish job; the latest in-progress code is just the head of `main`. -- **`beta`** — removed. The dedicated beta channel and its branch go away. A - specific change that warrants extended baking can still be merged early and - exercised via the git-tracked `main` before the next scheduled stable release; - it just isn't a permanent, separately-published channel. - -> How `ember-source` consumers consume `main` from git — since the published -> package is normally a build artifact — is the main detail to settle. See -> [Unresolved questions](#unresolved-questions). +- **`alpha`** — published automatically from `main`, the way Embroider published + its prereleases. This is the bleeding-edge stream, and it gives `main` a + turnkey npm dist-tag (`ember-source@alpha`) — something canary never had on + npm. Because `ember-source` lives in git, `main` also remains directly + consumable as a git ref (e.g. `emberjs/ember.js#`), exactly as canary is + consumed today. There is no separate channel branch or nightly promotion job + behind it; the latest in-progress code is just the head of `main`. +- **`beta`** — removed. The dedicated `beta` channel, its branch, and the + `ember-source@beta` dist-tag go away; its consumers move to `@alpha` (or the + git ref). A change that warrants extra baking can still be merged early and + exercised via `@alpha` before the next scheduled stable release. ### Deprecations, majors, and LTS — unchanged @@ -198,7 +200,7 @@ This is called out as a design consideration rather than decided here; see ### What is removed -- The published `canary` and `beta` channels and their dedicated branches. +- The `canary` and `beta` branches, and the `ember-source@beta` dist-tag. - The cherry-pick / back-merge machinery for keeping three live channels. - The release-manager checklist: manual version bumps, manual changelog assembly, manual lockstep `npm publish`, and channel cut/promote steps. @@ -222,33 +224,34 @@ environment) review the pre-computed release PR and approve the deployment. The role shrinks from "run the channel/checklist ceremony" to "review and click approve." -**Consumers of `canary`/`beta`** are the most important audience for this -change, because their workflow moves. Anyone who today depends on -`ember-source@canary` or `ember-source@beta` — including `ember-try` scenarios, -addon CI matrices that test against upcoming Ember, and people bisecting -regressions — needs to switch to tracking `main` via git. The migration guide -must spell out exactly how to do that (see Unresolved questions), and the -default `ember-try` / blueprint scenarios should be updated to the new approach. +**Consumers of `beta`** are the audience whose workflow moves. Anyone who today +depends on `ember-source@beta` — `ember-try` scenarios, addon CI matrices that +test against upcoming Ember, people bisecting regressions — switches to +`ember-source@alpha` (or a git ref). Canary consumers are unaffected: they +already use a git ref, and that keeps working. The default `ember-try` / +blueprint scenarios should be updated to point at `@alpha`. Documentation work: - Rewrite the website Releases page: `main` is the release line, the six-week - cadence is unchanged, and there are no longer published `canary`/`beta` - channels — track git for the edge. + cadence is unchanged, the prerelease stream is `@alpha` (auto-published from + `main`), and `beta` no longer exists. - Update the contributor guide with the label/changelog workflow. - Document the environment, the approver group, and the approval procedure. -- A migration/announcement blog post focused on the `canary`/`beta` → git change. +- A migration/announcement blog post focused on `@beta` → `@alpha`. ## Drawbacks -- **Losing published `canary`/`beta` builds is a real regression for some - workflows.** `npm install ember-source@beta` is lower-friction than depending - on a git ref, and a lot of ecosystem CI (`ember-try`, addon test matrices) is - built around those dist-tags. Tracking `main` from git must be made genuinely - easy or this trades maintainer effort for downstream effort. -- **`ember-source` is a build artifact**, so "just depend on the git ref" is not - as turnkey as it is for a typical source-published addon. The consumption story - for `main` needs to be concrete and supported, not hand-waved. +- **Removing the `@beta` dist-tag still moves some consumers.** `ember-try` + scenarios and addon CI matrices pinned to `ember-source@beta` have to switch to + `@alpha`. This is a one-time, mechanical migration rather than a loss of + capability — `@alpha` is auto-published from `main` and the canary git-ref + workflow is unchanged — but it is still ecosystem-wide churn that needs + coordinating. +- **Collapsing `beta` into `alpha` removes a soak stage.** Today `beta` is a + distinct, more-stable-than-canary checkpoint. Folding it into `@alpha` means + there is one prerelease stream, not two; changes get less differentiated baking + before a scheduled stable release. - **Per-PR labeling discipline.** A wrong impact label yields a wrong bump. `release-plan` makes the bump deterministic, but the input is human-supplied. - **Concentrated publish authority.** Publish power moves to whoever can approve @@ -263,12 +266,11 @@ Documentation work: - **Keep the train as-is (status quo).** Pays the recurring branch-management and RM-ceremony cost, and the bus-factor risk, indefinitely. -- **Keep a published `canary` (or `beta`), but automate it.** A middle ground: - drop only `beta`, and have `release-plan` publish a `canary` prerelease - dist-tag off `main` automatically so existing `@canary` consumers don't have to - move to git. This preserves the convenient dist-tag at the cost of keeping one - prerelease channel. Worth considering if the git-tracking story proves too - rough for downstream CI. +- **Keep `beta` as a second prerelease stream.** Retain a `@beta` dist-tag + alongside `@alpha`, both auto-published from `main` (e.g. `@beta` from the + pending release PR). This preserves the extra soak stage at the cost of a + second prerelease tag to reason about. Worth considering if a single `@alpha` + stream proves too coarse for downstream CI. - **Use `changesets` instead of `release-plan`.** Functionally similar; `release-plan` is preferred because the Ember ecosystem has standardized on it. - **Also drop the cadence (fully continuous releases).** A more radical model @@ -277,13 +279,12 @@ Documentation work: ## Unresolved questions -- **How consumers track `main` via git.** Given `ember-source` is a built - artifact: is it a git dependency on a build branch, an automated prerelease tag - cut from `main`, published nightly git snapshots, or documented local-build - instructions? This is the crux of the proposal and must be settled. -- **Whether any prerelease dist-tag is retained at all** (the "automate canary" - alternative) versus purely git-tracked `main`. -- **`ember-try` and ecosystem CI migration** off `@canary`/`@beta` scenarios. +- **`@alpha` publishing cadence.** Does `@alpha` publish on every merge to + `main`, or on a lighter schedule (e.g. nightly)? Either is straightforward with + `release-plan`; this is a tuning decision. +- **Whether `beta` is fully dropped or kept as a second `@beta` prerelease + stream** (the alternative above). +- **`ember-try` and ecosystem CI migration** off `@beta` scenarios onto `@alpha`. - **Lockstep.** Do `ember-source`, `ember-cli`, `ember-data`/WarpDrive keep lockstep versioning, or release independently? If lockstep, where does the coordination live in the workflow? From 87e46df4355c8d6397b950cd06936d0088f9d85d Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 21:03:53 -0400 Subject: [PATCH 05/19] Holistic pass: reconcile cadence with release-plan and RFC 830 Close the gaps the incremental edits left: - Explain that release-plan collapses a cycle's labeled PRs into a single bump (highest impact wins -> one minor per six weeks, as today), and how the continuous @alpha relates to the scheduled stable snapshot. - Reconcile majors with RFC 830: the M.10 freeze already gates when a breaking change may merge, so a label-driven major bump only happens in the major window -- not mid-cycle. - Clarify the scheduled cut is triggered by a maintainer or scheduled workflow; note ember-data is now WarpDrive. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 8bea172c5e..9f83ceca8a 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -131,10 +131,11 @@ The cadence is unchanged; the *mechanics* are automated: 1. `release-plan prepare` keeps a release PR continuously up to date — it computes the pending version and changelog from the metadata of everything merged since the last release. -2. On the scheduled six-week release date, that release PR is merged, which - triggers the publish workflow. (The schedule is the rhythm; nothing forces a - release between scheduled dates, and a date can still be held or moved by the - same people who manage the calendar today.) +2. On the scheduled six-week release date, that release PR is merged — by a + maintainer or a scheduled workflow — which triggers the publish workflow. + (The schedule is the rhythm; nothing forces a release between scheduled + dates, and a date can still be held or moved by the same people who manage + the calendar today.) 3. The publish job targets a **protected GitHub Environment** (e.g. `npm-publish`) with a *required reviewers* protection rule. The npm token / trusted-publishing identity is scoped to that environment, so nothing can @@ -143,6 +144,14 @@ The cadence is unchanged; the *mechanics* are automated: 4. On approval, the job runs `release-plan publish`: it tags, pushes, and publishes to npm (with provenance via OIDC trusted publishing). +Across a cycle, `release-plan` collapses all the labeled PRs into a *single* +version bump — highest impact wins, so six weeks of `minor`-labeled work yields +one minor, exactly as one stable minor per cycle does today. `@alpha` publishes +the in-progress version continuously (e.g. `6.5.0-alpha.N`); the scheduled +stable cut publishes the finalized version (`6.5.0`) as `@latest`. The stable +release is just a snapshot of `main` at the scheduled date, which is what +promoting `release` from `beta` produced before. + The deliberate approval click is the entire residual ceremony — there is no checklist, no manual version edit, no manual changelog, no manual `npm publish`, and no channel to cut or promote. Because the gate is a GitHub Environment, the @@ -174,9 +183,13 @@ Because the six-week cadence is retained, the policies layered on top of it are - **Deprecations** are still introduced as SemVer-minor and removed in majors, on the same deprecation-freeze schedule. - **Majors** still follow the major-version process of RFC #0830 (its `M.10` - deprecation freeze and `M.12` → `(M+1).0` train). That process is sequenced by - the minor cadence, which this proposal keeps; it simply *executes* via - `release-plan` from `main` instead of via the channel pipeline. + deprecation freeze and `M.12` → `(M+1).0` train). The key point: that process + already controls *when a breaking change is allowed to merge* — deprecation + removals land only in the major window, not mid-cycle. So a `breaking` label + only ever appears on `main` when a major is due, and `release-plan`'s + label-driven bump produces a major exactly when RFC #0830 says it should. The + process is unchanged; it simply *executes* via `release-plan` from `main` + instead of via the channel pipeline. - **LTS** continues as today. In other words, this RFC changes *how* a release is cut and *which branches/ @@ -185,8 +198,8 @@ guarantee*. ### Lockstep across packages -Historically `ember-source`, `ember-cli`, and `ember-data` released in lockstep. -`release-plan` operates per repository. This proposal does **not** mandate +Historically `ember-source`, `ember-cli`, and `ember-data` (now WarpDrive) +released in lockstep. `release-plan` operates per repository. This proposal does **not** mandate dropping lockstep, but it makes lockstep an explicit, opt-in coordination step rather than a side effect of the channel pipeline: From ad52c780877b5cb9a9accbc1a7115592d160ec17 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 21:08:51 -0400 Subject: [PATCH 06/19] Address review: no canary branch, accurate release-plan, drop ember-data Incorporate all inline review feedback: - canary was never a branch; remove canary-branch framing everywhere and the duplicate-the-git-repo section - release-plan is label-driven (breaking/enhancement/bug/documentation/ internal); changelog comes from PR titles; remove changeset "fluff" - we publish from CI already (no "hand-publish"); the cost is maintainer time and bus-factor (Katie/Chris), and release-plan lets any maintainer release - emphasize release-plan handles version+changelog for us - majors aren't manual: breaking label + RFC 830's 12x6-week cadence - drop ember-data mentions; strip em-dash/colon taglines from headings - remove the "main lives in git" statement from the summary Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 289 ++++++++---------- 1 file changed, 132 insertions(+), 157 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 9f83ceca8a..e56f965db3 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -21,27 +21,24 @@ Ember's "release train" today bundles two separate things: 1. **A six-week release cadence.** A new minor ships on a steady, predictable clock. -2. **A multi-channel promotion pipeline.** Code flows through dedicated - `canary` → `beta` → `release` branches, cut and promoted by a rotating - release manager working a mostly-manual checklist each cycle. +2. **A multi-branch promotion pipeline.** Code is promoted from the default + branch through dedicated `beta` and `release` branches (plus LTS branches), + shepherded by a small number of people who manage each release. **This RFC keeps (1) and replaces (2).** The six-week cadence is the part that -works, and it stays exactly as it is. What goes away is the multi-channel branch -machinery and the hand-run release ceremony. +works, and it stays exactly as it is. What goes away is the extra release +branches and the bespoke per-cycle process. Concretely: -- A single long-lived branch: **`main`**. No `canary`, `beta`, or `release` - branches. +- A single long-lived branch: **`main`**. No `beta` or `release` branches. - Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule, with the `npm publish` gated behind a protected [GitHub deployment environment][gh-environments] — a required reviewer approves before it publishes. -- The **`canary` and `beta` channels collapse into a single `alpha`**, published - automatically from `main` (the way Embroider published its prereleases). - Because `ember-source` lives in git, `main` is also directly consumable as a - git ref. Either way, "bleeding edge" is just "whatever is on `main`" — there is - no separate channel branch or nightly promotion job behind it. +- The `beta` channel collapses into a single **`alpha`**, published + automatically from `main` the way Embroider and Glint *used to* publish their + prereleases. `ember-source@beta` consumers move to `ember-source@alpha`. SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch @@ -54,169 +51,153 @@ structure and the publishing mechanics change. ## Motivation The expensive part of the release train is not the cadence — it's the machinery -around it. +around it, and the time it takes maintainers with limited availability to manage +it all. -### Three live channels are a standing branch-management cost +### Releases depend on too few people -Maintaining `canary`, `beta`, and `release` as separate, simultaneously-live -lines (plus LTS) means ongoing cherry-picks, back-merges, and branch bookkeeping -that exist only to service the shape of the train. Every cycle a release manager -(RM) works a long, mostly-manual checklist: cut `beta` from `canary`, promote -`beta` to `release`, hand-publish `ember-source`, `ember-cli`, and (historically) -`ember-data` in lockstep, regenerate and proofread the changelog, and coordinate -the release. That work: +There is no formal release-manager rotation. In practice the release depends on +essentially one person per project — Katie for `ember.js`, Chris for +`ember-cli`. That is a real bus-factor and burnout risk: when that person is +unavailable, the release slips. -- **Needs a volunteer on a clock, indefinitely**, and slips when no RM is - available. It is a recurring source of burnout and bus-factor risk. -- **Is manual and therefore error-prone** — version bumps, changelog assembly, - and multi-package lockstep publishing are exactly the mechanical steps tooling - does more reliably than a person at the end of a checklist. +The biggest win of moving off the train is that releasing stops being +specialized knowledge. With [`release-plan`][release-plan], **any maintainer can +trivially cut a release**: they review the release-preview PR and approve a +deployment. No npm keys on anyone's machine, no checklist, no tribal knowledge. -### The ecosystem already automated this +### Extra release branches are overhead -Nearly every modern Ember addon releases with [`release-plan`][release-plan]. It -derives the SemVer bump and changelog from merged PRs and publishes to npm with -provenance, no scheduled human ceremony required. The framework does not need a -bespoke, hand-run process that is strictly more work than the tooling everyone -else already trusts. **We can get away with `release-plan`.** +Keeping `beta`, `release`, and the LTS branches alive alongside the default +branch means ongoing backporting and branch bookkeeping that exists only to +service the shape of the train. A single `main` removes all of it. -### A separate `canary` branch mostly duplicates "the git repo" +### release-plan automates the mechanical work -The reason `canary` exists is so people can run unreleased Ember — and it is -already consumed straight from git, not from an `ember-source@canary` npm -dist-tag. But "the latest unreleased code" is exactly what the default branch -*is*. A separate `canary` branch plus its nightly promotion job is extra -plumbing to approximate "whatever is on `main` right now." Publishing an `alpha` -prerelease automatically from `main` (and keeping the existing git-ref workflow) -delivers the same capability with none of the branch machinery. +Computing the next version number and assembling the changelog are exactly the +mechanical, error-prone steps that should not be done by hand — and this is an +important point: **`release-plan` does this for us**, deterministically, from the +labels and titles of the PRs that were merged. The Ember ecosystem has already +standardized on `release-plan` for almost every addon; the framework does not +need a bespoke process that is strictly more work than the tooling everyone else +already trusts. -### Keep the cadence — it works +### Keep the cadence -The six-week cadence is predictable, well-understood, and well-loved. It is *not* -what makes the train expensive, so this proposal deliberately keeps it. We are -moving off the *channel-and-ceremony* model, not the calendar. +The six-week cadence is predictable, well-understood, and well-loved. It is +*not* what makes the train expensive, so this proposal deliberately keeps it. We +are moving off the *branch-and-process* model, not the calendar. ## Detailed design ### Branching model A single long-lived branch: **`main`**. All pull requests merge here. `main` is -both where work integrates and the source of every release. There are no -`canary`, `beta`, or `release` branches, and none of the cherry-pick / -back-merge machinery that keeping three live channels requires. +both where work integrates and the source of every release. There are no `beta` +or `release` branches, and none of the backport machinery that keeping multiple +release branches requires. ### Per-PR release metadata -Every PR that changes shipped code declares: +`release-plan` is label-driven — this is the one way it works. Every PR is +labeled with one of its labels: -1. **A SemVer impact** — `patch`, `minor`, or `breaking` (major) — via a label - (or a changeset file, whichever the team prefers; `release-plan` supports the - label-driven flow out of the box). -2. **A changelog entry** — the human-readable "what changed," authored as part - of review rather than reconstructed afterward. +- `breaking` → major impact +- `enhancement` → minor impact +- `bug` → patch impact +- `documentation`, `internal` → recorded, no release on their own -`release-plan` aggregates these across everything merged since the last release -to compute the next version number and assemble the changelog. The decision a -release manager used to make by reading the diff is now made, deterministically, -from metadata that was reviewed when the change landed. +The changelog entry for a release *is* the set of merged PR titles (editable +later by editing the title), so there is nothing extra to author. `release-plan` +reads the labels and titles of everything merged since the last release to +compute the next version and assemble the changelog. The judgment a maintainer +used to apply by reading the diff is captured by the label on each PR at the time +it lands. -Breaking changes (`breaking` label) still require the same approval they require -today: a `major` is a steering-level decision, sequenced by the major-version -process (RFC #0830), not something an automated bump performs silently. +### Releasing -### Releasing: the six-week schedule, automated and gated +The cadence is unchanged; the *mechanics* are `release-plan`'s defaults: -The cadence is unchanged; the *mechanics* are automated: - -1. `release-plan prepare` keeps a release PR continuously up to date — it - computes the pending version and changelog from the metadata of everything - merged since the last release. -2. On the scheduled six-week release date, that release PR is merged — by a +1. `release-plan` keeps a **release-preview PR** up to date — it bumps the + version in `package.json`, edits `CHANGELOG.md`, and records the plan, from + the labels and titles merged since the last release. +2. On the scheduled six-week release date, that preview PR is merged — by a maintainer or a scheduled workflow — which triggers the publish workflow. (The schedule is the rhythm; nothing forces a release between scheduled - dates, and a date can still be held or moved by the same people who manage - the calendar today.) + dates, and a date can still be held or moved as it is today.) 3. The publish job targets a **protected GitHub Environment** (e.g. - `npm-publish`) with a *required reviewers* protection rule. The npm token / + `npm-publish`) with a *required reviewers* rule. The npm token / trusted-publishing identity is scoped to that environment, so nothing can publish until a required reviewer clicks **Approve** on the pending - deployment. -4. On approval, the job runs `release-plan publish`: it tags, pushes, and + deployment. Maintainers never need npm keys locally. +4. On approval, the CI job runs `release-plan publish`: it tags, pushes, and publishes to npm (with provenance via OIDC trusted publishing). -Across a cycle, `release-plan` collapses all the labeled PRs into a *single* -version bump — highest impact wins, so six weeks of `minor`-labeled work yields -one minor, exactly as one stable minor per cycle does today. `@alpha` publishes -the in-progress version continuously (e.g. `6.5.0-alpha.N`); the scheduled -stable cut publishes the finalized version (`6.5.0`) as `@latest`. The stable -release is just a snapshot of `main` at the scheduled date, which is what -promoting `release` from `beta` produced before. - -The deliberate approval click is the entire residual ceremony — there is no -checklist, no manual version edit, no manual changelog, no manual `npm publish`, -and no channel to cut or promote. Because the gate is a GitHub Environment, the -existing GitHub permission and audit model applies: who may approve, the record -of who approved what, and required-reviewer rotation are all standard repo -configuration rather than tribal release-manager knowledge. +Across a cycle, `release-plan` collapses everything merged into a *single* +version bump — highest impact wins, so six weeks of `enhancement` PRs yields one +minor, exactly as one stable minor per cycle does today. `@alpha` publishes the +in-progress version continuously (e.g. `6.5.0-alpha.N`); the scheduled stable cut +publishes the finalized version (`6.5.0`) as `@latest`. The stable release is +just a snapshot of `main` at the scheduled date — the same thing promoting +`release` from `beta` produced before. + +The approval click is the entire residual ceremony: no manual version edit, no +manual changelog, no manual publish, and no branch to cut or promote. Because the +gate is a GitHub Environment, the existing GitHub permission and audit model +applies — who may approve and the record of who approved what are standard repo +configuration rather than tribal knowledge. ### Channels - **Stable (`latest`)** — published from `main` through the gated environment, on the six-week cadence, as above. -- **`alpha`** — published automatically from `main`, the way Embroider published - its prereleases. This is the bleeding-edge stream, and it gives `main` a - turnkey npm dist-tag (`ember-source@alpha`) — something canary never had on - npm. Because `ember-source` lives in git, `main` also remains directly - consumable as a git ref (e.g. `emberjs/ember.js#`), exactly as canary is - consumed today. There is no separate channel branch or nightly promotion job - behind it; the latest in-progress code is just the head of `main`. -- **`beta`** — removed. The dedicated `beta` channel, its branch, and the - `ember-source@beta` dist-tag go away; its consumers move to `@alpha` (or the - git ref). A change that warrants extra baking can still be merged early and - exercised via `@alpha` before the next scheduled stable release. - -### Deprecations, majors, and LTS — unchanged +- **`alpha`** — published automatically from `main`, the way Embroider and Glint + *used to* publish their prereleases. This is the bleeding-edge stream, and it + gives `main` a turnkey npm dist-tag (`ember-source@alpha`). +- **`beta`** — removed. The dedicated `beta` branch and the `ember-source@beta` + dist-tag go away; its consumers move to `@alpha`. A change that warrants extra + baking can still be merged early and exercised via `@alpha` before the next + scheduled stable release. + +### Deprecations, majors, and LTS Because the six-week cadence is retained, the policies layered on top of it are -**unaffected**: +unaffected: - **Deprecations** are still introduced as SemVer-minor and removed in majors, on the same deprecation-freeze schedule. -- **Majors** still follow the major-version process of RFC #0830 (its `M.10` - deprecation freeze and `M.12` → `(M+1).0` train). The key point: that process - already controls *when a breaking change is allowed to merge* — deprecation - removals land only in the major window, not mid-cycle. So a `breaking` label - only ever appears on `main` when a major is due, and `release-plan`'s - label-driven bump produces a major exactly when RFC #0830 says it should. The - process is unchanged; it simply *executes* via `release-plan` from `main` - instead of via the channel pipeline. +- **Majors** are not a manual process either. A PR is labeled `breaking`, and + because RFC #0830 puts a major on the normal cadence — one major roughly every + twelve 6-week minors, after the `M.10` deprecation freeze — `breaking` PRs only + merge in that window. So `release-plan`'s label-driven bump produces the major + on schedule, with no manual version work. The major-version process is + unchanged; it simply executes via `release-plan` from `main`. - **LTS** continues as today. -In other words, this RFC changes *how* a release is cut and *which branches/ -channels exist*, not *when* releases happen or *what compatibility they -guarantee*. +In other words, this RFC changes *how* a release is cut and *which branches +exist*, not *when* releases happen or *what compatibility they guarantee*. ### Lockstep across packages -Historically `ember-source`, `ember-cli`, and `ember-data` (now WarpDrive) -released in lockstep. `release-plan` operates per repository. This proposal does **not** mandate +Historically `ember-source` and `ember-cli` released in lockstep. +`release-plan` operates per repository. This proposal does **not** mandate dropping lockstep, but it makes lockstep an explicit, opt-in coordination step -rather than a side effect of the channel pipeline: +rather than a side effect of the branch pipeline: - If lockstep is kept, the release workflow coordinates the version across the packages at release time. - If lockstep is relaxed, normal SemVer ranges already express cross-package compatibility, and each package releases on its own schedule. -This is called out as a design consideration rather than decided here; see +This is a design consideration rather than something decided here; see [Unresolved questions](#unresolved-questions). ### What is removed -- The `canary` and `beta` branches, and the `ember-source@beta` dist-tag. -- The cherry-pick / back-merge machinery for keeping three live channels. -- The release-manager checklist: manual version bumps, manual changelog - assembly, manual lockstep `npm publish`, and channel cut/promote steps. +- The `beta` and `release` branches, and the `ember-source@beta` dist-tag. +- The backport machinery for keeping multiple release branches alive. +- The bespoke per-cycle release process: version bumps, changelog assembly, and + cut/promote steps now handled by `release-plan`. ### What is kept @@ -228,62 +209,56 @@ This is called out as a design consideration rather than decided here; see ## How we teach this -**Contributors** learn one habit: every PR that changes shipped code gets a -SemVer-impact label and a changelog entry, authored during review. This is the -same workflow they already follow in virtually every addon. +**Contributors** keep doing what they already do in virtually every addon: label +each PR with a `release-plan` label. The PR title becomes the changelog entry. -**Release approvers** (a documented, rotating group with access to the protected -environment) review the pre-computed release PR and approve the deployment. The -role shrinks from "run the channel/checklist ceremony" to "review and click -approve." +**Maintainers** gain the ability to release. The role shrinks from "shepherd the +branches and the cut" to "review the release-preview PR and approve the +deployment" — something any maintainer can do, from anywhere, without npm keys. **Consumers of `beta`** are the audience whose workflow moves. Anyone who today depends on `ember-source@beta` — `ember-try` scenarios, addon CI matrices that test against upcoming Ember, people bisecting regressions — switches to -`ember-source@alpha` (or a git ref). Canary consumers are unaffected: they -already use a git ref, and that keeps working. The default `ember-try` / -blueprint scenarios should be updated to point at `@alpha`. +`ember-source@alpha`. The default `ember-try` / blueprint scenarios should be +updated to point at `@alpha`. Documentation work: - Rewrite the website Releases page: `main` is the release line, the six-week cadence is unchanged, the prerelease stream is `@alpha` (auto-published from `main`), and `beta` no longer exists. -- Update the contributor guide with the label/changelog workflow. -- Document the environment, the approver group, and the approval procedure. +- Update the contributor guide with the labeling workflow. +- Document the environment, who can approve, and the approval procedure. - A migration/announcement blog post focused on `@beta` → `@alpha`. ## Drawbacks -- **Removing the `@beta` dist-tag still moves some consumers.** `ember-try` - scenarios and addon CI matrices pinned to `ember-source@beta` have to switch to - `@alpha`. This is a one-time, mechanical migration rather than a loss of - capability — `@alpha` is auto-published from `main` and the canary git-ref - workflow is unchanged — but it is still ecosystem-wide churn that needs - coordinating. +- **Removing the `@beta` dist-tag moves some consumers.** `ember-try` scenarios + and addon CI matrices pinned to `ember-source@beta` have to switch to `@alpha`. + This is a one-time, mechanical migration rather than a loss of capability, but + it is still ecosystem-wide churn that needs coordinating. - **Collapsing `beta` into `alpha` removes a soak stage.** Today `beta` is a - distinct, more-stable-than-canary checkpoint. Folding it into `@alpha` means - there is one prerelease stream, not two; changes get less differentiated baking - before a scheduled stable release. -- **Per-PR labeling discipline.** A wrong impact label yields a wrong bump. - `release-plan` makes the bump deterministic, but the input is human-supplied. -- **Concentrated publish authority.** Publish power moves to whoever can approve - the protected environment; that group's security and rotation matter. + distinct checkpoint between the default branch and stable. Folding it into + `@alpha` means one prerelease stream, not two; changes get less differentiated + baking before a scheduled stable release. +- **Per-PR labeling discipline.** A wrong label yields a wrong bump. + `release-plan` makes the bump deterministic, but the label is human-supplied. +- **Concentrated publish authority.** The set of people who can approve the + protected environment matters; that group's security and rotation need care. - **Tooling dependency.** The framework's release process becomes coupled to `release-plan` — a small, community-owned tool, but a new dependency. -- **Cultural change.** `canary` and `beta` are long-standing, load-bearing parts - of Ember's testing culture and infrastructure; removing them as channels is not - only a mechanical change. +- **Cultural change.** `beta` is a long-standing, load-bearing part of Ember's + testing culture and infrastructure; removing it is not only a mechanical + change. ## Alternatives -- **Keep the train as-is (status quo).** Pays the recurring branch-management and - RM-ceremony cost, and the bus-factor risk, indefinitely. +- **Keep the train as-is (status quo).** Pays the recurring branch-management + cost and the bus-factor risk indefinitely. - **Keep `beta` as a second prerelease stream.** Retain a `@beta` dist-tag - alongside `@alpha`, both auto-published from `main` (e.g. `@beta` from the - pending release PR). This preserves the extra soak stage at the cost of a - second prerelease tag to reason about. Worth considering if a single `@alpha` - stream proves too coarse for downstream CI. + alongside `@alpha`, both auto-published from `main`. This preserves the extra + soak stage at the cost of a second prerelease tag to reason about. Worth + considering if a single `@alpha` stream proves too coarse for downstream CI. - **Use `changesets` instead of `release-plan`.** Functionally similar; `release-plan` is preferred because the Ember ecosystem has standardized on it. - **Also drop the cadence (fully continuous releases).** A more radical model @@ -298,8 +273,8 @@ Documentation work: - **Whether `beta` is fully dropped or kept as a second `@beta` prerelease stream** (the alternative above). - **`ember-try` and ecosystem CI migration** off `@beta` scenarios onto `@alpha`. -- **Lockstep.** Do `ember-source`, `ember-cli`, `ember-data`/WarpDrive keep - lockstep versioning, or release independently? If lockstep, where does the - coordination live in the workflow? +- **Lockstep.** Do `ember-source` and `ember-cli` keep lockstep versioning, or + release independently? If lockstep, where does the coordination live in the + workflow? - **Approver group.** Who can approve the protected environment, how membership - rotates, and the security posture (2FA, OIDC trusted publishing, audit). + rotates, and the security posture (OIDC trusted publishing, audit). From 618d3da73a74ef4ed0363e8df78f176308ecff2c Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 21:20:25 -0400 Subject: [PATCH 07/19] Reflow: one line per paragraph and list item, no hard wrapping Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 223 +++++------------- 1 file changed, 55 insertions(+), 168 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index e56f965db3..1670c6d05a 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -19,30 +19,18 @@ suite: Ember's "release train" today bundles two separate things: -1. **A six-week release cadence.** A new minor ships on a steady, predictable - clock. -2. **A multi-branch promotion pipeline.** Code is promoted from the default - branch through dedicated `beta` and `release` branches (plus LTS branches), - shepherded by a small number of people who manage each release. +1. **A six-week release cadence.** A new minor ships on a steady, predictable clock. +2. **A multi-branch promotion pipeline.** Code is promoted from the default branch through dedicated `beta` and `release` branches (plus LTS branches), shepherded by a small number of people who manage each release. -**This RFC keeps (1) and replaces (2).** The six-week cadence is the part that -works, and it stays exactly as it is. What goes away is the extra release -branches and the bespoke per-cycle process. +**This RFC keeps (1) and replaces (2).** The six-week cadence is the part that works, and it stays exactly as it is. What goes away is the extra release branches and the bespoke per-cycle process. Concretely: - A single long-lived branch: **`main`**. No `beta` or `release` branches. -- Stable releases are cut from `main` by [`release-plan`][release-plan] on the - same six-week schedule, with the `npm publish` gated behind a protected - [GitHub deployment environment][gh-environments] — a required reviewer - approves before it publishes. -- The `beta` channel collapses into a single **`alpha`**, published - automatically from `main` the way Embroider and Glint *used to* publish their - prereleases. `ember-source@beta` consumers move to `ember-source@alpha`. - -SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version -process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch -structure and the publishing mechanics change. +- Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule, with the `npm publish` gated behind a protected [GitHub deployment environment][gh-environments] — a required reviewer approves before it publishes. +- The `beta` channel collapses into a single **`alpha`**, published automatically from `main` the way Embroider and Glint *used to* publish their prereleases. `ember-source@beta` consumers move to `ember-source@alpha`. + +SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch structure and the publishing mechanics change. [release-plan]: https://github.com/embroider-build/release-plan [gh-environments]: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment @@ -50,154 +38,86 @@ structure and the publishing mechanics change. ## Motivation -The expensive part of the release train is not the cadence — it's the machinery -around it, and the time it takes maintainers with limited availability to manage -it all. +The expensive part of the release train is not the cadence — it's the machinery around it, and the time it takes maintainers with limited availability to manage it all. ### Releases depend on too few people -There is no formal release-manager rotation. In practice the release depends on -essentially one person per project — Katie for `ember.js`, Chris for -`ember-cli`. That is a real bus-factor and burnout risk: when that person is -unavailable, the release slips. +There is no formal release-manager rotation. In practice the release depends on essentially one person per project — Katie for `ember.js`, Chris for `ember-cli`. That is a real bus-factor and burnout risk: when that person is unavailable, the release slips. -The biggest win of moving off the train is that releasing stops being -specialized knowledge. With [`release-plan`][release-plan], **any maintainer can -trivially cut a release**: they review the release-preview PR and approve a -deployment. No npm keys on anyone's machine, no checklist, no tribal knowledge. +The biggest win of moving off the train is that releasing stops being specialized knowledge. With [`release-plan`][release-plan], **any maintainer can trivially cut a release**: they review the release-preview PR and approve a deployment. No npm keys on anyone's machine, no checklist, no tribal knowledge. ### Extra release branches are overhead -Keeping `beta`, `release`, and the LTS branches alive alongside the default -branch means ongoing backporting and branch bookkeeping that exists only to -service the shape of the train. A single `main` removes all of it. +Keeping `beta`, `release`, and the LTS branches alive alongside the default branch means ongoing backporting and branch bookkeeping that exists only to service the shape of the train. A single `main` removes all of it. ### release-plan automates the mechanical work -Computing the next version number and assembling the changelog are exactly the -mechanical, error-prone steps that should not be done by hand — and this is an -important point: **`release-plan` does this for us**, deterministically, from the -labels and titles of the PRs that were merged. The Ember ecosystem has already -standardized on `release-plan` for almost every addon; the framework does not -need a bespoke process that is strictly more work than the tooling everyone else -already trusts. +Computing the next version number and assembling the changelog are exactly the mechanical, error-prone steps that should not be done by hand — and this is an important point: **`release-plan` does this for us**, deterministically, from the labels and titles of the PRs that were merged. The Ember ecosystem has already standardized on `release-plan` for almost every addon; the framework does not need a bespoke process that is strictly more work than the tooling everyone else already trusts. ### Keep the cadence -The six-week cadence is predictable, well-understood, and well-loved. It is -*not* what makes the train expensive, so this proposal deliberately keeps it. We -are moving off the *branch-and-process* model, not the calendar. +The six-week cadence is predictable, well-understood, and well-loved. It is *not* what makes the train expensive, so this proposal deliberately keeps it. We are moving off the *branch-and-process* model, not the calendar. ## Detailed design ### Branching model -A single long-lived branch: **`main`**. All pull requests merge here. `main` is -both where work integrates and the source of every release. There are no `beta` -or `release` branches, and none of the backport machinery that keeping multiple -release branches requires. +A single long-lived branch: **`main`**. All pull requests merge here. `main` is both where work integrates and the source of every release. There are no `beta` or `release` branches, and none of the backport machinery that keeping multiple release branches requires. ### Per-PR release metadata -`release-plan` is label-driven — this is the one way it works. Every PR is -labeled with one of its labels: +`release-plan` is label-driven — this is the one way it works. Every PR is labeled with one of its labels: - `breaking` → major impact - `enhancement` → minor impact - `bug` → patch impact - `documentation`, `internal` → recorded, no release on their own -The changelog entry for a release *is* the set of merged PR titles (editable -later by editing the title), so there is nothing extra to author. `release-plan` -reads the labels and titles of everything merged since the last release to -compute the next version and assemble the changelog. The judgment a maintainer -used to apply by reading the diff is captured by the label on each PR at the time -it lands. +The changelog entry for a release *is* the set of merged PR titles (editable later by editing the title), so there is nothing extra to author. `release-plan` reads the labels and titles of everything merged since the last release to compute the next version and assemble the changelog. The judgment a maintainer used to apply by reading the diff is captured by the label on each PR at the time it lands. ### Releasing The cadence is unchanged; the *mechanics* are `release-plan`'s defaults: -1. `release-plan` keeps a **release-preview PR** up to date — it bumps the - version in `package.json`, edits `CHANGELOG.md`, and records the plan, from - the labels and titles merged since the last release. -2. On the scheduled six-week release date, that preview PR is merged — by a - maintainer or a scheduled workflow — which triggers the publish workflow. - (The schedule is the rhythm; nothing forces a release between scheduled - dates, and a date can still be held or moved as it is today.) -3. The publish job targets a **protected GitHub Environment** (e.g. - `npm-publish`) with a *required reviewers* rule. The npm token / - trusted-publishing identity is scoped to that environment, so nothing can - publish until a required reviewer clicks **Approve** on the pending - deployment. Maintainers never need npm keys locally. -4. On approval, the CI job runs `release-plan publish`: it tags, pushes, and - publishes to npm (with provenance via OIDC trusted publishing). - -Across a cycle, `release-plan` collapses everything merged into a *single* -version bump — highest impact wins, so six weeks of `enhancement` PRs yields one -minor, exactly as one stable minor per cycle does today. `@alpha` publishes the -in-progress version continuously (e.g. `6.5.0-alpha.N`); the scheduled stable cut -publishes the finalized version (`6.5.0`) as `@latest`. The stable release is -just a snapshot of `main` at the scheduled date — the same thing promoting -`release` from `beta` produced before. - -The approval click is the entire residual ceremony: no manual version edit, no -manual changelog, no manual publish, and no branch to cut or promote. Because the -gate is a GitHub Environment, the existing GitHub permission and audit model -applies — who may approve and the record of who approved what are standard repo -configuration rather than tribal knowledge. +1. `release-plan` keeps a **release-preview PR** up to date — it bumps the version in `package.json`, edits `CHANGELOG.md`, and records the plan, from the labels and titles merged since the last release. +2. On the scheduled six-week release date, that preview PR is merged — by a maintainer or a scheduled workflow — which triggers the publish workflow. (The schedule is the rhythm; nothing forces a release between scheduled dates, and a date can still be held or moved as it is today.) +3. The publish job targets a **protected GitHub Environment** (e.g. `npm-publish`) with a *required reviewers* rule. The npm token / trusted-publishing identity is scoped to that environment, so nothing can publish until a required reviewer clicks **Approve** on the pending deployment. Maintainers never need npm keys locally. +4. On approval, the CI job runs `release-plan publish`: it tags, pushes, and publishes to npm (with provenance via OIDC trusted publishing). + +Across a cycle, `release-plan` collapses everything merged into a *single* version bump — highest impact wins, so six weeks of `enhancement` PRs yields one minor, exactly as one stable minor per cycle does today. `@alpha` publishes the in-progress version continuously (e.g. `6.5.0-alpha.N`); the scheduled stable cut publishes the finalized version (`6.5.0`) as `@latest`. The stable release is just a snapshot of `main` at the scheduled date — the same thing promoting `release` from `beta` produced before. + +The approval click is the entire residual ceremony: no manual version edit, no manual changelog, no manual publish, and no branch to cut or promote. Because the gate is a GitHub Environment, the existing GitHub permission and audit model applies — who may approve and the record of who approved what are standard repo configuration rather than tribal knowledge. ### Channels -- **Stable (`latest`)** — published from `main` through the gated environment, - on the six-week cadence, as above. -- **`alpha`** — published automatically from `main`, the way Embroider and Glint - *used to* publish their prereleases. This is the bleeding-edge stream, and it - gives `main` a turnkey npm dist-tag (`ember-source@alpha`). -- **`beta`** — removed. The dedicated `beta` branch and the `ember-source@beta` - dist-tag go away; its consumers move to `@alpha`. A change that warrants extra - baking can still be merged early and exercised via `@alpha` before the next - scheduled stable release. +- **Stable (`latest`)** — published from `main` through the gated environment, on the six-week cadence, as above. +- **`alpha`** — published automatically from `main`, the way Embroider and Glint *used to* publish their prereleases. This is the bleeding-edge stream, and it gives `main` a turnkey npm dist-tag (`ember-source@alpha`). +- **`beta`** — removed. The dedicated `beta` branch and the `ember-source@beta` dist-tag go away; its consumers move to `@alpha`. A change that warrants extra baking can still be merged early and exercised via `@alpha` before the next scheduled stable release. ### Deprecations, majors, and LTS -Because the six-week cadence is retained, the policies layered on top of it are -unaffected: - -- **Deprecations** are still introduced as SemVer-minor and removed in majors, - on the same deprecation-freeze schedule. -- **Majors** are not a manual process either. A PR is labeled `breaking`, and - because RFC #0830 puts a major on the normal cadence — one major roughly every - twelve 6-week minors, after the `M.10` deprecation freeze — `breaking` PRs only - merge in that window. So `release-plan`'s label-driven bump produces the major - on schedule, with no manual version work. The major-version process is - unchanged; it simply executes via `release-plan` from `main`. +Because the six-week cadence is retained, the policies layered on top of it are unaffected: + +- **Deprecations** are still introduced as SemVer-minor and removed in majors, on the same deprecation-freeze schedule. +- **Majors** are not a manual process either. A PR is labeled `breaking`, and because RFC #0830 puts a major on the normal cadence — one major roughly every twelve 6-week minors, after the `M.10` deprecation freeze — `breaking` PRs only merge in that window. So `release-plan`'s label-driven bump produces the major on schedule, with no manual version work. The major-version process is unchanged; it simply executes via `release-plan` from `main`. - **LTS** continues as today. -In other words, this RFC changes *how* a release is cut and *which branches -exist*, not *when* releases happen or *what compatibility they guarantee*. +In other words, this RFC changes *how* a release is cut and *which branches exist*, not *when* releases happen or *what compatibility they guarantee*. ### Lockstep across packages -Historically `ember-source` and `ember-cli` released in lockstep. -`release-plan` operates per repository. This proposal does **not** mandate -dropping lockstep, but it makes lockstep an explicit, opt-in coordination step -rather than a side effect of the branch pipeline: +Historically `ember-source` and `ember-cli` released in lockstep. `release-plan` operates per repository. This proposal does **not** mandate dropping lockstep, but it makes lockstep an explicit, opt-in coordination step rather than a side effect of the branch pipeline: -- If lockstep is kept, the release workflow coordinates the version across the - packages at release time. -- If lockstep is relaxed, normal SemVer ranges already express cross-package - compatibility, and each package releases on its own schedule. +- If lockstep is kept, the release workflow coordinates the version across the packages at release time. +- If lockstep is relaxed, normal SemVer ranges already express cross-package compatibility, and each package releases on its own schedule. -This is a design consideration rather than something decided here; see -[Unresolved questions](#unresolved-questions). +This is a design consideration rather than something decided here; see [Unresolved questions](#unresolved-questions). ### What is removed - The `beta` and `release` branches, and the `ember-source@beta` dist-tag. - The backport machinery for keeping multiple release branches alive. -- The bespoke per-cycle release process: version bumps, changelog assembly, and - cut/promote steps now handled by `release-plan`. +- The bespoke per-cycle release process: version bumps, changelog assembly, and cut/promote steps now handled by `release-plan`. ### What is kept @@ -209,72 +129,39 @@ This is a design consideration rather than something decided here; see ## How we teach this -**Contributors** keep doing what they already do in virtually every addon: label -each PR with a `release-plan` label. The PR title becomes the changelog entry. +**Contributors** keep doing what they already do in virtually every addon: label each PR with a `release-plan` label. The PR title becomes the changelog entry. -**Maintainers** gain the ability to release. The role shrinks from "shepherd the -branches and the cut" to "review the release-preview PR and approve the -deployment" — something any maintainer can do, from anywhere, without npm keys. +**Maintainers** gain the ability to release. The role shrinks from "shepherd the branches and the cut" to "review the release-preview PR and approve the deployment" — something any maintainer can do, from anywhere, without npm keys. -**Consumers of `beta`** are the audience whose workflow moves. Anyone who today -depends on `ember-source@beta` — `ember-try` scenarios, addon CI matrices that -test against upcoming Ember, people bisecting regressions — switches to -`ember-source@alpha`. The default `ember-try` / blueprint scenarios should be -updated to point at `@alpha`. +**Consumers of `beta`** are the audience whose workflow moves. Anyone who today depends on `ember-source@beta` — `ember-try` scenarios, addon CI matrices that test against upcoming Ember, people bisecting regressions — switches to `ember-source@alpha`. The default `ember-try` / blueprint scenarios should be updated to point at `@alpha`. Documentation work: -- Rewrite the website Releases page: `main` is the release line, the six-week - cadence is unchanged, the prerelease stream is `@alpha` (auto-published from - `main`), and `beta` no longer exists. +- Rewrite the website Releases page: `main` is the release line, the six-week cadence is unchanged, the prerelease stream is `@alpha` (auto-published from `main`), and `beta` no longer exists. - Update the contributor guide with the labeling workflow. - Document the environment, who can approve, and the approval procedure. - A migration/announcement blog post focused on `@beta` → `@alpha`. ## Drawbacks -- **Removing the `@beta` dist-tag moves some consumers.** `ember-try` scenarios - and addon CI matrices pinned to `ember-source@beta` have to switch to `@alpha`. - This is a one-time, mechanical migration rather than a loss of capability, but - it is still ecosystem-wide churn that needs coordinating. -- **Collapsing `beta` into `alpha` removes a soak stage.** Today `beta` is a - distinct checkpoint between the default branch and stable. Folding it into - `@alpha` means one prerelease stream, not two; changes get less differentiated - baking before a scheduled stable release. -- **Per-PR labeling discipline.** A wrong label yields a wrong bump. - `release-plan` makes the bump deterministic, but the label is human-supplied. -- **Concentrated publish authority.** The set of people who can approve the - protected environment matters; that group's security and rotation need care. -- **Tooling dependency.** The framework's release process becomes coupled to - `release-plan` — a small, community-owned tool, but a new dependency. -- **Cultural change.** `beta` is a long-standing, load-bearing part of Ember's - testing culture and infrastructure; removing it is not only a mechanical - change. +- **Removing the `@beta` dist-tag moves some consumers.** `ember-try` scenarios and addon CI matrices pinned to `ember-source@beta` have to switch to `@alpha`. This is a one-time, mechanical migration rather than a loss of capability, but it is still ecosystem-wide churn that needs coordinating. +- **Collapsing `beta` into `alpha` removes a soak stage.** Today `beta` is a distinct checkpoint between the default branch and stable. Folding it into `@alpha` means one prerelease stream, not two; changes get less differentiated baking before a scheduled stable release. +- **Per-PR labeling discipline.** A wrong label yields a wrong bump. `release-plan` makes the bump deterministic, but the label is human-supplied. +- **Concentrated publish authority.** The set of people who can approve the protected environment matters; that group's security and rotation need care. +- **Tooling dependency.** The framework's release process becomes coupled to `release-plan` — a small, community-owned tool, but a new dependency. +- **Cultural change.** `beta` is a long-standing, load-bearing part of Ember's testing culture and infrastructure; removing it is not only a mechanical change. ## Alternatives -- **Keep the train as-is (status quo).** Pays the recurring branch-management - cost and the bus-factor risk indefinitely. -- **Keep `beta` as a second prerelease stream.** Retain a `@beta` dist-tag - alongside `@alpha`, both auto-published from `main`. This preserves the extra - soak stage at the cost of a second prerelease tag to reason about. Worth - considering if a single `@alpha` stream proves too coarse for downstream CI. -- **Use `changesets` instead of `release-plan`.** Functionally similar; - `release-plan` is preferred because the Ember ecosystem has standardized on it. -- **Also drop the cadence (fully continuous releases).** A more radical model - where every merge can release. Explicitly **not** proposed here — the six-week - cadence is retained deliberately because it works. +- **Keep the train as-is (status quo).** Pays the recurring branch-management cost and the bus-factor risk indefinitely. +- **Keep `beta` as a second prerelease stream.** Retain a `@beta` dist-tag alongside `@alpha`, both auto-published from `main`. This preserves the extra soak stage at the cost of a second prerelease tag to reason about. Worth considering if a single `@alpha` stream proves too coarse for downstream CI. +- **Use `changesets` instead of `release-plan`.** Functionally similar; `release-plan` is preferred because the Ember ecosystem has standardized on it. +- **Also drop the cadence (fully continuous releases).** A more radical model where every merge can release. Explicitly **not** proposed here — the six-week cadence is retained deliberately because it works. ## Unresolved questions -- **`@alpha` publishing cadence.** Does `@alpha` publish on every merge to - `main`, or on a lighter schedule (e.g. nightly)? Either is straightforward with - `release-plan`; this is a tuning decision. -- **Whether `beta` is fully dropped or kept as a second `@beta` prerelease - stream** (the alternative above). +- **`@alpha` publishing cadence.** Does `@alpha` publish on every merge to `main`, or on a lighter schedule (e.g. nightly)? Either is straightforward with `release-plan`; this is a tuning decision. +- **Whether `beta` is fully dropped or kept as a second `@beta` prerelease stream** (the alternative above). - **`ember-try` and ecosystem CI migration** off `@beta` scenarios onto `@alpha`. -- **Lockstep.** Do `ember-source` and `ember-cli` keep lockstep versioning, or - release independently? If lockstep, where does the coordination live in the - workflow? -- **Approver group.** Who can approve the protected environment, how membership - rotates, and the security posture (OIDC trusted publishing, audit). +- **Lockstep.** Do `ember-source` and `ember-cli` keep lockstep versioning, or release independently? If lockstep, where does the coordination live in the workflow? +- **Approver group.** Who can approve the protected environment, how membership rotates, and the security posture (OIDC trusted publishing, audit). From c9d1183bb352b4e4bd34b7abeed31220a261da0f Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 23:50:58 -0400 Subject: [PATCH 08/19] Resolve open decisions: nightly @alpha, drop beta, timing-lockstep, maintainer cut, informal approvers - @alpha publishes nightly from main (not every merge) - beta fully dropped; the keep-beta option is now an alternative, not a TBD - lockstep is timing-based: ember-source/ember-cli release independently on the same six-week date, with no cross-repo coordination - the six-week stable cut is a maintainer merging the release-preview PR - publish approvers are the same active people who release today (informal) - prune the now-decided unresolved questions Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 1670c6d05a..3350b1266a 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -28,7 +28,7 @@ Concretely: - A single long-lived branch: **`main`**. No `beta` or `release` branches. - Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule, with the `npm publish` gated behind a protected [GitHub deployment environment][gh-environments] — a required reviewer approves before it publishes. -- The `beta` channel collapses into a single **`alpha`**, published automatically from `main` the way Embroider and Glint *used to* publish their prereleases. `ember-source@beta` consumers move to `ember-source@alpha`. +- The `beta` channel collapses into a single **`alpha`**, published nightly from `main` the way Embroider and Glint *used to* publish their prereleases. `ember-source@beta` consumers move to `ember-source@alpha`. SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch structure and the publishing mechanics change. @@ -80,18 +80,18 @@ The changelog entry for a release *is* the set of merged PR titles (editable lat The cadence is unchanged; the *mechanics* are `release-plan`'s defaults: 1. `release-plan` keeps a **release-preview PR** up to date — it bumps the version in `package.json`, edits `CHANGELOG.md`, and records the plan, from the labels and titles merged since the last release. -2. On the scheduled six-week release date, that preview PR is merged — by a maintainer or a scheduled workflow — which triggers the publish workflow. (The schedule is the rhythm; nothing forces a release between scheduled dates, and a date can still be held or moved as it is today.) +2. On the scheduled six-week release date, a maintainer merges that preview PR, which triggers the publish workflow. (The schedule is the rhythm; nothing forces a release between scheduled dates, and a date can still be held or moved as it is today.) 3. The publish job targets a **protected GitHub Environment** (e.g. `npm-publish`) with a *required reviewers* rule. The npm token / trusted-publishing identity is scoped to that environment, so nothing can publish until a required reviewer clicks **Approve** on the pending deployment. Maintainers never need npm keys locally. 4. On approval, the CI job runs `release-plan publish`: it tags, pushes, and publishes to npm (with provenance via OIDC trusted publishing). -Across a cycle, `release-plan` collapses everything merged into a *single* version bump — highest impact wins, so six weeks of `enhancement` PRs yields one minor, exactly as one stable minor per cycle does today. `@alpha` publishes the in-progress version continuously (e.g. `6.5.0-alpha.N`); the scheduled stable cut publishes the finalized version (`6.5.0`) as `@latest`. The stable release is just a snapshot of `main` at the scheduled date — the same thing promoting `release` from `beta` produced before. +Across a cycle, `release-plan` collapses everything merged into a *single* version bump — highest impact wins, so six weeks of `enhancement` PRs yields one minor, exactly as one stable minor per cycle does today. `@alpha` publishes the in-progress version nightly (e.g. `6.5.0-alpha.N`); the scheduled stable cut publishes the finalized version (`6.5.0`) as `@latest`. The stable release is just a snapshot of `main` at the scheduled date — the same thing promoting `release` from `beta` produced before. The approval click is the entire residual ceremony: no manual version edit, no manual changelog, no manual publish, and no branch to cut or promote. Because the gate is a GitHub Environment, the existing GitHub permission and audit model applies — who may approve and the record of who approved what are standard repo configuration rather than tribal knowledge. ### Channels - **Stable (`latest`)** — published from `main` through the gated environment, on the six-week cadence, as above. -- **`alpha`** — published automatically from `main`, the way Embroider and Glint *used to* publish their prereleases. This is the bleeding-edge stream, and it gives `main` a turnkey npm dist-tag (`ember-source@alpha`). +- **`alpha`** — published nightly from `main`, the way Embroider and Glint *used to* publish their prereleases. This is the bleeding-edge stream, and it gives `main` a turnkey npm dist-tag (`ember-source@alpha`). - **`beta`** — removed. The dedicated `beta` branch and the `ember-source@beta` dist-tag go away; its consumers move to `@alpha`. A change that warrants extra baking can still be merged early and exercised via `@alpha` before the next scheduled stable release. ### Deprecations, majors, and LTS @@ -106,12 +106,7 @@ In other words, this RFC changes *how* a release is cut and *which branches exis ### Lockstep across packages -Historically `ember-source` and `ember-cli` released in lockstep. `release-plan` operates per repository. This proposal does **not** mandate dropping lockstep, but it makes lockstep an explicit, opt-in coordination step rather than a side effect of the branch pipeline: - -- If lockstep is kept, the release workflow coordinates the version across the packages at release time. -- If lockstep is relaxed, normal SemVer ranges already express cross-package compatibility, and each package releases on its own schedule. - -This is a design consideration rather than something decided here; see [Unresolved questions](#unresolved-questions). +`ember-source` and `ember-cli` continue to release in lockstep — but lockstep here is a *timing* property, not a coupling. Each repo releases independently via `release-plan` (its natural per-repo mode), and the releases don't need to know about each other. Because both cut stable on the same six-week date, their versions stay aligned. There is no cross-repo coordination step in the workflow; the shared cadence is what keeps them in step. ### What is removed @@ -137,7 +132,7 @@ This is a design consideration rather than something decided here; see [Unresolv Documentation work: -- Rewrite the website Releases page: `main` is the release line, the six-week cadence is unchanged, the prerelease stream is `@alpha` (auto-published from `main`), and `beta` no longer exists. +- Rewrite the website Releases page: `main` is the release line, the six-week cadence is unchanged, the prerelease stream is `@alpha` (published nightly from `main`), and `beta` no longer exists. - Update the contributor guide with the labeling workflow. - Document the environment, who can approve, and the approval procedure. - A migration/announcement blog post focused on `@beta` → `@alpha`. @@ -147,21 +142,18 @@ Documentation work: - **Removing the `@beta` dist-tag moves some consumers.** `ember-try` scenarios and addon CI matrices pinned to `ember-source@beta` have to switch to `@alpha`. This is a one-time, mechanical migration rather than a loss of capability, but it is still ecosystem-wide churn that needs coordinating. - **Collapsing `beta` into `alpha` removes a soak stage.** Today `beta` is a distinct checkpoint between the default branch and stable. Folding it into `@alpha` means one prerelease stream, not two; changes get less differentiated baking before a scheduled stable release. - **Per-PR labeling discipline.** A wrong label yields a wrong bump. `release-plan` makes the bump deterministic, but the label is human-supplied. -- **Concentrated publish authority.** The set of people who can approve the protected environment matters; that group's security and rotation need care. +- **Publish authority.** The people who can approve the protected environment are the same active folks who cut releases today — informal, no change — but it does mean the npm publish is only as locked-down as that environment's protection rules and those accounts' security (2FA / OIDC). - **Tooling dependency.** The framework's release process becomes coupled to `release-plan` — a small, community-owned tool, but a new dependency. - **Cultural change.** `beta` is a long-standing, load-bearing part of Ember's testing culture and infrastructure; removing it is not only a mechanical change. ## Alternatives - **Keep the train as-is (status quo).** Pays the recurring branch-management cost and the bus-factor risk indefinitely. -- **Keep `beta` as a second prerelease stream.** Retain a `@beta` dist-tag alongside `@alpha`, both auto-published from `main`. This preserves the extra soak stage at the cost of a second prerelease tag to reason about. Worth considering if a single `@alpha` stream proves too coarse for downstream CI. +- **Keep `beta` as a second prerelease stream.** Retain a `@beta` dist-tag alongside `@alpha`. This proposal drops `beta` for a single `@alpha` stream; the alternative would preserve the extra soak stage at the cost of a second prerelease tag, and could be revisited if a single `@alpha` proves too coarse for downstream CI. - **Use `changesets` instead of `release-plan`.** Functionally similar; `release-plan` is preferred because the Ember ecosystem has standardized on it. - **Also drop the cadence (fully continuous releases).** A more radical model where every merge can release. Explicitly **not** proposed here — the six-week cadence is retained deliberately because it works. ## Unresolved questions -- **`@alpha` publishing cadence.** Does `@alpha` publish on every merge to `main`, or on a lighter schedule (e.g. nightly)? Either is straightforward with `release-plan`; this is a tuning decision. -- **Whether `beta` is fully dropped or kept as a second `@beta` prerelease stream** (the alternative above). -- **`ember-try` and ecosystem CI migration** off `@beta` scenarios onto `@alpha`. -- **Lockstep.** Do `ember-source` and `ember-cli` keep lockstep versioning, or release independently? If lockstep, where does the coordination live in the workflow? -- **Approver group.** Who can approve the protected environment, how membership rotates, and the security posture (OIDC trusted publishing, audit). +- **`@alpha` version scheme.** The exact prerelease format and the `release-plan` config that produces it (e.g. `semverIncrementAs` / `semverIncrementTag`) so nightly `@alpha` builds version sensibly ahead of the next stable. +- **`ember-try` and ecosystem CI migration.** The mechanics of moving the default `ember-try` / blueprint scenarios off `@beta` onto `@alpha`, and helping the ecosystem follow. From 170408e52ecee8e5d52a6a8a867802f93fcdb671 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 23:56:02 -0400 Subject: [PATCH 09/19] Clarify canary is unchanged; alpha scheme is release-plan default; beta cleanup is removal - canary stays exactly as today (main consumed from git); add it explicitly to the channels list and stop implying alpha replaces it - @alpha is a new published npm prerelease of main; release-plan handles the version scheme, nothing special - ember-try/beta consumers remove their @beta scenarios (canary unaffected) - Unresolved questions: none outstanding; only implementation remains Co-Authored-By: Claude Opus 4.8 (1M context) --- ...f-the-release-train-to-simpler-release-model.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 3350b1266a..0804c01090 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -28,7 +28,7 @@ Concretely: - A single long-lived branch: **`main`**. No `beta` or `release` branches. - Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule, with the `npm publish` gated behind a protected [GitHub deployment environment][gh-environments] — a required reviewer approves before it publishes. -- The `beta` channel collapses into a single **`alpha`**, published nightly from `main` the way Embroider and Glint *used to* publish their prereleases. `ember-source@beta` consumers move to `ember-source@alpha`. +- Drop the `beta` channel. A new **`@alpha`** prerelease is published nightly from `main` (the way Embroider and Glint *used to* publish theirs); `ember-source@beta` consumers move to `@alpha` or drop the scenario. **Canary is unchanged** — consuming `main` from git works exactly as it does today. SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch structure and the publishing mechanics change. @@ -91,8 +91,9 @@ The approval click is the entire residual ceremony: no manual version edit, no m ### Channels - **Stable (`latest`)** — published from `main` through the gated environment, on the six-week cadence, as above. -- **`alpha`** — published nightly from `main`, the way Embroider and Glint *used to* publish their prereleases. This is the bleeding-edge stream, and it gives `main` a turnkey npm dist-tag (`ember-source@alpha`). -- **`beta`** — removed. The dedicated `beta` branch and the `ember-source@beta` dist-tag go away; its consumers move to `@alpha`. A change that warrants extra baking can still be merged early and exercised via `@alpha` before the next scheduled stable release. +- **Canary** — *unchanged by this RFC.* Canary is the default branch (`main`) consumed from git, and that keeps working exactly as it does today. +- **`alpha`** — new. A published, npm-installable prerelease of `main`, cut nightly the way Embroider and Glint *used to* publish theirs. It gives `main` a turnkey npm dist-tag (`ember-source@alpha`) for people who want canary's code without consuming from git. The `release-plan` defaults handle the version scheme; nothing special is needed. +- **`beta`** — removed. The dedicated `beta` branch and the `ember-source@beta` dist-tag go away. A change that warrants extra baking can still be merged early and exercised via canary or `@alpha` before the next scheduled stable release. ### Deprecations, majors, and LTS @@ -128,7 +129,7 @@ In other words, this RFC changes *how* a release is cut and *which branches exis **Maintainers** gain the ability to release. The role shrinks from "shepherd the branches and the cut" to "review the release-preview PR and approve the deployment" — something any maintainer can do, from anywhere, without npm keys. -**Consumers of `beta`** are the audience whose workflow moves. Anyone who today depends on `ember-source@beta` — `ember-try` scenarios, addon CI matrices that test against upcoming Ember, people bisecting regressions — switches to `ember-source@alpha`. The default `ember-try` / blueprint scenarios should be updated to point at `@alpha`. +**Consumers of `beta`** are the audience whose workflow moves. Anyone whose `ember-try` config or CI matrix tests `ember-source@beta` removes that scenario, since `@beta` no longer exists; they can add `@alpha` if they want a published prerelease. Canary testing (consuming `main` from git) is unaffected. Documentation work: @@ -139,7 +140,7 @@ Documentation work: ## Drawbacks -- **Removing the `@beta` dist-tag moves some consumers.** `ember-try` scenarios and addon CI matrices pinned to `ember-source@beta` have to switch to `@alpha`. This is a one-time, mechanical migration rather than a loss of capability, but it is still ecosystem-wide churn that needs coordinating. +- **Removing the `@beta` dist-tag moves some consumers.** `ember-try` scenarios and addon CI matrices pinned to `ember-source@beta` have to remove that scenario (optionally adopting `@alpha`). This is a one-time, mechanical cleanup rather than a loss of capability — canary testing is unaffected — but it is still ecosystem-wide churn that needs coordinating. - **Collapsing `beta` into `alpha` removes a soak stage.** Today `beta` is a distinct checkpoint between the default branch and stable. Folding it into `@alpha` means one prerelease stream, not two; changes get less differentiated baking before a scheduled stable release. - **Per-PR labeling discipline.** A wrong label yields a wrong bump. `release-plan` makes the bump deterministic, but the label is human-supplied. - **Publish authority.** The people who can approve the protected environment are the same active folks who cut releases today — informal, no change — but it does mean the npm publish is only as locked-down as that environment's protection rules and those accounts' security (2FA / OIDC). @@ -155,5 +156,4 @@ Documentation work: ## Unresolved questions -- **`@alpha` version scheme.** The exact prerelease format and the `release-plan` config that produces it (e.g. `semverIncrementAs` / `semverIncrementTag`) so nightly `@alpha` builds version sensibly ahead of the next stable. -- **`ember-try` and ecosystem CI migration.** The mechanics of moving the default `ember-try` / blueprint scenarios off `@beta` onto `@alpha`, and helping the ecosystem follow. +None outstanding. The design choices — nightly `@alpha`, dropping `beta`, keeping canary as-is, timing-based lockstep, a maintainer cutting the release, and the existing folks approving the publish — are settled above. What remains is implementation: wiring up the `release-plan` workflows and the protected environment, and the ecosystem-side removal of `ember-source@beta` test scenarios. From 50a3b23efcbbe59cb2f62428b8bd0d9d91d47693 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Jun 2026 23:56:24 -0400 Subject: [PATCH 10/19] Unresolved questions: n/a Co-Authored-By: Claude Opus 4.8 (1M context) --- .../0000-move-off-the-release-train-to-simpler-release-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 0804c01090..e4dad51b61 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -156,4 +156,4 @@ Documentation work: ## Unresolved questions -None outstanding. The design choices — nightly `@alpha`, dropping `beta`, keeping canary as-is, timing-based lockstep, a maintainer cutting the release, and the existing folks approving the publish — are settled above. What remains is implementation: wiring up the `release-plan` workflows and the protected environment, and the ecosystem-side removal of `ember-source@beta` test scenarios. +n/a From cd12ee092d24c664a537d8cbcf3d8e8eb98d1e9d Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:03:01 -0400 Subject: [PATCH 11/19] Rewrite in NullVoxPopuli's RFC voice; remove all em-dashes Conversational tone (today/folks, underscore emphasis, parentheticals), no em-dashes, lighter bold lead-ins. Content unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-release-train-to-simpler-release-model.md | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index e4dad51b61..8e53aac00d 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -17,20 +17,20 @@ suite: ## Summary -Ember's "release train" today bundles two separate things: +Ember's "release train" today is really two things bundled together: -1. **A six-week release cadence.** A new minor ships on a steady, predictable clock. -2. **A multi-branch promotion pipeline.** Code is promoted from the default branch through dedicated `beta` and `release` branches (plus LTS branches), shepherded by a small number of people who manage each release. +1. A six-week release cadence. A new minor ships on a steady, predictable clock. +2. A multi-branch promotion pipeline. Code gets promoted from the default branch through dedicated `beta` and `release` branches (plus LTS branches), shepherded by a small number of folks who manage each release. -**This RFC keeps (1) and replaces (2).** The six-week cadence is the part that works, and it stays exactly as it is. What goes away is the extra release branches and the bespoke per-cycle process. +This RFC keeps (1) and replaces (2). The six-week cadence is the part that works, so it stays exactly as it is. What goes away is the extra release branches and the bespoke, per-cycle process that goes with them. Concretely: -- A single long-lived branch: **`main`**. No `beta` or `release` branches. -- Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule, with the `npm publish` gated behind a protected [GitHub deployment environment][gh-environments] — a required reviewer approves before it publishes. -- Drop the `beta` channel. A new **`@alpha`** prerelease is published nightly from `main` (the way Embroider and Glint *used to* publish theirs); `ember-source@beta` consumers move to `@alpha` or drop the scenario. **Canary is unchanged** — consuming `main` from git works exactly as it does today. +- One long-lived branch, `main`. No `beta` or `release` branches. +- Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule. The `npm publish` is gated behind a protected [GitHub deployment environment][gh-environments], so a maintainer approves before anything publishes. +- We drop the `beta` channel. A new `@alpha` prerelease is published nightly from `main` (the way Embroider and Glint _used to_ publish theirs), and `ember-source@beta` users either move to `@alpha` or drop the scenario. Canary is unchanged: consuming `main` from git works exactly like it does today. -SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from RFC [#0830][rfc-830] are all **unchanged**. Only the branch structure and the publishing mechanics change. +SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from [RFC #0830][rfc-830] are all unchanged. The only things that change are the branch structure and the publishing mechanics. [release-plan]: https://github.com/embroider-build/release-plan [gh-environments]: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment @@ -38,121 +38,121 @@ SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version ## Motivation -The expensive part of the release train is not the cadence — it's the machinery around it, and the time it takes maintainers with limited availability to manage it all. +The expensive part of the release train isn't the cadence. It's all the machinery around it, plus the time it takes maintainers with limited availability to keep it running. ### Releases depend on too few people -There is no formal release-manager rotation. In practice the release depends on essentially one person per project — Katie for `ember.js`, Chris for `ember-cli`. That is a real bus-factor and burnout risk: when that person is unavailable, the release slips. +We don't actually have a formal release-manager rotation. In practice the release comes down to essentially one person per project: Katie for `ember.js`, Chris for `ember-cli`. That's a real bus-factor and burnout risk, because when that person isn't around, the release slips. -The biggest win of moving off the train is that releasing stops being specialized knowledge. With [`release-plan`][release-plan], **any maintainer can trivially cut a release**: they review the release-preview PR and approve a deployment. No npm keys on anyone's machine, no checklist, no tribal knowledge. +The biggest win of getting off the train is that releasing stops being specialized knowledge. With [`release-plan`][release-plan], _any_ maintainer can trivially cut a release: they look over the release-preview PR and approve a deployment. No npm keys on anyone's laptop, no checklist, no tribal knowledge. ### Extra release branches are overhead -Keeping `beta`, `release`, and the LTS branches alive alongside the default branch means ongoing backporting and branch bookkeeping that exists only to service the shape of the train. A single `main` removes all of it. +Keeping `beta`, `release`, and the LTS branches alive next to the default branch means constant backporting and branch bookkeeping that only exists to service the shape of the train. A single `main` gets rid of all of it. ### release-plan automates the mechanical work -Computing the next version number and assembling the changelog are exactly the mechanical, error-prone steps that should not be done by hand — and this is an important point: **`release-plan` does this for us**, deterministically, from the labels and titles of the PRs that were merged. The Ember ecosystem has already standardized on `release-plan` for almost every addon; the framework does not need a bespoke process that is strictly more work than the tooling everyone else already trusts. +Working out the next version number and assembling the changelog are exactly the mechanical, error-prone steps that shouldn't be done by hand, and this is the important bit: release-plan does this for us, deterministically, from the labels and titles of the PRs that got merged. The ecosystem has already standardized on release-plan for almost every addon, so the framework doesn't need a bespoke process that's strictly more work than the tooling everyone else already trusts. ### Keep the cadence -The six-week cadence is predictable, well-understood, and well-loved. It is *not* what makes the train expensive, so this proposal deliberately keeps it. We are moving off the *branch-and-process* model, not the calendar. +The six-week cadence is predictable, well understood, and well loved. It's _not_ what makes the train expensive, so we deliberately keep it. We're moving off the branch-and-process model, not the calendar. ## Detailed design ### Branching model -A single long-lived branch: **`main`**. All pull requests merge here. `main` is both where work integrates and the source of every release. There are no `beta` or `release` branches, and none of the backport machinery that keeping multiple release branches requires. +One long-lived branch: `main`. Every PR merges here, so `main` is both where work integrates and the source of every release. There are no `beta` or `release` branches, and none of the backporting that keeping multiple release branches requires. ### Per-PR release metadata -`release-plan` is label-driven — this is the one way it works. Every PR is labeled with one of its labels: +release-plan is label-driven. That's the one way it works, so there's nothing to invent here. Every PR gets one of its labels: -- `breaking` → major impact -- `enhancement` → minor impact -- `bug` → patch impact -- `documentation`, `internal` → recorded, no release on their own +- `breaking` (major impact) +- `enhancement` (minor impact) +- `bug` (patch impact) +- `documentation`, `internal` (recorded, but don't cause a release on their own) -The changelog entry for a release *is* the set of merged PR titles (editable later by editing the title), so there is nothing extra to author. `release-plan` reads the labels and titles of everything merged since the last release to compute the next version and assemble the changelog. The judgment a maintainer used to apply by reading the diff is captured by the label on each PR at the time it lands. +The changelog entry for a release _is_ the set of merged PR titles (you can edit a title later to fix it up), so there's nothing extra to write. release-plan reads the labels and titles of everything merged since the last release, then works out the next version and assembles the changelog. The judgment a maintainer used to apply by reading the diff is captured by the label on each PR when it lands. ### Releasing -The cadence is unchanged; the *mechanics* are `release-plan`'s defaults: +The cadence is unchanged. The mechanics are just release-plan's defaults: -1. `release-plan` keeps a **release-preview PR** up to date — it bumps the version in `package.json`, edits `CHANGELOG.md`, and records the plan, from the labels and titles merged since the last release. -2. On the scheduled six-week release date, a maintainer merges that preview PR, which triggers the publish workflow. (The schedule is the rhythm; nothing forces a release between scheduled dates, and a date can still be held or moved as it is today.) -3. The publish job targets a **protected GitHub Environment** (e.g. `npm-publish`) with a *required reviewers* rule. The npm token / trusted-publishing identity is scoped to that environment, so nothing can publish until a required reviewer clicks **Approve** on the pending deployment. Maintainers never need npm keys locally. -4. On approval, the CI job runs `release-plan publish`: it tags, pushes, and publishes to npm (with provenance via OIDC trusted publishing). +1. release-plan keeps a release-preview PR up to date. It bumps the version in `package.json`, edits `CHANGELOG.md`, and records the plan from the labels and titles merged since the last release. +2. On the scheduled six-week date, a maintainer merges that preview PR, which kicks off the publish workflow. (The schedule is the rhythm. Nothing forces a release between scheduled dates, and a date can still be held or moved like it can today.) +3. The publish job targets a protected GitHub environment (say, `npm-publish`) with a required-reviewers rule. The npm token / trusted-publishing identity is scoped to that environment, so nothing publishes until a required reviewer hits Approve on the pending deployment. Maintainers never need npm keys locally. +4. Once approved, the CI job runs `release-plan publish`: it tags, pushes, and publishes to npm (with provenance via OIDC trusted publishing). -Across a cycle, `release-plan` collapses everything merged into a *single* version bump — highest impact wins, so six weeks of `enhancement` PRs yields one minor, exactly as one stable minor per cycle does today. `@alpha` publishes the in-progress version nightly (e.g. `6.5.0-alpha.N`); the scheduled stable cut publishes the finalized version (`6.5.0`) as `@latest`. The stable release is just a snapshot of `main` at the scheduled date — the same thing promoting `release` from `beta` produced before. +Across a cycle, release-plan collapses everything merged into a single version bump (highest impact wins), so six weeks of `enhancement` PRs becomes one minor, exactly like one stable minor per cycle does today. `@alpha` publishes the in-progress version nightly (e.g. `6.5.0-alpha.N`), and the scheduled stable cut publishes the finished version (`6.5.0`) as `@latest`. That stable release is just a snapshot of `main` on the scheduled date, which is the same thing promoting `release` from `beta` produced before. -The approval click is the entire residual ceremony: no manual version edit, no manual changelog, no manual publish, and no branch to cut or promote. Because the gate is a GitHub Environment, the existing GitHub permission and audit model applies — who may approve and the record of who approved what are standard repo configuration rather than tribal knowledge. +The approval click is the entire ceremony that's left. No manual version edit, no manual changelog, no manual publish, and no branch to cut or promote. Because the gate is a GitHub environment, the normal GitHub permission and audit model applies, so who can approve and who approved what are just repo configuration instead of tribal knowledge. ### Channels -- **Stable (`latest`)** — published from `main` through the gated environment, on the six-week cadence, as above. -- **Canary** — *unchanged by this RFC.* Canary is the default branch (`main`) consumed from git, and that keeps working exactly as it does today. -- **`alpha`** — new. A published, npm-installable prerelease of `main`, cut nightly the way Embroider and Glint *used to* publish theirs. It gives `main` a turnkey npm dist-tag (`ember-source@alpha`) for people who want canary's code without consuming from git. The `release-plan` defaults handle the version scheme; nothing special is needed. -- **`beta`** — removed. The dedicated `beta` branch and the `ember-source@beta` dist-tag go away. A change that warrants extra baking can still be merged early and exercised via canary or `@alpha` before the next scheduled stable release. +- Stable (`latest`): published from `main` through the gated environment, on the six-week cadence, as above. +- Canary: _unchanged by this RFC._ Canary is the default branch (`main`) consumed from git, and that keeps working exactly like it does today. +- `alpha`: new. A published, npm-installable prerelease of `main`, cut nightly the way Embroider and Glint _used to_ publish theirs. It gives `main` a turnkey npm dist-tag (`ember-source@alpha`) for folks who want canary's code without consuming from git. release-plan's defaults handle the version scheme, so there's nothing special to set up. +- `beta`: removed. The `beta` branch and the `ember-source@beta` dist-tag both go away. Anything that needs extra baking can still merge early and get exercised via canary or `@alpha` before the next scheduled stable release. ### Deprecations, majors, and LTS -Because the six-week cadence is retained, the policies layered on top of it are unaffected: +Since we keep the six-week cadence, everything layered on top of it is unaffected: -- **Deprecations** are still introduced as SemVer-minor and removed in majors, on the same deprecation-freeze schedule. -- **Majors** are not a manual process either. A PR is labeled `breaking`, and because RFC #0830 puts a major on the normal cadence — one major roughly every twelve 6-week minors, after the `M.10` deprecation freeze — `breaking` PRs only merge in that window. So `release-plan`'s label-driven bump produces the major on schedule, with no manual version work. The major-version process is unchanged; it simply executes via `release-plan` from `main`. -- **LTS** continues as today. +- Deprecations still land as SemVer-minor and get removed in majors, on the same deprecation-freeze schedule. +- Majors aren't a manual process either. A PR gets labeled `breaking`, and because RFC #0830 puts a major on the normal cadence (roughly one major every twelve 6-week minors, after the `M.10` deprecation freeze), `breaking` PRs only merge in that window. So release-plan's label-driven bump produces the major right on schedule, with no manual version work. The major-version process itself doesn't change; it just runs through release-plan from `main`. +- LTS keeps working like it does today. -In other words, this RFC changes *how* a release is cut and *which branches exist*, not *when* releases happen or *what compatibility they guarantee*. +So this RFC changes _how_ a release is cut and _which branches exist_, not _when_ releases happen or _what_ they guarantee. ### Lockstep across packages -`ember-source` and `ember-cli` continue to release in lockstep — but lockstep here is a *timing* property, not a coupling. Each repo releases independently via `release-plan` (its natural per-repo mode), and the releases don't need to know about each other. Because both cut stable on the same six-week date, their versions stay aligned. There is no cross-repo coordination step in the workflow; the shared cadence is what keeps them in step. +`ember-source` and `ember-cli` keep releasing in lockstep, but lockstep here is a timing thing, not a coupling. Each repo releases independently through release-plan (its natural per-repo mode), and the releases don't need to know about each other. Since both cut stable on the same six-week date, their versions stay lined up. There's no cross-repo coordination step in the workflow; the shared cadence is what keeps them in step. -### What is removed +### What this removes - The `beta` and `release` branches, and the `ember-source@beta` dist-tag. -- The backport machinery for keeping multiple release branches alive. -- The bespoke per-cycle release process: version bumps, changelog assembly, and cut/promote steps now handled by `release-plan`. +- The backporting needed to keep multiple release branches alive. +- The bespoke per-cycle release process. Version bumps, changelog assembly, and the cut/promote steps are all release-plan's job now. -### What is kept +### What this keeps -- **The six-week release cadence**, unchanged. -- SemVer and every existing compatibility guarantee. +- The six-week release cadence, unchanged. +- SemVer and every compatibility guarantee we make today. - The deprecation policy, LTS, and the major-version process (RFC #0830). -- Steering control over majors and over what ships. -- A deliberate human gate before each publish. +- Steering's control over majors and over what ships. +- A deliberate human gate before every publish. ## How we teach this -**Contributors** keep doing what they already do in virtually every addon: label each PR with a `release-plan` label. The PR title becomes the changelog entry. +Contributors keep doing what they already do in basically every addon: label each PR with a release-plan label. The PR title becomes the changelog entry. -**Maintainers** gain the ability to release. The role shrinks from "shepherd the branches and the cut" to "review the release-preview PR and approve the deployment" — something any maintainer can do, from anywhere, without npm keys. +Maintainers get the ability to release. The role shrinks from "shepherd the branches and the cut" down to "look over the release-preview PR and approve the deployment," which any maintainer can do, from anywhere, without npm keys. -**Consumers of `beta`** are the audience whose workflow moves. Anyone whose `ember-try` config or CI matrix tests `ember-source@beta` removes that scenario, since `@beta` no longer exists; they can add `@alpha` if they want a published prerelease. Canary testing (consuming `main` from git) is unaffected. +`beta` users are the folks whose workflow moves. If your `ember-try` config or CI matrix tests `ember-source@beta`, you remove that scenario, since `@beta` won't exist anymore. You can add `@alpha` if you want a published prerelease. Canary testing (consuming `main` from git) is unaffected. -Documentation work: +Docs work: -- Rewrite the website Releases page: `main` is the release line, the six-week cadence is unchanged, the prerelease stream is `@alpha` (published nightly from `main`), and `beta` no longer exists. +- Rewrite the website Releases page: `main` is the release line, the six-week cadence is unchanged, the prerelease stream is `@alpha` (published nightly from `main`), and `beta` is gone. - Update the contributor guide with the labeling workflow. -- Document the environment, who can approve, and the approval procedure. -- A migration/announcement blog post focused on `@beta` → `@alpha`. +- Document the environment, who can approve, and how approval works. +- A migration / announcement blog post about dropping `@beta`. ## Drawbacks -- **Removing the `@beta` dist-tag moves some consumers.** `ember-try` scenarios and addon CI matrices pinned to `ember-source@beta` have to remove that scenario (optionally adopting `@alpha`). This is a one-time, mechanical cleanup rather than a loss of capability — canary testing is unaffected — but it is still ecosystem-wide churn that needs coordinating. -- **Collapsing `beta` into `alpha` removes a soak stage.** Today `beta` is a distinct checkpoint between the default branch and stable. Folding it into `@alpha` means one prerelease stream, not two; changes get less differentiated baking before a scheduled stable release. -- **Per-PR labeling discipline.** A wrong label yields a wrong bump. `release-plan` makes the bump deterministic, but the label is human-supplied. -- **Publish authority.** The people who can approve the protected environment are the same active folks who cut releases today — informal, no change — but it does mean the npm publish is only as locked-down as that environment's protection rules and those accounts' security (2FA / OIDC). -- **Tooling dependency.** The framework's release process becomes coupled to `release-plan` — a small, community-owned tool, but a new dependency. -- **Cultural change.** `beta` is a long-standing, load-bearing part of Ember's testing culture and infrastructure; removing it is not only a mechanical change. +- Dropping `@beta` moves some consumers. Any `ember-try` scenario or addon CI matrix pinned to `ember-source@beta` has to remove that scenario (and can adopt `@alpha` if it wants). It's a one-time, mechanical cleanup rather than a lost capability, since canary testing is unaffected, but it's still ecosystem-wide churn that needs coordinating. +- Collapsing `beta` into `alpha` loses a soak stage. Today `beta` is a distinct checkpoint between the default branch and stable. With one prerelease stream instead of two, changes get less differentiated baking before a scheduled stable release. +- Labeling discipline matters. A wrong label means a wrong bump. release-plan makes the bump deterministic, but the label itself is human-supplied. +- Publish authority. The folks who can approve the protected environment are the same active people who cut releases today (informal, no change), but it does mean the npm publish is only as locked down as that environment's rules and those accounts' security (2FA / OIDC). +- Tooling dependency. The framework's release process becomes coupled to release-plan. It's a small, community-owned tool, but it's a new dependency. +- Cultural change. `beta` is a long-standing, load-bearing part of Ember's testing culture and infrastructure, so removing it isn't only a mechanical change. ## Alternatives -- **Keep the train as-is (status quo).** Pays the recurring branch-management cost and the bus-factor risk indefinitely. -- **Keep `beta` as a second prerelease stream.** Retain a `@beta` dist-tag alongside `@alpha`. This proposal drops `beta` for a single `@alpha` stream; the alternative would preserve the extra soak stage at the cost of a second prerelease tag, and could be revisited if a single `@alpha` proves too coarse for downstream CI. -- **Use `changesets` instead of `release-plan`.** Functionally similar; `release-plan` is preferred because the Ember ecosystem has standardized on it. -- **Also drop the cadence (fully continuous releases).** A more radical model where every merge can release. Explicitly **not** proposed here — the six-week cadence is retained deliberately because it works. +- Keep the train as-is (status quo). Keeps paying the branch-management cost and the bus-factor risk indefinitely. +- Keep `beta` as a second prerelease stream. Hold onto a `@beta` dist-tag next to `@alpha`. This RFC drops `beta` for a single `@alpha` stream; the alternative keeps the extra soak stage at the cost of a second prerelease tag, and we could revisit it if one `@alpha` turns out too coarse for downstream CI. +- Use `changesets` instead of `release-plan`. Pretty similar in practice; we prefer release-plan because the ecosystem already standardized on it. +- Drop the cadence too (fully continuous releases). A more radical model where every merge can release. We're explicitly _not_ proposing that here, because the six-week cadence works. ## Unresolved questions From 489a4ca8d973a18c1683830447ebb79b6a758f7b Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:03:38 -0400 Subject: [PATCH 12/19] Drop the status-quo alternative Co-Authored-By: Claude Opus 4.8 (1M context) --- text/0000-move-off-the-release-train-to-simpler-release-model.md | 1 - 1 file changed, 1 deletion(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 8e53aac00d..ba390418d4 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -149,7 +149,6 @@ Docs work: ## Alternatives -- Keep the train as-is (status quo). Keeps paying the branch-management cost and the bus-factor risk indefinitely. - Keep `beta` as a second prerelease stream. Hold onto a `@beta` dist-tag next to `@alpha`. This RFC drops `beta` for a single `@alpha` stream; the alternative keeps the extra soak stage at the cost of a second prerelease tag, and we could revisit it if one `@alpha` turns out too coarse for downstream CI. - Use `changesets` instead of `release-plan`. Pretty similar in practice; we prefer release-plan because the ecosystem already standardized on it. - Drop the cadence too (fully continuous releases). A more radical model where every merge can release. We're explicitly _not_ proposing that here, because the six-week cadence works. From 1ded12547052227dec750d115de14e7fd29c66be Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:04:06 -0400 Subject: [PATCH 13/19] Alternatives: drop keep-beta and changesets options Co-Authored-By: Claude Opus 4.8 (1M context) --- .../0000-move-off-the-release-train-to-simpler-release-model.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index ba390418d4..dcb334b2a6 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -149,8 +149,6 @@ Docs work: ## Alternatives -- Keep `beta` as a second prerelease stream. Hold onto a `@beta` dist-tag next to `@alpha`. This RFC drops `beta` for a single `@alpha` stream; the alternative keeps the extra soak stage at the cost of a second prerelease tag, and we could revisit it if one `@alpha` turns out too coarse for downstream CI. -- Use `changesets` instead of `release-plan`. Pretty similar in practice; we prefer release-plan because the ecosystem already standardized on it. - Drop the cadence too (fully continuous releases). A more radical model where every merge can release. We're explicitly _not_ proposing that here, because the six-week cadence works. ## Unresolved questions From 6fa1d8302615779a6f51506a923c5715bdef3cdf Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:18:36 -0400 Subject: [PATCH 14/19] Address PR review: trim drawbacks, drop Steering, keep LTS backports - remove redundant 'long-lived branch' phrasing; main is always that - just state release-plan's labels, no preamble - drop the soak-stage, labeling-discipline, publish-authority, and tooling-dependency drawbacks (non-issues / we own release-plan) - remove the Steering bullet and the steering team; steering doesn't decide this - scope removed backporting to the beta/release branches; keep LTS backports when needed - note consumers won't notice a difference Co-Authored-By: Claude Opus 4.8 (1M context) --- ...the-release-train-to-simpler-release-model.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index dcb334b2a6..adda6bcd10 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -6,7 +6,6 @@ release-versions: teams: # delete teams that aren't relevant - cli - framework - - steering prs: accepted: # Fill this in with the URL for the Proposal RFC PR project-link: @@ -26,7 +25,7 @@ This RFC keeps (1) and replaces (2). The six-week cadence is the part that works Concretely: -- One long-lived branch, `main`. No `beta` or `release` branches. +- Just `main`. No `beta` or `release` branches. - Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule. The `npm publish` is gated behind a protected [GitHub deployment environment][gh-environments], so a maintainer approves before anything publishes. - We drop the `beta` channel. A new `@alpha` prerelease is published nightly from `main` (the way Embroider and Glint _used to_ publish theirs), and `ember-source@beta` users either move to `@alpha` or drop the scenario. Canary is unchanged: consuming `main` from git works exactly like it does today. @@ -62,11 +61,11 @@ The six-week cadence is predictable, well understood, and well loved. It's _not_ ### Branching model -One long-lived branch: `main`. Every PR merges here, so `main` is both where work integrates and the source of every release. There are no `beta` or `release` branches, and none of the backporting that keeping multiple release branches requires. +Everything merges to `main`. It's both where work integrates and the source of every release. There are no `beta` or `release` branches, and none of the backporting onto them that the train needs. ### Per-PR release metadata -release-plan is label-driven. That's the one way it works, so there's nothing to invent here. Every PR gets one of its labels: +release-plan uses these labels: - `breaking` (major impact) - `enhancement` (minor impact) @@ -112,7 +111,7 @@ So this RFC changes _how_ a release is cut and _which branches exist_, not _when ### What this removes - The `beta` and `release` branches, and the `ember-source@beta` dist-tag. -- The backporting needed to keep multiple release branches alive. +- The backporting onto the `beta` and `release` branches. - The bespoke per-cycle release process. Version bumps, changelog assembly, and the cut/promote steps are all release-plan's job now. ### What this keeps @@ -120,8 +119,9 @@ So this RFC changes _how_ a release is cut and _which branches exist_, not _when - The six-week release cadence, unchanged. - SemVer and every compatibility guarantee we make today. - The deprecation policy, LTS, and the major-version process (RFC #0830). -- Steering's control over majors and over what ships. +- Backports to LTSes, when needed. - A deliberate human gate before every publish. +- From a consumer's point of view, nothing changes. They won't notice a difference. ## How we teach this @@ -141,10 +141,6 @@ Docs work: ## Drawbacks - Dropping `@beta` moves some consumers. Any `ember-try` scenario or addon CI matrix pinned to `ember-source@beta` has to remove that scenario (and can adopt `@alpha` if it wants). It's a one-time, mechanical cleanup rather than a lost capability, since canary testing is unaffected, but it's still ecosystem-wide churn that needs coordinating. -- Collapsing `beta` into `alpha` loses a soak stage. Today `beta` is a distinct checkpoint between the default branch and stable. With one prerelease stream instead of two, changes get less differentiated baking before a scheduled stable release. -- Labeling discipline matters. A wrong label means a wrong bump. release-plan makes the bump deterministic, but the label itself is human-supplied. -- Publish authority. The folks who can approve the protected environment are the same active people who cut releases today (informal, no change), but it does mean the npm publish is only as locked down as that environment's rules and those accounts' security (2FA / OIDC). -- Tooling dependency. The framework's release process becomes coupled to release-plan. It's a small, community-owned tool, but it's a new dependency. - Cultural change. `beta` is a long-standing, load-bearing part of Ember's testing culture and infrastructure, so removing it isn't only a mechanical change. ## Alternatives From 39cbdfe86f3b04bec034189f4639ab4d392d906d Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:22:04 -0400 Subject: [PATCH 15/19] Don't frame the cadence as in question; state the cost directly Co-Authored-By: Claude Opus 4.8 (1M context) --- ...000-move-off-the-release-train-to-simpler-release-model.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index adda6bcd10..5ed7e04679 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -37,7 +37,7 @@ SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version ## Motivation -The expensive part of the release train isn't the cadence. It's all the machinery around it, plus the time it takes maintainers with limited availability to keep it running. +The expensive part of the release train is all the machinery around it, plus the time it takes maintainers with limited availability to keep it running. ### Releases depend on too few people @@ -55,7 +55,7 @@ Working out the next version number and assembling the changelog are exactly the ### Keep the cadence -The six-week cadence is predictable, well understood, and well loved. It's _not_ what makes the train expensive, so we deliberately keep it. We're moving off the branch-and-process model, not the calendar. +The six-week cadence is predictable, well understood, and well loved, so we keep it. We're moving off the branch-and-process model, not the calendar. ## Detailed design From a3363d1d0638fb545c2f718f26253eb6b73a4e04 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:24:50 -0400 Subject: [PATCH 16/19] Apply suggestion from @NullVoxPopuli --- .../0000-move-off-the-release-train-to-simpler-release-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 5ed7e04679..7f9a6e804f 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -27,7 +27,7 @@ Concretely: - Just `main`. No `beta` or `release` branches. - Stable releases are cut from `main` by [`release-plan`][release-plan] on the same six-week schedule. The `npm publish` is gated behind a protected [GitHub deployment environment][gh-environments], so a maintainer approves before anything publishes. -- We drop the `beta` channel. A new `@alpha` prerelease is published nightly from `main` (the way Embroider and Glint _used to_ publish theirs), and `ember-source@beta` users either move to `@alpha` or drop the scenario. Canary is unchanged: consuming `main` from git works exactly like it does today. +- We drop the `beta` channel. `@alpha` prerelease is nearly the same as before, but published nightly from `main`, and `ember-source@beta` users either move to `@alpha` or drop the scenario. SemVer, the six-week cadence, the deprecation policy, LTS, and the major-version process from [RFC #0830][rfc-830] are all unchanged. The only things that change are the branch structure and the publishing mechanics. From 01c82b90e3e3d2b17f0d66a42ffa420b1b1a4e99 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:49:02 -0400 Subject: [PATCH 17/19] Apply suggestion from @NullVoxPopuli --- .../0000-move-off-the-release-train-to-simpler-release-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 7f9a6e804f..0bafa67874 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -41,7 +41,7 @@ The expensive part of the release train is all the machinery around it, plus the ### Releases depend on too few people -We don't actually have a formal release-manager rotation. In practice the release comes down to essentially one person per project: Katie for `ember.js`, Chris for `ember-cli`. That's a real bus-factor and burnout risk, because when that person isn't around, the release slips. +We don't actually have a formal release-manager rotation. In practice the release comes down to essentially one person per project: @katiegengler for `ember.js`, @mansona for `ember-cli`. This is a bus-factor and burnout risk -- which other teams have already faced. When that person isn't around (due to life, unplanned things, etc (this alone is not a big deal)), the release slips. The biggest win of getting off the train is that releasing stops being specialized knowledge. With [`release-plan`][release-plan], _any_ maintainer can trivially cut a release: they look over the release-preview PR and approve a deployment. No npm keys on anyone's laptop, no checklist, no tribal knowledge. From c2952c27936c5c198b03c6ab89843023df72567f Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:49:39 -0400 Subject: [PATCH 18/19] Apply suggestion from @NullVoxPopuli --- .../0000-move-off-the-release-train-to-simpler-release-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index 0bafa67874..be6e5ef834 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -43,7 +43,7 @@ The expensive part of the release train is all the machinery around it, plus the We don't actually have a formal release-manager rotation. In practice the release comes down to essentially one person per project: @katiegengler for `ember.js`, @mansona for `ember-cli`. This is a bus-factor and burnout risk -- which other teams have already faced. When that person isn't around (due to life, unplanned things, etc (this alone is not a big deal)), the release slips. -The biggest win of getting off the train is that releasing stops being specialized knowledge. With [`release-plan`][release-plan], _any_ maintainer can trivially cut a release: they look over the release-preview PR and approve a deployment. No npm keys on anyone's laptop, no checklist, no tribal knowledge. +The biggest win of getting off the train is that releasing stops being specialized knowledge (despite efforts to write it all down). With [`release-plan`][release-plan], _any_ maintainer can trivially cut a release: they look over the release-preview PR and approve a deployment. ### Extra release branches are overhead From 8d6ed76e5af40dc99d0eb449c5d679c4c9c83a0b Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:54:06 -0400 Subject: [PATCH 19/19] Tone down marketing language Drop superlatives and sales phrasing (biggest win, trivially, turnkey, well loved, the important bit, everyone else already trusts, from anywhere, tribal knowledge) in favor of plain description. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...the-release-train-to-simpler-release-model.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/text/0000-move-off-the-release-train-to-simpler-release-model.md b/text/0000-move-off-the-release-train-to-simpler-release-model.md index be6e5ef834..17fdc8a68a 100644 --- a/text/0000-move-off-the-release-train-to-simpler-release-model.md +++ b/text/0000-move-off-the-release-train-to-simpler-release-model.md @@ -21,7 +21,7 @@ Ember's "release train" today is really two things bundled together: 1. A six-week release cadence. A new minor ships on a steady, predictable clock. 2. A multi-branch promotion pipeline. Code gets promoted from the default branch through dedicated `beta` and `release` branches (plus LTS branches), shepherded by a small number of folks who manage each release. -This RFC keeps (1) and replaces (2). The six-week cadence is the part that works, so it stays exactly as it is. What goes away is the extra release branches and the bespoke, per-cycle process that goes with them. +This RFC keeps (1) and replaces (2). The six-week cadence stays as it is. What goes away is the extra release branches and the per-cycle process that goes with them. Concretely: @@ -43,19 +43,19 @@ The expensive part of the release train is all the machinery around it, plus the We don't actually have a formal release-manager rotation. In practice the release comes down to essentially one person per project: @katiegengler for `ember.js`, @mansona for `ember-cli`. This is a bus-factor and burnout risk -- which other teams have already faced. When that person isn't around (due to life, unplanned things, etc (this alone is not a big deal)), the release slips. -The biggest win of getting off the train is that releasing stops being specialized knowledge (despite efforts to write it all down). With [`release-plan`][release-plan], _any_ maintainer can trivially cut a release: they look over the release-preview PR and approve a deployment. +Moving off the train means releasing stops being specialized knowledge (despite efforts to write it all down). With [`release-plan`][release-plan], _any_ maintainer can cut a release: they look over the release-preview PR and approve a deployment. ### Extra release branches are overhead -Keeping `beta`, `release`, and the LTS branches alive next to the default branch means constant backporting and branch bookkeeping that only exists to service the shape of the train. A single `main` gets rid of all of it. +Keeping `beta`, `release`, and the LTS branches alive next to the default branch means constant backporting and branch bookkeeping that only exists to service the shape of the train. A single `main` removes that. ### release-plan automates the mechanical work -Working out the next version number and assembling the changelog are exactly the mechanical, error-prone steps that shouldn't be done by hand, and this is the important bit: release-plan does this for us, deterministically, from the labels and titles of the PRs that got merged. The ecosystem has already standardized on release-plan for almost every addon, so the framework doesn't need a bespoke process that's strictly more work than the tooling everyone else already trusts. +Working out the next version number and assembling the changelog are mechanical, error-prone steps that shouldn't be done by hand. release-plan does this for us, deterministically, from the labels and titles of the PRs that got merged. The ecosystem has already standardized on release-plan for almost every addon, so the framework doesn't need a bespoke process that's more work than the tooling everyone else uses. ### Keep the cadence -The six-week cadence is predictable, well understood, and well loved, so we keep it. We're moving off the branch-and-process model, not the calendar. +The six-week cadence is predictable and well understood, so we keep it. We're moving off the branch-and-process model, not the calendar. ## Detailed design @@ -85,13 +85,13 @@ The cadence is unchanged. The mechanics are just release-plan's defaults: Across a cycle, release-plan collapses everything merged into a single version bump (highest impact wins), so six weeks of `enhancement` PRs becomes one minor, exactly like one stable minor per cycle does today. `@alpha` publishes the in-progress version nightly (e.g. `6.5.0-alpha.N`), and the scheduled stable cut publishes the finished version (`6.5.0`) as `@latest`. That stable release is just a snapshot of `main` on the scheduled date, which is the same thing promoting `release` from `beta` produced before. -The approval click is the entire ceremony that's left. No manual version edit, no manual changelog, no manual publish, and no branch to cut or promote. Because the gate is a GitHub environment, the normal GitHub permission and audit model applies, so who can approve and who approved what are just repo configuration instead of tribal knowledge. +Approving the deployment is the only manual step left. No manual version edit, no manual changelog, no manual publish, and no branch to cut or promote. Because the gate is a GitHub environment, the normal GitHub permission and audit model applies, so who can approve and who approved what live in repo settings and the audit log. ### Channels - Stable (`latest`): published from `main` through the gated environment, on the six-week cadence, as above. - Canary: _unchanged by this RFC._ Canary is the default branch (`main`) consumed from git, and that keeps working exactly like it does today. -- `alpha`: new. A published, npm-installable prerelease of `main`, cut nightly the way Embroider and Glint _used to_ publish theirs. It gives `main` a turnkey npm dist-tag (`ember-source@alpha`) for folks who want canary's code without consuming from git. release-plan's defaults handle the version scheme, so there's nothing special to set up. +- `alpha`: new. A published, npm-installable prerelease of `main`, cut nightly the way Embroider and Glint _used to_ publish theirs. It gives `main` an npm dist-tag (`ember-source@alpha`) for folks who want canary's code without consuming from git. release-plan's defaults handle the version scheme. - `beta`: removed. The `beta` branch and the `ember-source@beta` dist-tag both go away. Anything that needs extra baking can still merge early and get exercised via canary or `@alpha` before the next scheduled stable release. ### Deprecations, majors, and LTS @@ -127,7 +127,7 @@ So this RFC changes _how_ a release is cut and _which branches exist_, not _when Contributors keep doing what they already do in basically every addon: label each PR with a release-plan label. The PR title becomes the changelog entry. -Maintainers get the ability to release. The role shrinks from "shepherd the branches and the cut" down to "look over the release-preview PR and approve the deployment," which any maintainer can do, from anywhere, without npm keys. +Maintainers get the ability to release. The role goes from "shepherd the branches and the cut" to "look over the release-preview PR and approve the deployment," which any maintainer can do without npm keys. `beta` users are the folks whose workflow moves. If your `ember-try` config or CI matrix tests `ember-source@beta`, you remove that scenario, since `@beta` won't exist anymore. You can add `@alpha` if you want a published prerelease. Canary testing (consuming `main` from git) is unaffected.