Repository navigation
Commit 246edf5
docs(releases): the versioning policy states the 17.x launch-window rule and the v18 prerelease regime (#22963)
Fixes #22933
Clause-②: no
Release-owned channel 2: a factual error in
`content/docs/releases/index.mdx`, fixed in a dedicated docs-only PR, as
triage graded it (comment 6118040748). One file, one section
("Versioning policy"), +15 / -7 lines. No gate script is edited and no
release action is taken.
## What was false
On `main` at `51b43a8dc` the section said a launch-window override is
"in force today" and that `scripts/check-changeset-no-major.mjs` "fails
any pull request that declares a major bump". On that same commit,
`.changeset/pre.json` reads `{"mode": "pre", "tag": "next"}`, and the
guard's `judge()` returns its `exempt` verdict (exit 0) for any major a
diff introduces while `pre.mode === 'pre'`.
## Each changed sentence, before and after, and the `main` fact it rests
on
**1. Opening sentence**
- Before: "ObjectStack follows Semantic Versioning, with one override
that is in force today: **while the launch window is open, a breaking
change ships as a minor release.**"
- After: "ObjectStack follows Semantic Versioning."
- Rests on: the deleted clause is the false claim. The override is not
what governs a release cut from `main` today (see 5 and 6).
**2. Lockstep sentence**
- Before: "All `@objectstack/*` packages version in lockstep, so a
single major bump would promote the whole platform; instead a breaking
change lands in a **minor** ..."
- After: "All `@objectstack/*` packages version in lockstep, so a single
major bump promotes the whole platform, and which version a breaking
change ships in depends on the line."
- Rests on: the `fixed` group in `.changeset/config.json`. The guard's
own header (WHY THIS EXISTS) says Changesets applies the highest bump in
the group to every package in it.
**3. The 17.x rule, now history** (new bold lead)
- After: "**The 17.x minors ran under a launch-window override: a
breaking change shipped as a minor release.**"
- Rests on: `.changeset/pre.json` was created by `a87d8be29` ("enter
Changesets pre mode (`next`) with one `major` marker", 2026-10-08). It
is absent at `a87d8be29^` and at `dc1b98680` (2026-09-23, inside the
17.x minors). Control: `.changeset/config.json` is present at
`dc1b98680`. So the guard was in its enforcing branch through the 17.x
minors. The v17 page's banner says 17.1.0 through 17.7.0 are "minors by
version number, not by blast radius".
**4. How the 17.x rule was enforced**
- Before: "... alongside a changeset entry marked `**BREAKING**`, and
`scripts/check-changeset-no-major.mjs` fails any pull request that
declares a major bump."
- After: "It landed in a **minor** alongside a changeset entry marked
`**BREAKING**`, and `scripts/check-changeset-no-major.mjs` failed a pull
request that declared a major bump."
- Rests on: `judge()` returns `enforce` (exit 1) when `pre.json` is
absent. The sentence is now past tense. "any" became "a" because the
script's header documents an `allow-major` label escape hatch outside
pre mode.
**5. The v18 regime** (new bold lead)
- After: "**The v18 line is a major in prerelease.**"
- Rests on: `a87d8be29` opened the line in pre mode with a `major`
marker. The v18 page (`content/docs/releases/v18/index.mdx`) has the
release-status banner.
**6. The guard in pre mode** (new)
- After: "Changesets is in pre mode with the tag `next`, and
`check-changeset-no-major.mjs` stands aside in pre mode, so a pull
request may declare a major bump."
- Rests on: `.changeset/pre.json`; `judge()` line 673 (`if (pre?.mode
=== 'pre') return { verdict: 'exempt', ... }`); the RC EXEMPTION
paragraph at `:105`. The exemption has no package condition and no
pending-marker condition; those belong only to the separate
protocol-lockstep verdict. 21 pending changesets on `main` declare a
`major`.
**7. What publishes** (new)
- After: "Every release cut from `main` is the next `18.0.0-next.N` on
npm's `next` tag, while `latest` stays on 17.x."
- Rests on: the v18 page, which says this in the same words (`:6`-`:8`,
`:27`-`:29`). `release.yml` triggers on pushes to `main` only.
**8. Link** (new)
- After: "See [how a breaking stage lands on the v18
line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line)."
- Rests on: that heading exists on the v18 page, and `check:doc-anchors`
resolves the link. The link points at the v18 page and does not restate
its `next`-before-`latest` paragraphs (ADR-0087 D6), so the two pages
cannot drift apart on them.
## Where this departs from the card's direction (measured)
- **I did not write "breaking changes land as `major` changesets."** Of
the 113 pending changesets carrying `**BREAKING**`, 93 declare no
`major` at all. At least four of them were added after pre mode was
entered: `d1dbe70eb`, `ffb31fca3`, `7518ee397` and `9ad3d2470` each have
`a87d8be29` as an ancestor (`merge-base --is-ancestor` exit 0, which
holds in a shallow clone). What `main` supports is narrower: the guard
admits a `major` in pre mode, and every cut from `main` is the next
`18.0.0-next.N`. The section says exactly that.
- **I did not write "it still holds for any 17.x release."**
`release.yml` publishes only on a push to `main`, and `main` is in pre
mode on `next`, so the current pipeline cannot cut a 17.x release. The
section states the 17.x rule in the past tense, as what the 17.x minors
ran under, and makes no claim about a future 17.x release.
- No date and no version beyond `18.0.0-next.N` (triage's bound). The
section says nothing about what happens when pre mode exits.
## Measured hypotheses
- **The section spans `:46`-`:72`**, not `:46`-`:54`. The later
paragraphs (the major/minor/patch bullets and the warning paragraph) do
not restate the "in force today" rule. The minor bullet's examples are
released history and stay true. They are unchanged.
- **The sync script is untouched by this edit.** `node
scripts/sync-release-index-currency.mjs` printed "every in-scope entry
... (v16, v17) already names the newest GA release of its major ...
nothing rewritten", exit 0. The file's `git hash-object` is the same
before and after the run, and equal to the HEAD blob (`b2df87d2a`). Its
`--self-test` printed "42 cases pass", exit 0. `indexEntryLine()` takes
the first line containing `](/docs/releases/vN)`. The new link ends
`v18#how-...)`, so that needle cannot match it.
## Tests: all at `83a041d35` (HEAD of this branch)
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 47 commands at `83a041d35`,
identical to the dispatch's lead list. I ran all 47 with each exit code
captured before any pipe. The `--ran` reconciliation printed: "47
derived famil(ies) accounted for — 47 run, 0 NOT-MEASURED (a DERIVED
zero — all 47 recorded an exit code and none of them is 3)."
- Five gates first refused with exit 3 on missing builds
(`check:doc-formula-expressions`, `check:doc-security-posture`,
`check:docs`, `check:skill-examples`, `check:docs-transcript-drift`).
Those were prerequisite refusals, not findings. I built `spec`,
`formula`, `lint`, `client` and `client-react` with their closures under
the verify lock (`VERDICT command-exit 0` twice) and re-ran them. All
five gates now exit 0, for example "262 prose examples type-check across
3 surface(s)".
- `check:doc-anchors`: "485 internal #fragment link(s) across 419 source
file(s) all resolve to a real heading". A probe through its own
`extractFragmentLinks()` shows the new link is extracted from this file
and its target id exists on the v18 page.
- **Lint, a proven narrowing rather than a skipped run.** ① The
population comes from eslint's own config: `eslint.config.mjs` `files`
globs are `**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` and narrower TypeScript
globs, with no `.mdx` glob. ② `npx eslint --no-inline-config --format
json content/docs/releases/index.mdx` returned 1 file, 0 errors, and one
warning: "File ignored because no matching configuration was supplied."
③ The diff changes only this file, which no config object matches, so no
linted file's verdict can move.
- No package is touched, so no package `test` or `typecheck` is owed. CI
runs the Build Docs and Test Core jobs; they were not measured locally.
## Acceptance notes
- **Not filed here; reported to the seat:**
`content/docs/protocol/backward-compatibility.mdx:18` still says "While
the launch window is open, a breaking change ships as a MINOR release —
not a MAJOR one. ... This rule is in force today". The table and the
Deprecation Timeline repeat the same claim. This section links that page
"for the full policy", so after this lands the two pages disagree. The
page is outside this card's file surface.
- The warning paragraph's "Read the release note for the version you are
moving to" has no curated note to point at for an `18.0.0-next.N`
prerelease. The new v18 link leads to that page's "read the CHANGELOG of
every prerelease you move to", so I did not edit the paragraph further.
- `skip-changeset`: no published package lists `content/`, `docs` or
`releases` in `files[]` (control: `@objectstack/spec`'s `files[]` reads
back).
---
_Generated by [Claude
Code](https://claude.ai/code/session_012yK1ddAdAqfzwigRHZq3r2)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8d8f965 commit 246edf5
1 file changed
Lines changed: 15 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
55 | 63 | | |
56 | 64 | | |
57 | 65 | | |
| |||
0 commit comments