Repository navigation
Commit f35b4e1
docs(protocol): Support Windows states latest-only support, and the releases index closes the v17 line (#25146)
Fixes #25102
Clause-②: no
A docs-only PR. It executes the maintainer's ruling `6328214336` on
#25102, **letter A, latest-only** (「25134 25092 25102 同意」). It changes
two hand-written pages by +9 / -7 lines:
- `content/docs/protocol/backward-compatibility.mdx`;
- `content/docs/releases/index.mdx`. This is the releases index, not a
per-release page. No file under `content/docs/releases/v17/**` or
`v18/**` is touched.
It edits no code, workflow, gate, changeset or ADR, and it takes no
release action.
## What the ruling binds, and what this PR does not do
- **Done (ruling item 1):** the Support Windows table is replaced by a
latest-only statement. The previous MAJOR receives no fixes, and an
application upgrades through ADR-0087's conversion layer and `os migrate
meta`. The page links both.
- **Not done (ruling item 3):** no B (an on-demand security line) and no
C (a two-branch model).
- **Not written (ruling item 2):** the emergency-branch option. The page
names no emergency branch, no date or window, and no EE-specific
promise.
## Support Windows: before and after
**Before** (`:153`-`:159` at `abc25a3536`):
```text
### Support Windows
| Version | Status | Support Level |
|:---|:---|:---|
| Current MAJOR (N) | **Active** | Full feature development, bug fixes, security patches |
| Previous MAJOR (N-1) | **Maintenance** | Critical bug fixes and security patches only |
| Older versions (N-2+) | **End of Life** | No support; upgrade recommended |
```
**After:** the heading text "Support Windows" is unchanged, so
`#support-windows` still resolves (see *Inbound anchors* below). Then a
lead sentence and five bullets, in the bold-lead bullet shape of the
page's "SemVer Guarantees" and "Migration Guides" sections. Each clause
and its source:
| Clause on the page | Source |
|:---|:---|
| **Only the current MAJOR receives fixes.** There is no maintenance
line for an older MAJOR: compatibility across majors is carried by the
conversion layer and migration chain (ADR-0087), not by patching old
releases. | Ruling item 1. npm re-check below: no version line of ours
has ever published again after a newer line shipped. |
| **The current MAJOR**: bug fixes and security patches ship in the next
release cut from `main`. While the next MAJOR is in prerelease, every
release cut from `main` is one of its prereleases on the pre-mode
dist-tag (`next` on the v18 line), so a fix reaches `latest` only when
that MAJOR goes GA. | `.github/workflows/release.yml`'s header: `npm
publish` lives in one job, reachable only from a `push` to `main` and
from the repair dispatch, and the repair "cannot mint a version". The
card's fact 4 measured the consequence: between 17.7.0 and 18.0.0,
`latest` received no fix. The page's own top callout already states the
pre-mode half. The tag is written "the pre-mode dist-tag" because
nothing fixes it: `changeset pre enter` takes the tag as an argument,
v16 and v17 ran on `rc` and v18 on `next`, and
`.github/workflows/cut-rc.yml:235` requires `tag: rc` for its own lane
only. |
| **The previous MAJOR, and every older one**: receives no fixes, and no
backport or maintenance release is promised. To take a fix, upgrade to
the current MAJOR. | Ruling item 1. |
| **The conversion layer** (ADR-0087 D2): as the runtime loads your
metadata, it converts the previous MAJOR's lossless changes (renames,
alias removals, field moves) to the current shape and emits a
deprecation notice per conversion. Those shapes keep loading after you
move the runtime forward. It covers only the previous MAJOR and only
lossless changes; `os migrate meta` carries everything else. | ADR-0087
D2: scope "losslessly mappable changes only — renames, alias removals,
field moves, enum re-spellings"; window "N accepts N−1 shapes at load";
"emitting a structured deprecation notice per applied conversion";
"Semantic changes are excluded … it goes to D3". |
| **`os migrate meta`** (ADR-0087 D3): `--from N` replays the chain from
the major your metadata was authored against (protocol 16 or later, the
chain's floor) to the current one in a single pass, and lists the edits
your source needs. With `--write` it writes the mechanical ones it can
trace to one literal into your source files. It reads no database.
`--stored` replays the same chain over one deployment's `sys_metadata`
rows and, with `--apply`, rewrites the ones still carrying an older
shape. Links: the CLI reference
(`/docs/deployment/cli#os-migrate-meta---stored`) and Upgrading, "The
metadata app". | ADR-0087 D3 (cross-major in one command).
`content/docs/deployment/cli.mdx`: the `os migrate meta --stored` row of
the Data migrations table (`:1252`) and the section's "division of
labour" paragraph (`:1819`), which states what `--from N` and `--stored`
each do. `--write` and `--apply`: the flag descriptions in
`packages/cli/src/commands/migrate/meta.ts`. The floor:
`MIGRATION_SUPPORT_FLOOR = 16`
(`packages/spec/src/migrations/registry.ts.template:79`) and
`content/docs/upgrading.mdx:233`. |
| **Testing a MAJOR before it reaches `latest`**:
`scripts/check-changeset-no-major.mjs` fails a pull request that
declares a `major` bump outside Changesets pre mode and stands aside
inside it, and a MAJOR declared in pre mode publishes only prereleases,
on the pre-mode dist-tag, until pre mode exits. The v18 line used `next`
(`18.0.0-next.0` … `18.0.0-next.5`) before 18.0.0. Install from that tag
to run your app against a MAJOR before it becomes `latest`. |
`scripts/check-changeset-no-major.mjs` at `origin/main` `459afea1f6`:
`judge()` (`:678`-`:683`) returns `exempt` when `.changeset/pre.json`
has `mode: 'pre'` (`:681`) and `enforce` otherwise (`:682`). The
`exempt` case exits 0 (`:818`-`:826`), and the `enforce` case prints the
refusal (`:828`). The RC EXEMPTION header (`:113`-`:120`) states the
Changesets semantics: in pre mode "a `major` bump only ever produces a …
PRE-RELEASE version — nothing final publishes until `changeset pre
exit`". npm: `18.0.0-next.0` (2026-10-13) to `18.0.0-next.5`
(2026-10-19), then `18.0.0` (2026-10-20). The page says the guard fails
"a" pull request, not "any", and it does not say a MAJOR can be declared
only in pre mode, because the guard's `allow-major` label escape hatch
skips it outside pre mode (header `:122`-`:124`; the label is read live
at `.github/workflows/pr-automation.yml:1063`). |
The CLI binary is spelled `os`, as everywhere else on this page.
`packages/cli/package.json` `bin` maps both `os` and `objectstack` to
the same `./bin/run.js`, so this is the ruling's `objectstack migrate
meta`.
## Premise re-checks (ruling item 1), verbatim, at `origin/main`
`abc25a3536`
```text
$ git grep -n -iE "End of Life|security patches only|support window" origin/main -- content skills README.md
origin/main:content/docs/protocol/backward-compatibility.mdx:153:### Support Windows
origin/main:content/docs/protocol/backward-compatibility.mdx:158:| Previous MAJOR (N-1) | **Maintenance** | Critical bug fixes and security patches only |
origin/main:content/docs/protocol/backward-compatibility.mdx:159:| Older versions (N-2+) | **End of Life** | No support; upgrade recommended |
(exit 0)
```
```text
$ for p in @objectstack/spec @object-ui/core better-auth; do printf '%s: ' "$p"; curl -s "https://registry.npmjs.org/$p" | jq -r '.time | to_entries[] | select(.key | test("^[0-9]+\\.[0-9]+\\.[0-9]+$")) | "\(.value) \(.key)"' | sort | awk '{split($2,v,"."); print v[1]"."v[2]}' | uniq | sort | uniq -d | tr '\n' ' '; echo; done
@objectstack/spec:
@object-ui/core:
better-auth: 1.4 1.5 1.6 1.7
(exit 0)
```
Both hold as the card states. Neither of ours prints a re-published
line, and the `better-auth` positive control prints `1.4 1.5 1.6 1.7`.
The card's grep has no "maintenance" term, so a wider grep was run to
catch another page that states a maintenance line. The pattern covered
`maintenance (line|release|branch|mode|window|period)`, `previous
major`, `N-1`, `LTS`, `long-term support`, `backport`, `older
(major|versions)`, `end-of-life` and `EOL`, over `content skills
README.md`. It found 13 lines; none states a support window or a
maintenance line:
- the two lines above;
- a retry formula (`backoffMultiplier^(n-1)`);
- a database maintenance window;
- the auto-generated plugin-versioning schema reference;
- Node.js end-of-life in the 17.0 notes;
- five lines on metadata authored against an older or previous major,
which describe the conversion window, the stored-row replay, or a
meaning that changed across a major (`upgrading.mdx:23`, `:199`,
`cli.mdx:1742`, `api/declarative-endpoints.mdx:266`,
`protocol/kernel/http-protocol.mdx:1173`), plus the matching skill line
`skills/objectstack-api/SKILL.md:201`;
- one unrelated `forms.mdx` hit.
So no other page joins the surface.
## Every other sentence changed (ruling items 3 and 4)
1. **`backward-compatibility.mdx:142`, the Migration Support callout's
second sentence.** This PR's new upgrade clause would have contradicted
it on the same page.
- Before: "An automated `codemod` command is referenced in the migration
guides but is not yet available; until then, combine `os doctor
--scan-deprecations`, `os diff`, and the CHANGELOG migration notes
(below) to upgrade across MAJOR versions."
- After: "There is no `codemod` command either: to upgrade your metadata
across MAJOR versions, run `os migrate meta` (see [Support
Windows](#support-windows)), and use `os doctor --scan-deprecations`,
`os diff`, and the CHANGELOG migration notes (below) for what it does
not rewrite."
- Why: the old sentence names doctor, diff and the CHANGELOG as *the*
way across majors, ten lines above a section that now says an app
upgrades through the conversion layer and `os migrate meta`.
- Why "your metadata": `os migrate meta` rewrites metadata. It does not
rewrite the TypeScript patterns `os doctor --scan-deprecations` reports,
and the comment at `packages/cli/src/commands/doctor.ts:2683` explains
why doctor's own hint must not point at it. So the sentence scopes the
command to metadata and keeps doctor, diff and the CHANGELOG for the
rest.
- Its "referenced in the migration guides" clause was also false. `git
grep -i codemod -- content/docs skills` outside `releases/` finds only
this sentence. The CLI has no `codemod` command:
`doctor-deprecation-hint-commands.test.ts:160`-`:162` asserts that
`objectstack codemod v2-to-v3` and `os codemod` resolve to no command.
- The first sentence of the callout is unchanged.
2. **`releases/index.mdx:22`, the v17 entry's trailing status.**
- Before: "(current series: 17.7.0, released 2026-10-06)".
- After: "(final release: 17.7.0)".
- Why: the index labelled both v18 and v17 "current series", and the
latest-only statement allows exactly one. "final release: X" is the
index's own wording for a closed line: v16 "(final release: 16.1.0)",
v15 "(final release: 15.1.1)", v14 "(final release: 14.8.0)". The v16
label was set the same way when that line closed (`a1ef2c9d63`, the day
after 17.0.0 shipped).
- Gate compatibility: `check-release-section-coverage.mjs`
`indexCurrencyFindings` passes any trailing status that names the newest
17.x GA (17.7.0). `check-release-page-status.mjs` names both wordings as
valid. `sync-release-index-currency.mjs` rewrites only a stale `current
series:` field and never a `final release:` one, so the version step
leaves this entry alone.
- The release date is dropped to match the three sibling entries. It
stays on the v17 page.
No other sentence changed. The rest of `backward-compatibility.mdx` was
read in full at `BASE`, including the PR #22988 sentences: the 17.x
launch-window rule, the v18 prerelease regime and the "When it stops
applying" section. Each of them describes versioning history or the
breaking-change regime, and none promises a fix to an earlier line, so
all stay.
**Releases index (ruling item 4).** No sentence in
`content/docs/releases/` promises a previous-major fix. The grep, at
`abc25a3536`:
```text
git grep -n -i -E "maintenance|backport|security (fix|patch)|previous major|N-1\b|support(ed)? (window|line|policy|period)|end[- ]of[- ]life|\bLTS\b|long[- ]term|current series|final release|receives? (fixes|patches)|patch(es)? (for|to) (the )?(previous|older|17\.x|16\.x)" abc25a3 -- content/docs/releases
```
It found 14 lines (exit 0):
- index entries `:21`-`:25`, the status labels;
- `v16.mdx:20`, `:21` and `:863`, which state that v16 is closed;
- `v17/17-0.mdx:91`, Node.js end-of-life;
- `v17/17-4.mdx:355`, a store `maintenance` key;
- `v17/17-7.mdx:60`, `:192`, `:563` and `:915`, which concern long-term
agent memory.
The only contrary statement is the v17 "current series" label (item 2).
`meta.json` holds page order only, and `implementation-status.mdx`
matched nothing.
**Inbound anchors.** `git grep -n -i "#support-windows"` exits 1 in this
repo and exits 1 in `../objectui` at `b014f5eea`; the control leg in
`../objectui` exits 0. `git grep "backward-compatibility#"` finds one
inbound fragment link, `releases/index.mdx:82`, which targets the
launch-window anchor and is unchanged. The heading text is kept anyway,
because links from outside both repositories cannot be enumerated.
## Gates
All readings below are at this branch's head **`f453405903`**, against
`origin/main` merge base `abc25a3536`.
- **Derivation.** `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 49 commands: 33 pnpm and 16 direct
node; 41 matched by path and 8 declared whole-tree. The change set is 2
paths and 16 changed lines. The list is identical at `2b3b50b4da`,
`062bfaf1dd` and `f453405903` (`diff` exit 0).
- **Ran all 49.** Each exit code was captured before any pipe. All 49
exited 0.
- **Reconciliation.** `--ran` printed: "✓ dispatch-gates --ran: 49
derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED (a DERIVED
zero — all 49 recorded an exit code and none of them is 3)."
- **Prerequisite build.** `pnpm exec turbo run build
--filter='@objectstack/lint...' --filter='@objectstack/client-react...'
--concurrency=2` ran under `scripts/pm/os-verify-lock.sh`: `VERDICT
command-exit 0`, 37 of 37 tasks. In this round all 37 were cache hits on
the same inputs.
- **Docs-link and docs-audit verdict lines:**
- `check:doc-anchors`: "566 internal #fragment link(s) across 424 source
file(s) all resolve to a real heading". That covers the new
`#support-windows`, `/docs/deployment/cli#os-migrate-meta---stored` and
`/docs/upgrading#the-metadata-app` links.
- `check:docs-redirects`: "OK (apps/docs/redirects.mjs: 98 entries -- 95
page destination(s) resolved against content/docs …)".
- `check:docs-audit-scope`: "docs-accuracy-audit scope is in sync with
content/docs/: 206 hand-written doc(s)". No `handwritten-docs.json`
write was asked for or made.
- `check:docs-transcript-drift`: "4 declared transcript value(s) across
418 page(s) … equal what the registry derives today".
- **Release-index verdict lines:**
- `check-release-section-coverage.mjs`, plain and `--strict`: "OK — 11
published minor(s) across 3 GA major(s) in scope (v16, v17, v18); …
every index entry names the newest release of its major". This covers
the v17 entry's new `(final release: 17.7.0)`.
- `check:release-page-status`: "OK — 3 GA major(s) in scope (v16, v17,
v18); each page's release-status blockquote and index entry describe a
shipped release".
- `check:release-notes`: "OK".
- **Other verdict lines:**
- `check:lockstep-package-count`: "all 3 occurrence(s) agree with the
true count (73)". The page's lockstep sentences are untouched.
- `check:nul-bytes`: "OK (scanned 11726 text file(s) … no raw ASCII
control bytes)".
- `@objectstack/spec check:docs`: "227 generated files in sync".
- `check:skill-examples`: "262 prose examples type-check across 3
surface(s)".
- **MDX syntax.** Both edited pages compile with the workspace's
`@mdx-js/mdx` 3.1.1. Negative control: an unclosed `Callout` is refused
("Expected a closing tag").
- **Docs site build.** Run locally at `062bfaf1dd` as the production
build that CI's `Build Docs` runs (`apps/docs/vercel.json`
`buildCommand`), with one declared difference. CI forces turbo
(`TURBO_FORCE`) so the docs task cannot be a replayed cache hit; locally
the docs package's own `build` script ran directly, outside turbo's
cache, after its dependencies built: `pnpm exec turbo run build
--filter='@objectstack/docs^...' --concurrency=2 && pnpm --filter
@objectstack/docs run build`, under the verify lock. Results:
- `VERDICT command-exit 0`;
- "✓ Compiled successfully in 2.2min";
- "✓ Generating static pages using 2 workers (1275/1275)";
- `apps/docs/.next/BUILD_ID` present.
It was not re-run locally at `f453405903`. The derivation names it only
as CI's `Build Docs` job, as before, and the fix changed text inside two
bullets without changing the MDX shape: both pages compile, and the
unclosed-`Callout` control is refused. CI's `Build Docs` check-run on
`f453405903` concluded `success`.
- **Merge probe.** `git merge-tree --write-tree` against `origin/main`
`459afea1f6` exits 0 (tree `e475552dea`). `main` has moved two commits
since the base, and neither touches either path.
- **`skip-changeset`, re-measured.**
- None of the 73 non-private manifests has a `files[]` entry naming
`content` or `docs`.
- Under `packages/` (with `dist/`, excluding `node_modules`), the
removed phrase "Critical bug fixes and security patches only", the added
"Only the current MAJOR receives fixes", "current series: 17.7.0" and
"Support Windows" each match 0 files.
- Positive control: "defineStack" matches 989 files.
- **Not run locally, CI-owned:**
- no package is touched, so no package `test` or `typecheck` is owed;
- `test:repo` and the `Test Core` shards;
- the four Type Check lanes;
- the three workflow-valued families (`check-issue-citations.mjs
--census`, shard attestation, test completeness);
- `pnpm lint`.
The PR's check-runs answer them: `Lint & Repo Gates`, `TypeScript Type
Check`, `Test Core`, `Build Core`, `Build Docs`.
## Acceptance notes
- **ADR-0087 D6 is not cited.** D6 reads "Every major is published to
the `next` dist-tag at least one RC cycle before `latest`", but the
ADR's 2026-07-15 as-built addendum lists "P3 — MCP tools + RC discipline
(D6): deferred, evidence-gated", and npm `@objectstack/spec` does not
follow it:
- majors 8 to 15 published no prerelease version at all (0 in the
registry). That was the pre-launch window, which the same addendum
ratifies for 12 to 15.
- 16 and 17 ran `-rc.N` prereleases (`16.0.0-rc.0` and `rc.1`;
`17.0.0-rc.0` to `rc.6`), and the `rc` dist-tag still points at
`17.0.0-rc.6`.
- only 18 ran on `next`.
So both prerelease clauses rest on
`scripts/check-changeset-no-major.mjs`, which holds today, and name
`next` only as the v18 instance. Nothing found fixes which tag a future
pre mode uses, so the page names none. Not filed: it is history, with no
reach. Carrier: none.
- **Out of this PR's surface, reported to the seat and filed by it as
#25148 (one card for both):**
- `content/docs/releases/index.mdx:21` reads "(current series: 18.0.0,
released 2026-10-19)". The 18.0.0 Version Packages commit `06cab4849a`
is dated 2026-10-20T11:47Z, npm's `time["18.0.0"]` is 2026-10-20T12:51Z,
and `releases/v18/18-0.mdx:3` says "published 2026-10-20".
- `content/docs/releases/v17/index.mdx`'s status blockquote still says
"A plain install now resolves 17.7.0" while `latest` is 18.0.0. That is
a per-release page, which this PR does not edit.
- **Prose that quotes the old callout wording.** Three places paraphrase
the `:142` sentence this PR rewrites: the comments at
`packages/cli/src/commands/doctor.ts:2680` and
`packages/cli/src/commands/doctor-deprecation-hint-commands.test.ts:18`,
and the source note at `docs/qa/platform-checklist/areas/cli.json:1189`.
Each quotes "not yet available"; the page now says there is no `codemod`
command. None of them reads the page, and no gate compares them. The
same `doctor.ts` comment still says both of `os migrate meta`'s writes
are `--out` JSON snapshots, which predates `--write`. They are code and
QA data, outside this PR's surface. Not filed. Carrier: none.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c71d03a commit f35b4e1
2 files changed
Lines changed: 9 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
142 | | - | |
| 142 | + | |
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
| |||
152 | 152 | | |
153 | 153 | | |
154 | 154 | | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | | - | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
160 | 162 | | |
161 | 163 | | |
162 | 164 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
0 commit comments