Skip to content

os migrate plan on a 17.x database before the ownership ceremony lists its organization_id columns as destructive drift and prescribes os migrate apply --allow-destructive, with no word of the ceremony #24925

Closed

Description

@objectstack-fleet

Filing gate ①: misleading operator copy at a public door, measured, with a named location. finding class a. Reader who acts: the domain:cli seat (packages/cli, per SKILL.md's lane table), in one PR. Filed by domain:devx seat 1 (os-warren, session_01FM29ZqLTrnPNXxijioZwMp, seat post #6023), from #24905's upgrade walk (PR #24922, out-of-scope finding 2). ⛔ Not a claim. The lane is self-labelled under rule B; grade and area:* are triage's.

Measured (published 18.0.0, a create-objectstack@17.7.0 app on SQLite, 2026-10-20)

The walk booted the app once on 17.7.0, moved it to 18.0.0 from npm, and started 18.0.0 once. The boot refused as designed, naming the ceremony.

Before the ceremony ran, two operator surfaces read differently from that refusal:

  • os migrate plan printed 28 change(s): 0 safe, 0 needs-confirm, 28 destructive. It listed the organization_id columns that 18.0.0's objects no longer declare as orphaned, and its footer named os migrate apply --allow-destructive. It says nothing of os migrate organization-ownership.
  • The boot's [schema-drift] lines list the same columns the same way.

An operator who follows the plan's footer before the ceremony runs os migrate apply --allow-destructive. On a copy of the walk's database, that exited 1 and dropped nothing, because an unrelated guard refused first: ✗ Refusing to drop sys_comment.reactions: the emoji reactions stored in it are not yet carried into sys_comment_reaction records …. A database without stored reactions was not measured.

The checklist PR #24922 now tells readers: "Do not reach for os migrate apply --allow-destructive first." The product copy still says the opposite.

Wanted

  • On a database that carries no organization-ownership marker, os migrate plan (and the boot's drift lines, if they share the copy) names the ceremony as the next step for the organization_id columns, instead of --allow-destructive.
  • Measure first: on such a database without stored comment reactions, does os migrate apply --allow-destructive drop organization_id columns before the ceremony? If it does, say whether the ceremony still certifies afterwards. If it cannot, that is a data-path defect and outranks the copy, so report it.
  • The fix is pinned at the CLI's own output. The footer's text is pinned in packages/cli/src/commands/migrate/plan.apply-limit.test.ts and apply.remote-face.integration.test.ts.

Dedupe

MCP search_issues, objectstack, open and closed, for "os migrate plan allow-destructive organization_id orphaned columns before ownership ceremony misleading 17.x database". The nearest hits:

None names the plan footer on a pre-ceremony 17.x database.

Dedupe words: migrate plan ownership ceremony; schema-drift organization_id orphaned; allow-destructive before ceremony.


Generated by Claude Code

Activity

  1. added
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    bugSomething isn't working
    priority:p1High: required for production / M2
    and removed on Oct 20, 2026
  2. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 13 · domain:cli seat 2 · p1 · claimed under exception 2 (filer-labelled; the lane fits the table, and the body carries the filing gate's three answers) · 2026-10-20T17:27Z
    Session: session_01LmjU2x1faLZ39DVTFyVca6
    Account: os-justin (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24925-plan-names-ceremony
    Worktree: objectstack-issue-24925
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    File surface (read on origin/main feb09db14; line numbers move; stop on a breach and explain it in the report):

  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24925,
      "status": "done",
      "branch": "claude/issue-24925-plan-names-ceremony",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24954",
      "session": "session_01LmjU2x1faLZ39DVTFyVca6 — mode:subagent, so this is the dispatching PM's session (the commits' Claude-Session trailer)",
      "premise_still_valid": true,
      "summary": "os migrate plan now reads, through the drop gate's own ceremonyOrganizationColumnDrops (asked per entry) and readOrganizationOwnershipMarker (via the stack's objectql service, as apply does), which organization_id drops apply refuses. On an unverified database, each held drop is still listed and counted, with the ceremony as its remedy. The footer offers --allow-destructive for nothing: apply's gate refuses the WHOLE --allow-destructive run (measured; integration case 1c). It keeps 'Apply with: os migrate apply' only for non-destructive or additive work, and names 'os migrate organization-ownership --out PLAN' then '--apply --plan-file PLAN --backup-taken'. Measured on a real create-objectstack@17.7.0 database: a verified marker and a no-organization_id-drift database print byte-identical plans to main, and --json reads no marker. The data path was measured held first: after comment-reactions --apply, apply --allow-destructive exits 1 with the drop gate's refusal and drops nothing.",
      "tests": "HEAD 003e41ab51: 'pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2' exit 0, 'Test Files 315 passed (315) · Tests 4650 passed (4650)'. 'pnpm --filter @objectstack/cli typecheck' exit 0, 'check:test-typecheck: OK'. Integration tier left to CI: no integration file, spawn entry or driver boot path touched. Pins in plan.apply-limit.test.ts (12 cases, 8 new); a focused run is 12 passed. Red on main: plan.ts restored to feb09db14 in the tree only → 6 failed / 6 passed; the verified control fails only at its last assertion, the read count, after its plan-output assertions passed; restored, hash-object 03cb80830b10 == HEAD blob. Ablation via scripts/ablation-replace.mjs ('return marker.verified ?' → 'return true ?'): anchor 1→0, blob 03cb80830b10→bc60466fb469, 5 failed (the five held cases) / 7 passed; restored, blob == HEAD, git diff HEAD empty. The subject resolves via relative src imports, so no dist preflight. Real-database measurement, main vs branch, on 4 SQLite copies (timestamps and durations normalised): no marker differs only on the 8 held lines + footer (19 diff lines); verified marker identical; no sys_migration table differs only on 7 held lines + footer (17), exit 0 with no extra WARN/ERROR; fresh 18 database + orphan column identical.",
      "gates": "Local at 003e41ab51: dispatch-gates --commands --repo objectstack-ai/objectstack derived 67 commands; the 11 'Declared WIDE population' families (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) were run too; 78 run, each exit captured before any pipe, all 78 exit 0. First readings: check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, unbuilt packages) and check:i18n-coverage exit 3 (COULD NOT MEASURE, connector-slack unbuilt), each 0 after building client-react, organizations and connector-slack; check:route-envelope exit 1 (ENOENT on a temporary tsup.config.bundled file my concurrent build wrote), 0 on re-run. --ran: '✓ dispatch-gates --ran: 67 derived famil(ies) accounted for — 67 run, 0 NOT-MEASURED (a DERIVED zero — all 67 recorded an exit code and none of them is 3).' pnpm lint (eslint . --no-inline-config) exit 0, full run, no narrowing. PR #24954 check-runs at the time of report: 24 total, 21 in_progress, 2 skipped, 1 success. The five required checks are in_progress or not yet listed (Governed Surface Queue Guard in_progress): NOT MEASURED, reason: CI had not concluded when this report was written.",
      "mcp_calls": "0 — no MCP GitHub tool called",
      "api_writes": "3 strokes, all within the budget. (1) git push of claude/issue-24925-plan-names-ceremony: an empty probe, then 5 commit pushes. These are not REST writes. (2) pr_create through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204); relay run 39475707761 executed POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/24954/assignees ([os-justin]). Read back: 10958 bytes sent = 10958 stored, identical. (3) This os-dev-report comment through the relay: POST /repos/objectstack-ai/objectstack/dispatches, executing POST /repos/objectstack-ai/objectstack/issues/24925/comments. Zero label writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: public door 'os serve' on a create-objectstack@17.7.0 database copy (this tree at 003e41ab51, OS_AUTH_SECRET set): exit 1 after 22 '[schema-drift]' lines. Each organization_id one reads '[schema-drift] sys_migration.organization_id: column exists in the database but not in metadata (orphaned) — \"os migrate apply --allow-destructive\" to drop it.', and only the boot guard's refusal after them names the ceremony. The same lines print during 'os migrate apply --yes'. This is the card's own second half ('the boot's drift lines, if they share the copy'): they share the differ's message. It is held off here (driver-sql, the drivers lane; PR #24893 touches the file). Seam: renderer: packages/drivers/driver-sql/src/schema-drift.ts DESTRUCTIVE_COLUMN_REMEDY → runtime: the boot schema sync's drift log. Suggested: a sub-issue of #24925 in the drivers lane · dedupe words: schema-drift organization_id allow-destructive boot; boot drift lines before ownership ceremony; serve schema-drift remedy 17.x",
        "class: a · reach: public door 'os migrate apply --yes' (no flag) on the same 17.x copy: exit 0. It prints '⚠ 22 destructive change(s) will be SKIPPED (re-run with --allow-destructive to include them).' before applying and '⚠ Skipped 22 destructive change(s) (re-run with --allow-destructive to include them).' after; that re-run is refused whole by apply's own drop gate (measured exit 1). Its pre-prompt listing (renderPlan with apply limits only) also prints the differ's --allow-destructive remedy on each organization_id drop. Same family as #24925, in apply.ts (held off by the claim). Seam: renderer: packages/cli/src/commands/migrate/apply.ts pre-prompt listing and skipped lines (about :341-366 and the post-apply skipped line) → runtime: the same file's drop gate (about :555-605) · dedupe words: apply skipped re-run allow-destructive ownership ceremony; apply deferred destructive organization_id; re-run with --allow-destructive refused",
        "class: a · reach: public door 'os migrate plan' on the same 17.x copy lists 'sys_comment.reactions: column exists in the database but not in metadata (orphaned) — \"os migrate apply --allow-destructive\" to drop it.'. Then 'os migrate apply --allow-destructive --yes' exits 1 with '✗ Refusing to drop sys_comment.reactions … Run \"os migrate comment-reactions\" to preview …' (measured). The plan never names 'os migrate comment-reactions'. Same family (the plan offers a remedy apply refuses). It is not folded in here: apply's predicate for this drop is inline in apply.ts, with no exported function to share, so the plan would need a second inventory or an apply.ts seam. Seam: renderer: packages/cli/src/commands/migrate/plan.ts → runtime: apply.ts sys_comment.reactions gate (about :502-553) · dedupe words: plan comment-reactions allow-destructive; sys_comment.reactions remedy plan; reactions drop before conversion plan"
      ],
      "files_changed": [
        "packages/cli/src/commands/migrate/plan.ts (+92 −10)",
        "packages/cli/src/commands/migrate/plan.apply-limit.test.ts (+235 −1)",
        ".changeset/24925-plan-names-ownership-ceremony.md (+15, @objectstack/cli patch)"
      ],
      "line_budget": "353 changed lines (+342 −11, 3 files) vs the 3000-line human-merge threshold: under. No governed surface touched (Tier: none). No skills/** or packages/spec/** edit.",
      "deviations": [
        "Zone 3 route: the footer offers --allow-destructive for NO entry while a drop is held, not for 'the rest'. apply's drop gate refuses the whole --allow-destructive run (measured, and integration case 1c), so Zone 2 item 4's own criterion ('only for destructive entries apply would perform') yields zero.",
        "Pins folded into plan.apply-limit.test.ts rather than a new file, so the suite pays one module load. Its harness's stderr spy now runs the write callback, removing a 2 s drain wait per case (both inside the claimed file surface).",
        "origin/main was not merged before opening the PR. 6 commits landed after feb09db14 (to 865d2c8cf4), touching none of packages/cli, packages/drivers/driver-sql or packages/platform-objects/src/system (empty diff stat); the PR's CI runs on the merge ref.",
        "Real-database behaviour (marker read through the composed objectql service, a missing sys_migration table) is measured on 4 SQLite copies, but pinned only at unit level with a fake engine; no integration test was added.",
        "Three gates were re-run on the same head after a first reading of exit 3/3/1 (prerequisite builds, and a transient ENOENT from my own concurrent build). Both readings are recorded in 'gates'.",
        "The harness attribution reminder named a model-bearing Co-Authored-By trailer; commits carry AGENTS.md's model-free pair instead (Claude-Session and Co-authored-by: Claude), and the PR body ends with the session-URL footer."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24954 comment 6298379061 · domain:cli seat 2 · session session_01LmjU2x1faLZ39DVTFyVca6 · 2026-10-20T18:41Z · head 003e41ab5. Before the ceremony verifies, os migrate plan lists the organization_id drops with the ceremony as their remedy and offers --allow-destructive for nothing, reading the drop gate's own functions. The data path was measured held first. Landing waits on every check of the head.

    The rest of the family is filed:


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #24954 → 5f11cec3a (Fixes #24925)

    domain:cli seat 2 · os-justin · session session_01LmjU2x1faLZ39DVTFyVca6 · 2026-10-20T19:27Z

    Reading Result
    PR #24954 Merged through the merge queue (enqueued 2026-10-20T19:03Z, merged 2026-10-20T19:27Z)
    origin/main 5f11cec3a is on it, with a single parent 5d27d326c (squash). 3 files, +342 / −11. Each file's blob equals the ACCEPTed head 003e41ab5's
    Queue branch No gh-readonly-queue/main/*pr-24954* head remains
    This card Closed completed by the PR's Fixes. In the stroke after this comment, pm:dispatched comes off

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:clipriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions