Repository navigation
Commit 5b14b7f
docs(tenancy): 18.0 states the three ownership rules and its fresh-install, single-only scope; the operator messages say so too (#24506)
Part of #15214
Clause-②: no — docs, a driver docblock and three operator-facing message
strings (one a new `os doctor` warning row); no API, schema, rule,
permission or exit-code face
The open-source docs half of C11. It runs in parallel to the 18.0
release and never holds it (ruling record 6261853191 on #15194). The
ADR-0131 D14 dated note is a separate PR on
`claude/issue-15214-c11-d14-note`, because `docs/adr/**` is Tier H. Card
close-outs stay with the epic seat: #13564, #13636, PR #14923, #11611
and #10103 remain open from this PR.
## What changes
**Docs: the three sentences** (ADR-0131 D1–D7), stated as 18.0 delivers
them.
- `content/docs/deployment/tenancy-modes.mdx` gets a new section, "Who
owns a row", with the three rules:
- a managed package's metadata is not in the database;
- a row in a table with an organization column was written by that
organization, and `NULL` is not an owner;
- a table with no organization column is deployment-level or code-level,
protected by permissions.
- The same section states the 18.0 `single` behaviour of 8b's ruling: a
system write that names no organization is stamped with the Default
Organization, or refused loudly when there are zero or two or more
organizations.
- A callout answers the platform-tables question of #11611. A platform
table has one of the two shapes and never a third. Organization-less
rows a v17 install left are what the 18.x ceremony resolves, so until
then the database stays on 17.x.
- The posture table's "`organization_id` filled in on write" cell for
`single` read "no". It now reads "yes — the Default Organization"
(ADR-0131 D11: `single` is never a reason for a row to be
organization-less).
**Docs: the 18.0 scope.** The existing "18.0: single only" callout on
the tenancy page is where an operator chooses a posture. It gains two
facts:
- a database an 18.0 install creates takes the same 18.x upgrade path;
- the walled refusal covers the `os` boot path only. The one-shot `os
migrate` / `os verify` / `os secret` / `os storage` boots and embedded
hosts (`createStandaloneStack`, `@objectstack/adapter-hono`) are not
refused, and are not a supported walled deployment in 18.0.
The six sentences #24480 added (`cli.mdx` ×3,
`environment-variables.mdx:101`, `self-hosting.mdx:470`,
`tenancy-modes.mdx:20-21`) agree with this text and are not repeated.
**Docs: the replaced mechanism.** Wording that read `NULL` as a global
or platform-wide row is gone from the tenancy and permissions pages:
- `sharing-rules.mdx`: the scope was "this organization ∪ the
platform-global rows". It is now the rule rows that organization wrote.
- `sharing-rules.mdx`, `permissions-matrix.mdx`, `rls.mdx`:
`tenancy.enabled: false` was called "platform-global". It is now
"deployment-level: no organization column".
- `system-context.mdx` row 23d: "a row stored with no organization is a
global grant". It now says such a row is not judged, and that under
`single` 18.0 stamps a new grant.
**SQL driver docblock.** The `applyTenantScope` comment called the `IS
NULL` arm "a GLOBAL/platform row". It now names that as the v17 reading
and states the three rules. It says the arm matches nothing on a fresh
18.0 `single` install, and is removed on the 18.x line (ADR-0131 D14).
The comment is the only change; the code is not touched.
**Riders** (review 6261404044 ③, ruling record 6261853191 item 2):
1. **Boot guard**
(`packages/runtime/src/organization-ownership-boot-guard.ts`,
`organizationOwnershipRefusalMessage`). One sentence goes ahead of the
ceremony command on a v17-shaped database: "18.0 does not upgrade a v17
database: keep this one on a 17.x release until the 18.x release that
carries the upgrade path, and run these steps there." The operator no
longer follows two hops.
2. **`TENANCY_POSTURE_FIX_HINTS`**
(`packages/cli/src/utils/tenancy-posture-hints.ts`). This was the
smaller truthful change. `group` and `isolated` stay listed, because
they are the vocabulary, with a suffix `; refused at boot in 18.0, which
supports single only` (`WALLED_POSTURE_RELEASE_NOTE`, which the 18.x
restore deletes). `os serve` and `os doctor` both render it.
3. **`os doctor`.** A valid posture used to print nothing. Under `group`
/ `isolated` it now prints a `Tenancy posture` **warning**: 18.0
supports `single` only, so the next `os serve` (and so `os dev` / `os
start`) refuses it at boot. The row is a warning, not an error, so
doctor's exit code is unchanged. The one-shot commands doctor also
fronts still boot under a walled posture.
**Changeset** `.changeset/15214-c11-fresh-install-messages.md`: `patch`
for `@objectstack/cli` and `@objectstack/runtime`. `driver-sql` changes
a comment only and publishes no string. Nothing is touched under
`content/docs/releases/` or any `CHANGELOG.md`.
## File surface
- Measured against the claim 6261901354.
- 8b's branch (`claude/issue-15212-8b-stamp-or-refuse`, fetched at
`af977a6`) touches 36 files under `packages/objectql`,
`packages/core/src/security`, `plugin-security`, `plugin-auth` and
others. None of them is in this diff, so no rider was dropped.
- None of the four files open PR #24482 touches is in this diff.
- `serve.ts` and `migrate/organization-ownership.ts` are not touched.
## Tests
Measured at `77a7a78ab`; its tree is byte-identical to the tree the runs
below read.
| command | result |
|---|---|
| `pnpm --filter '@objectstack/cli^...' build` (lock) | VERDICT
command-exit 0 |
| `pnpm --filter @objectstack/cli exec vitest run --project unit` |
308/312 files passed in the first run. The 4 others are unrelated to the
diff: 2 timed out at 5 s while the gate sweep loaded the box, and 2
needed `packages/cli/dist`, which the first build did not make. After
`pnpm --filter @objectstack/cli build` they were rerun:
`published-subpath-console` and `published-subpath-hook-body` passed 2/2
files; `boot-preparation-parity` and `hook-timeout-override-refusal`
passed 2/2 files, 41/41 tests, with the box idle |
| `pnpm --filter @objectstack/cli typecheck` | exit 0 |
| `pnpm --filter @objectstack/runtime test` | 421/421 files, 6643
passed, 19 skipped |
| `pnpm --filter @objectstack/runtime typecheck` | exit 0 |
Pins moved, and why:
- `serve-organizations-message-spelling.test.ts` site 6 and
`doctor-organizations-message-spelling.test.ts` legs (i) and the CONTROL
now carry the 18.0 suffix. The `group` legs read the shared table and
move with it.
- `doctor-tenancy-posture-report.test.ts` and
`doctor-env-provenance.test.ts` used `isolated` as the "a posture that
can start" control. In 18.0 that premise is false, so the control is now
`single`.
- New: a unit sweep of `walledPostureFinding` over the posture
vocabulary, and an in-process `os doctor` run under `group` (warning
row, exit 0).
- `organization-ownership-boot-guard.test.ts` asserts that the 18.0 /
17.x answer appears ahead of the command.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` named 102 families, plus 48 roster commands
on stderr. All 150 were run, exit codes recorded before any pipe.
- `--ran` reconciliation: "102 derived famil(ies) accounted for — 102
run, 0 NOT-MEASURED (a DERIVED zero — all 102 recorded an exit code and
none of them is 3)".
- Seven first answered `PREREQUISITE NOT MET` (exit 3): missing `dist/`,
or a shallow-clone fixture. After building `client-react`, `studio` and
the six packages `check:published-readme-exports` named, and fetching
the fixture commit, all seven exit 0: `check-plugin-teardown-shape
--self-test`, `check:skill-examples`, `check:dual-build-cjs-loads`,
`check:i18n`, `check:i18n-coverage`, `check:i18n-walk-parity`,
`check:published-readme-exports`.
- Four roster gates need a PR or workflow context and say so (NOT WIRED
/ NOT MEASURED, exit 2 or 1): `check-closing-target-claim`,
`check-partof-closing-keyword`, `check-single-claim-paths`,
`check-governed-queue-guard`. CI runs them on this PR.
- NOT MEASURED locally, CI's: `Build Docs`, `Build Core`, the `Test
Core` shards, the type-check lanes.
## Acceptance notes
- **The card's sentence 2 says "the column is `NOT NULL`".** The docs
say every row carries its organization and that `NULL` is not an owner.
They do not claim the DDL constraint, because 8e (NOT NULL) moved to
18.x (ruling record 6259698357 item 3). An 18.0 database therefore has
no such constraint, and a page claiming one would be false in 18.0.
- **8b has no PR yet.** The `single` stamping is described as ruled
(6259698357, 6259899486), not from its internals.
- Docs outside the tenancy and permissions pages still describe the
`COALESCE(organization_id, '__global__')` index key and the NULL
partition: `data-modeling/indexing.mdx`, `protocol/objectql/schema.mdx`,
`deployment/seed-tenancy-repair.mdx`, `deployment/cli.mdx`. The
mechanism is still live in 18.0 (8d moved to 18.x), so these pages are
true today. They are this card's family for the 18.x docs pass.
Successor: none.
- `system-context.mdx` row 62 describes the enterprise organizations
plugin's elevated insert under a wall. It was not changed: 18.0 refuses
walled boots at `os serve`, and the walled half is 18.x. Successor:
none.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9ZWcgyzEyyKR2nnDzto54)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent f4755a6 commit 5b14b7f
15 files changed
Lines changed: 168 additions & 28 deletions
File tree
- .changeset
- content/docs
- deployment
- permissions
- packages
- cli/src
- commands
- utils
- drivers/driver-sql/src
- runtime/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
22 | 27 | | |
23 | 28 | | |
24 | 29 | | |
| |||
38 | 43 | | |
39 | 44 | | |
40 | 45 | | |
41 | | - | |
| 46 | + | |
42 | 47 | | |
43 | 48 | | |
44 | 49 | | |
| |||
101 | 106 | | |
102 | 107 | | |
103 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
104 | 145 | | |
105 | 146 | | |
106 | 147 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
84 | | - | |
| 84 | + | |
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | | - | |
| 64 | + | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| |||
195 | 195 | | |
196 | 196 | | |
197 | 197 | | |
198 | | - | |
| 198 | + | |
199 | 199 | | |
200 | 200 | | |
201 | 201 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
129 | 129 | | |
130 | 130 | | |
131 | 131 | | |
132 | | - | |
| 132 | + | |
133 | 133 | | |
134 | 134 | | |
135 | 135 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
455 | 455 | | |
456 | 456 | | |
457 | 457 | | |
458 | | - | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
459 | 461 | | |
460 | 462 | | |
461 | 463 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
168 | 168 | | |
169 | 169 | | |
170 | 170 | | |
171 | | - | |
| 171 | + | |
172 | 172 | | |
173 | 173 | | |
174 | 174 | | |
| |||
242 | 242 | | |
243 | 243 | | |
244 | 244 | | |
245 | | - | |
| 245 | + | |
246 | 246 | | |
247 | 247 | | |
248 | | - | |
| 248 | + | |
249 | 249 | | |
250 | 250 | | |
251 | 251 | | |
252 | | - | |
| 252 | + | |
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| 64 | + | |
64 | 65 | | |
65 | 66 | | |
66 | 67 | | |
| |||
290 | 291 | | |
291 | 292 | | |
292 | 293 | | |
293 | | - | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
294 | 297 | | |
295 | 298 | | |
296 | 299 | | |
| |||
343 | 346 | | |
344 | 347 | | |
345 | 348 | | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
346 | 378 | | |
0 commit comments