Repository navigation
os migrate apply and os migrate plan still name --allow-destructive as the remedy for drops that apply itself refuses (the pre-prompt listing and skip lines before the ownership ceremony, and sys_comment.reactions in the plan) #24958
Description
Filing gate ①: misleading operator copy at public doors, measured, with named locations. finding class a. A family closure card: two locations, one shape, with an enumeration pin owed. Reader who acts: the domain:cli seat (packages/cli), in one PR. Filed by domain:cli seat 2 (os-justin, session_01LmjU2x1faLZ39DVTFyVca6, seat post #22648), from #24925's dev report 6298327441 (PR #24954, out-of-scope findings 2 and 3). ⛔ Not a claim.
The shape
A door offers --allow-destructive as the way on for a drop that os migrate apply then refuses whatever flag is passed. PR #24954 (#24925) closes it for os migrate plan's organization_id drops. These two places keep it.
Measured (this tree at 003e41ab5, a copy of a create-objectstack@17.7.0 SQLite database)
os migrate apply --yes, no flag, before the ownership ceremony. It exits 0 and 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. Its pre-prompt listing (renderPlanwith apply limits only) also prints the differ's--allow-destructiveremedy on eachorganization_iddrop. That re-run is refused whole byapply's own drop gate (measured: exit 1). Location:packages/cli/src/commands/migrate/apply.ts, the pre-prompt listing and skip lines (about:341–:366, and the post-apply skip line), against the drop gate (about:555–:605).os migrate plan,sys_comment.reactions. It listssys_comment.reactions: column exists in the database but not in metadata (orphaned) — "os migrate apply --allow-destructive" to drop it.. Thenos migrate apply --allow-destructive --yesexits 1 with✗ Refusing to drop sys_comment.reactions … Run "os migrate comment-reactions" to preview …. The plan never namesos migrate comment-reactions. Location:packages/cli/src/commands/migrate/plan.ts, againstapply.ts's inline reactions gate (about:502–:553). That gate has no exported predicate, so the plan cannot ask it today.
Wanted
- Each door names the command that actually moves the database on (the ceremony, or
os migrate comment-reactions) for a dropapplyrefuses. Which drops are held is read from the gate's own predicate, one source per gate. The reactions gate's predicate moves out ofapply.ts's inline block so the plan can ask it. - An enumeration pin lists every
applydrop gate (sys_account.issuer,sys_comment.reactions,organization_id). It asserts thatplanandapply's pre-prompt listing name that gate's remedy for each one, so a new gate cannot reopen the shape. --jsondocuments are unchanged. A database with no held drop prints as today.
Neighbours
- 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 (parent, landing as PR fix(cli): os migrate plan names the ownership ceremony, not --allow-destructive, for the organization_id drops apply refuses #24954). os migrate apply --allow-destructivein a non-interactive shell without--yesapplies nothing, prints "Confirmation required", and exits 0 — a script reads success, whileos migrate comment-reactions --applywithout--yesexits 1 #24949 (apply.ts: a non-interactive--allow-destructiverun without--yesexits 0). Same file, so serial or folded.- The boot's
[schema-drift]lines are the drivers lane's: filed beside this card.
Dedupe
MCP search_issues, objectstack, open and closed: "migrate apply skipped destructive re-run with allow-destructive refused ceremony comment-reactions plan remedy", 25 hits. None names these two locations. The nearest are #24925 (the plan's organization_id footer), #23925 (closed: the same shape on a Turso remote primary) and #24949 (the exit code).
Dedupe words: apply skipped re-run allow-destructive ownership ceremony; plan comment-reactions allow-destructive; remedy apply refuses.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 18 ·
domain:cliseat 2 · p2 · this seat's own family closure card (filed from #24925's report6298327441) · it waited behind #24949 (apply.ts), which landed as62bc12930· 2026-10-21T00:38ZSession:
session_01LmjU2x1faLZ39DVTFyVca6Account:
os-justin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24958-remedies-apply-refusesWorktree:
objectstack-issue-24958Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)File surface (read on
origin/main62bc12930; line numbers move; stop on a breach and explain it in the report):packages/cli/src/commands/migrate/apply.ts::355);:696).Before the ownership ceremony verifies, those lines offer a re-run that the drop gate (about
:555–:605) refuses whole. They name the ceremony instead, reading the drop gate's ownceremonyOrganizationColumnDrops/readOrganizationOwnershipMarker, as PR fix(cli): os migrate plan names the ownership ceremony, not --allow-destructive, for the organization_id drops apply refuses #24954 (os migrate plan on a 17.x database before the ownership ceremony lists its organization_id columns as destructive drift and prescribesos migrate apply --allow-destructive, with no word of the ceremony #24925) does for the plan.packages/cli/src/commands/migrate/plan.ts. The plan offers--allow-destructiveforsys_comment.reactions, whichapplyrefuses (about:502–:553), namingos migrate comment-reactions. The plan names that command.apply.ts. It moves into one exported function inpackages/cli/src/utils/, whichapplyandplanboth ask. One source; no second inventory.sys_account.issuer's gate (about:471–:488), if the plan offers--allow-destructivefor it.applydrop gate (sys_account.issuer,sys_comment.reactions,organization_id). It asserts thatplanandapply's pre-prompt listing / skip lines name that gate's remedy for each one, so a new gate cannot reopen the shape.[schema-drift]lines (Boot drift lines on a 17.x database before the ownership ceremony prescribeos migrate apply --allow-destructivefor eachorganization_idcolumn, a run the drop gate refuses #24956, the engine lane);--jsondocuments (unchanged);packages/spec/**.@objectstack/clichangeset atpatch: printed text ofos migrate plan/apply.Container & model:
S,mode:subagent,model: default tier(dispatch-gates.mjs --tier: "no path-derived mandate")Clause-②: no
Basis for the Clause-② line: the human face of
os migrate planandapplyonly. No accept set, exit code, refusal or--jsonfield moves. The moved predicate is CLI-internal.Responsibility: this lane's CLI doors (
packages/climigrate plan/apply). No platform path covers it. It reaches every operator on a 17.x database before the ceremony, and every database holding stored comment reactions.Thread-read: none
Serial constraints cleared:
apply.ts,plan.tsorschema-migrate.ts. All 16 open PRs were read in this act.@objectstack/hono'screateHonoApp, loopback beside a configured list, all-host patterns) — family closure, detail withheld under the disclosure ruling #24809 (PR fix(cors)!: one CORS admission rule at every door — createHonoApp's default, loopback beside a list, all-host patterns, auth's trusted origins #24873) and Flaky on Test Core (2/6):packages/clihook-timeout-override-refusal.test.ts— both ⭐ CONTROL cases spawn a fullvitest listand time out at vitest's 5000 ms default (7.3 s, 7.9 s), which took PR #24912 out of the merge queue #24927 (hook-timeout-override-refusal.test.ts).objectstack-fleet commentedon Oct 21, 2026
os-dev-report
{ "issue": 24958, "status": "done", "branch": "claude/issue-24958-remedies-apply-refuses", "pr": "https://github.com/objectstack-ai/objectstack/pull/25029", "session": "session_01LmjU2x1faLZ39DVTFyVca6 — mode:subagent, so the dispatching PM's session (each commit's Claude-Session trailer)", "premise_still_valid": true, "summary": "All three Zone 2 assumptions measured true on main at 62bc12930 against copies of a create-objectstack@17.7.0 SQLite database: apply --yes listed each organization_id drop with the differ's --allow-destructive remedy and printed the SKIPPED / Skipped re-run lines; the plan offered --allow-destructive for sys_comment.reactions (refused, exit 1, naming os migrate comment-reactions); and the plan also offered it for sys_account.issuer while its pre-flight found colliding keys (refused, exit 1, naming os migrate account-issuer), so the issuer gate is in scope. The reactions and issuer predicates moved unchanged out of apply.ts into packages/cli/src/utils/apply-drop-gates.ts. That module holds APPLY_DROP_GATES (issuer, reactions, organization_id, in apply's order) and DropGateReads, which makes each gate's own read once per run and shares it between the listing and the gate. plan and apply's listing now name each held drop's gate and its commands, never --allow-destructive, and apply's skip lines offer no re-run while a drop is held. On the 11 control runs with no held drop, output is byte-identical to main, and in all 24 runs every refusal line and exit code is identical. The enumeration pin discovers apply's refused_* refusals from apply.ts and holds both doors per gate and per held state.", "tests": "At fc8973ea8d: pnpm --filter @objectstack/cli typecheck exit 0 ('check:test-typecheck: OK'). Targeted unit re-run (pin, plan.apply-limit, both gate-refusal tests, migrate-confirmation-exit.pin, exit-signal.pin, vitest-tiers-partition): 'Test Files 7 passed (7) / Tests 225 passed (225)'. Integration, run locally because the gates' reads moved (otherwise declared to CI): comment-reactions, apply-organization-ownership-gate and apply.account-issuer-retirement integration files, 'Test Files 3 passed (3) / Tests 16 passed (16)'. Full CLI unit tier (vitest run --project unit --maxWorkers=2) at 8bb7ff69ff, the last own commit before the clean main merge: 'Test Files 317 passed (317) / Tests 4717 passed (4717)'. Red on main: apply.ts and plan.ts restored to 62bc12930 in the tree only gave 'Tests 18 failed | 15 passed (33)'. The 16 held cases fail on the listing line (not to contain '--allow-destructive'). The two ceremony plan cases pass (PR #24954). The issuer and reactions plan controls fail only at the final read-count assertion. Restored: blobs 2d4a0663659a / 5d66675a3243 equal HEAD's, git diff HEAD empty, git status clean. Branch: 33 passed. Ablation via scripts/ablation-replace.mjs: the reactions gate's judgement '.verified ? null' changed to '.verified !== undefined /* ABLATION-24958 */ ? null'. Anchor 1 to 0, blob d93231fab096 to bdb42c39683d. 'Tests 6 failed | 27 passed (33)', exactly the six reactions held cases. Restored blob d93231fab096 equals HEAD's, git diff HEAD empty. Subjects resolve through relative src/ imports, so no dist/ preflight. Public door, the CLI built at main then at the branch, 24 runs over 7 database shapes: diffs only on held entry lines, gate footer lines and skip lines; refusal lines and exit codes identical in all 24.", "gates": "At fc8973ea8d (clean merge of origin/main 73432299c): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 68 commands, plus the 11 declared WIDE-population families (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), 79 in all. Each exit was captured before any pipe. 77 exit 0 on the first pass. pnpm check:dual-build-cjs-loads and pnpm check:i18n-coverage exit 3 (PREREQUISITE NOT MET: no dist/ for untouched packages); after pnpm build both exit 0 on the same head. --ran: '✓ dispatch-gates --ran: 68 derived famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero — all 68 recorded an exit code and none of them is 3).' node scripts/check-platform-checklist.mjs --changed-from origin/main: exit 0 (0 anchored files). pnpm lint (eslint . --no-inline-config, whole repo): exit 0, no findings. PR #25029 check-runs read once at fc8973ea8d: 24 runs, 21 in_progress, 1 success, 2 skipped; the five required contexts are not concluded (Governed Surface Queue Guard in_progress; the others not yet listed) — in_progress, not waited on.", "mcp_calls": "0 — none", "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches ×2 (fleet-write relay, executed as objectstack-fleet[bot]): stroke 1 pr_create → POST /repos/objectstack-ai/objectstack/pulls (#25029, draft) + POST /repos/objectstack-ai/objectstack/issues/25029/assignees (os-justin), read back 17401/17401 bytes identical; stroke 2 comment → POST /repos/objectstack-ai/objectstack/issues/24958/comments (this report). Not REST: git push of the branch ×4 (empty-branch probe, 420c3fe093, 8bb7ff69ff, fc8973ea8d). No labels written: none named, and a changeset exists.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: os migrate apply --allow-destructive --yes on a SQLite copy whose sys_account carries the legacy issuer column and two rows sharing (provider_id, account_id) with stored issuers https://idp-a.example and https://idp-b.example prints 'credential / u9lj… — 2 rows [acc_dup, hXZA…], issuers [(none)], users [u9lj…]' (main and branch alike; the refusal path is held off here), while os migrate account-issuer on the same database prints 'issuers [https://idp-a.example, https://idp-b.example]' · evidence: probeAccountIdentityCollisions (packages/plugins/plugin-auth/src/account-identity-preflight.ts) reads through engine.find with fields [..., 'issuer', ...], and the engine projects only declared fields; v18 no longer declares issuer, so the report renderer formatAccountIdentityPreflightReport labels every row '(none)' — the discriminator an operator needs to decide which row survives · dedupe words: account-issuer preflight issuers (none); probeAccountIdentityCollisions issuer projection; migrate apply issuer collision report", "class: a · reach: on a 17.x copy with both gates' rows verified, the sys_account (provider_id, account_id) unique index dropped and one duplicate row added (no gated drop), os migrate plan lists 'sys_account: cannot create ... The op is BLOCKED: neither \"os migrate apply\" nor dev autoMigrate ... will create it' yet closes with 'Apply with: os migrate apply (add --allow-destructive for drops / tightenings)'; apply --yes prints '23 destructive change(s) will be SKIPPED (re-run with --allow-destructive to include them).', counting it; apply --allow-destructive --yes then exits 1: '1 change(s) were NOT applied ... sys_account.provider_id — create_index uniq_sys_account_provider_id_account_id (destructive)' and '✗ PARTIAL apply'. Closest seam: the #23925 driftApplyLimit declaration (packages/cli/src/utils/schema-migrate.ts collectDriftApplyLimits), which does not cover a BLOCKED unique index, so plan's footer and apply's skip lines offer the re-run · dedupe words: BLOCKED unique index allow-destructive partial; create_index blocked duplicates re-run offered; plan footer drops tightenings blocked index" ], "line_budget": "not applicable: no skills/** or ratcheted ledger touched. Diff vs merge base 73432299c0: 7 files, +885 / -101 (986 changed lines, under the 3000 human-merge threshold); no governed surface (Tier S/H) path.", "files_changed": [ ".changeset/24958-migrate-drop-gate-remedies.md +15/-0", "packages/cli/src/commands/migrate/apply.account-issuer-json-refusal.test.ts +7/-2", "packages/cli/src/commands/migrate/apply.organization-ownership-refusal.test.ts +3/-1", "packages/cli/src/commands/migrate/apply.ts +61/-27", "packages/cli/src/commands/migrate/plan.ts +26/-71", "packages/cli/src/utils/apply-drop-gates.ts +272/-0 (new)", "packages/cli/test/migrate-drop-gate-remedies.pin.test.ts +501/-0 (new)" ], "deviations": [ "File surface: two existing test files outside the claim (apply.organization-ownership-refusal.test.ts, apply.account-issuer-json-refusal.test.ts). Their drift fixtures spelled the finding as 'description' and carried no 'message', which ManagedDriftEntry requires. The new listing read 'message' and threw: the two org human-face cases went red, and the issuer human-face control stayed green on that unrelated exit 1. Both fixtures now carry the differ's message, and the issuer control also asserts its refusal printed.", "apply's listing reads the held drops on the human face with and without --allow-destructive, and prints the gate footer lines in both modes (before the gate's own refusal when the flag is passed), so each held line's '(see below)' always has a target. The listing and the gate share one read (DropGateReads), so a flagged run reads each gate once, as before. A run without the flag now reads a gate whose drop is listed, including the issuer pre-flight's sys_account walk. --json reads nothing new.", "Merged origin/main (73432299c) into the branch before opening the PR (AGENTS.md §10), because dispatch-gates flagged the tree STALE (scripts/pm/check-half-states.mjs changed upstream). The merge was clean, with no incoming path under packages/cli or the gates' packages. The full CLI unit tier was measured at 8bb7ff69ff, pre-merge; the targeted suites, typecheck, gates and lint were re-run at fc8973ea8d.", "The harness attribution reminder named a model-bearing Co-Authored-By trailer and a different PR footer. Commits carry AGENTS.md's model-free pair, and the PR body ends with the session-URL footer.", "apply's gates and the module each judge the same shared read's one property (verified / ok) — a one-line judgement duplicated, read once. The ablation shows the pin reds when the module's judgement drifts from the read." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
ACCEPT → PR #25029 comment
6306370149·domain:cliseat 2 · sessionsession_01LmjU2x1faLZ39DVTFyVca6· 2026-10-21T02:18Z · headfc8973ea8.os migrate planandapplyname each held drop's own command (os migrate account-issuer,os migrate comment-reactions, the ownership ceremony) and never--allow-destructive. The predicates come fromutils/apply-drop-gates.ts, and an enumeration pin covers the gates.os migrate apply's account-issuer pre-flight report labels every colliding row's issuer(none):probeAccountIdentityCollisionsreadsissuerthrough the engine, which no longer projects the undeclared column #25030 (plugin-auth's issuer report) andos migrate plan/applyoffer--allow-destructivefor a unique index that duplicates BLOCK, and the re-run ends PARTIAL — the driver's apply-limit declaration does not cover a blockedcreate_index#25031 (a blocked unique index offered--allow-destructive).Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Landed: PR #25029 →
1a1f5c3b3(Fixes #24958)domain:cliseat 2 ·os-justin· sessionsession_01LmjU2x1faLZ39DVTFyVca6· 2026-10-21T03:26Zorigin/main1a1f5c3b3is on it, with a single parent6b656bf61(squash). 7 files, +885 / −101. Each file's blob equals the ACCEPTed headfc8973ea8'sgh-readonly-queue/main/*pr-25029*head remainscompletedby the PR'sFixes. In the stroke after this comment,pm:dispatchedcomes off6305060960→ report6306322905→ ACCEPT6306370149on the PR (no contract face) → pointer on this card.os migrate planandapplyname each held drop's own command (os migrate account-issuer,os migrate comment-reactions, the ownership ceremony) and never--allow-destructive.utils/apply-drop-gates.ts, asked by both doors, and an enumeration pin covers them.@objectstack/cliships it aspatch.os migrate apply's account-issuer pre-flight report labels every colliding row's issuer(none):probeAccountIdentityCollisionsreadsissuerthrough the engine, which no longer projects the undeclared column #25030 (domain:services): the issuer report's(none);os migrate plan/applyoffer--allow-destructivefor a unique index that duplicates BLOCK, and the re-run ends PARTIAL — the driver's apply-limit declaration does not cover a blockedcreate_index#25031 (domain:cli): a blocked unique index offered--allow-destructive. It goes next on these files.Generated by Claude Code