Skip to content

Commit 9c43e19

Browse files
docs(releases): fold #25541, #25550 and #25429 into the 18.1.0 notes (#25562)
<!-- claude-projects-attribution: {"github_login":"marchtian"} --> _Requested by **田騏** · [project thread](https://claude.ai/code/project/chan_01VmuUhXU9SSUzqJYyXSmYSi?thread=cmsg_01VmuUhXU9SSUzqJYyXSmYSiRwWtsarkLPmw9fMYiFZ3LY)_ ## Summary Three more changesets reached `main` after #25553 was compiled at `19d3720af1`. They are still before the 18.1.0 cut. This PR folds them into the 18.1.0 notes. It changes docs only. - **#25550** is breaking (`minor`, narrowed). The permission save door now refuses a row policy that reads a membership key nothing owns. An object write that breaks a stored policy is refused too. Both are added under "Metadata the build and the save doors now refuse". - **#25541** is semantic. An `external` holder is now its own posture, with an owner-only org-wide default. It is added under "Semantic changes". - **#25429** is a fix to the skills docs. It is counted under other fixes. The counts become 253 entries, split 78 / 9 / 47 / 2 / 117 across the five sections. 87 changesets are declared breaking, 48 `minor` and 39 `patch`. `check-adr-0087-registration --list` confirms 87 out of 87. The counts are updated on the 18.1 page, the v18 index, the releases index and Backward Compatibility. ## Checks These all pass locally: - `check:role-word` - `check:release-notes` - `check:release-page-status` - `check:release-index-currency-sync` - `check:doc-anchors` - `check:docs-audit-scope` - `check:docs-single-h1` - an MDX compile of the page Clause-②: no Metadata-protocol: none 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01At7xrR4rgknXtej5ZkMJHd Co-authored-by: Claude <noreply@anthropic.com>
1 parent 041a120 commit 9c43e19

4 files changed

Lines changed: 36 additions & 14 deletions

File tree

‎content/docs/protocol/backward-compatibility.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ ObjectStack follows strict backward compatibility guarantees to ensure predictab
1919

2020
**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).
2121

22-
**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.
22+
**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.
2323
</Callout>
2424

2525
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.

‎content/docs/releases/index.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ meaning takes `minor`, and every other change takes `patch` — a REST door, a
7272
driver, a boot refusal, the CLI or the Console — breaking or not. A
7373
non-protocol breaking change keeps its `**BREAKING**` banner and its ADR-0087
7474
disposition. 18.1.0 is the first release cut under the rule: a minor carrying
75-
86 changesets declared breaking, 39 of them graded `patch`.
75+
87 changesets declared breaking, 39 of them graded `patch`.
7676

7777
What that means when you read a version number:
7878

‎content/docs/releases/v18/18-1.mdx‎

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ description: "Release notes and upgrade checklist for 18.1.0, the first minor of
55

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

2121
## Highlights — 18.1.0
2222

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

7373
---
@@ -89,7 +89,7 @@ else:
8989

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

103103
### Breaking changes you must act on
104104

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

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

228240
#### Datasources and drivers
229241

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

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

‎content/docs/releases/v18/index.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Each release below is a self-contained page: what it changed, what breaks, and
135135
its own upgrade checklist.
136136

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

0 commit comments

Comments
 (0)