Repository navigation
fix(cli): the ceremony's column drops answer not-declared for a table the booted composition declares no object for - #23446
Conversation
… (measurement) A measurement-first probe: boots the organization-ownership ceremony's own composition against a v17-shaped SQLite fixture and reads whether sys_flow_dispatch is declared and whether its organization_id drop is proposed, per composition, with sys_job as the control. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
The probe's readings are in the report; its source stays in this branch's history at the previous commit. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
… the booted composition declares no object for os migrate organization-ownership --apply drops organization_id only through the schema-drift ceremony, so only on an object its boot declares. Run in a composition that does not mount an object's provider (sys_flow_dispatch without requires automation, sys_presence without realtime, sys_audit_log without the auth family), the step blamed the release for a column the composition simply does not declare. The port now answers not-declared for such a table, and the step counts it into summary.notDeclared and names the remedy. The not-proposed blocker is unchanged and fires only for a declared object. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
…ow-dispatch-column-drop
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 28 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 87bb9460b045100f0b9887ce6f2569b8b4df7ced && git checkout 87bb9460b045100f0b9887ce6f2569b8b4df7ced
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a7a135dc267f0d154aca453f098c49ce8aac550c bc922becdd9a3bd53c14f1e1ed7b7f318c2460b6 && git checkout -B drift-repro a7a135dc267f0d154aca453f098c49ce8aac550c && git merge --no-ff bc922becdd9a3bd53c14f1e1ed7b7f318c2460b6
node scripts/docs-audit/affected-docs.mjs --json a7a135dc267f0d154aca453f098c49ce8aac550c
|
…the checkpoint A not-declared table always blocks the column-drops step, and the checkpoint records a step's summary only once the step is done. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
Fixes #23399
Clause-②: no
Seat ruling B (
6163376799), under claim6163392076. The measurement is in the round-1 report6163300422.What changes
This changes step 5 (
column-drops) ofos migrate organization-ownership --apply.columnDropsport inrunApply(packages/cli/src/commands/migrate/organization-ownership.ts) now reads the booted stack's declared objects:liveStack.allObjects(), each name passed throughStorageNameMapping.resolveTableName, which is the key the SQL driver gives its managed set. For a due table that the drift differ proposed no drop for, it answers:not-declaredwhen that set does not hold the table. A rotation shard is judged by its base, throughrotationBaseOf.not-proposedonly when the set does hold it.runApplyalready loads its other modules. That keeps the diff out of the import block, which the in-flight feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211 branches also edit.ColumnDropOutcome(packages/cli/src/utils/organization-ownership-apply.ts) gainsnot-declared.runColumnDropscounts it into the step'ssummary.notDeclaredand gives it a blocker that names the table and the remedy: re-run the ceremony where the deployment's composition is declared. The existingnot-proposedsentence is byte-identical, and it now fires only for a declared object.@objectstack/clipatch.Why
On
main, the ceremony's apply boot declares only what the composition it runs in mounts, andautomationis not onPLATFORM_ALWAYS_ON_CAPABILITIES. So in a bare run, or with a config that has norequires: ['automation'],sys_flow_dispatchis not declared and the differ proposes nothing. The step then told the operator that "the object this runtime declares still carries the column … Run the ceremony with the release whose declaration drops it". That is false: the object is not declared, and this release drops the column wherever it is declared. The same mechanism holdssys_presence(realtime) andsys_audit_log(the auth family). B covers all three, and any host object, by construction.Pins
organization-ownership-apply.test.ts, one new case. The port answersnot-declaredforsys_flow_dispatchand dropssys_job. Expected: step blocked, summary{ dropped: 1, due: 2, notDeclared: 1 }, and the column kept. The control is the resumed run, with the same table answerednot-proposed:notDeclared: 0, and its blocker differs.organization-ownership-not-declared.integration.test.ts, converted from the round-1 probe atb2ce88193f. It runs the real command on a v17-shaped SQLite fixture: a served boot of this tree, thenorganization_idadded tosys_flow_dispatch, and tosys_migration_journalas the in-run control.requires:notDeclared1, the column kept,sys_migration_journal's column dropped in the same run, exit 1.requires: ['automation']: the column is dropped, andnotDeclaredis 0.e7e7ad5145and 4.52 s ate99ada9e83, under the 10 s rule, so it stays its own file.organization-ownership-apply.test.tsimportsbetter-sqlite3, sovitest-tiers.tsputs it in the integration tier. Both files run under--project integration.Reverse verification
Both mutations ran at
e7e7ad5145throughscripts/ablation-replace.mjsin wrap mode. Each restore was proven: blob equal to HEAD, and an emptygit diff HEAD.declared.has(rotationBaseOf(table) ?? table)replaced bytrue, so every unproposed table is classed as declared, which was the old behaviour. Pin (i) went red (expected +0 to be 1, thenotDeclaredcount) and (ii) stayed green.if (outcome.outcome === 'not-declared') {replaced byif (false) {. The new unit case went red (notDeclared: +0vs1) and the other 17 stayed green.Local verification
e99ada9e83(main04a314873cmerged; the delta againstmainis these 5 files, +292/−4):pnpm --filter @objectstack/cli typecheck:tsc --noEmitpluscheck:test-typecheck, green. All four changed TS files are in thetscprogram (--listFiles).vitest run --project unit): 287/287 files, 4307/4307 tests.dispatch-gates --commands --repo objectstack-ai/objectstack: 65 commands, run ate7e7ad5145, whose diff is identical. The list re-derived ate99ada9e83is the same.check:dual-build-cjs-loadsandcheck:i18n-coveragefirst exited 3 (PREREQUISITE NOT MET: packages outside the CLI closure were not built). After a full build both exit 0.--ranreconciliation: 65 run, 0 NOT MEASURED.e99ada9e83:--format jsonreports 4 files, 0 errors and 0 warnings.eslint.config.mjsenables no type-aware linting, and its file inputs (eslint.config.mjs,scripts/slot-lookup-baseline.json,scripts/query-options-erasure-baseline.json) are unchanged by this diff. So no untouched file's verdict can move.The seat's hypotheses, measured
StorageNameMapping.resolveTableName. In pin (i) the foursys_metadata-family tables (declared, not proposed) are classednot-proposed, sincenotDeclaredis exactly 1. No column-drop object rotates onmain: onlysys_activitydoes, and its fate is attribution. The datasource does diverge (see the first acceptance note).git merge-treeof this branch withclaude/issue-15211-c7b-plan-digest(bc3525a9cf) and withclaude/issue-15211-c7b-promotions(7a9c239658) exits 0 for both. Neither had landed bya7a135dc26, which also merges cleanly with this head.Acceptance notes
OS_TELEMETRY_DBset for the ceremony run andrequires: ['automation'],sys_flow_dispatchis declared, so the port saysnot-proposed. But its drift is read on the telemetry sibling, where the v17 table does not exist. The column stays, and thenot-proposedsentence is false there too. Whether a real v17 database keeps that table in the primary or in the sibling under that posture was not established.not-declaredby construction; this is an inference and was not measured.sys_metadatafamily, and feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211 remains open. feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211 S3 will later edit this same port (permission_set_id).Generated by Claude Code