Skip to content

Commit b53057a

Browse files
docs(releases): fold the changesets merged after b2c0752 into the 18.1.0 notes (#25553)
<!-- claude-projects-attribution: {"github_login":"marchtian"} --> _Requested by **田騏** · [project thread](https://claude.ai/code/project/chan_01VmuUhXU9SSUzqJYyXSmYSi?thread=cmsg_01VmuUhXU9SSUzqJYyXSmYSiRwWtsarkLPmw9fMYiFZ3LY)_ ## Summary The 18.1.0 notes (#25460) were compiled at `b2c0752900`. Twenty-three changesets have landed on `main` since then, before the cut. This PR folds them in. It changes docs only. - **Breaking changes you must act on** gains four entries: - a bulk action `confirmText` that the template cannot fill (#25451) - a `runAs: 'system'` flow that writes identity objects (#25483) - an out-of-office delegation outside its own organization (#25439) - a rollback `toVersion` that is not a JSON whole number (#25532) - **Semantic changes**: membership keys are now staged per request (#25528). Until now this section said there were none. - **New capabilities**: - the `external` organization role (#25515) - `ObjectSchema.membershipKeys` (#25513) - the upgrade impact report (#25512) - `OS_PLATFORM_OWNER_ORGANIZATION` (#25489) - the live mail transport statement (#25448) - **Pins**: the scaffolds now write exact `@objectstack/*` pins (#25539). The upgrade checklist's pin step says how to move an older project off caret ranges. - **Console**: the pin moves to `2958f17e9c6a`. That is eight refreshes, 130 entries, ten of them `minor`. - **Counts**: 250 entries, split 77 / 9 / 47 / 1 / 116 across the five sections. 86 changesets are declared breaking, and 39 of them are graded `patch`. The counts are updated on the 18.1 page, the v18 index, the releases index and Backward Compatibility. `check-adr-0087-registration --list` confirms 86 out of 86. - **Checklist**: steps 3 and 4 gain the new refusals. The page still says it has not been walked. That changes after 18.1.0 publishes. ## Checks These all pass locally: - `check:release-notes` - `check:release-page-status` - `check:release-index-currency-sync` - `check:doc-anchors` - `check:docs-audit-scope` - `check:docs-single-h1` - `check:docs-redirects` - `check-release-section-coverage` - an MDX compile of the four pages 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 36cac5a commit b53057a

4 files changed

Lines changed: 102 additions & 24 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 82 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 86 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-
82 changesets declared breaking, 37 of them graded `patch`.
75+
86 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: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +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-
`b2c0752900` (2026-10-23), before the 18.1.0 cut, through a rehearsed
9-
`pnpm run version` in a throwaway clone: 73 packages move to 18.1.0, and their
10-
`## 18.1.0` sections hold **227 distinct entries**. The five sections below use
11-
the grouping the GitHub Release body uses, and the counts are that grouping's:
12-
**73** need you to act, **9** are converted for you, **41** add capabilities,
13-
**0** declare a semantic change, and **104** are other fixes. Nothing here was
8+
`19d3720af1` (2026-10-23), before the 18.1.0 cut: a rehearsed
9+
`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
12+
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
1416
read off a published 18.1.0 package or walked on an upgrade yet: every refusal
1517
is described as its changeset describes it. Anything that lands on `main` before
1618
the cut is in the CHANGELOG and not on this page.
1719
</Callout>
1820

1921
## Highlights — 18.1.0
2022

21-
- ⚠️ **A minor that breaks.** 18.1.0 is a minor version, and 82 of its
23+
- ⚠️ **A minor that breaks.** 18.1.0 is a minor version, and 86 of its
2224
changesets are declared breaking. Under the level rule the maintainer ruled
2325
on 2026-10-21 (#25105, "ruling A"), the v18 line takes no further major: a
2426
change to the metadata protocol takes `minor`, and every other change takes
@@ -62,6 +64,11 @@ the cut is in the CHANGELOG and not on this page.
6264
sets on its hosted containers (`c6bdcac`, #25399). It ships as `patch` and
6365
is breaking: see [Cloud-managed environments](#cloud-managed-environments-os_cloud_managed1).
6466
Self-hosted deployments are unchanged.
67+
- **An `external` membership grade for portal users** (`9019af9`, #25515). A
68+
supplier's or customer's portal user is a member of the organization but
69+
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
71+
as `current_user.KEY` (`dc532e8`, #25513; `e5eab06`, #25528).
6572

6673
---
6774

@@ -81,15 +88,21 @@ else:
8188
| Everything else: REST, drivers, services, the CLI, the Console, `feat:` and `fix!:` alike | `patch` |
8289

8390
So a **patch** release can break a REST client, a boot or a deployment
84-
script, and 18.1.0 carries 37 changesets graded `patch` that say **BREAKING**,
85-
beside 45 graded `minor` ones. Pin exact `@objectstack/*` versions, and read the
91+
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
8693
release notes for every version you cross. Each entry in a CHANGELOG states its
8794
kind on its own `Metadata-protocol:` line, and the GitHub Release body groups the
8895
entries under the same five headings this page uses.
8996

97+
From 18.1.0 the scaffolds follow the same advice: `npm create objectstack`,
98+
`os init` and `os create` write the exact `@objectstack/*` version
99+
(`18.1.0`, not `^18.1.0`), and `--in-repo` still writes `workspace:*`
100+
(`975d5b3`, #25539). A project scaffolded earlier keeps its caret ranges until
101+
you change them.
102+
90103
### Breaking changes you must act on
91104

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

@@ -205,6 +218,12 @@ door (`422 INVALID_METADATA`), where 18.0.0 accepted it.
205218
at read time.
206219
- **`rollbackMetaItem` and `revertCommit` refuse a version the save door
207220
refuses** (`69913f8`, #25312), instead of writing it back live.
221+
- **A bulk action `confirmText` the template cannot fill** (`ecb4e69`,
222+
#25451): a hole over a name that is neither `count` nor one of the action's
223+
params, a single-brace `{x}`, logic or an unknown formatter inside a hole, or
224+
a blank `''`. 18.0.0 warned. Fix: write holes over `count` or the params,
225+
double a literal single brace, and delete a blank `confirmText`. A view
226+
already stored still loads.
208227

209228
#### Datasources and drivers
210229

@@ -238,6 +257,17 @@ door (`422 INVALID_METADATA`), where 18.0.0 accepted it.
238257
#25150; `5801a4f`, #25288). Fix: code that builds an `ExecutionContext` with
239258
`onBehalfOf` by hand sets `tenantId` to such an organization, or leaves it
240259
unset.
260+
- **An out-of-office delegation applies only in its own organization**
261+
(`b3fddd1`, #25439). A `sys_approval_delegation` row with no organization
262+
reroutes nothing, and logs a warning. Fix: under `single`, run the ownership
263+
ceremony; under a wall, recreate the delegation in the organization it is
264+
for.
265+
- **A `runAs: 'system'` flow that writes `sys_user_position`,
266+
`sys_user_permission_set`, `sys_invitation` or `sys_member` is refused**
267+
`403 PERMISSION_DENIED` (`a285ff2`, #25483). Fix: run a flow that grants
268+
positions or permission sets as `runAs: 'user'`, so the caller's own rights
269+
decide; invite, cancel, change a membership grade or remove a member through the
270+
organization endpoints.
241271
- **Deleting a business unit that still has child units is refused**
242272
(`4b8f597`, #24984). Fix: re-parent or delete the children first.
243273

@@ -252,6 +282,10 @@ door (`422 INVALID_METADATA`), where 18.0.0 accepted it.
252282
refused by every data verb (`8010b64`, `72fad92`, `a1e51d6`, `fb7501b`,
253283
`882372d`). Fix: nothing if the declarations say what you mean; otherwise set
254284
`enable.apiEnabled`, or re-enable the package.
285+
- **`POST /api/v1/meta/:type/:name/rollback` takes `toVersion` as a JSON whole
286+
number of at least 1** (`0fab7ba`, #25532), and anything else answers
287+
`400 INVALID_REQUEST`. The query form `?toVersion=3` still works. Fix: send
288+
`{"toVersion": 2}`, not `"2"` or `2.5`.
255289
- **The Data API refuses a malformed or negative page window with `400`**
256290
naming the parameter (`17f3c65`, #25260), and the published list-query
257291
schemas declare it a non-negative integer (`5f30f00`, #25300). Fix: send a
@@ -359,7 +393,26 @@ rows.
359393
- **Package upgrades report what they un-make.** At boot an administrator is
360394
told when a package upgrade renamed, removed or collided with an access item
361395
the customer switched off, cloned or authored (`54df640`, #25311), and the
362-
marketplace door asks for confirmation (above).
396+
marketplace door asks for confirmation (above). A second report covers
397+
flows, actions, webhooks, overlays, object extensions, navigation
398+
contributions and settings, on the `system.upgrade_impact` topic (`e207b97`,
399+
#25512). Under a wall, `OS_PLATFORM_OWNER_ORGANIZATION` names the
400+
organization where both reports reach the operators in-app (`915e940`,
401+
#25489).
402+
- **An `external` membership grade** (`9019af9`, #25515). A portal user added
403+
with it holds `org_external` instead of the `everyone` anchor, so the
404+
deployment's default permission sets do not reach it, and it reads only its
405+
own identity rows, through the shipped `external_default` set. It is never
406+
placed in a business unit: grant it data through an organization-wide
407+
position or a permission set. `org_external` is now a reserved name, so a
408+
position can no longer take it.
409+
- **`ObjectSchema.membershipKeys`** (`dc532e8`, #25513): a roster object
410+
declares the `current_user.KEY` sets it defines (`userField`, `valueField`,
411+
an optional `filter`), the metadata twin of a registered membership resolver.
412+
`defineStack` refuses one key declared on two objects. See
413+
[Semantic changes](#semantic-changes-in-1810) for what reads it.
414+
- **Setup → Email states which mail transport is live** (`ab510f4`, #25448),
415+
and says so when no mail leaves the machine.
363416
- **Setup → Positions says who holds each position and what it grants**,
364417
through `GET /meta/position?include=grants` (`27b3f1f`, #25369). Create User
365418
can grant a permission set in the same step (`3306cad`, #25381), and Create Key
@@ -387,8 +440,18 @@ rows.
387440

388441
### Semantic changes in 18.1.0
389442

390-
No changeset in 18.1.0 declares `Metadata-protocol: semantic`. A change of
391-
meaning that also breaks is listed with the breaking changes above.
443+
- **A declared membership key is staged per request** (`e5eab06`, #25528), so
444+
a row policy reading `current_user.KEY` now reads the roster object's rows:
445+
the distinct `valueField` values on the rows whose `userField` is the
446+
signed-in user and on which `filter` holds, inside the caller's own
447+
organization. Before this change such a policy matched no rows. Every miss
448+
fails closed: no roster row, more than 1,000 values, a failed read, a
449+
`filter` that does not compile, or a key two objects declare leaves the key
450+
unresolved, and a policy reading it matches nothing. Whoever can write a
451+
roster row can grant the key, and the object-level explain names each key a
452+
policy reads.
453+
454+
A change of meaning that also breaks is listed with the breaking changes above.
392455

393456
### Notable fixes in 18.1.0
394457

@@ -421,16 +484,18 @@ meaning that also breaks is listed with the breaking changes above.
421484
- **Setup.** Passwords typed into action dialogs are masked (`7035682`,
422485
#24867), forms show option labels instead of machine values (`bdf762c`,
423486
#25370), and the scaffolds ship a starter test so a new project's `npm test`
424-
passes (`6bc8cc6`, #25236; `9c431ba`, #25196).
487+
passes (`6bc8cc6`, #25236; `9c431ba`, #25196). Saving the mail provider
488+
"None (log only)" stops delivery, where the SMTP transport the boot built kept
489+
sending (`0ec4c5a`, #25497).
425490

426491
The other fixes are in the per-package CHANGELOGs.
427492

428493
### New in Console (Studio) — objectui pins in 18.1.0
429494

430495
18.0.0 bundled objectui at `616df6598b42`. 18.1.0 bundles it at the pin
431-
committed at the cut, `1d178e06ac5e` when this page was compiled, through six
432-
refreshes, each with its own `@objectstack/console` changeset: 91 entries that
433-
objectui declared, five of them `minor`.
496+
committed at the cut, `2958f17e9c6a` when this page was compiled, through
497+
eight refreshes, each with its own `@objectstack/console` changeset: 130
498+
entries that objectui declared, ten of them `minor`.
434499

435500
- **Approvals.** An approver who is not an administrator sees the whole
436501
approval request page again, and the Approvals Inbox opens a request on the
@@ -447,6 +512,11 @@ objectui declared, five of them `minor`.
447512
ask before they drop unsaved edits; a member who can read none of an app's
448513
objects lands on one page saying whom to ask; a page node or tab whose
449514
`visibleWhen` faults is no longer drawn.
515+
- **Organizations and actions.** The console labels the `external`
516+
membership grade, an action's `confirmText`, `successMessage` and
517+
`errorMessage` fill their `{{ }}` holes, a single-record action launched with
518+
0 or several rows selected is refused before its dialog opens, and on a
519+
cloud-managed environment Setup → Users says where people are invited.
450520
- **Charts and branding.** A dataset chart draws its null bucket under its
451521
`nullLabel`, a single-series bar chart uses its series colour, and the console
452522
document carries the runtime branding in its `<title>` and Open Graph tags
@@ -486,7 +556,9 @@ Move every `@objectstack/*` dependency to `18.1.0` together, and
486556
`@objectstack/skills` with them, as in the
487557
[18.0.0 walk](/docs/releases/v18/18-0#1-move-the-pins). Pin the exact version:
488558
a caret range takes every later 18.x by itself, and a patch can break a
489-
non-protocol surface.
559+
non-protocol surface. A project `npm create objectstack` scaffolded before
560+
18.1.0 carries caret ranges: replace each `^18.x.y` with `18.1.0` and
561+
reinstall.
490562

491563
#### 2. Before the first boot
492564

@@ -514,8 +586,9 @@ Fix each refusal with the line in
514586
[Converted for you](#converted-for-you-by-os-migrate-meta). The ones an app is
515587
most likely to meet: `addRecord` without `enabled`, a webhook without
516588
`'webhooks'` and `'realtime'` in `requires`, an HTML email template hole, a
517-
`validations[]` rule that reads `parent`, and an `external` object on a managed
518-
datasource.
589+
`validations[]` rule that reads `parent`, a bulk action `confirmText` hole over
590+
a name that is neither `count` nor a param, and an `external` object on a
591+
managed datasource.
519592

520593
#### 4. After the boot
521594

@@ -527,4 +600,9 @@ datasource.
527600
- If the boot reports a notification preference collision, resolve it with
528601
`os migrate duplicates` and restart.
529602
- Check that REST and MCP callers do not depend on a disabled package's actions
530-
or on an object declared `apiEnabled: false`.
603+
or on an object declared `apiEnabled: false`, and that a client calling
604+
metadata rollback sends `toVersion` as a JSON number.
605+
- Run each `runAs: 'system'` flow that writes positions, permission-set
606+
assignments, invitations or memberships, and move it to `runAs: 'user'` or
607+
the organization endpoints.
608+
- Read the boot's upgrade impact report beside the access upgrade report.

‎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: 82 of its changesets are declared breaking (ruling A grades a
138+
breaks: 86 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)