Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions content/docs/protocol/backward-compatibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ ObjectStack follows strict backward compatibility guarantees to ensure predictab
**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.

**The v18 line has exited prerelease mode.** While Changesets was in pre mode with the tag `next`, every release cut from `main` was the next `18.0.0-next.N` on npm's `next` tag, and `scripts/check-changeset-no-major.mjs` stood aside in pre mode, so a pull request could declare a major bump. Changesets has exited pre mode, so the line's GA version is `18.0.0`, which publishes to npm's `latest` tag, and the guard again fails a pull request that declares a `major` bump. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line).

**After 18.0.0, a breaking change ships in a MINOR or a PATCH release.** The maintainer's 2026-10-21 ruling ([#25105](https://github.com/objectstack-ai/objectstack/issues/25105)) keeps the no-major guard and grades each change by the metadata protocol: a key, value or metadata type added, narrowed or given a new meaning takes MINOR, and every other change takes PATCH, breaking or not. A non-protocol breaking change (a REST response, a boot refusal, a CLI exit status, a removed runtime export) keeps its `**BREAKING**` banner and ships as a PATCH. 18.1.0, the first release under the rule, is a MINOR that carries 82 changesets declared breaking.
</Callout>

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.

| Version Component | When Incremented | Guarantee |
|:---|:---|:---|
| **MAJOR** (X.0.0) | Incompatible API changes | May contain breaking changes |
| **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 |
| **PATCH** (0.0.X) | Bug fixes, backward-compatible | No behavior changes, only fixes |
| **MINOR** (0.X.0) | New features — and, in the 17.x minors, breaking changes; after 18.0.0, any change to the metadata protocol (added, narrowed or semantic) | Existing code may require migration; the [release notes](/docs/releases) lead with what broke |
| **PATCH** (0.0.X) | Bug fixes — and, after 18.0.0, every change that is not to the metadata protocol, breaking ones included | Before 18.0.0, no behavior changes. After it, a PATCH can break a REST client, a boot or a script: read its release notes |

### SemVer Guarantees

Expand Down Expand Up @@ -201,14 +203,14 @@ The convention was enforced rather than informal — `scripts/check-changeset-no
### What this means for an upgrade

- **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.
- **Pin exact versions** instead of caret ranges if you cannot review each MINOR before it lands.
- **Pin exact versions** instead of caret ranges if you cannot review each MINOR before it lands, and after 18.0.0 each PATCH as well.
- **Diff your own metadata** across the upgrade with `os diff <before> <after> --breaking-only`.

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 the next one — but in the 17.x minors an individual breaking change did not, on its own, force one.

### When it stops applying

The tables and deprecation timeline above state this rule for the 17.x minors directly, beside what the v18 prerelease did. 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).
The tables and deprecation timeline above state this rule for the 17.x minors directly, beside what the v18 prerelease did. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes. **It did not close at the 18.0.0 GA.** The 2026-10-21 ruling ([#25105](https://github.com/objectstack-ai/objectstack/issues/25105)) keeps the no-major guard on the v18 line and grades each change by the metadata protocol instead, so after 18.0.0 a breaking change ships in a MINOR when it narrows the metadata protocol and in a PATCH when it does not. **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). For the releases after 18.0.0, it is that ruling: read every release's notes, PATCH releases included, and pin exact versions.

---

Expand Down
14 changes: 13 additions & 1 deletion content/docs/releases/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ publishes to npm's `latest` tag, and the guard again fails a pull request that
declares a major bump. See
[how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line).

**After 18.0.0, the level follows the metadata protocol.** The maintainer's
2026-10-21 ruling ([#25105](https://github.com/objectstack-ai/objectstack/issues/25105))
keeps the no-major guard and grades every changeset by what it does to the
metadata protocol: a key, value or metadata type added, narrowed or given a new
meaning takes `minor`, and every other change takes `patch` — a REST door, a
driver, a boot refusal, the CLI or the Console — breaking or not. A
non-protocol breaking change keeps its `**BREAKING**` banner and its ADR-0087
disposition. 18.1.0 is the first release cut under the rule: a minor carrying
82 changesets declared breaking, 37 of them graded `patch`.

What that means when you read a version number:

- A **major** release may remove or change schemas in `@objectstack/spec`,
Expand All @@ -74,7 +84,9 @@ What that means when you read a version number:
`sys_position.permissions`; `15.1.0` removed `tenancy.strategy` and
`tenancy.crossTenantAccess` — all in minor releases. As the v17 notes put it,
17.1.0 and 17.2.0 are minors by version number, not by blast radius.
- A **patch** release fixes bugs.
- A **patch** release fixes bugs. On the v18 line after 18.0.0 it may also
carry a breaking change to anything that is not the metadata protocol: a REST
response, a boot refusal, a CLI exit status, a removed runtime export.

⚠️ **The version number is not the upgrade-safety signal.** Read the release
note for the version you are moving to — each one leads with breaking changes
Expand Down
8 changes: 6 additions & 2 deletions content/docs/releases/v18/18-0.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ Read with `npm view` and `npm pack` on 2026-10-20, after the publish:
| Prereleases | `18.0.0-next.0` … `next.3` and `next.5`. `next.4` was never published, although the CHANGELOG carries a section for it |

Install 18 by its exact version (`18.0.0`) or a caret range (`^18.0.0`); both
resolve `latest`. Moving the stale `next` tag is a release act, outside these
resolve `latest`. A caret range also takes every later 18.x release by itself,
and under the v18 level rule a later minor or patch can break you: 18.1.0 does
(see the [18.1.0 notes](/docs/releases/v18/18-1)). Pin the exact version when
you cannot read each release's notes before it lands. Moving the stale `next` tag is a release act, outside these
notes.

### How to read the 18.0.0 CHANGELOG
Expand Down Expand Up @@ -622,7 +625,8 @@ Every line here was answered by the walk's 18.0.0 server after the ceremony.
The v18 line keeps landing on `main`, and the version commit did not take
everything that landed before it. Read on `main` at `71696121a6`: 26 pending
changesets, ten of them `minor`, so the release that publishes them is
**18.1.0**. They fall in two sets.
**18.1.0**. They fall in two sets. (By the 18.1.0 cut the pending set had grown
to more than 220 changesets; the [18.1.0 notes](/docs/releases/v18/18-1) cover all of them.)

**Shipped in 18.0.0, listed in no 18.0.0 CHANGELOG.** Eight of the 26 belong to
commits that landed before the 18.0.0 version commit (`06cab4849a`), so their
Expand Down
Loading
Loading