Repository navigation
Commit 0524c16
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
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | | - | |
| 23 | + | |
22 | 24 | | |
23 | 25 | | |
24 | 26 | | |
25 | 27 | | |
26 | | - | |
| 28 | + | |
27 | 29 | | |
28 | 30 | | |
29 | 31 | | |
30 | 32 | | |
31 | | - | |
| 33 | + | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
| |||
63 | 65 | | |
64 | 66 | | |
65 | 67 | | |
66 | | - | |
| 68 | + | |
67 | 69 | | |
68 | 70 | | |
69 | 71 | | |
| |||
77 | 79 | | |
78 | 80 | | |
79 | 81 | | |
80 | | - | |
| 82 | + | |
81 | 83 | | |
82 | 84 | | |
83 | 85 | | |
84 | | - | |
| 86 | + | |
85 | 87 | | |
86 | 88 | | |
87 | 89 | | |
| |||
90 | 92 | | |
91 | 93 | | |
92 | 94 | | |
93 | | - | |
| 95 | + | |
94 | 96 | | |
95 | | - | |
| 97 | + | |
96 | 98 | | |
97 | 99 | | |
98 | 100 | | |
| |||
112 | 114 | | |
113 | 115 | | |
114 | 116 | | |
115 | | - | |
| 117 | + | |
116 | 118 | | |
117 | 119 | | |
118 | 120 | | |
| |||
171 | 173 | | |
172 | 174 | | |
173 | 175 | | |
174 | | - | |
| 176 | + | |
175 | 177 | | |
176 | 178 | | |
177 | 179 | | |
| |||
185 | 187 | | |
186 | 188 | | |
187 | 189 | | |
188 | | - | |
| 190 | + | |
189 | 191 | | |
190 | 192 | | |
191 | 193 | | |
192 | 194 | | |
193 | | - | |
| 195 | + | |
194 | 196 | | |
195 | | - | |
| 197 | + | |
196 | 198 | | |
197 | 199 | | |
198 | 200 | | |
199 | 201 | | |
200 | 202 | | |
201 | 203 | | |
202 | 204 | | |
203 | | - | |
| 205 | + | |
204 | 206 | | |
205 | 207 | | |
206 | 208 | | |
207 | | - | |
| 209 | + | |
208 | 210 | | |
209 | 211 | | |
210 | 212 | | |
| |||
0 commit comments