Repository navigation
fix(cli): os migrate apply names the birth marker, not a ceremony, when it lets an organization_id drop through - #25500
Conversation
…id drop success line Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX
📓 Docs Drift CheckThis PR changes 1 package(s): 10 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 30 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 36077993ac4332f2eda7511b18a338e739a4f7f7 && git checkout 36077993ac4332f2eda7511b18a338e739a4f7f7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4c8f40a1961f75b84d5f2222554c471e035a3744 b3854a5fa405038954a269b05b5654051c2c07de && git checkout -B drift-repro 4c8f40a1961f75b84d5f2222554c471e035a3744 && git merge --no-ff b3854a5fa405038954a269b05b5654051c2c07de
node scripts/docs-audit/affected-docs.mjs --json 4c8f40a1961f75b84d5f2222554c471e035a3744
|
… --json documents Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX
席位核对ACCEPT ·
Generated by Claude Code |
Requested by Sales · project thread
Fixes #25466
Clause-②: no
Metadata-protocol: none
What changed
os migrate apply --allow-destructive, when the organization-ownership drop gate lets anorganization_iddrop through, now says which row let it through:details.birth, written by the boot guard on a datastore created from empty):organization_id on acme_note: this database was created from empty (CREATED_AT, platform VERSION) and carries the organization-ownership birth marker, so it holds no legacy organization rows and the ceremony is not needed for it — the drop may proceed.details: { checkpoint }): the same sentence as before,the organization-ownership ceremony verified (VERIFIED_AT) — the drop may proceed.The distinction is read from the
flagthe gate's one marker read (gateReads.organizationOwnershipMarker) already returned: no second database read. The new pure helperorganizationOwnershipBirthOf(flag)inpackages/cli/src/utils/organization-ownership-drop-gate.tsparsesdetails.birthwith the spec's existingOrganizationOwnershipBirthMarkerSchema(@objectstack/spec/system, already exported). No new export frompackages/runtimeorpackages/spec.Unchanged: the
--jsondocument (it prints no such line), exit codes, the accept set (isDataMigrationFlagVerifiedstill decides), and the refusal branch.plan.tsprints no such line on a verified database, so it is untouched.Discriminator: where it comes from
The runtime boot guard does not tell the two apart. It decides on the shape generation alone (
organizationOwnershipShapeGenerationOf, which readsdetails.birth ?? details.checkpoint), and no exported reader answers "birth or ceremony". The spec's docblock onORGANIZATION_OWNERSHIP_MIGRATION_IDdefines the birth marker as the row whosedetails.birthis anOrganizationOwnershipBirthMarker. The ceremony's ledger write (organization-ownership.ts,details: { checkpoint }) replaces the whole field, so a row carries one or the other. The helper reads that declared key with the declared schema. Ifdetails.birthis present but does not parse, the line still takes the birth arm and names the row'sverified_at, which the guard writes from the same clock.Measured reproduction (real CLI, SQLite, harness copied from the earlier tenant:false run)
Database created by
os migrate apply --yeson an empty file. Itssys_migrationrow is{"birth":{"shapeGeneration":1,"platformVersion":"18.0.0","createdAt":"…"}}. Thenacme_notedeclaressystemFields: { tenant: false }andos migrate apply --allow-destructive --yesruns:apply.tsfrom baseab510f42e7, cli rebuilt✓ organization_id on acme_note: the organization-ownership ceremony verified (2026-10-23T15:32:50.299Z) — the drop may proceed.b58c1ed845✓ organization_id on acme_note: this database was created from empty (2026-10-23T15:32:05.034Z, platform 18.0.0) and carries the organization-ownership birth marker, so it holds no legacy organization rows and the ceremony is not needed for it — the drop may proceed.In both legs the column is dropped (
pragma_table_infohas noorganization_idafterwards) andApplied 1 change(s).prints. With--json, both legs exit 0 with the same key set. After normalising timestamps and the database file name, the documents differ only induration(959 vs 907 ms). Neither prints the line. The before leg was restored withgit checkout HEAD -- apply.ts:git diff HEADwas empty, the hash matched the HEAD blob, and cli was rebuilt (the marker string is back indist/).Tests
packages/cli/src/commands/migrate/apply.organization-ownership-refusal.test.tsruns the realMigrateApply.runpath, with the boot and the marker read replaced. It adds three cases:details.checkpoint): exit 0, and the line keepsthe organization-ownership ceremony verified (VERIFIED_AT);--jsonfor both rows: exit 0, no success line, and the two documents are equal.Local gates (head
b58c1ed845)pnpm --workspace-concurrency=2 --filter '@objectstack/cli...' build: VERDICT command-exit 0pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: VERDICT command-exit 0, Test Files 326 passed (326), Tests 4808 passed (4808)pnpm --filter @objectstack/cli typecheck: VERDICT command-exit 0pnpm lint: VERDICT command-exit 0dispatch-gates --commands: 67 derived. 65 ran with exit 0. 2 are NOT MEASURED (exit 3, PREREQUISITE NOT MET):check:dual-build-cjs-loadsandcheck:i18n-coverage, because both need a whole-repopnpm buildand only the cli closure was built here. CI runs them.--ranreconciliation: 67 derived, 65 run, 2 NOT-MEASURED (derived from recorded exit 3), 0 UNRUN.Acceptance notes
sys_comment.reactionssibling success line was checked and left alone. Its marker (sys-comment-reactions-to-records) is written only byos migrate comment-reactions --apply(comment-reactions-conversion.ts) and is not creation-attested (CREATION_ATTESTED_MIGRATION_IDSholds only file-references and value-shapes), so a verified row there always comes from a real run.🤖 Generated with Claude Code
https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX