Repository navigation
fix(cli): the organization-ownership drop refusal says what 18.0 supports for a v17 database - #24635
Conversation
…orts for a v17 database os migrate apply --allow-destructive, os migrate organization-ownership --help and os dev's way-on line named steps of os migrate organization-ownership, which 18.0 refuses on every database. They now state 18.0's position in the boot refusal's terms: keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYEJqDL3NXxrJRBVqaMu9T
…abase 18.0 created The drop gate also fires on a fresh 18.0 database whose object newly declares systemFields.tenant false over its injected organization_id, so the refusal names a v17 database rather than this one. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYEJqDL3NXxrJRBVqaMu9T
📓 Docs Drift CheckThis PR changes 1 package(s): 12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 29 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b7fb116b2293dc0756cab9ed6e6a7652d2893768 && git checkout b7fb116b2293dc0756cab9ed6e6a7652d2893768
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b12e16a561cae3da15c09b463b470ce760b11834 0df297be078c9575aeddc013b7064f9453efa90e && git checkout -B drift-repro b12e16a561cae3da15c09b463b470ce760b11834 && git merge --no-ff 0df297be078c9575aeddc013b7064f9453efa90e
node scripts/docs-audit/affected-docs.mjs --json b12e16a561cae3da15c09b463b470ce760b11834
|
席位核对 — ACCEPT
Generated by Claude Code |
席位核对 — REWORK (patch round 1: one integration pin still reads the old
|
…detail The integration pin still matched the withdrawn os migrate organization-ownership --apply in the --json detail; it now asserts the ceremony is named and no step of the command 18.0 refuses is. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYEJqDL3NXxrJRBVqaMu9T
席位核对 — ACCEPT (patch round 1)
Generated by Claude Code |
#24588 (step 1) has landed. The drop refusal, os dev's way-on line and their pins from #24635 said "18.0 does not upgrade; keep a v17 database on 17.x": with the ceremony open they name its steps again, and the refusing class and its pin stay deleted. Claude-Session: https://claude.ai/code/session_014UATXWqAXqkaZkbG92ox6V Co-authored-by: Claude <noreply@anthropic.com>
Fixes #24567
Clause-②: no — basis: refusal and help text only. No accept set, flag, exit code, JSON key or behaviour moves: the gate still holds back the whole
--allow-destructiverun withrefused_organization_ownership_unmigrated, exit 1.Row 3 of #24567 (rows 1–2 landed in #24592),
domain:cliseat 1, claim6273418995. The drop-gate refusal thatos migrate apply --allow-destructivegives on a 17.x-created database told the operator to runos migrate organization-ownership --out PLAN, then--apply --plan-file PLAN, and 18.0 refuses that command on every database (NOT_IMPLEMENTED). The refusal now says what 18.0 supports for a v17 database, in the boot refusal's terms, and names no step of that command. The command's--helpexamples andos dev's way-on line had the same problem and are fixed with it.Before and after
Measured through the real CLI (
node packages/cli/bin/run.js, built from each tree), on a 17.x-shaped SQLite made the wayapply-organization-ownership-gate.integration.test.tsmakes it: a v18 database created byos migrate apply --yes --jsonon an empty file, thenALTER TABLE sys_platform_setting ADD COLUMN organization_id TEXT. The command wasos migrate apply --allow-destructive --yes --database-url file:COPY.db. It exited 1 both times, and the column was still there afterwards.Before (base
65a8eeb7d):On the same file, the command it prescribed answers this (exit 1, no plan file written):
After (head
69778e478):The boot refusal it now matches (
packages/runtime/src/organization-ownership-boot-guard.ts, v17-shape branch, not touched here): "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 drop gate cannot tell a v17 database from one 18.0 created (see the branch map), so it says "keep a v17 database" where the guard says "keep this one". That is also the wording of the command's own 18.0 refusal.--json, thedetail(the only prose the--jsonrefusal carries):No run of "os migrate organization-ownership --apply --plan-file FILE" is recorded on this database.No run of the organization-ownership ceremony is recorded on this database.message: refused_organization_ownership_unmigrated,tables: ["sys_platform_setting"],marker: null,applied: [],created: []and exit 1 are unchanged (measured).os migrate organization-ownership --help, EXAMPLES — before: six invocations, every one refused in 18.0:after (an override on the registered 18.0 class,
MigrateOrganizationOwnership; the ceremony classOrganizationOwnershipCeremonykeeps its six examples for the 18.x release that registers it):os dev, the way-on line (utils/dev-organization-ownership-upgrade.ts; unit-measured, not run live). Before: "Runos migrate organization-ownership --dev(review its plan), then the same command with --apply --plan-file FILE --backup-taken, and start os dev again." After: "18.0 supports fresh installs only and does not upgrade a v17 database: keep a v17 development database on a 17.x release until the 18.x release that carries the upgrade path, and upgrade it there."Branch map of the refusal
The gate sits in
MigrateApply.run()(packages/cli/src/commands/migrate/apply.ts), below the drift report, above the confirmation prompt and above both writes.drop_column organization_idamong the intended drift (ceremonyOrganizationColumnDrops(intended)is empty): every run without--allow-destructive, and every fresh 18.0 database whose objects keep their injected columnsys_migrationmarkeradr-0131-organization-ownershipis verifiedorganization_id on TABLES: the organization-ownership ceremony verified (DATE) — the drop may proceed.flagnull)No run of the organization-ownership ceremony is recorded on this database., then the refusal aboveThe last run of the organization-ownership ceremony recorded on this database (DATE) is not verified (N blocking item(s))., then the refusal above--jsonon either refused branch{ message: refused_organization_ownership_unmigrated, tables, detail, marker }, exit 1, with no prescription sentence, so onlydetaildetailchangedWhen does it fire on a fresh 18.0 install? Only when an object newly declares no organization column over a table that already carries the injected one:
systemFields: { tenant: false }on an existing object. I measured this at69778e478. On a database this 18.0 CLI created with one app objectacme_note(its table getsorganization_id), declaringsystemFields: { tenant: false }makes the drift report offeracme_note.organization_id … "os migrate apply --allow-destructive" to drop it., and that run is refused with the text above, exit 1, the column kept. The changeset for 6074eaf names this population on purpose: the ceremony plans that table.The premise that
organization-ownership --applyIS supported on a fresh install does not hold atorigin/main. The registeredMigrateOrganizationOwnership.run()refuses plan and apply on every database before it reads anything. Its doc says "plan and apply alike", andorganization-ownership.release-refusal.test.tspins this. So no branch of the old text was true in 18.0 on any database. Both refused branches change. The verified branch, the only one naming no command, is kept. The new text is true on a fresh database too: it says "keep a v17 database", not "keep this one". It gives such a database no way on, because 18.0 has none. That is an Acceptance note below, not a change here.Census
git grep -n "organization-ownership"overpackages/cli(source, help text, README, CHANGELOG) andcontent/docs, at65a8eeb7d. These are the places that send a 17.x-created database's operator to the command:cli/src/commands/migrate/apply.tsdrop-gate refusal (was:586–:591)Run "os migrate organization-ownership --out PLAN" … then "… --apply --plan-file PLAN"cli/src/commands/migrate/apply.tsstatus lines, thedetail(was:575/:577)No run of "os migrate organization-ownership --apply --plan-file FILE" is recorded …/The last run of "…" … is not verified--jsoncarries only this sentence, and it named the command with--plan-filecli/src/commands/migrate/apply.tsverified line (:607)the organization-ownership ceremony verified (DATE) — the drop may proceed.cli/src/commands/migrate/organization-ownership.tsexamples (:330–:337):1134)cli/src/commands/migrate/organization-ownership.tsdescription and flag helpcli/src/utils/dev-organization-ownership-upgrade.tsmanual(:127)os migrate organization-ownership --dev(review its plan), then the same command with --apply …"cli/src/utils/dev-organization-ownership-upgrade.tsagain(:178, used:194,:207)os migrate organization-ownership --dev --apply --plan-file …"cli/src/commands/dev.ts:822cli/src/utils/organization-ownership-{apply,plan,inventory,column-drops}.ts,reportReservationTextinorganization-ownership.tscli/src/commands/serve.ts,schema-migrate.ts,migrate-occupancy-gate.ts,security-catalog-overlays.ts,schema-migration-plugins.ts,vitest.config.tsserveprints the runtime boot guard's textcli/README.mdcli/CHANGELOG.md(6074eaf, dd29bbe, 4369677 and others)cli/src/**/*.test.ts(about 35 files)content/docspages generated from the CLIcontent/docs/references/is generated from the spec and has no hit, anddeployment/cli.mdxis hand-written (scripts/docs-audit/handwritten-docs.json)content/docsdomain:devx'sTests
At head
69778e478(patch round 1 at head0df297be0, marked below), afterpnpm --filter '@objectstack/cli^...' build(lock VERDICT command-exit 0) and the CLI's own build:src/commands/migrate/apply.organization-ownership-refusal.test.ts(unit tier: no boot, no driver). It runs the real command with the boot, the occupancy probe, the exit and the marker read replaced. For both refused marker states (none, unverified) it pins: the human face exits 1, the refusal names the table, it does not matchos migrate organization-ownership|--plan-file|organization-ownership --out, it names the 17.x and the 18.x release, and nothing is written. It pins the same absence on the--jsondetail. A verified-marker control reaches the writes and exits 0.apply-organization-ownership-gate.integration.test.ts: thedetailpin/os migrate organization-ownership --apply/is now/organization-ownership ceremony/plus the same absence, on the real command against a v17-shape SQLite table. Run locally because the diff edits it: 5/5 passed.organization-ownership-platform-global-column-drop.integration.test.ts:439(patch round 1,0df297be0) is the samedetailpin,/os migrate organization-ownership --apply/. This PR's rewrite turned it red in CI (Test Core (3/6), 1 failed, 1,894 passed). It now asserts/organization-ownership ceremony/plus the same absence. A census of everypackages/clitest file, unit and integration, looked for any other assertion on the removed wording:organization-ownership --apply/--out,--plan-file PLAN/FILE,--devas a prescription,write and review its plan,start os dev again,refused_organization_ownership_unmigratedand the registered class'sexamples. It found none. The remaining hits aredescribetitles, the ceremony class's own argv in suites that run it directly, and theagainlines this PR keeps.organization-ownership.release-refusal.test.ts: new case. Every example of the registered class names the command without--out/--apply/--plan-file/--fate-supplement, and its description namesNOT_IMPLEMENTED. The ceremony class keeps an--apply --plan-fileexample.dev-organization-ownership-upgrade.test.ts: the non-local case's pintoContain('os migrate organization-ownership --dev')is now the 17.x release plus the absence of the command and of--plan-file. The refused-plan case gains the same.pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 --project unit: 314 files, 4640 tests passed (run atc6cea949b; the last commit changes only the wording these pins read, and they were re-run at69778e478: 4 files 99 tests, plus the integration file 5/5).pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2, in full at0df297be0, underscripts/pm/os-verify-lock.sh, exit captured before any pipe (VERDICT command-exit 0): 161 files passed, 1 skipped (162); 1384 tests passed, 9 skipped (1393). The unit tier was re-run there as well: 314 files, 4640 tests passed.pnpm --filter @objectstack/cli typecheck: exit 0 at69778e478and again at0df297be0(tsc --noEmitprogram lists all seven edited.tsfiles, 0 errors;check:test-typecheckOK, ledger unchanged).69778e478, vianode scripts/ablation-replace.mjs: the old prescription was put back into the refusal (anchor'until the 18.x release that carries the upgrade path, whose ceremony drops these columns itself, in its ', x1 → x0; blob7a6414669b72→e8260625ac6f). Result: 2 failed | 3 passed. The two human-face cases went red; the two--jsoncases and the control stayed green, because the mutation touched the message, not thedetail. Restored blob == HEAD,git diff HEADempty. The subject resolves through a relative./apply.jsimport (src, nodist), so no build leg was owed. A first attempt atc6cea949bwas refused by the tool as a no-op: its replacement contained its anchor, so the anchor count did not move and nothing was measured. It was re-run with a new anchor (same 2 red).detailpins at0df297be0(patch round 1): the olddetailwas put back into the no-marker branch (anchor'No run of the organization-ownership ceremony is recorded on this database.', x1 → x0). Result: 3 failed, 2 passed (6 filtered out by-t). The no-marker human-face and--jsoncases in the unit file went red, and so did:439in the integration file; the unverified-marker cases stayed green. Restored blob == HEAD,git diff HEADempty.Gates
All at head
69778e478, with every exit code captured before any pipe. All of it was re-run at0df297be0(patch round 1) with the same result: 9 paths, 292 changed lines, the same 68 derived commands, all exit 0. The same two PREREQUISITE NOT MET first attempts re-ran exit 0 after the build. The 11 wide families exit 0.--rananswers68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN.pnpm lintexits 0, andeslintover the round's file exits 0.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 68 commands: a change set of 8 paths, 288 changed lines against merge base65a8eeb7d. All 68 exit 0.check:dual-build-cjs-loadsandcheck:i18n-coveragefirst exited 3 with PREREQUISITE NOT MET, because some packages this diff does not touch had nodist. Those attempts measured nothing. Afterturbo run buildof those packages (75 tasks, all cache hits), both re-ran:✓ check:dual-build-cjs-loads — 111 published require entry point(s) across 69 package(s) loadandcheck-i18n-coverage: OK (14 config(s), 621 baselined untranslated string(s), none new).check:init-service-contract,live-db-isolation,meta-type-normalized,optional-error-sink,resume-authority-declared,route-envelope,runner-env-posture,settings-bind-window,startup-registry-verdict,verify-stand-in,wildcard-fallthrough): all exit 0.--ranreconciliation answersRun reconciliation — 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN.That zero is derived from the recorded exit codes. The 11 wide families are recorded as outside the derivation.pnpm lint(eslint . --no-inline-config, the full union): exit 0.eslint --no-inline-configover the 7 changed.tsfiles: exit 0, nothing printed. The control-byte self-scan of the changed files found none.✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).·✓ This diff introduces no major bump.·✓ doc authoring guard: 19124 customer-facing string(s) across 1437 spec sources clean.Acceptance notes
os serveboot may already have added v18 schema to that database. That question stays with the maintainer.os migrate applyitself writes nothing before this refusal: the gate is above every write, and the boot defers DDL.acme_note). An object that newly declaressystemFields: { tenant: false }over its injectedorganization_idmakes the drift report prescribe--allow-destructive, and this gate refuses it. By source, the v18 boot guard then refusesos servewith "This database is still in the v17 organization-ownership shape … keep this one on a 17.x release" (findV17ShapeTablesreads onlytenant === falseplus the physical column). Itsos servehalf was not measured. The 18.0 ruling covers v17 upgrades; this population is outside it. Reported to the seat for a decision. The text here makes no claim about it that is false.content/docs(domain:devx), not edited:deployment/cli.mdx:1027–:1029describes the drop gate as waiting for "a verifiedos migrate organization-ownership --apply --plan-file FILErun", with no 18.0 qualifier there.deployment/cli.mdx:1477–:1494has an 18.0 callout over a fenced plan and apply block.deployment/cli.mdx:200–:222(os dev) has a callout, then "run the ceremony yourself" and "they refuse and name the command".concepts/metadata-lifecycle.mdx:109says "an 18.xos serverefuses to start … until the v18 migration ceremony (os migrate organization-ownership --out PLAN, then--apply --plan-file PLAN)".permissions/capabilities.mdx:220andpermissions/permission-sets.mdx:399describe what--applydoes. The hits indeployment/environment-variables.mdx,deployment/install-modes.mdx,ui/public-data-collection.mdxandreleases/v18/index.mdxare the ADR-0131 file name in a URL.packages/cli, held off here:metadata-core/src/legacy-organization-row.ts:78("Runos migrate organization-ownershipon this database first."),metadata-protocol/src/seed-loader.ts:1739("os migrate organization-ownership, then the same command with --apply"),plugins/plugin-security/src/catalog-reference-report.ts:437–:438(plan, then--apply --backup-taken --plan-file). Also two of the runtime boot guard's three refusals: the unverified-marker branch and the read-failure branch name the steps with no 18.0 sentence. And the spec's ADR-0087 entries18.sys-metadata{,-audit,-commit,-history}-organization-column-retired,18.ups-permission-set-id-column-retired,18.sharing-rule-declared-mirror-retiredand18.cli-organization-ownership-apply-plan-file-required(domain:spec). Listed in the report for the seat to fold into one family card.dev.ts:822still prints "upgrading it in place" before an 18.0 upgrade that always stops at the plan's refusal. The lines after it now say why and what to do. It names no command, so it is kept.Generated by Claude Code