Skip to content

Commit 9e2722a

Browse files
fix(runtime): the organization-ownership boot refusals say what 18.0 supports and name no step of the withdrawn ceremony command (#24682)
Fixes #24651 Clause-②: no (refusal text only: the refusal code `ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED`, the refused tables and the boot outcome do not move) In 18.0, `os migrate organization-ownership` refuses plan and apply on every database (`ORGANIZATION_OWNERSHIP_UPGRADE_UNAVAILABLE`, `NOT_IMPLEMENTED`). Three branches of the v18 boot refusal in `packages/runtime/src/organization-ownership-boot-guard.ts` still sent the operator to that command's `--out PLAN` and `--apply --plan-file PLAN --backup-taken`. Each branch now closes on the sentence the landed member of this family (PR #24635, `f89d29e38`) put into `os migrate apply`'s drop gate and `os dev`'s way on: > 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, and upgrade it there. A development boot (`{ dev: true }`, as `os serve --dev` composes the guard) says "a v17 development database", the exact wording of the landed `os dev` text. No branch names the command or any step of it. `ceremonyCommand()` (which only built the `--dev` command name) is gone. The `dev` option stays, and still changes the text. The refusal code, the refused tables, `isOrganizationOwnershipBootRefusal`, the plugin's ordering and every boot outcome are unchanged. This is the `domain:cli` row of the family close-out #24637. ## Branch map | branch | selected when | source | refused tables | |:--|:--|:--|:--| | **A, v17 shape** | `findV17ShapeTables` finds a table whose object declares no organization column (`systemFields.tenant: false`, no declared `organization_id`) but still has a physical `organization_id`, and the marker is absent, unverified or unreadable | `organizationOwnershipRefusalMessage`, `tables` not empty (was about `:252`–`:258`) | the tables found | | **B1, marker not verified** | no table in the v17 shape, and the `sys_migration` row `adr-0131-organization-ownership` exists but is not verified: a ceremony ran every step and its post-check found discrepancies | `organizationOwnershipRefusalMessage`, `tables` empty, marker `unverified` (was about `:239`–`:243`) | none | | **B2, marker unreadable** | no table in the v17 shape, and a store serves the ledger but its read of the marker threw | the same return as B1, marker `unreadable` (was about `:239`–`:243`) | none | | **C, column read failed** | `findV17ShapeTables` itself threw while reading a table's columns | `assertOrganizationOwnershipMigrated`'s `catch` (was about `:339`–`:344`) | none | The card's ":242" is B1 and B2 (one return statement), ":256–257" is A, and ":342–343" is C. A marker that is absent with no v17 table, and a verified marker, still boot. ## Before and after, per branch Rendered by importing the guard at `bfc1539fe` (before) and at this PR's head (after) and calling it. TABLES stands for the table list. The `--dev` before-texts are the same with `os migrate organization-ownership --dev`. **A, v17 shape** (marker absent; unverified and unreadable differ only in the state clause) - Before: "This database is still in the v17 organization-ownership shape: TABLES still carry a physical organization_id column, and the ceremony has not run on it. The server refuses to start rather than serve v17 data with v18 semantics. 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. Run 'os migrate organization-ownership --out PLAN' to write and review its plan, take a backup, then run 'os migrate organization-ownership --apply --plan-file PLAN --backup-taken', and start again. To keep 17.x semantics, run a 17.x runtime." - After: "This database is still in the v17 organization-ownership shape: TABLES still carry a physical organization_id column, and the ceremony has not run on it. The server refuses to start rather than serve v17 data with v18 semantics. 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, and upgrade it there." - What was kept: everything up to "v18 semantics", and the 17.x answer. What was dropped: the two steps, "run these steps there", and "To keep 17.x semantics, run a 17.x runtime.", which now repeats "keep a v17 database on a 17.x release". **B1, marker not verified** - Before: "The organization-ownership ceremony has not finished on this database: its marker (sys_migration 'adr-0131-organization-ownership') is not verified: the post-check still found rows the ceremony has not carried. The server refuses to start rather than serve rows of an organization-owned object with no organization. Resolve what the apply report names, then run 'os migrate organization-ownership --out PLAN' again, review it, run 'os migrate organization-ownership --apply --plan-file PLAN --backup-taken', and start again." - After: "The organization-ownership ceremony has not finished on this database: its marker (sys_migration 'adr-0131-organization-ownership') is not verified: the post-check still found rows the ceremony has not carried. The server refuses to start rather than serve rows of an organization-owned object with no organization. 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, and upgrade it there." - Dropped: "Resolve what the apply report names", the re-plan and the re-apply. In 18.0 nothing can verify the marker, so resolving the report's rows does not let this database boot. The 18.x release finishes the ceremony. **B2, marker unreadable** - Before: the B1 text with "its marker (sys_migration 'adr-0131-organization-ownership') could not be read" as the state. It prescribed the same re-plan and re-apply. - After: "The organization-ownership ceremony has not finished on this database: its marker (sys_migration 'adr-0131-organization-ownership') could not be read. The server refuses to start rather than serve rows of an organization-owned object with no organization. Fix the read, then start again. 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, and upgrade it there." - Added: "Fix the read, then start again." This branch also reaches a fresh 18.0 database whose ledger read failed: no v17 table, and a marker the guard could not read. On that database the true way on is the read. Once the read answers, a database with no marker boots. The old text sent it to an apply report it never had. **C, column read failed** - Before: "The server refuses to start: it could not read whether this database is still in the v17 organization-ownership shape (ERROR), and an unread answer is never taken for a migrated database. Fix the read, or run the ceremony ('os migrate organization-ownership --out PLAN', then 'os migrate organization-ownership --apply --plan-file PLAN --backup-taken'), then start again." - After: "The server refuses to start: it could not read whether this database is still in the v17 organization-ownership shape (ERROR), and an unread answer is never taken for a migrated database. Fix the read, then start again. 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, and upgrade it there." - Kept: "Fix the read". Dropped: the command as the alternative. ## Census: `packages/runtime` `git grep -n -E "migrate organization-ownership|--plan-file|--out PLAN|backup-taken|ceremony" -- packages/runtime` at the base, every hit read: | location | what it is | disposition | |:--|:--|:--| | `organization-ownership-boot-guard.ts` about `:242`, `:256`–`:257`, `:342`–`:343` | the three branches | changed (above) | | `organization-ownership-boot-guard.ts:110` `ceremonyCommand()` | built the command name the refusals interpolated | removed. The `dev` option now selects "development database". | | `organization-ownership-boot-guard.ts:19`, `standalone-stack.ts:336` | code comments that describe the ceremony | excluded (comments). The module docblock sentence beside `:19` ("says which command to run") was corrected anyway, because it described this behaviour. | | `packages/runtime/CHANGELOG.md` `:473`, `:481`, `:912` (and `:296`, `:444`, `:900`, `:913`) | released entries naming the ceremony's steps | not changed. Release-owned, never edited in a code PR (AGENTS.md, Documentation Guardrails). Each entry records what its release shipped. | | `seed-loader-null-organization-holder-real-driver.integration.test.ts:215` | a runtime test pinning `'os migrate organization-ownership'` in `@objectstack/metadata-protocol`'s seed-loader refusal | not changed. The string lives in `packages/metadata-protocol/src/seed-loader.ts`, outside this lane (see Acceptance notes) | | everything else (`action-execution.ts`, `domain-handler-registry.ts`, `flow-clone.ts`, `sandbox/quickjs-runner.ts`, `security/security-catalog.testkit.ts`) | the word "ceremony" in unrelated comments | not this family | | `docs/qa/platform-checklist/areas/platform-core.json`, item `platform-core.v18-boot-refusals` (outside `packages/runtime`) | a QA checklist item whose title and `acceptance[1]` expected the refusal to name the command | changed in patch round 1 (revision 4, `21f7e90f4`) | **Met and not pre-empted:** branch A also reaches a database that 18.0 itself created, when one of its objects newly declares `systemFields: { tenant: false }`. This PR still calls such a database "still in the v17 organization-ownership shape". The 17.x sentence is false for that database, and this PR adds no sentence for it. That wording is the open maintainer decision in #24638, and #24638 remains open. ## Pins and the ablation - `organization-ownership-boot-guard.test.ts`: the old pins on the command text are rewritten. A new block holds one pin per branch: A (absent, unverified and unreadable markers), B1, B2, C, and a development boot across all four. Each asserts the refusal code, the closing text (B2 and C with "Fix the read, then start again." before it), and that the message matches none of `os migrate organization-ownership`, `--plan-file`, `--out PLAN`, `--backup-taken`. - `organization-ownership-boot-guard.kernel.test.ts`: case 4 (B1 on a real kernel) asserted `--apply --plan-file PLAN --backup-taken`. It now asserts the 18.0 sentence and the absence. - `packages/qa/dogfood/test/organization-ownership-boot-refusal-order.dogfood.test.ts` step 1 asserted the refusal contains `'os migrate organization-ownership'`. It now asserts the 18.0 sentence and the absence. Dogfood runs only on `main`'s full battery, not on this PR's CI, so it was run locally (below). - `docs/qa/platform-checklist/areas/platform-core.json`, item `platform-core.v18-boot-refusals`, revision 3 to 4 (patch round 1, commit `21f7e90f4`): the title and `acceptance[1]` (clause, verify) asserted that the refusal names `os migrate organization-ownership`. They now assert the named tables, the closing 18.0 sentence, and 0 hits for `--out PLAN`, `--plan-file` and the command. The ⛔ on the code literal is kept, and a history row cites #24682. - **Ablation**, on the committed head `7ac4f50e9`: `node scripts/ablation-replace.mjs` put branch C's old "Fix the read, or run the ceremony ('os migrate organization-ownership --out PLAN', then '… --apply --plan-file PLAN --backup-taken'), then start again." back. On disk: anchor x1 to x0, replacement x0 to x1, blob `65f1c9e2` to `dad453f6`. The unit file then ran 2 failed and 20 passed. The red pins were "a column read that failed: fix the read first" and "a development boot says the same … in every branch". The restore leg: blob back to `65f1c9e2` (HEAD), `git diff HEAD` empty. The unit test imports the guard by relative path (source), so no build or `dist` leg applies. ## Tests and gates (code at `7ac4f50e9`; the union, wide families, lint and dogfood re-run at `21f7e90f4`, the final commit) - `@objectstack/runtime`, every project: `pnpm --filter @objectstack/runtime test` (project `local`) passed 425 files, 6676 tests, 19 skipped. `test:repo` (project `repo`) passed 3 files, 808 tests. `typecheck` passed: `tsc --noEmit`, plus `check:test-typecheck` OK with the debt ledger held. `tsc -p tsconfig.test.json --listFilesOnly` lists both edited test files. - `@objectstack/cli`, the tests that read the boot refusal (`apply.organization-ownership-refusal.test.ts`, `organization-ownership.no-organization.integration.test.ts`, `organization-ownership.declared-rule-mirrors.integration.test.ts`), against runtime's rebuilt `dist`: 3 files and 8 tests passed. The runtime `dist` carries the new sentence and has 0 hits of `--apply --plan-file PLAN`. - `@objectstack/dogfood`, `test/organization-ownership-boot-refusal-order.dogfood.test.ts`, run once at this head after its closure built (66 tasks, exit 0): 1 file and 5 tests passed. - Derived gates: `node scripts/pm/dispatch-gates.mjs --commands` named 68 commands, and each exit was captured before any pipe. 67 exited 0 on the first pass. `pnpm check:dual-build-cjs-loads` first answered PREREQUISITE NOT MET (exit 3), because not every package had a `dist`. After the whole-repo build it was re-run and exited 0 (111 require entry points across 69 packages load). `--ran` reconciliation: "68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN". At `21f7e90f4` the new path adds `pnpm check:platform-checklist` and `pnpm --filter @objectstack/lint run check:doc-formula-expressions`. The derivation named 70 commands, and all 70 exited 0 after the whole-repo build was restored from cache. `--ran`: "70 derived, 70 run, 0 NOT-MEASURED, 0 UNRUN". - Declared-wide families, all 11 exited 0: `check:init-service-contract`, `check:live-db-isolation`, `check:meta-type-normalized`, `check:optional-error-sink`, `check:resume-authority-declared`, `check:route-envelope`, `check:runner-env-posture`, `check:settings-bind-window`, `check:startup-registry-verdict`, `check:verify-stand-in`, `check:wildcard-fallthrough`. `pnpm lint` (`eslint . --no-inline-config`) exited 0. At `21f7e90f4` all 11 and `pnpm lint` were re-run and exited 0. - `node scripts/check-platform-checklist.mjs --changed-from origin/main` exited 0 at both heads: 3 anchors judged, 0 lost. At `21f7e90f4`, `pnpm check:platform-checklist` exited 0 ("16 areas, 327 items"). `@objectstack/plugin-auth`'s `checklist-refusal-envelope-consistency.test.ts`, which reads every area file, passed 1 file and 5 tests. The dogfood file re-run at `21f7e90f4` passed 1 file and 5 tests. - `origin/main` has moved since this branch was cut, but nothing new touches these files, so `main` was not merged in. ## Acceptance notes - **Same family, outside `packages/runtime` and outside #24637's rows.** The parent's census regex does not match a backtick right after the command name, so it misses two strings. `packages/metadata-protocol/src/seed-loader.ts:1739` (the seed loader's unowned-holder refusal) says "(`os migrate organization-ownership`, then the same command with --apply)". `packages/metadata-core/src/legacy-organization-row.ts:78` (`LegacyOrganizationRowsUnreadable`) says "Run `os migrate organization-ownership` on this database first." Both are reported to the seat to fold into #24637. They are not changed here. - **The QA checklist clause this PR made stale is fixed here.** Patch round 1 used the seat's in-place exemption: conditions ① to ④ hold, and the seat measured ③. See Pins. - The pending changeset `15214-c11-fresh-install-messages.md` (#24506) put the 18.0 sentence ahead of the steps. It is left as it is: it correctly describes its own change, and this PR's changeset describes the removal of the steps. --- _Generated by [Claude Code](https://claude.ai/code/session_01SYEJqDL3NXxrJRBVqaMu9T)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 045dae8 commit 9e2722a

6 files changed

Lines changed: 174 additions & 59 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/runtime': patch
3+
---
4+
5+
fix(runtime): the organization-ownership boot refusals say what 18.0 supports for a v17 database and name no step of `os migrate organization-ownership`, which 18.0 refuses on every database
6+
7+
Clause-②: no (refusal text only: the refusal code `ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED`, the refused tables and the boot outcome do not move)
8+
9+
In 18.0, `os migrate organization-ownership` refuses plan and apply on every database (`NOT_IMPLEMENTED`): 18.0 supports fresh installs only. Three branches of the v18 boot refusal still told the operator to run that command's plan and its `--apply --plan-file PLAN --backup-taken`, so following them led to a second refusal.
10+
11+
Each branch now closes on the sentence `os migrate apply`'s drop gate and `os dev` already use: "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, and upgrade it there."
12+
13+
- **A table still in the v17 shape** (an object that declares no organization column, over a table that still carries `organization_id`). This branch already said that 18.0 does not upgrade a v17 database. It no longer follows that with the command's plan and apply steps.
14+
- **No table left in the v17 shape, and the ceremony's marker is not verified.** The refusal no longer prescribes a re-plan and a re-apply, which 18.0 cannot run.
15+
- **No table left in the v17 shape, and the marker could not be read.** The refusal now says to fix the read and start again: a database no ceremony ran on boots once the read answers. It no longer prescribes a re-plan and a re-apply.
16+
- **The column read itself failed.** The refusal still says to fix the read. It no longer offers the command as the alternative.
17+
18+
A development boot (`OrganizationOwnershipBootGuardPlugin({ dev: true })`, as `os serve --dev` composes it) says "a v17 development database" in place of "a v17 database". Before, it named the command with `--dev`.

‎docs/qa/platform-checklist/areas/platform-core.json‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3042,10 +3042,10 @@
30423042
},
30433043
{
30443044
"id": "platform-core.v18-boot-refusals",
3045-
"title": "A v18 server refuses to boot rather than serve wrongly: against a 17.x-shaped database (a physical organization_id on a non-tenant table, no verified ADR-0131 ceremony marker) it stops before listening with ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED naming the tables and the command, and with security on but no identity objects it stops with AuthzIdentityObjectsMissingError",
3045+
"title": "A v18 server refuses to boot rather than serve wrongly: against a 17.x-shaped database (a physical organization_id on a non-tenant table, no verified ADR-0131 ceremony marker) it stops before listening with ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED, naming the tables and saying that 18.0 supports fresh installs only, and with security on but no identity objects it stops with AuthzIdentityObjectsMissingError",
30463046
"since": "v18",
30473047
"status": "active",
3048-
"revision": 3,
3048+
"revision": 4,
30493049
"priority": "P0",
30503050
"surface": "cli",
30513051
"personas": [
@@ -3070,9 +3070,9 @@
30703070
"evidence": "the vitest summary line per pin file, with the package name and the subject sha"
30713071
},
30723072
{
3073-
"clause": "the v18 serve against the v17 DB exits 1 before kernel:listening with the organization-ownership refusal, which names the tables and `os migrate organization-ownership`. ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED is that refusal's `code` (packages/runtime/src/organization-ownership-boot-guard.ts#OrganizationOwnershipBootRefusal) and the boot never prints it — `os serve` prints the refusal's message only — so the code is the pins' evidence (acceptance[0]), not this log's",
3073+
"clause": "the v18 serve against the v17 DB exits 1 before kernel:listening with the organization-ownership refusal, which names the tables and ends with 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, and upgrade it there.\"), and names neither `os migrate organization-ownership` nor any step of it (no `--out PLAN`, no `--apply --plan-file`): 18.0 refuses that command on every database (NOT_IMPLEMENTED). ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED is that refusal's `code` (packages/runtime/src/organization-ownership-boot-guard.ts#OrganizationOwnershipBootRefusal) and the boot never prints it — `os serve` prints the refusal's message only — so the code is the pins' evidence (acceptance[0]), not this log's",
30743074
"oracle": "log",
3075-
"verify": "boot output + exit code + no listening line: the printed refusal names each table still carrying organization_id and the `os migrate organization-ownership` command. ⛔ Do not grep the log for the code literal — it is not printed (packages/cli/src/commands/serve.ts prints the refusal's message)",
3075+
"verify": "boot output + exit code + no listening line: the printed refusal names each table still carrying organization_id and ends 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, and upgrade it there.\"; the refusal excerpt has 0 hits each for `--out PLAN`, `--plan-file` and `os migrate organization-ownership`. ⛔ Do not grep the log for the code literal — it is not printed (packages/cli/src/commands/serve.ts prints the refusal's message)",
30763076
"evidence": "the refusal excerpt"
30773077
},
30783078
{
@@ -3123,6 +3123,12 @@
31233123
"date": "2026-10-19",
31243124
"change": "steps[1], acceptance[1] (clause, verify) and acceptance[3].verify corrected — two checklist-accuracy findings of the 18.0 QA sweep (QA run #24557, platform-core.v18-boot-refusals; wave anchor #24579 §3). steps[1] booted `os dev` with `services: {auth: false}`, which is not a stack key (packages/spec/src/stack.zod.ts), so the run recorded acceptance[3] blocked(fixture); a stock boot composes the security plugin only beside AuthPlugin (packages/core/src/stack-auth.ts resolvePlatformAuthComposition), so the step now names a scratch config that mounts SecurityPlugin itself with the auth tier off, marked as not yet replayed live, and acceptance[3] names its pin as the fallback. acceptance[1] said the boot exits with ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED on a log oracle; that literal is only the refusal error's code (OrganizationOwnershipBootRefusal) and serve.ts prints the message alone, so the log oracle now reads the named tables and command and the code is left to the pins. acceptance[2] and steps[0] (#24592) are unchanged",
31253125
"ref": "#24632"
3126+
},
3127+
{
3128+
"revision": 4,
3129+
"date": "2026-10-19",
3130+
"change": "title and acceptance[1] (clause, verify) re-worded for 18.0's boot refusal text (card #24651). In 18.0 `os migrate organization-ownership` refuses plan and apply on every database (NOT_IMPLEMENTED: 18.0 supports fresh installs only), so the boot refusal no longer names that command or its `--out PLAN` / `--apply --plan-file PLAN --backup-taken` steps. Every branch now ends 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, and upgrade it there.\" (packages/runtime/src/organization-ownership-boot-guard.ts, freshInstallsOnly; `os serve --dev` says \"a v17 development database\"). The clause asserted that the refusal names the command, so a run against the new text would have recorded FAIL on a correct product. It now asserts the tables, that sentence, and the absence of the command and its steps. The ⛔ on the code literal is kept. acceptance[0], acceptance[2], acceptance[3] and both steps are unchanged",
3131+
"ref": "#24682"
31263132
}
31273133
]
31283134
},

‎packages/qa/dogfood/test/organization-ownership-boot-refusal-order.dogfood.test.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
// that declare no organization column) still carrying a physical
1717
// `organization_id`:
1818
//
19-
// 1. the v18 boot is refused, naming the command — and the refused boot has
19+
// 1. the v18 boot is refused, saying what 18.0 supports for a v17 database and
20+
// naming no step of the command 18.0 refuses — and the refused boot has
2021
// ALREADY added the column the ceremony waits for: the guard judges in its
2122
// own `start()`, after the engine's `start()`, which is where the schema
2223
// sync runs (and before every plugin that writes rows at start, so the
@@ -109,7 +110,7 @@ describe('[ADR-0131 D10 item 5] a 17.x database upgrades through the boot refusa
109110
rmSync(dir, { recursive: true, force: true });
110111
});
111112

112-
it('1. a v17-shape database is refused, naming the command', async () => {
113+
it('1. a v17-shape database is refused, saying what 18.0 supports and naming no step of the refused command', async () => {
113114
const seed = new DatabaseSync(file);
114115
seed.exec(`
115116
CREATE TABLE sys_user_permission_set (id TEXT PRIMARY KEY, user_id TEXT, permission_set_id TEXT, organization_id TEXT);
@@ -121,7 +122,8 @@ describe('[ADR-0131 D10 item 5] a 17.x database upgrades through the boot refusa
121122

122123
refusal = refusalIn(await boot(file));
123124
expect(refusal, 'the boot was refused for the v17 shape').toBeDefined();
124-
expect(String((refusal as Error).message)).toContain('os migrate organization-ownership');
125+
expect(String((refusal as Error).message)).toContain('18.0 supports fresh installs only and does not upgrade a v17 database');
126+
expect(String((refusal as Error).message)).not.toMatch(/os migrate organization-ownership|--plan-file|--out PLAN|--backup-taken/);
125127
expect((refusal as { tables?: string[] }).tables).toEqual(expect.arrayContaining(['sys_metadata', 'sys_migration_journal']));
126128
}, 240_000);
127129

‎packages/runtime/src/organization-ownership-boot-guard.kernel.test.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
* 3. a fresh database boots;
1717
* 4. a database the ceremony ran on to the end (no column left) whose marker
1818
* is NOT verified — its post-check found rows still without their
19-
* organization — is refused, naming the re-run;
19+
* organization — is refused, saying what 18.0 supports (it cannot re-run
20+
* the ceremony);
2021
* 5. ⭐ a refused boot writes nothing: the plugins that write rows in their
2122
* own `start()` — `AppPlugin` (the inline seed) and plugin-auth's
2223
* identity-objects plugin (the Default Organization, `AuthPlugin`'s twin
@@ -181,7 +182,7 @@ describe('the v18 boot refusal on a real kernel (ADR-0131 D10 item 5)', () => {
181182
await expect(engine.findOne('sys_migration', { where: { id: MARKER }, context: { isSystem: true } })).resolves.toBeNull();
182183
});
183184

184-
it('⭐ a ceremony that ran every step and did not verify (no v17 column left, marker unverified) is refused, naming the re-run', async () => {
185+
it('⭐ a ceremony that ran every step and did not verify (no v17 column left, marker unverified) is refused, saying what 18.0 supports', async () => {
185186
const file = databaseFile();
186187
const db = new DatabaseSync(file);
187188
writeMarker(db, null, 602);
@@ -191,7 +192,10 @@ describe('the v18 boot refusal on a real kernel (ADR-0131 D10 item 5)', () => {
191192
expect(refusal?.code, String(error)).toBe(ORGANIZATION_OWNERSHIP_BOOT_REFUSAL_CODE);
192193
expect(refusal?.tables).toEqual([]);
193194
expect(refusal?.message).toMatch(/^The organization-ownership ceremony has not finished on this database: its marker/);
194-
expect(refusal?.message).toContain("os migrate organization-ownership --apply --plan-file PLAN --backup-taken");
195+
// 18.0 cannot re-run the ceremony (its `os migrate organization-ownership` refuses on every
196+
// database), so the refusal names no step of it and says what 18.0 supports instead.
197+
expect(refusal?.message).toContain('18.0 supports fresh installs only and does not upgrade a v17 database');
198+
expect(refusal?.message).not.toMatch(/os migrate organization-ownership|--plan-file|--out PLAN|--backup-taken/);
195199
expect(kernel.getState()).not.toBe('running');
196200
});
197201

0 commit comments

Comments
 (0)