From 5da97c5952bd1d0474601df3f8d7d56ee2c80bb9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 22:35:09 +0000 Subject: [PATCH 1/3] docs: the organization-ownership passages say what 18.0 supports for a v17 database Four hand-written pages walked an operator through the organization-ownership plan and apply with no 18.0 qualifier, and 18.0 refuses that command on every database (NOT_IMPLEMENTED). Each passage that tells an 18.0 reader what happens now carries the 18.0 sentence ("18.0 supports fresh installs only and does not upgrade a v17 database: keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path"); each passage that teaches the ceremony's mechanics keeps them under an explicit 18.x qualifier. The os dev section now says what os dev does in 18.0 on a v17 database (copy, refused plan, exit, database unchanged), and the drop-gate bullet names the population the gate actually holds: every organization_id drop the drift offers, not only the D7 tables and the sys_metadata family. Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude --- content/docs/concepts/metadata-lifecycle.mdx | 2 +- content/docs/deployment/cli.mdx | 52 +++++++++++++------- content/docs/permissions/capabilities.mdx | 7 ++- content/docs/permissions/permission-sets.mdx | 5 +- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/content/docs/concepts/metadata-lifecycle.mdx b/content/docs/concepts/metadata-lifecycle.mdx index a2d1780fa27..53b44a3f4e2 100644 --- a/content/docs/concepts/metadata-lifecycle.mdx +++ b/content/docs/concepts/metadata-lifecycle.mdx @@ -106,7 +106,7 @@ later ships. See [ADR-0070](https://github.com/objectstack-ai/objectstack/blob/m **Most metadata types must not be overlaid by an environment** — overlaying a packaged item would break the physical schema or the behaviour its package ships. The whitelist lives in **one** place: `MetadataTypeRegistryEntry.allowEnvironmentOverlay` in `packages/spec/src/kernel/metadata-plugin.zod.ts` (spelled `allowOrgOverride` before protocol 18; `GET /api/v1/meta/types` serves it per entry as `allowEnvironmentOverlay`). -> **ADR-0131 D6 — the per-organization overlay axis is retired.** The `/meta` doors (REST and the runtime dispatcher) carry no organization into a metadata write or read: a Studio or `PUT /api/v1/meta/...` write of the five ✅ types below lands **environment-wide** (since ADR-0131 D7 the `sys_metadata` family declares no organization column) and is served to every organization, and an organization admin holding only the retired `manage_org_presentation` capability is refused (`403`). The protocol itself refuses every organization-scoped write, and every read — item, list, layers, cached, history, diff, audit, drafts and the commit timeline — is environment → code, whatever organization a caller names. An overlay row an earlier release stored under an organization — under the `single` posture, every Studio save of these types, filed under the Default Organization — is served by no read, and an 18.x `os serve` refuses to start on a database still holding one until the v18 migration ceremony (ADR-0131 C7: `os migrate organization-ownership --out PLAN`, then `--apply --plan-file PLAN`) has carried it to the environment layer and dropped the column; nothing deletes or rewrites it before then. The anonymous form doors read the environment alone too: the ceremony carries every organization-layer public-form withdrawal onto the environment, fail-closed. The table still records which types take an overlay of a shipped item. +> **ADR-0131 D6 — the per-organization overlay axis is retired.** The `/meta` doors (REST and the runtime dispatcher) carry no organization into a metadata write or read: a Studio or `PUT /api/v1/meta/...` write of the five ✅ types below lands **environment-wide** (since ADR-0131 D7 the `sys_metadata` family declares no organization column) and is served to every organization, and an organization admin holding only the retired `manage_org_presentation` capability is refused (`403`). The protocol itself refuses every organization-scoped write, and every read — item, list, layers, cached, history, diff, audit, drafts and the commit timeline — is environment → code, whatever organization a caller names. An overlay row an earlier release stored under an organization — under the `single` posture, every Studio save of these types, filed under the Default Organization — is served by no read, and a v18 `os serve` refuses to start on a database still holding one. 18.0 supports fresh installs only and does not upgrade a v17 database: keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path. In that release, the v18 migration ceremony (ADR-0131 C7: `os migrate organization-ownership --out PLAN`, then `--apply --plan-file PLAN`) carries such a row to the environment layer and drops the column; nothing deletes or rewrites it before then. The anonymous form doors read the environment alone too: in that release the ceremony carries every organization-layer public-form withdrawal onto the environment, fail-closed. The table still records which types take an overlay of a shipped item. | Type | `allowEnvironmentOverlay` | Rationale | | :--- | :---: | :--- | diff --git a/content/docs/deployment/cli.mdx b/content/docs/deployment/cli.mdx index 4a9160f8908..dee6c782aa2 100644 --- a/content/docs/deployment/cli.mdx +++ b/content/docs/deployment/cli.mdx @@ -196,18 +196,25 @@ os dev --database file:./data/test.db --auth-secret $(openssl rand -hex 32) | `--seed-admin` / `--no-seed-admin` | — | Seed a dev admin (`admin@objectos.ai` / `admin123`) on an empty DB — default on; override with `--admin-email` / `--admin-password` | | `-v, --verbose` | — | Verbose output | -##### A 17.x development database is upgraded in place +##### A 17.x development database A v18 server refuses to start on a database still in the v17 organization-ownership shape until the [ownership ceremony](#os-migrate-organization-ownership) -has run on it. `os dev` runs that ceremony for you when the database is a local -SQLite file: +has run on it. -18.0 supports fresh `single` installs; upgrades and walled postures arrive in 18.x. In 18.0 the ceremony refuses, so `os dev` stops at step 2 with its -refusal and changes nothing. +18.0 supports fresh installs only and does not upgrade a v17 database: keep a +v17 database on a 17.x release until the 18.x release that carries the upgrade +path. In 18.0, `os dev` on a v17 database in a local SQLite file still takes +step 1's copy, then stops at step 2: the ceremony refuses (`NOT_IMPLEMENTED`), +and `os dev` prints that refusal and where the copy is, and exits without +changing the database. Any other database gets no copy, and `os dev` exits +with the same answer. +In the 18.x release that carries the upgrade path, `os dev` runs that ceremony +for you when the database is a local SQLite file: + 1. It copies the database file and its `telemetry` sibling (each with its `-wal` / `-shm`) into `/backups/organization-ownership-/`. 2. It runs `os migrate organization-ownership --dev` (the plan), then applies @@ -215,12 +222,14 @@ refusal and changes nothing. beside the copy. 3. It prints the ceremony's warnings and starts the server again. -If a step blocks, or the post-check still holds rows (a row whose owner cannot +In that release, if a step blocks, or the post-check still holds rows (a row whose owner cannot be derived, named per table with its remedy), `os dev` stops and prints what holds it, where the copy is, and the command that resumes the run. It tries once per session. A database that is not a local SQLite file (PostgreSQL, a remote libSQL, `:memory:`) is not -upgraded in place: run the ceremony yourself. `os serve` and `os start` never run -it; they refuse and name the command. +upgraded in place: run the ceremony yourself. + +`os serve` and `os start` never run the ceremony: they refuse to start, and on +a v17 database their 18.0 refusal says that 18.0 does not upgrade it. ##### Serving dev over https @@ -1025,13 +1034,18 @@ safe. No flag skips these refusals: - `sys_account.issuer` is dropped only when its retirement pre-flight reads the table clean (`os migrate account-issuer` reports the colliding rows). - `sys_comment.reactions` waits for a verified `os migrate comment-reactions --apply` run. -- `organization_id` on any table the organization-ownership ceremony covers - waits for a verified `os migrate organization-ownership --apply --plan-file FILE` - run. These tables are the ADR-0131 D7 tables and the `sys_metadata` family. - The ceremony drops those columns itself, in its column-drops step, before it - records the run verified. Dropping one first would turn every legacy +- `organization_id` waits for a verified run of the organization-ownership + ceremony, on every table the drift offers to drop it from: the ceremony plans + every table that still carries the column, the ADR-0131 D7 tables and the + `sys_metadata` family among them. Dropping one first would turn every legacy organization row into an environment row. Under `--json` the refusal answers `message: 'refused_organization_ownership_unmigrated'` and lists the `tables`. + 18.0 itself never records a verified run, so on a v17 database this refusal + holds on every run. 18.0 supports fresh installs only and does not upgrade a + v17 database: keep a v17 database on a 17.x release until the 18.x release + that carries the upgrade path. In that release, the ceremony's + `os migrate organization-ownership --apply --plan-file FILE` run drops these + columns itself, in its column-drops step, before it records the run verified. Each refusal holds the whole `--allow-destructive` run: nothing in it is created, applied or dropped, the run's other changes included. A run without @@ -1478,14 +1492,16 @@ reports the failed read and exits `1`. A failed read or write exits `1`. The v18 ownership ceremony (ADR-0131 D10). A v18 `os serve` / `os start` refuses to start on a database still in the v17 organization-ownership shape until this -has run. Under `os dev` it runs on its own for a local SQLite database (see -[`os dev`](#os-dev)). +has run. In the 18.x release that carries the upgrade path, `os dev` runs it on +its own for a local SQLite database (see [`os dev`](#os-dev)). -18.0 supports fresh `single` installs; upgrades and walled postures arrive in 18.x. In 18.0 this command refuses, plan and apply alike: it exits `1` with +18.0 supports fresh installs only and does not upgrade a v17 database: keep a +v17 database on a 17.x release until the 18.x release that carries the upgrade +path. In 18.0 this command refuses, plan and apply alike: it exits `1` with `NOT_IMPLEMENTED` (under `--json`, `code` beside `error`) and reads and -writes nothing. Keep a v17 database on a 17.x release until the 18.x release -that carries the upgrade path. +writes nothing. The invocations below, and what the list after them says the +ceremony does, are that 18.x release's. ```bash diff --git a/content/docs/permissions/capabilities.mdx b/content/docs/permissions/capabilities.mdx index 9a3af4ea755..847f32c0bb1 100644 --- a/content/docs/permissions/capabilities.mdx +++ b/content/docs/permissions/capabilities.mdx @@ -217,8 +217,11 @@ and resource `requiredPermissions` match capability names **as strings**. The boot writes no row for a capability, declared or curated: both are served by the registry. The `sys_capability` object itself is retired and no longer registered (ADR-0131 D3): a row an older database still holds is served by no -route and read by nothing, and `os migrate organization-ownership --apply` -reports the authored rows and deletes the seeded copies (ADR-0131 D10). +route and read by nothing. In the 18.x release that carries the upgrade path +(18.0 does not upgrade a v17 database: see +[`os migrate organization-ownership`](/docs/deployment/cli#os-migrate-organization-ownership)), +`os migrate organization-ownership --apply` reports the authored rows and +deletes the seeded copies (ADR-0131 D10). Withdraw a capability by withdrawing the **grant**. diff --git a/content/docs/permissions/permission-sets.mdx b/content/docs/permissions/permission-sets.mdx index 84ec96ec1ec..f3710de1cc1 100644 --- a/content/docs/permissions/permission-sets.mdx +++ b/content/docs/permissions/permission-sets.mdx @@ -396,7 +396,10 @@ posture, the platform operator. This has three author-visible consequences: package. Uninstall the owning package to remove a packaged set entirely. An older database keeps the `sys_permission_set` table and its rows, and -nothing serves or reads them. `os migrate organization-ownership --apply` +nothing serves or reads them. In the 18.x release that carries the upgrade +path (18.0 does not upgrade a v17 database: see +[`os migrate organization-ownership`](/docs/deployment/cli#os-migrate-organization-ownership)), +`os migrate organization-ownership --apply` (ADR-0131 D10) converts the sets an organization authored in Setup into environment metadata under `single` and deletes the seeded copies. The shipped sets that an install created before 2026-08-15 stored as `managed_by: 'admin'` From d9897867464938d5e681c8b31c9b4e03c1d84e3a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 22:48:35 +0000 Subject: [PATCH 2/3] docs(cli): keep the walled-posture line of both callouts byte-identical The first line of the os dev and os migrate organization-ownership callouts is the one an open 18.x walled-lift PR rewrites. Leave it as it was and carry the 18.0 sentence and the 18.x qualifier on the lines after it, so the two changes do not meet on one line. Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude --- content/docs/deployment/cli.mdx | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/content/docs/deployment/cli.mdx b/content/docs/deployment/cli.mdx index dee6c782aa2..dd6f1128007 100644 --- a/content/docs/deployment/cli.mdx +++ b/content/docs/deployment/cli.mdx @@ -203,13 +203,13 @@ organization-ownership shape until the [ownership ceremony](#os-migrate-organiza has run on it. -18.0 supports fresh installs only and does not upgrade a v17 database: keep a -v17 database on a 17.x release until the 18.x release that carries the upgrade -path. In 18.0, `os dev` on a v17 database in a local SQLite file still takes -step 1's copy, then stops at step 2: the ceremony refuses (`NOT_IMPLEMENTED`), -and `os dev` prints that refusal and where the copy is, and exits without -changing the database. Any other database gets no copy, and `os dev` exits -with the same answer. +18.0 supports fresh `single` installs; upgrades and walled postures arrive in 18.x. In 18.0 the ceremony refuses, so `os dev` stops at step 2 with its +refusal and changes nothing. +18.0 does not upgrade a v17 database: keep a v17 database on a 17.x release +until the 18.x release that carries the upgrade path. For a local SQLite file, +`os dev` in 18.0 still takes step 1's copy first and prints where it is, +beside the ceremony's `NOT_IMPLEMENTED` refusal; any other database gets no +copy and the same answer. In the 18.x release that carries the upgrade path, `os dev` runs that ceremony @@ -1496,12 +1496,11 @@ has run. In the 18.x release that carries the upgrade path, `os dev` runs it on its own for a local SQLite database (see [`os dev`](#os-dev)). -18.0 supports fresh installs only and does not upgrade a v17 database: keep a -v17 database on a 17.x release until the 18.x release that carries the upgrade -path. In 18.0 this command refuses, plan and apply alike: it exits `1` with +18.0 supports fresh `single` installs; upgrades and walled postures arrive in 18.x. In 18.0 this command refuses, plan and apply alike: it exits `1` with `NOT_IMPLEMENTED` (under `--json`, `code` beside `error`) and reads and -writes nothing. The invocations below, and what the list after them says the -ceremony does, are that 18.x release's. +writes nothing. Keep a v17 database on a 17.x release until the 18.x release +that carries the upgrade path. The invocations below, and what the list after +them says the ceremony does, are that 18.x release's. ```bash From 65b2b74b81b45dc1346c64ab33d14f0b40e36870 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 23:07:25 +0000 Subject: [PATCH 3/3] docs(metadata-service): the overlay note says what 18.0 supports for a v17 database The Environment Customization note said an 18.x os serve refuses a database still holding an organization-layer overlay row "until the v18 migration ceremony has carried it", with no 18.0 qualifier, and 18.0 has no ceremony. It now carries the 18.0 sentence and puts both ceremony halves (the overlay row and the public-form withdrawals) under the 18.x qualifier, as its twin in metadata-lifecycle does. The refusal is stated as the boot guard measures it: on a SQL database, where the sys_metadata family still carries its organization_id column. The guard reads physical columns and is inert on memory and MongoDB, so the twin note in metadata-lifecycle gains the same condition. Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude --- content/docs/concepts/metadata-lifecycle.mdx | 2 +- .../docs/kernel/contracts/metadata-service.mdx | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/content/docs/concepts/metadata-lifecycle.mdx b/content/docs/concepts/metadata-lifecycle.mdx index 53b44a3f4e2..a3f1b64ef78 100644 --- a/content/docs/concepts/metadata-lifecycle.mdx +++ b/content/docs/concepts/metadata-lifecycle.mdx @@ -106,7 +106,7 @@ later ships. See [ADR-0070](https://github.com/objectstack-ai/objectstack/blob/m **Most metadata types must not be overlaid by an environment** — overlaying a packaged item would break the physical schema or the behaviour its package ships. The whitelist lives in **one** place: `MetadataTypeRegistryEntry.allowEnvironmentOverlay` in `packages/spec/src/kernel/metadata-plugin.zod.ts` (spelled `allowOrgOverride` before protocol 18; `GET /api/v1/meta/types` serves it per entry as `allowEnvironmentOverlay`). -> **ADR-0131 D6 — the per-organization overlay axis is retired.** The `/meta` doors (REST and the runtime dispatcher) carry no organization into a metadata write or read: a Studio or `PUT /api/v1/meta/...` write of the five ✅ types below lands **environment-wide** (since ADR-0131 D7 the `sys_metadata` family declares no organization column) and is served to every organization, and an organization admin holding only the retired `manage_org_presentation` capability is refused (`403`). The protocol itself refuses every organization-scoped write, and every read — item, list, layers, cached, history, diff, audit, drafts and the commit timeline — is environment → code, whatever organization a caller names. An overlay row an earlier release stored under an organization — under the `single` posture, every Studio save of these types, filed under the Default Organization — is served by no read, and a v18 `os serve` refuses to start on a database still holding one. 18.0 supports fresh installs only and does not upgrade a v17 database: keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path. In that release, the v18 migration ceremony (ADR-0131 C7: `os migrate organization-ownership --out PLAN`, then `--apply --plan-file PLAN`) carries such a row to the environment layer and drops the column; nothing deletes or rewrites it before then. The anonymous form doors read the environment alone too: in that release the ceremony carries every organization-layer public-form withdrawal onto the environment, fail-closed. The table still records which types take an overlay of a shipped item. +> **ADR-0131 D6 — the per-organization overlay axis is retired.** The `/meta` doors (REST and the runtime dispatcher) carry no organization into a metadata write or read: a Studio or `PUT /api/v1/meta/...` write of the five ✅ types below lands **environment-wide** (since ADR-0131 D7 the `sys_metadata` family declares no organization column) and is served to every organization, and an organization admin holding only the retired `manage_org_presentation` capability is refused (`403`). The protocol itself refuses every organization-scoped write, and every read — item, list, layers, cached, history, diff, audit, drafts and the commit timeline — is environment → code, whatever organization a caller names. An overlay row an earlier release stored under an organization — under the `single` posture, every Studio save of these types, filed under the Default Organization — is served by no read, and on a SQL database (SQLite, PostgreSQL, MySQL) still holding one a v18 `os serve` refuses to start. 18.0 supports fresh installs only and does not upgrade a v17 database: keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path. In that release, the v18 migration ceremony (ADR-0131 C7: `os migrate organization-ownership --out PLAN`, then `--apply --plan-file PLAN`) carries such a row to the environment layer and drops the column; nothing deletes or rewrites it before then. The anonymous form doors read the environment alone too: in that release the ceremony carries every organization-layer public-form withdrawal onto the environment, fail-closed. The table still records which types take an overlay of a shipped item. | Type | `allowEnvironmentOverlay` | Rationale | | :--- | :---: | :--- | diff --git a/content/docs/kernel/contracts/metadata-service.mdx b/content/docs/kernel/contracts/metadata-service.mdx index 3489e9ab65b..c082d2d5b3d 100644 --- a/content/docs/kernel/contracts/metadata-service.mdx +++ b/content/docs/kernel/contracts/metadata-service.mdx @@ -412,11 +412,16 @@ overlay axis is retired: the doors carry no organization, so the overlay is column, ADR-0131 D7) and served to every organization of the deployment, whatever the author's active organization. Every protocol read is environment → code, whatever organization a caller names. An overlay -row an earlier release stored under an organization is served by no read, and -an 18.x `os serve` refuses to start on a database still holding one until the -v18 migration ceremony (ADR-0131 C7) has carried it to the environment layer. The anonymous form doors read the environment alone too: the ceremony carries -every organization-layer public-form withdrawal onto the environment, -fail-closed. The per-user, +row an earlier release stored under an organization is served by no read. On a +SQL database (SQLite, PostgreSQL, MySQL) still holding one, a v18 `os serve` +refuses to start, because the `sys_metadata` family there still carries its +`organization_id` column. 18.0 supports fresh installs only and does not +upgrade a v17 database: keep a v17 database on a 17.x release until the 18.x +release that carries the upgrade path. In that release, the v18 migration +ceremony (ADR-0131 C7) carries such a row to the environment layer. The +anonymous form doors read the environment alone too; in that release the +ceremony carries every organization-layer public-form withdrawal onto the +environment, fail-closed. The per-user, per-field patch overlay a previous revision of this page taught here was removed in #13135 — it was never served by any route.