Skip to content

Commit 0524c16

Browse files
docs(protocol): the backward-compatibility page states the 17.x launch-window rule and the v18 prerelease regime (#22988)
Fixes #22964 Clause-②: no A docs-only PR on a hand-written page, `content/docs/protocol/backward-compatibility.mdx`, which is not release-owned. It changes one file by +18 / -16 lines (34 changed lines). It edits no gate script, takes no release action, and changes nothing under `content/docs/releases/**`. The new wording matches the releases index's "Versioning policy" section as PR #22963 left it on `main` (`246edf5b3`). ## What was false On `main` at `19805d895` (this PR's base), the page said four things that were not true: - the launch-window rule ("a breaking change ships as a MINOR release") "is in force today", and it overrides the tables; - `scripts/check-changeset-no-major.mjs` "fails any pull request that declares a `major` bump"; - "The current release, **17.2.0**, is a MINOR"; - the launch-window section "is the operative rule wherever any part of this page disagrees". The seat listed the line numbers on `da9c2bf2f`. They are the same on `19805d895`: the last commit to touch the file is `165587ee6`. ## The `main` facts each change rests on - **F1.** `.changeset/pre.json` reads `{"mode": "pre", "tag": "next"}`. - **F2.** In `scripts/check-changeset-no-major.mjs`, `judge()` at `:673` reads `if (pre?.mode === 'pre') return { verdict: 'exempt', ... }`, and the RC EXEMPTION paragraph is at `:105`. Outside pre mode, `:674` returns `enforce`. The header also documents an `allow-major` label escape hatch outside pre mode, so the past-tense sentences say the guard failed "a" pull request, not "any". - **F3.** `release.yml` triggers only on a push to `main` (`:177`-`:180`). The v18 page (`content/docs/releases/v18/index.mdx`) says every release cut from `main` is the next `18.0.0-next.N`, and that `latest` stays on 17.x (`:6`-`:8`, `:27`-`:29`). - **F4.** `npm view @objectstack/spec dist-tags`, read 2026-10-12, shows `latest: 17.7.0` and `rc: 17.0.0-rc.6`, and no `next` tag. No `18.0.0-next.N` has been published yet. The new sentences say what a cut from `main` produces. None of them says a cut has happened. - **F5.** `packages/spec/package.json` is `17.7.0`, and the CHANGELOG's newest heading is `## 17.7.0`, so "17.2.0" was not the current release. The v17 page's banner calls 17.1.0 through 17.7.0 "minors by version number, not by blast radius". It also says 17.0.0 was cut from an rc train after `changeset pre exit`. - **F6.** `.changeset/config.json`'s `fixed[0]` has 70 members. Both sentences that `check:lockstep-package-count` anchors on are kept word for word. ## Each changed sentence: before, after, and the fact it rests on 1. **Top callout, lead** (`:18`) - Before: "**While the launch window is open, a breaking change ships as a MINOR release — not a MAJOR one.**" - After: "**The 17.x minors ran under a launch-window override: a breaking change shipped as a MINOR release, not a MAJOR one.**" - Rests on: F2 (the guard enforced through the 17.x minors) and F5. The wording is #22963's sentence. 2. **Top callout, override clause** (`:18`) - Before: "This rule is in force today and overrides the MAJOR/MINOR mapping in the tables below wherever they disagree; read [Launch Window] ... before you plan an upgrade." - After: "read [Launch Window] ... before you upgrade across 17.x." - Rests on: F1 and F2. The rule is not what governs a cut from `main` today. The tables now label their 17.x mapping (see 11), so there is nothing left to override. 3. **Top callout, the v18 regime** (new paragraph) - After: "**The v18 line is a major in prerelease.** Changesets is in pre mode with the tag `next`, and `scripts/check-changeset-no-major.mjs` stands aside in pre mode, so a pull request may declare a major bump. Every release cut from `main` is the next `18.0.0-next.N` on npm's `next` tag, while `latest` stays on 17.x. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line)." - Rests on: F1, F2, F3 and F4. These are #22963's sentences, and the link replaces a restatement of the v18 page. 4. **Versioning Strategy, intro** (`:21`) - Before: "the launch-window rule above governs which component a breaking change actually lands in today." - After: "which version a breaking change ships in depends on the line, as the callout above says." - Rests on: F1 to F3. The phrase is #22963's "depends on the line". 5. **SemVer table, MINOR row** (`:26`) - Before: "New features — and, during the launch window, breaking changes" - After: "New features — and, in the 17.x minors, breaking changes" - Rests on: F5. 6. **SemVer Guarantees, Zod schemas bullet** (`:31`) - Before: "which during the launch window also ships in a MINOR release." - After: "which the 17.x minors also shipped in a MINOR release." - Rests on: F5. 7. **Phase 3 heading** (`:66`) - Before: "Phase 3: Removal (MINOR release, during the launch window)" - After: "Phase 3: Removal (MINOR release in 17.x, the next `18.0.0-next.N` on the v18 line)" - Rests on: F3 and F5. The heading's slug changes. `git grep 'backward-compatibility#'` finds exactly one inbound fragment link to this page, `releases/index.mdx:79`, and it targets the launch-window anchor. In `../objectui` the same grep finds nothing. 8. **Timeline mermaid, node C** (`:80`) - Before: "(MINOR release, during the launch window)" - After: "(MINOR release in 17.x, the next 18.0.0-next.N on the v18 line)" - Rests on: F3 and F5. 9. **"No minimum survival window" callout** (`:84`) - Before: "during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive." - After: "in the 17.x minors that removal itself landed in a MINOR release, so a MINOR bump was where to expect a removal to arrive. On the v18 line it lands in the next `18.0.0-next.N`." - Rests on: F3 and F5. 10. **What Constitutes a Breaking Change, lead** (`:93`) - Before: "during the launch window every row below — breaking or not — ships in a MINOR or PATCH release, so the bump size does not tell you whether you have work to do." - After: "in the 17.x minors every row below — breaking or not — shipped in a MINOR or PATCH release, and on the v18 line every row ships in the next `18.0.0-next.N`, so on neither line does the bump size tell you whether you have work to do." - Rests on: F3 and F5. 11. **Change-type table, column header** (`:95`) - Before: "Version Impact (launch window)" - After: "Version Impact (17.x minors)" - Rests on: F5. The cells are unchanged, and so is the table's shape. 12. **Breaking Change Process, step 4** (`:115`) - Before: "During the launch window the breaking change ships in the next **MINOR** version ... `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 70 published packages." - After: the same sentence in the past tense ("In the 17.x minors ... shipped ... failed a pull request that declared"). Then: "On the v18 line it ships in the next `18.0.0-next.N`: the guard stands aside in pre mode, so a pull request may declare a major bump." Then the v18 link. - Rests on: F1 to F3 and F6. The lockstep anchor `would promote all 70 published packages.` is kept word for word. 13. **Export Stability bullet** (`:174`) - Before: "during the launch window it ships in a MINOR release" - After: "the 17.x minors shipped it in a MINOR release, and on the v18 line it ships in the next `18.0.0-next.N`" - Rests on: F3 and F5. 14. **Launch Window callout** (`:188`) - Before: "**This exception overrides the MAJOR/MINOR mapping above — read it before you plan an upgrade.** ... while the launch window is open a breaking change ships as a **MINOR** ... The current release, **17.2.0**, is a MINOR and it contains a documented breaking change." - After: "**The 17.x minors ran under this exception — read it before you upgrade across them.** ... in the 17.x minors a breaking change shipped as a **MINOR** release rather than burning a MAJOR, so those minors contain documented breaking changes. The v18 line is a major in prerelease: every release cut from `main` is the next `18.0.0-next.N` on npm's `next` tag, while `latest` stays on 17.x." Then the v18 link. - Rests on: F3 to F5. The hard-coded current-release number is dropped rather than replaced, so it cannot go stale again. Readers who arrive from the releases index's anchor link land on this callout. 15. **Which surfaces this covers** (`:193`; tense only) - Before: "This is not scoped ... No published surface is exempt." - After: "It was not scoped ... No published surface was exempt." - The lockstep sentence and the `all **70** packages published from this repository` anchor are unchanged (F6). 16. **The guard paragraph** (`:195`) - Before: "The convention is enforced ... `scripts/check-changeset-no-major.mjs` fails any pull request that introduces a `major` bump ..." - After: the same sentence in the past tense, then "Changesets is now in pre mode with the tag `next`, and the guard stands aside in pre mode, so a pull request on the v18 line may declare a major bump." - Rests on: F1 and F2. 17. **The MAJOR paragraph** (`:203`) - Before: "MAJOR releases do still happen — 17.0.0 was cut ... — but an individual breaking change does not, on its own, force one." - After: "MAJOR releases do still happen — 17.0.0 was cut ..., and the v18 line is a major in prerelease — but in the 17.x minors an individual breaking change did not, on its own, force one." - Rests on: F1 and F5. 18. **When it stops applying** (`:207`). The override clause is **rewritten, not removed** (H3). - Before: "The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. ... Until it closes, **this section is the operative rule wherever any part of this page disagrees.**" - After: "The tables and deprecation timeline above state this rule for the 17.x minors directly, beside what the v18 line does. ... **For the 17.x minors, this section was the operative rule wherever any part of this page disagreed.** For the `18.0.0-next.N` prereleases, the operative rule is [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line)." - Why rewritten: the page still carries guarantees scoped to "within a MAJOR version" (`:33`-`:36`, `:179`-`:181`). One example is "Enum values are append-only within a MAJOR version. Existing values are never removed or renamed in MINOR/PATCH releases". The 17.x minors did not keep those guarantees: the change-type table's own 17.x column marks "Removing an enum value | Yes | MINOR". The clause is what reconciled the two. Removing it would leave those guarantees unqualified against 17.x history, so it now speaks in the past tense for the 17.x minors and sends `18.0.0-next.N` readers to the v18 section. ## Where this departs from the card's direction, or stops short of it - **Anchor.** The heading text "Launch Window: MINOR Releases Can Contain Breaking Changes" is unchanged, so `#launch-window-minor-releases-can-contain-breaking-changes` still resolves, and `releases/index.mdx:79` is not edited. The heading stays true: the 17.x minors are MINOR releases that contain breaking changes (F5). - **Not written:** "breaking changes land as `major` changesets". The card rules it out. The v18 sentences say only what the guard admits and what a cut from `main` publishes. - **Kept word for word:** "the window closes at GA, and strict SemVer resumes from that point". It does not state the current regime. It restates the guard header's END CONDITION ("at GA ... the group returns to STRICT SEMVER"). The page says nothing new about what happens when pre mode exits. - **Kept as history:** `:75`'s shipped examples (`17.2.0`, `15.1.0`). - **No date, and no version beyond `18.0.0-next.N`.** - **H5:** one file, 34 changed lines. ## Tests (all at `e8e1525ed`, the HEAD of this branch) - **Gate derivation.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 41 commands at `e8e1525ed`. That is the same set as the dispatch's lead list. I ran all 41 and captured each exit code before any pipe. - **Reconciliation.** `--ran` printed: "41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED (a DERIVED zero — all 41 recorded an exit code and none of them is 3)." - **Prerequisite refusals.** Five gates first refused on missing build output: `check:doc-formula-expressions` (3), `check:doc-security-posture` (3), `check:docs` (1, "Generate it first: gen:schema"), `check:skill-examples` (3) and `check:docs-transcript-drift` (3). These were refusals, not findings. - **Builds.** I built `@objectstack/lint...` (4 tasks) and then `@objectstack/client-react...` (35 tasks) under the verify lock. Both printed `VERDICT command-exit 0`. Re-run after the builds, all five exit 0. - **Verdict lines:** - `check:doc-anchors`: "489 internal #fragment link(s) across 419 source file(s) all resolve to a real heading". - `check:lockstep-package-count`: "all 3 occurrence(s) agree with the true count (70)". - `check:docs`: "226 generated files in sync with packages/spec". - `check:skill-examples`: "262 prose examples type-check across 3 surface(s)". - `check:nul-bytes`: "OK (scanned 10999 text file(s) ... no raw ASCII control bytes)". - **MDX syntax.** The page body compiles with the workspace's `@mdx-js/mdx` 3.1.1. Negative control: an unclosed `Callout` is refused ("Expected a closing tag for `Callout`"). - **Lint, a proven narrowing rather than a skipped run:** - The population comes from eslint's own config. Every `files` glob in `eslint.config.mjs` is a `{ts,tsx,mts,cts,js,jsx,mjs,cjs}` spelling or narrower. None of them matches `.mdx`. - `npx eslint --no-inline-config --format json` on the page returned 1 file, 0 errors and 1 warning: "File ignored because no matching configuration was supplied." - The config never enables type-aware linting (`eslint.config.mjs` `:328`), and the diff changes only a file no config object matches, so no linted file's verdict can move. - **Not run locally.** No package is touched, so no package `test` or `typecheck` is owed. Build Docs and Test Core belong to CI and were not measured here. - **`skip-changeset`.** I read 70 non-private tracked manifests, and no `files[]` entry names `content/docs`. In `@objectstack/spec`'s shipped paths (`dist`, `llms.txt`, `prompts`, `README.md`), "launch-window-minor-releases" and "This rule is in force today" match 0 files. Positive control: "defineStack" matches 87. ## Acceptance notes - **Untouched, outside the regime sentences:** the "Support Windows" table (`:153`-`:157`). It promises patches to the current and previous MAJOR. `release.yml` publishes only from `main` (F3), which is in pre mode, and npm shows no 16.x after `16.1.0` (2026-07-22), although `17.0.0` shipped on 2026-08-14. No lane exists to publish a maintenance patch for a line other than the one `main` is on. I did not file this; it is reported to the seat. - **Untouched:** the within-a-MAJOR guarantee sentences (`:33`-`:36`, `:179`-`:181`). The rewritten clause in 18 reconciles them with the 17.x history. --- _Generated by [Claude Code](https://claude.ai/code/session_012yK1ddAdAqfzwigRHZq3r2)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 340eab7 commit 0524c16

1 file changed

Lines changed: 18 additions & 16 deletions

File tree

‎content/docs/protocol/backward-compatibility.mdx‎

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,22 @@ ObjectStack follows strict backward compatibility guarantees to ensure predictab
1515
## Versioning Strategy
1616

1717
<Callout type="warn">
18-
**While the launch window is open, a breaking change ships as a MINOR release — not a MAJOR one.** Every published `@objectstack/*` package versions in lockstep, so the version number alone is not an upgrade-safety signal. This rule is in force today and overrides the MAJOR/MINOR mapping in the tables below wherever they disagree; read [Launch Window](#launch-window-minor-releases-can-contain-breaking-changes) at the end of this page before you plan an upgrade.
18+
**The 17.x minors ran under a launch-window override: a breaking change shipped as a MINOR release, not a MAJOR one.** Every published `@objectstack/*` package versions in lockstep, so the version number alone is not an upgrade-safety signal; read [Launch Window](#launch-window-minor-releases-can-contain-breaking-changes) at the end of this page before you upgrade across 17.x.
19+
20+
**The v18 line is a major in prerelease.** Changesets is in pre mode with the tag `next`, and `scripts/check-changeset-no-major.mjs` stands aside in pre mode, so a pull request may declare a major bump. Every release cut from `main` is the next `18.0.0-next.N` on npm's `next` tag, while `latest` stays on 17.x. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line).
1921
</Callout>
2022

21-
ObjectStack follows [Semantic Versioning 2.0.0](https://semver.org/) (`MAJOR.MINOR.PATCH`). The table below describes what each component means; the launch-window rule above governs which component a breaking change actually lands in today.
23+
ObjectStack follows [Semantic Versioning 2.0.0](https://semver.org/) (`MAJOR.MINOR.PATCH`). The table below describes what each component means; which version a breaking change ships in depends on the line, as the callout above says.
2224

2325
| Version Component | When Incremented | Guarantee |
2426
|:---|:---|:---|
2527
| **MAJOR** (X.0.0) | Incompatible API changes | May contain breaking changes |
26-
| **MINOR** (0.X.0) | New features — and, during the launch window, breaking changes | Existing code may require migration; the [release notes](/docs/releases) lead with what broke |
28+
| **MINOR** (0.X.0) | New features — and, in the 17.x minors, breaking changes | Existing code may require migration; the [release notes](/docs/releases) lead with what broke |
2729
| **PATCH** (0.0.X) | Bug fixes, backward-compatible | No behavior changes, only fixes |
2830

2931
### SemVer Guarantees
3032

31-
- **Zod schemas** are part of the public API surface. Adding optional properties is a MINOR change; removing or renaming properties is a breaking change, which during the launch window also ships in a MINOR release.
33+
- **Zod schemas** are part of the public API surface. Adding optional properties is a MINOR change; removing or renaming properties is a breaking change, which the 17.x minors also shipped in a MINOR release.
3234
- **TypeScript types** inferred from Zod (`z.infer<typeof Schema>`) follow the same guarantees as their source schemas.
3335
- **Helper functions** (e.g., `defineStack`, `defineStudioPlugin`) maintain their call signatures within a MAJOR version.
3436
- **Input formats** — `defineStack()` accepts both array and map (Record) format for all named metadata collections. Both formats are guaranteed stable within a MAJOR version.
@@ -63,7 +65,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
6365
- Code examples are updated to use the replacement
6466
- CLI tooling may provide automated migration commands
6567

66-
### Phase 3: Removal (MINOR release, during the launch window)
68+
### Phase 3: Removal (MINOR release in 17.x, the next `18.0.0-next.N` on the v18 line)
6769

6870
- Deprecated feature is removed from the schema
6971
- TypeScript types no longer include the property
@@ -77,11 +79,11 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
7779
```mermaid
7880
flowchart TD
7981
A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]
80-
B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
82+
B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release in 17.x, the next 18.0.0-next.N on the v18 line)"]
8183
```
8284

8385
<Callout type="warn">
84-
**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
86+
**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; in the 17.x minors that removal itself landed in a MINOR release, so a MINOR bump was where to expect a removal to arrive. On the v18 line it lands in the next `18.0.0-next.N`.
8587
</Callout>
8688

8789
---
@@ -90,9 +92,9 @@ flowchart TD
9092

9193
### What Constitutes a Breaking Change
9294

93-
Read the **Breaking?** column, not the version number: during the launch window every row below — breaking or not — ships in a MINOR or PATCH release, so the bump size does not tell you whether you have work to do.
95+
Read the **Breaking?** column, not the version number: in the 17.x minors every row below — breaking or not — shipped in a MINOR or PATCH release, and on the v18 line every row ships in the next `18.0.0-next.N`, so on neither line does the bump size tell you whether you have work to do.
9496

95-
| Change Type | Breaking? | Version Impact (launch window) |
97+
| Change Type | Breaking? | Version Impact (17.x minors) |
9698
|:---|:---|:---|
9799
| Removing a schema property | **Yes** | MINOR |
98100
| Renaming a schema property | **Yes** | MINOR |
@@ -112,7 +114,7 @@ Read the **Breaking?** column, not the version number: during the launch window
112114
1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
113115
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
114116
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
115-
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 70 published packages.
117+
4. **Release** — In the 17.x minors the breaking change shipped in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`, and `scripts/check-changeset-no-major.mjs` failed a pull request that declared a `major` bump, because under lockstep one `major` would promote all 70 published packages. On the v18 line it ships in the next `18.0.0-next.N`: the guard stands aside in pre mode, so a pull request may declare a major bump. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line).
116118

117119
---
118120

@@ -171,7 +173,7 @@ The `@objectstack/spec` package provides additional stability guarantees:
171173
### Export Stability
172174

173175
- All public exports are listed in the package's `index.ts` barrel files
174-
- Removing an export is always a breaking change; during the launch window it ships in a MINOR release
176+
- Removing an export is always a breaking change; the 17.x minors shipped it in a MINOR release, and on the v18 line it ships in the next `18.0.0-next.N`
175177
- Internal modules (prefixed with `_` or in `internal/` directories) are not covered by SemVer guarantees
176178

177179
### Runtime Behavior
@@ -185,26 +187,26 @@ The `@objectstack/spec` package provides additional stability guarantees:
185187
## Launch Window: MINOR Releases Can Contain Breaking Changes
186188

187189
<Callout type="warn">
188-
**This exception overrides the MAJOR/MINOR mapping above — read it before you plan an upgrade.** Every published `@objectstack/*` package versions in **lockstep**, and while the launch window is open a breaking change ships as a **MINOR** release rather than burning a MAJOR. The current release, **17.2.0**, is a MINOR and it contains a documented breaking change.
190+
**The 17.x minors ran under this exception — read it before you upgrade across them.** Every published `@objectstack/*` package versions in **lockstep**, and in the 17.x minors a breaking change shipped as a **MINOR** release rather than burning a MAJOR, so those minors contain documented breaking changes. The v18 line is a major in prerelease: every release cut from `main` is the next `18.0.0-next.N` on npm's `next` tag, while `latest` stays on 17.x. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line).
189191
</Callout>
190192

191193
### Which surfaces this covers
192194

193-
**All of them.** This is not scoped to an experimental corner or a pre-release channel: all **70** packages published from this repository belong to a single Changesets `fixed` group, so they share one version number and one policy. No published surface is exempt.
195+
**All of them.** It was not scoped to an experimental corner or a pre-release channel: all **70** packages published from this repository belong to a single Changesets `fixed` group, so they share one version number and one policy. No published surface was exempt.
194196

195-
The convention is enforced rather than informal — `scripts/check-changeset-no-major.mjs` fails any pull request that introduces a `major` bump, because under lockstep a single `major` on one package would promote the entire stack.
197+
The convention was enforced rather than informal — `scripts/check-changeset-no-major.mjs` failed a pull request that introduced a `major` bump, because under lockstep a single `major` on one package would promote the entire stack. Changesets is now in pre mode with the tag `next`, and the guard stands aside in pre mode, so a pull request on the v18 line may declare a major bump.
196198

197199
### What this means for an upgrade
198200

199201
- **Do not read a MINOR bump as safe to take unattended.** Read the [release notes](/docs/releases) for the version you are moving to: they lead with breaking changes and carry the migration steps.
200202
- **Pin exact versions** instead of caret ranges if you cannot review each MINOR before it lands.
201203
- **Diff your own metadata** across the upgrade with `os diff <before> <after> --breaking-only`.
202204

203-
MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking density was too high to carry `^16.x` consumers across on a caret range — but an individual breaking change does not, on its own, force one.
205+
MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking density was too high to carry `^16.x` consumers across on a caret range, and the v18 line is a major in prerelease — but in the 17.x minors an individual breaking change did not, on its own, force one.
204206

205207
### When it stops applying
206208

207-
The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
209+
The tables and deprecation timeline above state this rule for the 17.x minors directly, beside what the v18 line does. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. **For the 17.x minors, this section was the operative rule wherever any part of this page disagreed.** For the `18.0.0-next.N` prereleases, the operative rule is [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line).
208210

209211
---
210212

0 commit comments

Comments
 (0)