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
2 changes: 1 addition & 1 deletion content/docs/protocol/backward-compatibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ObjectStack follows strict backward compatibility guarantees to ensure predictab

**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 86 changesets declared breaking.
**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 87 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.
Expand Down
2 changes: 1 addition & 1 deletion content/docs/releases/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ 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
86 changesets declared breaking, 39 of them graded `patch`.
87 changesets declared breaking, 39 of them graded `patch`.

What that means when you read a version number:

Expand Down
44 changes: 33 additions & 11 deletions content/docs/releases/v18/18-1.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ description: "Release notes and upgrade checklist for 18.1.0, the first minor of

<Callout type="warn">
**How this page was compiled.** From the 18.1.0 changesets on `main` at
`19d3720af1` (2026-10-23), before the 18.1.0 cut: a rehearsed
`03243fc637` (2026-10-24), before the 18.1.0 cut: a rehearsed
`pnpm run version` in a throwaway clone at `b2c0752900` (73 packages move to
18.1.0, and their `## 18.1.0` sections held 227 distinct entries), and the 23
changesets merged after it, graded the same way. That makes **250 distinct
18.1.0, and their `## 18.1.0` sections held 227 distinct entries), and the 26
changesets merged after it, graded the same way. That makes **253 distinct
entries**. The five sections below use the grouping the GitHub Release body
uses, and the counts are that grouping's: **77** need you to act, **9** are
converted for you, **47** add capabilities, **1** declares a semantic change,
and **116** are other fixes. Nothing here was
uses, and the counts are that grouping's: **78** need you to act, **9** are
converted for you, **47** add capabilities, **2** declare a semantic change,
and **117** are other fixes. Nothing here was
read off a published 18.1.0 package or walked on an upgrade yet: every refusal
is described as its changeset describes it. Anything that lands on `main` before
the cut is in the CHANGELOG and not on this page.
</Callout>

## Highlights — 18.1.0

- ⚠️ **A minor that breaks.** 18.1.0 is a minor version, and 86 of its
- ⚠️ **A minor that breaks.** 18.1.0 is a minor version, and 87 of its
changesets are declared breaking. Under the level rule the maintainer ruled
on 2026-10-21 (#25105, "ruling A"), the v18 line takes no further major: a
change to the metadata protocol takes `minor`, and every other change takes
Expand Down Expand Up @@ -67,7 +67,7 @@ the cut is in the CHANGELOG and not on this page.
- **An `external` membership grade for portal users** (`9019af9`, #25515). A
supplier's or customer's portal user is a member of the organization but
reads only its own identity rows, and reaches data only through grants made
to it. A roster object can declare **membership keys** that a row policy reads
to it (`45f9962`, #25541). A roster object can declare **membership keys** that a row policy reads
as `current_user.KEY` (`dc532e8`, #25513; `e5eab06`, #25528).

---
Expand All @@ -89,7 +89,7 @@ else:

So a **patch** release can break a REST client, a boot or a deployment
script, and 18.1.0 carries 39 changesets graded `patch` that say **BREAKING**,
beside 47 graded `minor` ones. Pin exact `@objectstack/*` versions, and read the
beside 48 graded `minor` ones. Pin exact `@objectstack/*` versions, and read the
release notes for every version you cross. Each entry in a CHANGELOG states its
kind on its own `Metadata-protocol:` line, and the GitHub Release body groups the
entries under the same five headings this page uses.
Expand All @@ -102,7 +102,7 @@ you change them.

### Breaking changes you must act on

The 77 entries that are breaking and have no ADR-0087 conversion, and one boot
The 78 entries that are breaking and have no ADR-0087 conversion, and one boot
refusal (`db1c7cc`) that its changeset declares as an addition. Each line names
the one fix; the CHANGELOG entry carries the full FROM → TO.

Expand Down Expand Up @@ -224,6 +224,18 @@ door (`422 INVALID_METADATA`), where 18.0.0 accepted it.
a blank `''`. 18.0.0 warned. Fix: write holes over `count` or the params,
double a literal single brace, and delete a blank `confirmText`. A view
already stored still loads.
- **A row policy that reads `x in current_user.KEY` which nothing owns, at the
permission save door** (`b531016`, #25550). It answered 200 and matched no
rows; it is now `422 INVALID_METADATA`. An object write is now judged by the
RLS rule too, so an object whose `defaultRowScope` the CLI refuses, or a
write that drops a field or membership key a stored policy reads, is refused.
The gate blames a write only for a finding it adds, and drafts are not gated.
Fix: declare the key on its roster object's `membershipKeys`, or register the
resolver that stages it, before publishing the policy; use `in`, not `==`;
change the policy before dropping what it reads. A bad `membershipKeys`
declaration (a `userField` that is not a single user reference, a
`valueField` that is not a reference, a key declared twice) is refused at
`os validate` and `os build` too.

#### Datasources and drivers

Expand Down Expand Up @@ -409,7 +421,8 @@ rows.
- **`ObjectSchema.membershipKeys`** (`dc532e8`, #25513): a roster object
declares the `current_user.KEY` sets it defines (`userField`, `valueField`,
an optional `filter`), the metadata twin of a registered membership resolver.
`defineStack` refuses one key declared on two objects. See
`defineStack` refuses one key declared on two objects, and `os lint` judges
the declaration and the policies that read it (#25550). See
[Semantic changes](#semantic-changes-in-1810) for what reads it.
- **Setup → Email states which mail transport is live** (`ab510f4`, #25448),
and says so when no mail leaves the machine.
Expand Down Expand Up @@ -450,6 +463,15 @@ rows.
unresolved, and a policy reading it matches nothing. Whoever can write a
roster row can grant the key, and the object-level explain names each key a
policy reads.
- **An `external` holder is its own posture** (`45f9962`, #25541). Its
execution context carries `audience: 'external'`, and for it every object's
org-wide default is owner-only: it reads and writes the rows it owns and
the rows shared to it, by a share or a sharing rule, and a `public_read` or
`public_read_write` object is not open to it. A row policy on a permission
set or position it holds lifts that floor and bounds the rows instead. A
`controlled_by_parent` detail follows its master. Nothing changes for a
principal that does not hold the `external` grade, and no earlier release
shipped it.

A change of meaning that also breaks is listed with the breaking changes above.

Expand Down
2 changes: 1 addition & 1 deletion content/docs/releases/v18/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Each release below is a self-contained page: what it changed, what breaks, and
its own upgrade checklist.

- **[18.1.0](/docs/releases/v18/18-1)** — the first minor, and a minor that
breaks: 86 of its changesets are declared breaking (ruling A grades a
breaks: 87 of its changesets are declared breaking (ruling A grades a
non-protocol breaking change `patch` and a protocol narrowing `minor`). Its
[upgrade checklist](/docs/releases/v18/18-1#upgrade-checklist) moves an
18.0.0 app to 18.1.0; it was compiled from the changesets before the cut and
Expand Down
Loading