Repository navigation
Commit cdf3777
feat(cli,runtime): os dev upgrades a v17-shape development database in place; the ownership ceremony covers the telemetry sibling and adopts legacy shipped-set seeds (ADR-0131) (#23906)
## Why
On two long-lived 17.x development databases (hotcrm's and the
showcase's), the v18 boot refuses to start ("still in the v17
organization-ownership shape … Run 'os migrate
organization-ownership'"), and the prescribed ceremony then fails in
sequence:
1. `sys_scim_provider` has no inventory row, so the plan refuses. That
fix is **#23874**, not this PR.
2. Run from a shell without `NODE_ENV=development`, the ceremony
composes no security plugin and no audit plugin. Its authored-conversion
step blocks on the shipped permission sets, and its column-drops step
blocks on `sys_audit_log` ("declares no object").
3. Installs created before the 2026-08-15 seeder ruling store the eight
shipped permission sets as `managed_by: 'admin'`. They read as
Setup-authored, and every one whose stored body differs from the shipped
definition is refused with "rename the authored row".
4. A development boot keeps the lifecycle objects (`sys_audit_log`,
`sys_job_run`, `sys_notification`, …) in the `telemetry` sibling. The
boot guard reads their columns there, but the ceremony read only the
primary, so an upgraded database still never booted. Stale copies of
those tables in the primary also blocked column-drops, with the
misleading message "run the release whose declaration drops it".
The maintainer's ruling for this PR (2026-10-16, recorded verbatim in
the ADR-0131 amendment): customers' temporary development data must not
cost them effort; production data must not go wrong, so production gets
warnings. The ruling chose: `os dev` completes the upgrade itself; adopt
the legacy seeds automatically; full coverage of the telemetry sibling;
add `--dev` to the ceremony.
## What changes
- **`os dev` upgrades a v17-shape local SQLite development database in
place.** The refused `serve --dev` child tells its parent over IPC and
exits. The parent copies the database and its `telemetry` sibling (with
`-wal`/`-shm`) to `<data dir>/backups/organization-ownership-<ts>/`,
runs `os migrate organization-ownership --dev` (plan), applies that
plan, and boots again only when the report says `complete`. It runs once
per session. A blocked step ends `os dev` with the blocker, where the
copy is, and how to resume. `os serve` / `os start` and any non-file
database keep the refusal unchanged.
(`utils/dev-organization-ownership-upgrade.ts`;
`ServeRestartCoordinator` gains `recover`.)
- **Warnings.** The plan carries `warnings` (and `staleCopies`). The
plan prints them, `--apply` prints them again before its first write
(`onWarning`), and the apply report keeps them.
- **Each object in its own database.** `buildOrganizationOwnershipPlan`
/ `applyOrganizationOwnership` take `siblings` and `homeOf` (the booted
engine's routing). Tables are planned, attributed and column-dropped
where they live, and anchors are read where theirs live (a sibling
notification takes its organization from a record in the primary). A
table in a database its object no longer lives in is listed under
`staleCopies`, warned about, and never touched.
- **Legacy shipped-set seeds.** `LEGACY_SHIPPED_SET_SEEDS`
(`managed_by='admin'` and a name in `SHIPPED_PERMISSION_SET_NAMES`,
pinned equal to plugin-security's `securityDefaultPermissionSets`) is a
new `sys_permission_set` category in the plan. At the start of
authored-conversion those rows are re-owned `platform`, so the existing
mirror-deletion removes them once the rewrite has named every grant.
- **`--dev` on the ceremony.** It boots the stack as `os serve --dev`
does (`serveFlags.dev` for both boots and the occupancy probe).
`composeServedPlatform` now reads `serveFlags.dev` for the auth secret's
development fallback; before, it read `isDevelopmentBoot(undefined)`, so
`--dev` alone composed no auth family and no `sys_audit_log`. A
development boot's refusal names the command with `--dev`
(`OrganizationOwnershipBootGuardPlugin({ dev })`).
- **The boot refusal names an apply the ceremony accepts**: `--out
PLAN`, then `--apply --plan-file PLAN --backup-taken`. A bare `--apply`
is refused without a reviewed plan file.
- **ADR-0131** gains the 2026-10-16 amendment. The D10 text names the
one exception to "never an automatic boot step".
Unchanged on purpose: the `declared-rule-mirrors` departure still counts
in the post-check until the declared-rule seeder retires, as its
CHANGELOG entry says. After a full run the boot guard no longer needs
the marker, because no v17 column is left.
## Verification
- New suites: `organization-ownership-plan.siblings.integration.test.ts`
(6), `organization-ownership-apply.siblings.test.ts` (3),
`dev-organization-ownership-upgrade.test.ts` (5), coordinator `recover`
cases in `dev-restart.test.ts` (4), the shipped-name pin in
`organization-ownership-inventory.test.ts`, and the `--dev` refusal in
`organization-ownership-boot-guard.test.ts`. The existing ceremony
suites (15 files, 205 tests) pass unchanged.
- End to end, on a copy of the showcase's real 17.x dev database (with
its telemetry sibling), using this branch's CLI:
- `os migrate organization-ownership --dev` plans 9 tables `[telemetry]`
and warns about the 8 legacy sets.
- `--apply` completes all 9 steps. 37,923 rows are attributed (9,406
notifications in the sibling among them), the 8 seeds are adopted and
deleted, and 13 columns are dropped, including the sibling's.
- `os serve --dev` then boots (health 200).
- End to end, `os dev` on a fresh copy of the same database (the empty
`sys_scim_provider` dropped, standing in for #23874): it prints `⟳ …
upgrading it in place`, then the warning, then `✓ Upgraded … copied to
…/backups/organization-ownership-…`, then `✓ server started on the
upgraded database`, and health returns 200. The backup dir holds the
copy, `plan.json`, `apply.json` and `ceremony.log`.
- `pnpm --filter @objectstack/cli typecheck`, `pnpm --filter
@objectstack/runtime typecheck`, and eslint on the changed files are
clean.
Fixes #23871
Fixes #23872
Fixes #23873 (ruled 2026-10-16: adopt them automatically, and warn in
production; recorded verbatim in the ADR-0131 amendment)
Depends on #23874 for databases that still hold `sys_scim_provider`:
without that inventory row, the plan refuses before any of this runs.
Local test note: the full `@objectstack/cli` suite on macOS fails 7
files for environment reasons only: `/private/var` vs `/var` tmpdir
paths, the local Asia/Taipei timezone in `verify-dashboard-windows`, and
no util-linux `script(1)` for `dev-boot-output-order`. None of these
touches this diff; CI on Linux is the judge.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2ef4627 commit cdf3777
21 files changed
Lines changed: 1514 additions & 86 deletions
File tree
- .changeset
- content/docs
- deployment
- permissions
- docs/adr
- packages
- cli/src
- commands
- migrate
- utils
- 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 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
196 | 196 | | |
197 | 197 | | |
198 | 198 | | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
199 | 219 | | |
200 | 220 | | |
201 | 221 | | |
| |||
1172 | 1192 | | |
1173 | 1193 | | |
1174 | 1194 | | |
| 1195 | + | |
1175 | 1196 | | |
1176 | 1197 | | |
1177 | 1198 | | |
| |||
1442 | 1463 | | |
1443 | 1464 | | |
1444 | 1465 | | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
| 1481 | + | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
| 1494 | + | |
| 1495 | + | |
| 1496 | + | |
| 1497 | + | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + | |
| 1501 | + | |
1445 | 1502 | | |
1446 | 1503 | | |
1447 | 1504 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
398 | 398 | | |
399 | 399 | | |
400 | 400 | | |
401 | | - | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
402 | 405 | | |
403 | 406 | | |
404 | 407 | | |
| |||
0 commit comments