Skip to content

fix(cli): the ceremony's column drops answer not-declared for a table the booted composition declares no object for - #23446

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-23399-flow-dispatch-column-drop
Oct 14, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-23399-flow-dispatch-column-drop

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23399
Clause-②: no

Seat ruling B (6163376799), under claim 6163392076. The measurement is in the round-1 report 6163300422.

What changes

This changes step 5 (column-drops) of os migrate organization-ownership --apply.

  • The port. The columnDrops port in runApply (packages/cli/src/commands/migrate/organization-ownership.ts) now reads the booted stack's declared objects: liveStack.allObjects(), each name passed through StorageNameMapping.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:
  • The step. ColumnDropOutcome (packages/cli/src/utils/organization-ownership-apply.ts) gains not-declared. runColumnDrops counts it into the step's summary.notDeclared and gives it a blocker that names the table and the remedy: re-run the ceremony where the deployment's composition is declared. The existing not-proposed sentence is byte-identical, and it now fires only for a declared object.
  • One changeset: a @objectstack/cli patch.

Why

On main, the ceremony's apply boot declares only what the composition it runs in mounts, and automation is not on PLATFORM_ALWAYS_ON_CAPABILITIES. So in a bare run, or with a config that has no requires: ['automation'], sys_flow_dispatch is 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 holds sys_presence (realtime) and sys_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 answers not-declared for sys_flow_dispatch and drops sys_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 answered not-proposed: notDeclared: 0, and its blocker differs.
  • organization-ownership-not-declared.integration.test.ts, converted from the round-1 probe at b2ce88193f. It runs the real command on a v17-shaped SQLite fixture: a served boot of this tree, then organization_id added to sys_flow_dispatch, and to sys_migration_journal as the in-run control.
    • (i) A config without requires: notDeclared 1, the column kept, sys_migration_journal's column dropped in the same run, exit 1.
    • (ii) requires: ['automation']: the column is dropped, and notDeclared is 0.
    • vitest's per-file duration is 3.96 s at e7e7ad5145 and 4.52 s at e99ada9e83, under the 10 s rule, so it stays its own file.
  • Tier. organization-ownership-apply.test.ts imports better-sqlite3, so vitest-tiers.ts puts it in the integration tier. Both files run under --project integration.

Reverse verification

Both mutations ran at e7e7ad5145 through scripts/ablation-replace.mjs in wrap mode. Each restore was proven: blob equal to HEAD, and an empty git diff HEAD.

  1. The port. Anchor declared.has(rotationBaseOf(table) ?? table) replaced by true, so every unproposed table is classed as declared, which was the old behaviour. Pin (i) went red (expected +0 to be 1, the notDeclared count) and (ii) stayed green.
  2. The step. Anchor if (outcome.outcome === 'not-declared') { replaced by if (false) {. The new unit case went red (notDeclared: +0 vs 1) and the other 17 stayed green.

Local verification

  • Head e99ada9e83 (main 04a314873c merged; the delta against main is these 5 files, +292/−4):
    • full rebuild: 75/75;
    • both pin files: 20/20;
    • pnpm --filter @objectstack/cli typecheck: tsc --noEmit plus check:test-typecheck, green. All four changed TS files are in the tsc program (--listFiles).
    • CLI unit tier (vitest run --project unit): 287/287 files, 4307/4307 tests.
  • Derived gates, dispatch-gates --commands --repo objectstack-ai/objectstack: 65 commands, run at e7e7ad5145, whose diff is identical. The list re-derived at e99ada9e83 is the same.
    • 63 exited 0.
    • check:dual-build-cjs-loads and check:i18n-coverage first exited 3 (PREREQUISITE NOT MET: packages outside the CLI closure were not built). After a full build both exit 0.
    • --ran reconciliation: 65 run, 0 NOT MEASURED.
  • Lint, a proven narrowing at e99ada9e83:
    1. The population is read from ESLint's own config: the four changed TS files each return a result, and the fifth changed path (the changeset) is answered "File ignored because no matching configuration was supplied".
    2. --format json reports 4 files, 0 errors and 0 warnings.
    3. Invariance: eslint.config.mjs enables 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

  • (a) Names agree. The driver keys its managed tables with the same StorageNameMapping.resolveTableName. In pin (i) the four sys_metadata-family tables (declared, not proposed) are classed not-proposed, since notDeclared is exactly 1. No column-drop object rotates on main: only sys_activity does, and its fate is attribution. The datasource does diverge (see the first acceptance note).
  • (b) No conflict with the in-flight branches. git merge-tree of this branch with claude/issue-15211-c7b-plan-digest (bc3525a9cf) and with claude/issue-15211-c7b-promotions (7a9c239658) exits 0 for both. Neither had landed by a7a135dc26, which also merges cleanly with this head.

Acceptance notes


Generated by Claude Code

… (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>
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>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 3 documentable anchor(s).

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/permissions/capabilities.mdx (via os migrate organization-ownership (command, read off packages/cli/src/commands/migrate/organization-ownership.ts))
  • content/docs/permissions/permission-sets.mdx (via os migrate organization-ownership (command, read off packages/cli/src/commands/migrate/organization-ownership.ts))
What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 28 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json a7a135dc267f0d154aca453f098c49ce8aac550c → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 87bb9460b045100f0b9887ce6f2569b8b4df7ced — the merge of head bc922becdd9a3bd53c14f1e1ed7b7f318c2460b6 into base a7a135dc267f0d154aca453f098c49ce8aac550c, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs a7a135dc267f0d154aca453f098c49ce8aac550c → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…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>
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 14, 2026 09:26
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 14, 2026 09:26
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 14, 2026
Merged via the queue into main with commit 4369677 Oct 14, 2026
41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23399-flow-dispatch-column-drop branch October 14, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants