|
| 1 | +--- |
| 2 | +'@objectstack/cli': minor |
| 3 | +--- |
| 4 | + |
| 5 | +feat(cli): the organization-ownership ceremony carries the `sys_metadata` organization layer into the environment (ADR-0131 D10) |
| 6 | + |
| 7 | +Clause-②: yes (widening) |
| 8 | + |
| 9 | +The plan of `os migrate organization-ownership` gains a key, `metadataConflicts` on the `sys_metadata` table. It lists every overlay key that more than one row would hold once the organization leaves the key, with each row's id. `--apply` gains a flag, `--metadata-choice ROW_ID`. Nothing is removed from the plan or the apply. |
| 10 | + |
| 11 | +`--apply` shipped in 18.0.0-next.1. This adds to it, and refuses no invocation that release could finish: a conflict now blocks the new step until it is chosen, but on such a database 18.0.0-next.1's `--apply` already stopped at `column-drops`, which never drops `sys_metadata`'s still-declared column. |
| 12 | + |
| 13 | +`--apply` gains a step, `metadata-promotion`, after the authored catalog conversion: |
| 14 | + |
| 15 | +- **Form withdrawals are carried first, under every posture, and fail-closed.** When an organization withdrew a public form (`sharing.enabled` or `sharing.allowAnonymous` set to `false`, link kept), every view the environment serves under that name that still serves the form open takes the withdrawal. That covers stored views and views served from code, in every package. The ceremony saves it through the metadata door, into the view's own package, and any organization's withdrawal wins. |
| 16 | +- **Under `single`, an organization customization whose key no other row holds becomes the environment's, in place.** This covers the `sys_metadata` rows of `view`, `dashboard`, `report`, `translation` and `email_template`; the key is type, name, package and bundle member. The row takes its history with it. |
| 17 | +- **A key held by more than one row is a conflict, listed in the plan.** Under `single` it blocks the step until the operator chooses one row with `--metadata-choice ROW_ID`, once per conflict. |
| 18 | + - The key ends with exactly one environment row. Where the key has an environment row, that row takes the chosen body in place, and its version names the chosen row's snapshot. Where it has none, the chosen organization row is promoted in place. A withdrawal the body keeps is also saved through the metadata door. |
| 19 | + - The in-place write does to the row what the door does. It asks the door's lock verdict first, so a locked item stays locked and the conflict blocks. It keeps the row's stored `_lock` keys. It restamps `checksum`. |
| 20 | + - Any public-form withdrawal another row of the key holds is kept on that body. |
| 21 | + - The environment row is read back. Only then are the key's organization rows deleted, in one statement, by id. |
| 22 | + - A rerun after an interruption accepts the same `--metadata-choice` and finishes. |
| 23 | +- **Under a wall (`isolated`, `group`), nothing is promoted unless the operator names the row, and nothing is ever deleted.** A named row's body becomes the environment's as above. Every organization row left, the chosen one included where the key has an environment row, is named in the report as remaining and blocking `column-drops`, with the remedy. |
| 24 | + |
| 25 | +**A choice deletes only rows the reviewed plan listed.** `--metadata-choice` and `--template-choice` delete the other rows of their conflict. So the apply reads the conflict groups the plan file records (`metadataConflicts` and `templateConflicts`). It refuses, before anything is written, a choice whose conflict now holds other rows than the reviewed plan listed, with a new reason, `plan-conflict-mismatch`. The refusal names the conflict, the rows added and the rows gone. Plan again, review, and pass the new file. |
| 26 | + |
| 27 | +`event_seq` and every `(type, name, version)` of `sys_metadata_history` become unique environment-wide. The metadata door numbers a save from history in its own scope, so each lineage is placed where no environment save can land on it: |
| 28 | + |
| 29 | +- **A promoted row takes its history with it.** Before the row leaves its organization, that organization's lineage of the item is moved above the environment's numbers, and above every other number that organization holds, then becomes environment history. Every later environment save of the item numbers above it, and the row's version still names its own snapshot. |
| 30 | +- **A new step, `history-renumber`, runs just before `column-drops`, on every apply, even after a checkpoint.** It lays out every lineage still tagged to an organization above the environment's: each organization's event range by one offset, its commit ranges with it, and each item's version lineage, the organization's rows with it. |
| 31 | + - Under `single`, that history then becomes the environment's too. |
| 32 | + - Under a wall it stays its organization's. |
| 33 | + |
| 34 | +`column-drops` runs only after that same apply's `history-renumber` has verified `event_seq` and every `(type, name, version)` unique environment-wide. A duplicate it cannot lay out is named, and blocks. |
| 35 | + |
| 36 | +Order within an organization, and the version a rollback resolves, are unchanged. Nothing in history is deleted. |
| 37 | + |
| 38 | +On a 17.x database `sys_metadata` has no `bundle_member` column. The ceremony boots with schema changes held, so it reads that column as absent, the overlay key's NULL member, and plans the table rather than refusing it. |
| 39 | + |
| 40 | +```bash |
| 41 | +os migrate organization-ownership --out plan.json # review plan.json: metadataConflicts lists every conflict |
| 42 | +os migrate organization-ownership --apply --backup-taken --plan-file plan.json --metadata-choice ROW_ID # one per listed conflict |
| 43 | +``` |
0 commit comments