Skip to content

Commit cf15fee

Browse files
committed
fix(cli): os migrate apply holds back every organization_id drop until the organization-ownership ceremony verifies
The drop gate's table set was the platform inventory only, so --allow-destructive dropped organization_id from host-declared platform-global objects (ADR-0131 D7) and fate-supplement objects ahead of the ceremony's verified marker. The ceremony plans every table that physically carries the column, and the drift can only offer a column that is physically there, so the gate now holds back every organization_id drop the drift offers (ADR-0131 D10 item 5). Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
1 parent d39ed08 commit cf15fee

1 file changed

Lines changed: 25 additions & 10 deletions

File tree

‎packages/cli/src/utils/organization-ownership-drop-gate.ts‎

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,50 @@
66
* ceremony verified on this database?
77
*
88
* A v18 runtime declares no organization column on the ceremony's
9-
* column-drop tables (the D7 tables and the `sys_metadata` family), so the
10-
* drift report lists each physical one as an unmapped, destructive column and
9+
* column-drop tables (the D7 tables, the platform's and the ones a deployment
10+
* declares platform-global, and the `sys_metadata` family), so the drift
11+
* report lists each physical one as an unmapped, destructive column and
1112
* names `os migrate apply --allow-destructive` as its remedy. Applied ahead of
1213
* the ceremony, that drop skips its promotion and report steps, turns every
1314
* legacy organization row into an environment row, and leaves no physical
1415
* column for the v18 boot refusal to find — so the server starts with the
1516
* marker never written. D10 item 5 forbids exactly that escape hatch. So the
16-
* drop of `organization_id` on any table in the ceremony's inventory waits for
17-
* the ceremony's verified marker, as `sys_account.issuer` (#17440) and
17+
* drop of `organization_id` on any table the ceremony plans waits for the
18+
* ceremony's verified marker, as `sys_account.issuer` (#17440) and
1819
* `sys_comment.reactions` (#23281) wait for theirs. ⛔ No flag stands it down.
20+
*
21+
* ## Which tables: every `organization_id` drop the drift offers (#23684)
22+
*
23+
* The ceremony's table set is the database's, not a list's: its plan meets
24+
* every physical table that carries `organization_id`, with the platform
25+
* inventory's row, a host fate supplement's row, the application-object fate,
26+
* or a refusal by name (`organization-ownership-plan.ts`). The drift can offer
27+
* to drop only a column that is physically there. So every `organization_id`
28+
* drop it offers is on a table the ceremony plans, and this gate holds back
29+
* every one: the platform inventory's tables, the objects a deployment
30+
* declares platform-global (ADR-0131 D7), and the objects a host's fate
31+
* supplement names, a file `os migrate apply` is never handed. The v18 boot
32+
* refusal judges the same population, every object that declares no
33+
* organization column, not the inventory.
34+
*
35+
* Reading the drift rather than a composed table list keeps one source: no
36+
* second copy of the ceremony's inventory composition, and no part of the
37+
* answer this command could fail to read.
1938
*/
2039

2140
import type { MigrationFlagEngine } from '@objectstack/platform-objects/system';
2241
import type { DataMigrationFlag } from '@objectstack/spec/system';
23-
import { ORGANIZATION_OWNERSHIP_INVENTORY } from './organization-ownership-inventory.js';
2442

2543
/** The column the ceremony owns the drop of. */
2644
export const ORGANIZATION_COLUMN = 'organization_id';
2745

28-
/** Every table the ceremony's inventory names. */
29-
const CEREMONY_TABLES: ReadonlySet<string> = new Set(ORGANIZATION_OWNERSHIP_INVENTORY.map((entry) => entry.object));
30-
31-
/** The tables whose `organization_id` drop is among `intended`, sorted. */
46+
/** The tables whose `organization_id` drop is among `intended`, sorted: every one, since the ceremony plans each table that carries the column. */
3247
export function ceremonyOrganizationColumnDrops(
3348
intended: ReadonlyArray<{ table: string; op?: { type?: string; column?: string } }>,
3449
): string[] {
3550
const tables = new Set<string>();
3651
for (const d of intended) {
37-
if (d.op?.type === 'drop_column' && d.op.column === ORGANIZATION_COLUMN && CEREMONY_TABLES.has(d.table)) tables.add(d.table);
52+
if (d.op?.type === 'drop_column' && d.op.column === ORGANIZATION_COLUMN) tables.add(d.table);
3853
}
3954
return [...tables].sort();
4055
}

0 commit comments

Comments
 (0)