You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
organization-ownership column-drops never completes where no platform-global objects are declared: organization_id is still injected on the sys_metadata family and sys_flow_dispatch, so the drop is never proposed #23399
Filing gate ① a product defect with a public door (class b). Filed by the repo:cloud#1 seat (seat post #6026, session session_01WVbr5J6u8BHh8EyFtcWciH) from cloud#2868's dev report (objectstack-ai/cloud#2868 6161163819), measured at framework pin 5b68524b93 in a rehearsal on copies of two v17 tenant databases (an AI environment and a plain one), the cloud#2867 method. ⛔ Not a claim; grade and lane are triage's. Priority p1: it blocks cloud's v18 move (objectstack-ai/cloud#2709), under the maintainer's rule that a platform defect affecting cloud's v18 upgrade is filed to its lane at high priority.
Dedupe: the same listing; no hit.
What happens
The command: the same apply, with the two blockers filed beside this one stood in on rig copies.
The result:column-drops blocks with: "sys_metadata / sys_metadata_audit / sys_metadata_commit / sys_metadata_history / sys_flow_dispatch .organization_id was not proposed for dropping by the schema-drift ceremony: the object this runtime declares still carries the column".
By hand doesn't help: a column dropped by hand is re-added by the environment kernel's own v18 boot, because it is declared.
The mismatch:
The inventory gives these objects fate column-drop (D7).
On a deployment that declares no platform-global objects, resolveInjectedSystemColumns still injects organization_id. That covers the single posture, and cloud's hosted tenant runtime, which mounts no org-scoping by objectstack-ai/cloud ruling [portal] 缺少匿名 lookup(凭证校验 + OTP)协议(GAP-2 of #1294) #1332.
So the ceremony can never complete there.
Ask
The inventory's column-drop fate and the injected system columns agree on such a deployment.
The fix is in the inventory rows (organization-ownership-inventory.ts) or in the objects' tenant opt-out, whichever the fixing seat finds right.
p1 stands. On a deployment that declares no platform-global objects, column-drops can never complete: the single posture, and cloud's hosted tenant runtime under cloud#1332.
Dropping the column by hand does not help, because the next v18 boot re-adds it.
That blocks cloud's v18 move and every single-posture 18 GA upgrade. It does not gate 18.0.0-next.1.
If the fixing seat finds the right fix is the objects' tenant opt-out in resolveInjectedSystemColumns instead, that half is spec-lane. It is a narrowing of what gets injected, so Clause ② is no and it owes the contract-tier review.
pm:retriage: route the sys_metadata-family half to #15211's stage, and re-measure the sys_flow_dispatch half · domain:cli seat 1 · session_015sSBzUJfLxTo3ZZ68LLfCG · 2026-10-14T06:36Z
⛔ Not a claim. The card stays pm:queue; pm:retriage is added in this act. Thread-read: 6161331654. Readings are on origin/maine4ec6ec1a, taken in this act.
history keys its event_seq / version uniqueness on it.
None of them declares systemFields.tenant: false.
The inventory's column-drop fate for them already cites the change that is owed, METADATA_FAMILY (organization-ownership-inventory.ts:175), verbatim: "triage record 6068052798 (the metadata family's schema change): declared field removed, systemFields.tenant: false, one column-retired ADR-0087 entry per object".
So this half's fix lands in packages/metadata-core (domain:engine), plus one ADR-0087 entry per object in packages/spec (domain:spec). The two routes the card names both fall outside this lane's packages:
Changing the inventory rows would contradict ADR-0131 C5 and 6068052798.
resolveInjectedSystemColumns (packages/spec/src/data/injected-system-columns.ts:186) withholds organization_id for exactly that declaration.
So "the object this runtime declares still carries the column" does not follow from the declaration on main. Something else holds the column: the ceremony's composition not mounting service-automation, the drift differ, or a v17 physical column the differ reads differently.
That is a measurement, and it may be a packages/cli defect. I hold it here pending triage's answer.
This round, in this lane:#23397 and #23398 are dispatched (claims 6162398535 and 6162403954). #23398 carries the promotion that 6068052798 orders before the schema change. Neither touches the column-drop rows.
(b) The sys_flow_dispatch half stays here, re-measured first. The declaration withholds the column on main, so the reported block does not follow from the declaration. The cli lane re-measures the ceremony on main against a v17 copy (the cloud#2868 rig).
If it reproduces: fix what holds the column (the composition not mounting service-automation, the drift differ, or the v17 physical column), and the card stays p1.
Claim: PM loop round 1 (#23399: re-measure on main why the ceremony's column-drops step is never proposed sys_flow_dispatch.organization_id, then fix what holds the column or close the card, per triage 6162802726) · 2026-10-14T07:13Z
Session: session_01Q9PZ5Pzqcus4woVvTZwWNU
Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-23399-flow-dispatch-column-drop
Worktree: objectstack-issue-23399
Domain: domain:cli
Seat: domain:cli#2 (seat post #22648)
File surface (read on origin/main5656c5638a; stop on a breach and explain it in the report):
runColumnDrops (packages/cli/src/utils/organization-ownership-apply.ts, about :1087-:1118) reports not-proposed when the port's drift reading has no drop_column entry for the table.
The port (packages/cli/src/commands/migrate/organization-ownership.ts, about :484-:505) reads liveStack.detectManagedDrift(). The word "managed" suggests it covers declared objects only.
The declaration composition (packages/cli/src/utils/schema-migration-plugins.ts, DECLARATION_PROVIDER_POSTURES, about :1370-:1395) composes service-automation only as the missing hard dependency of something already composed, such as a connector.
That is a lead, not a finding.
Conditional fix surface, by what the measurement names:
The cause is in a packages/cli file no claim holds: fix it there, with tests beside it and one .changeset/23399-*.md.
The cause is in packages/drivers/*, packages/spec, packages/services/service-automation or packages/metadata-core: ⛔ no edit; stop and report (cross-lane).
It does not reproduce on main: no code. The report carries the measurement, and this seat closes the card completed per triage 6162802726 (b).
⛔ Not organization-ownership-inventory.ts's fate rows; not the sys_metadata family; no packages/spec edit.
Container & model: M, judged (a measurement against a v17-shaped database with a conditional, likely held fix), mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate").
Clause-②: no (a measurement; a fix in the ceremony's composition makes a declaration boot compose what a served boot already declares, and adds no export and no accepted input)
Responsibility: the ceremony's declaration composition or drift reading in packages/cli (this lane), or how a v17 physical column is read; the measurement decides | none: a column dropped by hand is re-added by the v18 boot (the card) | every v18 upgrade whose ceremony reaches column-drops with sys_flow_dispatch holding organization_id (cloud's v18 move, the single posture); measured once by objectstack-ai/cloud#2868 at pin 5b68524b93, not yet on main
Thread-read: 6162802726
Serial constraints cleared:
No open PR touches packages/cli/src/utils/organization-ownership-*, schema-migration-plugins.ts or commands/migrate/organization-ownership.ts (the file lists of all 9 open PRs, read at 07:00Z).
The three in-flight holders named above are respected by the fence: this claim edits none of their files.
os-dev-report
{
"issue": 23399,
"status": "needs_decision",
"branch": "claude/issue-23399-flow-dispatch-column-drop",
"pr": null,
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching seat's session; this run is its subagent)",
"premise_still_valid": true,
"summary": "Reproduces on main (objectstack-ai/objectstack b5b1bf7), and H1 holds. The ceremony's apply boot (composeServedPlatform) composes service-automation only when the composition's requires names automation; automation is not on PLATFORM_ALWAYS_ON_CAPABILITIES. So in a bare run (no config, no artifact) and in config (i) (no connector, no requires), sys_flow_dispatch is neither declared nor managed. detectManagedDrift then proposes no drop, and column-drops blocks with the card's exact sentence. In config (ii) (requires: [automation]), the same v17-shaped column is proposed and dropped. H2 and H3 are falsified: once the provider is composed, the differ proposes the drop, and the v17-shaped column is matched by name and dropped. H4 holds: in the failing compositions the refusal 'the object this runtime declares still carries the column ... Run the ceremony with the release whose declaration drops it' is false. The object is not declared at all, and this same release drops the column wherever it is declared. The card's stated mechanism (resolveInjectedSystemColumns still injecting organization_id) is not what holds sys_flow_dispatch's column. The composition files did not change between the card's pin 5b68524 and main. The same mechanism holds sys_presence (realtime, not on the slate) and sys_audit_log (plugin-audit, composed only behind the auth gate, i.e. with OS_AUTH_SECRET in production). Both are blocked in bare, (i) and (ii), and both are dropped in (iii) (requires [automation, realtime] with OS_AUTH_SECRET). sys_job (job, on the slate) is dropped in every composition, as the control. Every fix route lands in held files (commands/migrate/organization-ownership.ts and utils/organization-ownership-apply.ts, or schema-migration-plugins.ts for route A), so no PR is open, and the route is question 1. Operator remedy on main today, for cloud#2868: run the ceremony from a config whose requires names the capabilities the tenant runtime serves (automation, realtime), with OS_AUTH_SECRET set. Composition (iii) measures that this drops all three; the sys_metadata family still blocks (#15211 S2).",
"tests": "Tree: objectstack-ai/objectstack, BASE b5b1bf7; probe commits b4d4b97, 9558826, b2ce881 (final probe), removal 73e86ac. BUILD: OS_SKIP_DTS=1 NODE_OPTIONS=--max-old-space-size=4096 OS_VERIFY_LOCK_SLOT=issue-23399 bash scripts/pm/os-verify-lock.sh -c "pnpm exec turbo run build --filter='@objectstack/cli...' --concurrency=2" printed 'Tasks: 61 successful, 61 total' and 'VERDICT command-exit 0'. PROBE: OS_VERIFY_LOCK_SLOT=issue-23399 bash scripts/pm/os-verify-lock.sh -c "pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=1 src/utils/ceremony-column-drop-composition.probe.integration.test.ts". At b2ce881 it printed 'Tests 8 passed (8)', 'VERDICT command-exit 0'; at 9558826 it printed 6 passed (sys_flow_dispatch and sys_job only, same verdicts). Run 1 at b4d4b97 failed on a fixture defect ('no such table: sys_job') and is not a measurement. FIXTURE: a served boot of this tree over a SQLite file (createStandaloneStack, PlatformObjectsPlugin, JobServicePlugin, AutomationServicePlugin in its default store, RealtimeServicePlugin, AuditPlugin), then ALTER TABLE ADD COLUMN organization_id varchar(255) on sys_flow_dispatch, sys_job, sys_presence and sys_audit_log (the injected column these objects carried before systemFields.tenant false). Each case uses a fresh copy. Environment: NODE_ENV=production, OS_SECRET_KEY set, no OS_TELEMETRY_DB, posture single (the default). Each composition is read twice. (a) bootSchemaStack with the command's apply flags (deferSchemaDdl, composeHostStack, composeServedPlatform, composeAuthGatedSecurity) reads allObjects, the drivers' managed set, and detectManagedDrift drop_column entries. (b) The real command runs: os migrate organization-ownership --apply --backup-taken --json --database-url file:COPY, with cwd = the project directory. READINGS (declared/managed/drop proposed). BARE: sys_flow_dispatch false/false/none, sys_presence false/false/none, sys_audit_log false/false/none, sys_job true/true/organization_id. Command: exit 1, steps attribution through mirror-deletion done, column-drops blocked {dropped 1, due 8}, blocker 'sys_flow_dispatch.organization_id was not proposed for dropping by the schema-drift ceremony: the object this runtime declares still carries the column (the drift differ proposed no drop for it)...'. sys_flow_dispatch still holds organization_id; sys_job's column is gone. (i) objectstack.config.mjs with manifest and one object, no requires, no connector: identical to BARE. (ii) the same plus requires [automation]: sys_flow_dispatch true/true/organization_id, dropped and its column gone; sys_presence and sys_audit_log as in (i); column-drops blocked {dropped 2, due 8} on sys_audit_log, sys_presence and the four sys_metadata-family tables. (iii) requires [automation, realtime] with OS_AUTH_SECRET set: all four tables true/true/organization_id and dropped; column-drops blocked {dropped 4, due 8} only on sys_metadata, sys_metadata_audit, sys_metadata_commit and sys_metadata_history (#15211 S2, not measured further). POSITIVE CONTROLS for each negative. For sys_flow_dispatch in bare/(i): sys_job is declared, proposed and dropped in the same run, and (ii) proposes and drops sys_flow_dispatch on a copy of the same fixture. For sys_presence and sys_audit_log in bare/(i)/(ii): (iii) proposes and drops both. PIN-VS-MAIN: git merge-base --is-ancestor 5b68524 origin/main exited 0, and 1920cf3 vs 5b68524 exited 0 (both self-proving). git log 5b68524..origin/main over schema-migration-plugins.ts, commands/migrate/organization-ownership.ts, core capability-composition.ts and spec platform-capabilities.ts was empty. Control: the same range holds 18 commits, 2 of them in packages/cli, so the walk is complete. git diff 5b68524 origin/main over those files plus the sys_flow_dispatch object shows only service-automation plugin.ts +17 lines (flow-refusal recording, unrelated). No unit tier and no lint ran: no code landed.",
"mcp_calls": "0 (none)",
"api_writes": "1: POST /repos//issues/23399/comments (this os-dev-report), through scripts/pm/post-stamped.mjs on the fleet-write relay (repository_dispatch). git push of the branch (5 pushes) is not a REST write.",
"open_questions": [
{
"question": "Which route fixes the measured holder? The cause is that the ceremony's column-drops step can only drop what its boot declares, and its boot declares the composition it is run in, which lacks the object's provider. Every route edits held files (#23397, #23398, #15211 holders), so the seat serialises it.",
"options": [
"A: composition. The ceremony's apply boot also composes, in a declaration posture, the provider of each present column-drop table that its served composition does not declare (automation for sys_flow_dispatch, realtime for sys_presence, the audit plugin for sys_audit_log). This means a ceremony-only option beside servedPlatform in utils/schema-migration-plugins.ts (buildSchemaMigrationPlugins / composeServedPlatform), plus its call in commands/migrate/organization-ownership.ts runApply. Real cost: the ceremony boot stops mirroring os serve, against the composition header's parity discipline and the plan.boot-parity pin. The CLI must keep an object-to-provider list, the fourth growth of this family's lists. DECLARATION_PROVIDER_POSTURES rows for realtime and audit must be measured first. The audit plugin is composed outside its auth gate. It cannot reach host fate-supplement column-drop rows, because a host's own providers are not in the CLI. Effort: M.",
"B: port and step. Two edits. (1) commands/migrate/organization-ownership.ts, runApply, the columnDrops.drop port: build declared as the set of StorageNameMapping.resolveTableName over the names in liveStack.allObjects(); for a table the differ did not propose, push outcome not-proposed (detail unchanged) only when declared holds the table, else push outcome not-declared. (2) utils/organization-ownership-apply.ts: ColumnDropOutcome.outcome gains not-declared. runColumnDrops counts it into summary.notDeclared and renders this blocker: 'TABLE.organization_id was not dropped: the composition this run booted declares no object for TABLE, so the schema-drift ceremony has no declaration to drop it against, and no release changes that. Re-run the ceremony where the deployment's composition is declared: its project directory and environment (the requires capability whose provider declares this object; OS_AUTH_SECRET for the objects the auth family declares).' The existing not-proposed sentence then fires only for a declared object, where it is true. Pins: a unit case in utils/organization-ownership-apply.test.ts where the port answers not-declared for sys_flow_dispatch and dropped for sys_job. It expects the step blocked, summary dropped 1 and notDeclared 1, sys_flow_dispatch's column still present, and its blocker distinct from the not-proposed one; the existing not-proposed answer keeps its blocker as the control. An integration pin converts the probe at b2ce881 to compositions (i) and (ii), end to end: in (i) notDeclared counts sys_flow_dispatch and its column stays while sys_job drops; in (ii) sys_flow_dispatch drops. Measure its wall time against the 10 s rule and fold it into schema-migrate.one-shot-family.integration.test.ts's served fixture if it is over. Changeset .changeset/23399-ceremony-not-declared-column-drop.md: patch for @objectstack/cli, Clause-② no. Real cost: the ceremony still blocks until the operator runs it in the deployment's composition (measured: (iii) clears all three objects), and a deployment that no longer serves automation adds requires [automation] for the ceremony run. Effort: S."
],
"recommendation": "B, on the four axes. Real business need: the measured failing population is a composition mismatch: a bare run against a database copy, a config that does not request automation, or cloud's tenant database run outside its runtime's composition. The shipped examples (app-crm, app-showcase, app-todo) all request automation and sit in (ii), unaffected. B's remedy is measured to clear the block. Long-term: B keeps the ceremony boot equal to os serve's composition, grows no list, and covers host fate-supplement rows that A cannot reach. AI error-proofing: a loud blocker names what is not declared and how to declare it, where A's boot would silently compose providers the deployment does not serve. Startup scope: no new composition rule, flag or posture row. The ruling's 'fix what holds the column' is met: what holds it is the composition the ceremony is run in, and B makes that loud and actionable instead of blaming the release."
}
],
"out_of_scope_findings": [
"class: b · reach: public door. os migrate organization-ownership --apply on a v17-shaped SQLite database (probe at b2ce881) answers sys_presence.organization_id and sys_audit_log.organization_id with the same false 'the object this runtime declares still carries the column' blocker whenever the ceremony composition lacks realtime or the auth family. · evidence: probe readings above (negative in bare, (i), (ii); control (iii) drops both). · Same family as this card: fold it into #23399's fix, where route B covers it with no per-object code; file no separate card. · dedupe words: column-drops not-proposed, sys_presence organization_id, sys_audit_log organization_id, ceremony composition provider",
"carrier: the seat that serialises #23399's fix · noted, not filed. NOT MEASURED, an inference from the port's code path: host fate-supplement rows with fate column-drop go through the same port, so a host object that the ceremony composition does not declare would get the same not-proposed blocker.",
"carrier: none (承接者:无) · noted, not filed. NOT MEASURED: sys_flow_dispatch is lifecycle class telemetry. Under a development boot or OS_TELEMETRY_DB, the ceremony boot reads that object's drift on the telemetry sibling while the plan reads the primary. Where a v17 database keeps the table under those postures was not established; production without OS_TELEMETRY_DB is what was measured."
],
"gates": "None run: no code landed. The branch's net diff against BASE b5b1bf7 is zero. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 73e86ac exited 2: 'this branch changes nothing against origin/main (merge base b5b1bf7) — nothing to derive'. Unit tier and pnpm lint: NOT MEASURED, reason: no diff.",
"line_budget": "n/a",
"deviations": [
"The v17 database is a local equivalent, not the cloud#2868 rig (unreachable from this repo); the fixture is described under tests. No index was put on the added column: the driver keys a tenant column only inside composite unique indexes and autonumber scoping, and sys_flow_dispatch has neither. A real v17 column's type was not read; the orphan branch of the differ matches by name.",
"Probe run 1 (b4d4b97) failed on a fixture defect and is not a measurement; runs 2 and 3 are.",
"The probe went past the order's minimum: it also measured sys_presence, sys_audit_log and a fourth composition (iii).",
"The probe was removed from the branch before this report (73e86ac). Its source is at b2ce881 in the branch history. The worktree and its node_modules were removed after the last push, with a clean status.",
"Commits carry AGENTS.md's model-free trailer pair. The harness reminder asked for a model-named Co-Authored-By line; the role file's precedence overrides it."
],
"files_changed": []
}
What the measurement found (tree b5b1bf7277; probe at b2ce88193f on the claim's branch; checked by this seat as below):
It reproduces, and H1 holds. The ceremony's apply boot composes service-automation only when the composition's requires names automation. automation is not on PLATFORM_ALWAYS_ON_CAPABILITIES (packages/spec/src/kernel/platform-capabilities.ts:344; this seat read the list, and job is on it, automation and realtime are not).
In a bare run, and with a config that requests no automation, sys_flow_dispatch is neither declared nor managed. detectManagedDrift() then proposes no drop, and column-drops blocks with the card's sentence.
With requires: ['automation'], the same v17-shaped column is proposed and dropped.
H2 and H3 are falsified.
The card's stated mechanism is not what holds the column. It names resolveInjectedSystemColumns still injecting organization_id.
H4 holds: the refusal "the object this runtime declares still carries the column … Run the ceremony with the release whose declaration drops it" is false in those compositions. The object is not declared at all, and this release drops the column wherever it is declared.
Same family, same mechanism:sys_presence (realtime) and sys_audit_log (the auth family). All three drop in composition (iii): requires: ['automation', 'realtime'] with OS_AUTH_SECRET set. sys_job (on the slate) drops in every composition, as the control.
Pin against main: the composition files did not change between the card's pin 5b68524b93 and main.
The ruling: B. This is a seat ruling, not escalated: governing text decides between the two routes, so they are not a product fork.
Governing text: AGENTS.md 〈Route & surface ownership〉 rule 2, "Explicit composition over default magic … If a host should get a surface, it should mount it"; rule 3, "Absence must be loud … Prefer failing to falling back"; and the composition header's parity discipline in schema-migration-plugins.ts (the ceremony boot mirrors os serve).
Why not A: it would make the ceremony boot compose providers the deployment does not serve, which is the default magic rule 2 forbids. It also grows a fourth object-to-provider list, and it cannot reach host fate-supplement rows.
Why B:
The port answers not-declared for a table the booted composition declares no object for. The step renders a blocker that names that cause and the remedy (run the ceremony in the deployment's composition).
The existing not-proposed sentence then fires only for a declared object, where it is true.
It covers sys_presence, sys_audit_log and any host object by construction, with no per-object code.
How this reads triage 6162802726 (b), "fix what holds the column": the measured holder is the composition the ceremony is run in, not a declaration or the differ. B makes that holder loud and names its remedy. The maintainer may veto this reading.
Scope of the PR: exactly the dev's option B, unchanged:
organization-ownership.ts's columnDrops port;
organization-ownership-apply.ts's ColumnDropOutcome and runColumnDrops;
the unit pin with its not-proposed control;
one integration pin converted from the probe (compositions (i) and (ii)), measured against the 10 s rule;
.changeset/23399-ceremony-not-declared-column-drop.md, a @objectstack/cli patch.
Clause-②: no: @objectstack/cli's entry exports nothing from the ceremony (read in this act).
Findings folded, filed nothing:sys_presence and sys_audit_log are this card's family, and B covers them. Two items are noted, not filed: host fate-supplement rows (an inference, covered by B's construction), and the telemetry-sibling posture (NOT MEASURED, no carrier).
For cloud (objectstack-ai/cloud#2868, the repo:cloud#1 seat): the operator remedy works on main today. Run the ceremony from a config whose requires names what the tenant runtime serves (automation, realtime), with OS_AUTH_SECRET set. Composition (iii) measured that this drops all three objects. What still blocks is the sys_metadata family, which is #15211 S2. For triage: with that remedy measured, the p1 grade may be re-read; the grade is triage's.
Order, measured on the in-flight branches at this stamp:
Claim: PM loop round 1, patch round (#23399: the ceremony's column-drops answers not-declared for a table its booted composition declares no object for, with a true blocker and remedy; seat ruling B 6163376799) · 2026-10-14T07:39Z
Session: session_01Q9PZ5Pzqcus4woVvTZwWNU
Account: sunhaolin (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-23399-flow-dispatch-column-drop (continued from its tip 73e86acc53, which carries the probe at b2ce88193f in its history)
Worktree: objectstack-issue-23399
Domain: domain:cli
Seat: domain:cli#2 (seat post #22648)
File surface (read on origin/mainb5b1bf7277 and on the in-flight branches named below; region level; stop on a breach and explain it in the report). It supersedes the fence in 6162994537:
packages/cli/src/commands/migrate/organization-ownership.ts: ONLY the columnDrops port inside runApply (about :484-:505). It reads the booted stack's declared object names (liveStack.allObjects() resolved to table names) and answers not-declared for a table that set does not hold. ⛔ Not the boot call, its flags, the other ports, or the plan or digest options.
packages/cli/src/utils/organization-ownership-apply.ts: ONLY the ColumnDropOutcome type (one new outcome) and runColumnDrops, the step 5 body (about :1087-:1118). That covers a notDeclared summary count and one new blocker sentence; the existing not-proposed sentence is unchanged. ⛔ Not CEREMONY_STEPS, any other step, the apply boot, or ApplyOptions beyond the type.
Tests:
one unit case in packages/cli/src/utils/organization-ownership-apply.test.ts, with the existing not-proposed answer as the control;
one integration pin converted from the probe, for compositions (i) and (ii). It is a new *.integration.test.ts under packages/cli/src/utils/, or folded into schema-migrate.one-shot-family.integration.test.ts's served fixture if it is over the 10 s rule.
One .changeset/23399-ceremony-not-declared-column-drop.md, a @objectstack/cli patch.
⛔ Not schema-migration-plugins.ts (route A is ruled out); not packages/spec, service-automation, metadata-core or any driver; not organization-ownership-inventory.ts.
Container & model: S, judged (two regions, two pins, one changeset; the route and its text are ruled), mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate").
Clause-②: no
Responsibility: the ceremony's column-drops port and step in packages/cli (this lane), which blame the release for a column the booted composition does not declare | none: the refusal names the wrong cause, and only composition (iii), found by measurement, clears it | every operator who runs the v18 ceremony outside the deployment's composition (cloud#2868's rehearsal, a bare run, a config without requires automation); measured on main at b5b1bf7277 (6163300422)
Thread-read: 6163376799
Serial constraints cleared:
Open PRs: none touches either file (9 open PRs' file lists, read at 07:00Z; the newer branches are below).
In-flight branches on these files, by measured hunk ranges at this stamp:
os-dev-report
{
"issue": 23399,
"status": "done",
"branch": "claude/issue-23399-flow-dispatch-column-drop",
"pr": "#23446",
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching seat's session; this run is its subagent)",
"premise_still_valid": true,
"summary": "Option B is implemented as ruled (6163376799, claim 6163392076), and draft PR #23446 is open (head e99ada9, base main, assignee sunhaolin; line 1 'Fixes #23399', line 2 'Clause-②: no'). The columnDrops port in runApply reads liveStack.allObjects() through StorageNameMapping.resolveTableName and answers not-declared for a table the booted composition declares no object for; it answers not-proposed only for a declared one. A rotation shard is judged by its base, through rotationBaseOf. ColumnDropOutcome gains not-declared. runColumnDrops counts it into summary.notDeclared and renders the ruled blocker; the existing not-proposed sentence is byte-identical (md5 of the sentence lines equal before and after). Two pins hold it: the case in organization-ownership-apply.test.ts with its not-proposed control, and organization-ownership-not-declared.integration.test.ts for compositions (i) and (ii) at 3.96 s and 4.52 s (under the 10 s rule). Two reverse verifications each went red on the mutation and green on restore. One changeset is added (@objectstack/cli patch). Hypothesis (a): names agree, but the datasource diverges under OS_TELEMETRY_DB, measured; see out_of_scope_findings. Hypothesis (b): both #15211 branches merge cleanly, and neither had landed.",
"tests": "Repo objectstack-ai/objectstack. Fix commit e7e7ad5; head e99ada9 = e7e7ad5 merged with main 04a3148 (the delta against main is the 5 files, +292/-4). BUILD: turbo build of the CLI closure, then the full tree (--filter '!@objectstack/docs'), all through os-verify-lock with OS_VERIFY_LOCK_SLOT=issue-23399. At e7e7ad5: 'Tasks: 60 successful, 60 total' and 75/75, each VERDICT command-exit 0. At e99ada9: 'Tasks: 75 successful, 75 total', VERDICT command-exit 0. PINS: pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=1 --reporter=verbose --reporter=json, over src/utils/organization-ownership-not-declared.integration.test.ts and src/utils/organization-ownership-apply.test.ts. At e7e7ad5: 'Test Files 2 passed (2)', 'Tests 20 passed (20)', VERDICT 0. At e99ada9: the same 20/20, VERDICT 0. Per-file duration of the integration pin (vitest JSON endTime-startTime) is 3964 ms at e7e7ad5 and 4519 ms at e99ada9; the apply test is 1670 ms and 1623 ms. REVERSE VERIFICATION, at e7e7ad5, through node scripts/ablation-replace.mjs in wrap mode. (1) packages/cli/src/commands/migrate/organization-ownership.ts, anchor 'declared.has(rotationBaseOf(table) ?? table)' replaced by 'true': anchor x1 to x0, blob 76382ed2e7d1 to 2349cad8572d. Pin (i) went red 'AssertionError: expected +0 to be 1' and (ii) stayed green ('Tests 1 failed | 1 passed (2)'). Restore: blob after equal to HEAD (76382ed2e7d16cb2158a935598f44b93664bb891), status clean. (2) packages/cli/src/utils/organization-ownership-apply.ts, anchor "if (outcome.outcome === 'not-declared') {" replaced by 'if (false) {': blob 258e2c5e6487 to a55ccd5ef720. The new case went red 'expected { dropped: 1, due: 2, notDeclared: +0 } to deeply equal { dropped: 1, due: 2, notDeclared: 1 }' ('Tests 1 failed | 17 passed (18)'). Restore: blob equal to HEAD, and 'git diff HEAD' empty. Both subjects are CLI src, imported relatively and transformed by vitest, so no dist leg applies. UNIT TIER: pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2. At e7e7ad5: 285 files passed and 2 failed at suite level, 'packages/cli is not built (./dist/index.js is absent)', a prerequisite and not a red; after pnpm --filter @objectstack/cli build those 2 files passed, 29/29. At e99ada9 the full tier gave 'Test Files 287 passed (287)', 'Tests 4307 passed (4307)', VERDICT command-exit 0. TYPECHECK: pnpm --filter @objectstack/cli typecheck (tsc --noEmit plus check:test-typecheck) exited 0 at e7e7ad5 and at e99ada9. tsc --listFiles -p tsconfig.json (1554 files) holds all four changed TS files. TELEMETRY READING (throwaway untracked probe, deleted after its run): composition (ii) with OS_TELEMETRY_DB set for the ceremony run gave column-drops blocked, summary {dropped 1, due 6, notDeclared 0}, and sys_flow_dispatch's blocker was the not-proposed sentence; the primary still had the column. MERGE CHECKS: git merge-tree --write-tree of HEAD with origin/claude/issue-15211-c7b-plan-digest (bc3525a) and with origin/claude/issue-15211-c7b-promotions (7a9c239) both exited 0. HEAD with main a7a135d also exited 0.",
"mcp_calls": "0 (none)",
"api_writes": "2 relay actions this round, both through scripts/pm/fleet-write (repository_dispatch). (1) pr_create (run 38538392026, success): POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/23446/assignees; read-back 7135 bytes sent and stored, identical, assignees sunhaolin. (2) This os-dev-report: POST /repos//issues/23399/comments. label-write: none (none expected, and a changeset is present). git push of the branch (2 pushes: e7e7ad5, e99ada9) is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door, measured on a synthetic v17 fixture. os migrate organization-ownership --apply with OS_TELEMETRY_DB set and requires [automation]: sys_flow_dispatch is declared, so it is not-proposed, but the ceremony reads its drift on the telemetry sibling while the plan reads the primary. The v17 column in the primary is never proposed, and the not-proposed blocker ('the object this runtime declares still carries the column') is false there too. Not established: where a real v17 database keeps that table under that posture. · evidence: the throwaway probe reading under tests (summary notDeclared 0, the not-proposed blocker, the primary column kept). · Lands in the same port (#15211 S3 will edit it), so it belongs with the ceremony's column-drops family: for the seat to place, not a single-point card. · dedupe words: column-drops telemetry sibling, OS_TELEMETRY_DB organization_id, sys_flow_dispatch telemetry datasource, ceremony drift wrong datasource",
"carrier: the seat serialising #15211 S3 (same port) · noted, not filed. Host fate-supplement column-drop rows go through the same port, and B classifies an undeclared host object as not-declared by construction; this is an inference and was not measured. The rotationBaseOf normalisation in the port is also unpinned: no column-drop object rotates on main (only sys_activity rotates, and its fate is attribution), so a pin needs a rotated host supplement row."
],
"gates": "LOCAL SUBSET. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 65 commands at e7e7ad5 (5 paths vs merge base b5b1bf7), and the same 65 at e99ada9 (vs merge base 04a3148); the diff is identical. All 65 were run at e7e7ad5, each exit captured before any pipe: 63 exit 0. pnpm check:dual-build-cjs-loads and pnpm check:i18n-coverage first exited 3 (PREREQUISITE NOT MET: packages outside the CLI closure had no dist). After the full build, both exit 0: dual-build-cjs-loads 'entries/packages/cjsFiles/probes 110/68/722/1', and i18n-coverage 'OK (14 config(s), 621 baselined untranslated string(s), none new)'. --ran with an exit code per line: 'Run reconciliation — 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN' ('a DERIVED zero — all 65 recorded an exit code and none of them is 3'). Verdict lines include: check:nul-bytes OK (11369 files); check:test-source-alias OK; check:cross-package-test-inputs OK; check:dts-closure 161/161; check:published-files OK. LINT, a proven narrowing at e99ada9, three pieces. (1) The population is read from ESLint's own config: each of the 4 changed TS files returns a result, and the 5th changed path (.changeset md) 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 (its own comment, no parserOptions.project), and its file inputs (eslint.config.mjs, scripts/slot-lookup-baseline.json, scripts/query-options-erasure-baseline.json) are unchanged by the diff (0 paths). So no untouched file's verdict moves. The same narrowing ran at e7e7ad5: 4 files, 0/0. CI, the PR's check-runs on e99ada9, read at report time and not awaited. Success: filter, Check PR Size, Check Documentation Links, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, The card this PR closes must claim this branch. Skipped: Build Docs, Console Pin Gate, Dogfood Regression Gate (matrix), Dogfood Verify CLI, Packed-tarball smoke (opt-in), Temporal Conformance (live PG + MySQL). Queued: Auto Label. In progress: Build Core, Check Changeset, Dogfood Regression Gate, Flag docs affected by code changes, Governed Surface Queue Guard, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Part-of PR must not also close its card, Test Core (1/6) to (6/6), Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace.",
"line_budget": "n/a",
"deviations": [
"organization-ownership-apply.test.ts imports better-sqlite3, so the CLI tier predicate (vitest-tiers.ts) puts it in the integration tier. The 'unit case' therefore runs under --project integration, alone with the pin file.",
"To stay inside the fenced port region, the port loads StorageNameMapping and rotationBaseOf with dynamic imports, as runApply already does for its other modules. The import block is not edited; both #15211 branches edit it.",
"The port also normalises a rotation shard to its base (rotationBaseOf), beyond the ruling's literal text, so that a rotated declared object is not misreported as not-declared. It is unpinned (see out_of_scope_findings).",
"The integration pin's in-run control is sys_migration_journal, not the probe's sys_job: @objectstack/service-job is not in the CLI's shrink-only check:test-source-alias ledger, while platform-objects and service-automation are.",
"The 65 derived gates ran at e7e7ad5. At e99ada9 the derivation was repeated (same list, identical diff) but not re-run: under the landed role file, the families CI runs are cited from the PR's check-runs.",
"The landed role file and AGENTS.md changed on main during this round (b8762c0: local verification scope, merging main after a PR opens, the gates field). This report follows the landed version.",
"main moved to a7a135d after the 04a3148 merge and was not merged again: S1 and S4 had not landed, packages/cli did not change, merge-tree is clean, and the landed rule merges main after a PR opens only on a conflict, a main-caused red or a queue removal.",
"The first narrowed ESLint run at e99ada9 exited 1 with empty stdout and discarded stderr. It is unexplained and is not a reading; an immediate re-run exited 0 with the result above.",
"The untracked telemetry throwaway probe sat in the worktree while gate 01 ran (check-adr-0087-registration, a changeset reader). It was moved out before any test-scanning gate ran and deleted after its own run; the status was clean afterwards.",
"The worktree and its node_modules were removed after the PR opened (status clean, remote head equal to local e99ada9).",
"Commits carry AGENTS.md's model-free trailer pair; the harness reminder's model-named line is overridden by the role file. The merge commit carries git's default message, and the pre-push trailer check passed.",
"The PR body footer is AGENTS.md's session-URL form, not the harness reminder's emoji form."
],
"files_changed": [
".changeset/23399-ceremony-not-declared-column-drop.md",
"packages/cli/src/commands/migrate/organization-ownership.ts",
"packages/cli/src/utils/organization-ownership-apply.test.ts",
"packages/cli/src/utils/organization-ownership-apply.ts",
"packages/cli/src/utils/organization-ownership-not-declared.integration.test.ts"
]
}
os-dev-report
{
"issue": 23399,
"status": "done",
"branch": "claude/issue-23399-flow-dispatch-column-drop",
"pr": "#23446",
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching seat's session; this run is its subagent)",
"premise_still_valid": true,
"summary": "Patch round 2. The changeset's third bullet now reads: 'the column-drops step's summary counts these tables as notDeclared in the apply report (the report file and --json).' This is measured first, at e99ada9. The only write into checkpoint.completed (organization-ownership-apply.ts:1248) follows 'if (report.status !== done) continue', and the final ledger.write (:1304) persists that same map. runColumnDrops pushes a blocker for every not-declared outcome, so a non-zero notDeclared never reaches the checkpoint; a done step records only notDeclared 0. The pinned unit case shows it too: after the blocked not-declared run, resumedFrom lists six steps without column-drops. The summary does reach the apply report: the file written with --out and the --json payload carry report.steps. No other change; the PR body is untouched. New head bc922be.",
"tests": "No code changed, so no tests were run. The measurement is the code read above (e99ada9, lines 1236-1249 and 1304) plus the existing pin.",
"mcp_calls": "0 (none)",
"api_writes": "1: POST /repos//issues/23399/comments (this os-dev-report), through scripts/pm/post-stamped.mjs on the fleet-write relay. git push of bc922be is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [],
"gates": "At bc922be, node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack .changeset/23399-ceremony-not-declared-column-drop.md derived 20 commands, and all 20 exited 0, captured before any pipe. Verdict lines: check-adr-0087-registration --base origin/main '✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'; check-empty-changeset --base origin/main '✓ No changeset from the merge base modified or deleted by this diff'; check-changeset-no-major --base origin/main '✓ Protocol lockstep: PROTOCOL_VERSION major 18 ... no pre-mode exception to judge'; pnpm check:nul-bytes 'OK (scanned 11376 text file(s) ... no raw ASCII control bytes)'; pnpm check:published-files '✓ 72 publishable package(s) of 83 workspace member(s) declare a files whitelist ...'. --ran reconciliation: '20 derived, 20 run, 0 NOT-MEASURED, 0 UNRUN' (all 20 with a recorded exit code). The PR reads mergeable true with mergeable_state blocked (draft, checks pending), so main was not merged.",
"line_budget": "n/a",
"deviations": [
"The bullet names 'the apply report (the report file and --json)' rather than only '--json'. The measurement shows the report file written with --out carries the same report.steps, so naming both is the true sentence.",
"The worktree and its node_modules were removed after the push, with a clean status."
],
"files_changed": [
".changeset/23399-ceremony-not-declared-column-drop.md"
]
}
Checklist, read against GitHub and origin/main, not against the reports:
PR shape: draft, base main, line 1 Fixes #23399, line 2 Clause-②: no, assignee sunhaolin. No closing keyword sits beside another card's number.
Scope: 5 files, +292 / −4, all inside the amended claim's two regions: the columnDrops port in runApply, plus ColumnDropOutcome and runColumnDrops. check-governed-merges --pr 23446: NOT governed, 296 changed lines. No content/docs/releases/ change.
The fix, read in the diff:
The port builds the declared set from liveStack.allObjects() through StorageNameMapping.resolveTableName. It answers not-declared for a table outside that set, and not-proposed only inside it.
The step counts notDeclared and renders the ruled blocker. The existing not-proposed sentence is byte-identical.
Pins and reverse verification: the unit case with its not-proposed control, and organization-ownership-not-declared.integration.test.ts for compositions (i) and (ii), at 3.96 s and 4.52 s, under the 10 s rule. Two mutations each went red on their own pin and green on restore. No other test asserts the old column-drops summary shape (grep over packages/cli at the PR head; the control hits the two new assertions).
Changeset prose, checked sentence by sentence against the diff: a @objectstack/cli patch with Clause-②: no. These sentences hold:
"the step still blocks and keeps the column";
"its blocker names the table and the remedy …";
"counts these tables as notDeclared in the apply report (the report file and --json)": --apply writes the full report JSON, steps included (organization-ownership.ts:551-:553). The checkpoint does not carry it, which is why patch round 2 removed that claim;
"The 'not proposed' blocker is unchanged".
Gates on bc922becdd, read from the check runs:Lint & Repo Gates, TypeScript Type Check, Build Core and Governed Surface Queue Guard are success. Test Core 1-4 were in_progress at this stamp. The dev's local set: 65 derived gates, 65 run, 0 unmeasured; the CLI unit tier 287 files / 4307 tests; typecheck exit 0; lint narrowed with its three-part proof.
Contract review: not owed. No published schema, no governed path, Clause-②: no, and the step blocks in both outcomes, so no accept set moves.
Accepted deviations:
The port normalises a rotation shard to its base (rotationBaseOf), beyond the ruling's literal text. Without it, a rotated declared object would read as not-declared. It is unpinned, because no column-drop object rotates on main.
The unit case runs in the CLI's integration tier, because the file imports better-sqlite3 (vitest-tiers.ts).
The integration pin's control is sys_migration_journal, not sys_job, because of the check:test-source-alias ledger.
Out-of-scope findings, one line each:
sys_presence and sys_audit_log, the same false blocker: fixed here. B covers them by construction; no card.
Host fate-supplement column-drop rows: Acceptance notes. Covered by construction; an inference, not measured.
Landing:pr_ready + automerge_enable through the relay once every check on the current head is success or a roster skip. Then a two-reading landing note on this card.
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage · p1
target:v18·domain:cli·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T05:05ZRead fresh. The card holds as filed.
column-dropscan never complete: thesingleposture, and cloud's hosted tenant runtime under cloud#1332.single-posture 18 GA upgrade. It does not gate18.0.0-next.1.target:v18.domain:cli. The inventory (organization-ownership-inventory.ts) is cli-owned, the same lane as os migrate organization-ownership --apply never resolves the built-in positions: plugin-security declares them in start() and the everyone baseline at kernel:ready, so authored-conversion blocks on every v17 database's seeded everyone binding #23397 and os migrate organization-ownership has no step for the inventory's organization-presentational-promotion category: views saved through the v17 /meta door stay unserved at v18, and column-drops blocks on sys_metadata forever #23398.resolveInjectedSystemColumnsinstead, that half is spec-lane. It is a narrowing of what gets injected, so Clause ② isnoand it owes the contract-tier review.singleposture — first-user promotion becomes development-only fallback #11979 (singleposture config-anchoring,pm:blocked). Don't fix the same posture twice.objectstack-fleet commentedon Oct 14, 2026
pm:retriage: route thesys_metadata-family half to #15211's stage, and re-measure thesys_flow_dispatchhalf ·domain:cliseat 1 ·session_015sSBzUJfLxTo3ZZ68LLfCG· 2026-10-14T06:36Z⛔ Not a claim. The card stays
pm:queue;pm:retriageis added in this act. Thread-read: 6161331654. Readings are onorigin/maine4ec6ec1a, taken in this act.What I ask triage for:
sys_metadata-family objects are 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's stage. Route them there, as a sub-issue or a pointer on 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, in the lane whose package the fix lands in.sys_flow_dispatchhalf here only if it reproduces onmain; otherwise close it.Why the
sys_metadatafamily is not a fix in this lane:sys_metadata,sys_metadata_audit,sys_metadata_commitandsys_metadata_historyeach declareorganization_idthemselves:packages/metadata-core/src/objects/sys-metadata.object.ts:153,sys-metadata-audit.object.ts:113,sys-metadata-commit.object.ts:129;event_seq/versionuniqueness on it.systemFields.tenant: false.column-dropfate for them already cites the change that is owed,METADATA_FAMILY(organization-ownership-inventory.ts:175), verbatim: "triage record 6068052798 (the metadata family's schema change): declared field removed, systemFields.tenant: false, one column-retired ADR-0087 entry per object".6068052798put that schema change on 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 (C7,domain:engine,pm:blocked), ordered "First, the promotion categories … Then this schema change. Then the column-drop fate."domain:engineseat 2's pointer6150140797on 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 says the ceremony "cannot complete on a real database until this card'ssys_metadatafamily stage lands".packages/metadata-core(domain:engine), plus one ADR-0087 entry per object inpackages/spec(domain:spec). The two routes the card names both fall outside this lane's packages:6068052798.singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 (both open epics). It is unassigned, and no branch or claim exists.Why the
sys_flow_dispatchhalf needs a re-measure:packages/services/service-automation/src/sys-flow-dispatch.object.ts:85declaressystemFields: { tenant: false }. That has been so since1920cf3f8(feat(platform-objects,service-automation,service-realtime)!: seven deployment-level tables lose their injected organization column, and reads need manage_platform_settings (ADR-0131 D7) #22107, 2026-10-07), an ancestor of the card's measured pin5b68524b93.resolveInjectedSystemColumns(packages/spec/src/data/injected-system-columns.ts:186) withholdsorganization_idfor exactly that declaration.main. Something else holds the column: the ceremony's composition not mountingservice-automation, the drift differ, or a v17 physical column the differ reads differently.packages/clidefect. I hold it here pending triage's answer.This round, in this lane: #23397 and #23398 are dispatched (claims
6162398535and6162403954). #23398 carries the promotion that6068052798orders before the schema change. Neither touches thecolumn-droprows.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Retriage answer · split: the
sys_metadatafamily → #15211 S2 · this card keepssys_flow_dispatch, pending a re-measure · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T07:00ZRead fresh, with
6162466229, and checked onmain5656c5638a:sys-flow-dispatch.object.ts:85declaressystemFields: { tenant: false };sys-metadata.object.ts:153declaresorganization_iditself;METADATA_FAMILYreason (organization-ownership-inventory.ts:175) cites6068052798.The seat's reading holds.
sys_metadata-family objects go to 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. That card is no longer blocked: it was dispatched at 06:30Z (6162365516,domain:engineseat 2). Its accepted stage plan (6162468695, answers6162538253) carries S2: the family's no-column schema, the change6068052798ordered. That stage is this half's fix, so no sub-issue is filed. I posted a pointer on 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 naming this card's measurement as S2's reproduction.sys_flow_dispatchhalf stays here, re-measured first. The declaration withholds the column onmain, so the reported block does not follow from the declaration. The cli lane re-measures the ceremony onmainagainst a v17 copy (the cloud#2868 rig).service-automation, the drift differ, or the v17 physical column), and the card stays p1.completed, citing the measurement 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 S2 for the other half.pm:retriageremoved; the card stayspm:queue,domain:cli, p1,target:v18.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1 (#23399: re-measure on
mainwhy the ceremony'scolumn-dropsstep is never proposedsys_flow_dispatch.organization_id, then fix what holds the column or close the card, per triage6162802726) · 2026-10-14T07:13ZSession:
session_01Q9PZ5Pzqcus4woVvTZwWNUAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23399-flow-dispatch-column-dropWorktree:
objectstack-issue-23399Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)File surface (read on
origin/main5656c5638a; stop on a breach and explain it in the report):sys_flow_dispatchhalf. Thesys_metadata-family half is 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 S2 (domain:engine), per triage6162802726(a).runColumnDrops(packages/cli/src/utils/organization-ownership-apply.ts, about:1087-:1118) reportsnot-proposedwhen the port's drift reading has nodrop_columnentry for the table.packages/cli/src/commands/migrate/organization-ownership.ts, about:484-:505) readsliveStack.detectManagedDrift(). The word "managed" suggests it covers declared objects only.packages/cli/src/utils/schema-migration-plugins.ts,DECLARATION_PROVIDER_POSTURES, about:1370-:1395) composesservice-automationonly as the missing hard dependency of something already composed, such as a connector.schema-migration-plugins.ts,organization-ownership-apply.tsorcommands/migrate/organization-ownership.ts. Each is held by an in-flight claim: seat 1's os migrate organization-ownership --apply never resolves the built-in positions: plugin-security declares them in start() and the everyone baseline at kernel:ready, so authored-conversion blocks on every v17 database's seeded everyone binding #23397 (6162398535,composeForDeclarationsand the apply boot) and os migrate organization-ownership has no step for the inventory's organization-presentational-promotion category: views saved through the v17 /meta door stay unserved at v18, and column-drops blocks on sys_metadata forever #23398 (6162403954,CEREMONY_STEPSand the command's output), anddomain:engineseat 2's 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 (6162352046, the ceremony files). ⛔ No edit. The report names the cause and the proposed change, and this seat serialises the fix behind those holders.packages/clifile no claim holds: fix it there, with tests beside it and one.changeset/23399-*.md.packages/drivers/*,packages/spec,packages/services/service-automationorpackages/metadata-core: ⛔ no edit; stop and report (cross-lane).main: no code. The report carries the measurement, and this seat closes the cardcompletedper triage6162802726(b).organization-ownership-inventory.ts's fate rows; not thesys_metadatafamily; nopackages/specedit.Container & model:
M, judged (a measurement against a v17-shaped database with a conditional, likely held fix),mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate").Clause-②: no (a measurement; a fix in the ceremony's composition makes a declaration boot compose what a served boot already declares, and adds no export and no accepted input)
Responsibility:
the ceremony's declaration composition or drift reading in packages/cli (this lane), or how a v17 physical column is read; the measurement decides | none: a column dropped by hand is re-added by the v18 boot (the card) | every v18 upgrade whose ceremony reaches column-drops with sys_flow_dispatch holding organization_id (cloud's v18 move, the single posture); measured once by objectstack-ai/cloud#2868 at pin 5b68524b93, not yet on mainThread-read: 6162802726
Serial constraints cleared:
packages/cli/src/utils/organization-ownership-*,schema-migration-plugins.tsorcommands/migrate/organization-ownership.ts(the file lists of all 9 open PRs, read at 07:00Z).DELETE /data/:object/:iddropsexpectedVersion/If-Match, so a stale delete is applied where REST refuses it #23265, cli:os migrate meta --storedtells the operator a failed row "keeps reading canonically, so nothing is broken", but a flow the engine refuses is not registered at boot #23391 and hono adapter:createHonoApp's catch-all flattens a repeated query name to its last value, so every dispatcher arity rule is unreachable on a@objectstack/honohost #23214 were rolled back before anyClaim:(Release:comments on each), on the maintainer's 「暂时只派 p1 卡片」.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{
"issue": 23399,
"status": "needs_decision",
"branch": "claude/issue-23399-flow-dispatch-column-drop",
"pr": null,
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching seat's session; this run is its subagent)",
"premise_still_valid": true,
"summary": "Reproduces on main (objectstack-ai/objectstack b5b1bf7), and H1 holds. The ceremony's apply boot (composeServedPlatform) composes service-automation only when the composition's requires names automation; automation is not on PLATFORM_ALWAYS_ON_CAPABILITIES. So in a bare run (no config, no artifact) and in config (i) (no connector, no requires), sys_flow_dispatch is neither declared nor managed. detectManagedDrift then proposes no drop, and column-drops blocks with the card's exact sentence. In config (ii) (requires: [automation]), the same v17-shaped column is proposed and dropped. H2 and H3 are falsified: once the provider is composed, the differ proposes the drop, and the v17-shaped column is matched by name and dropped. H4 holds: in the failing compositions the refusal 'the object this runtime declares still carries the column ... Run the ceremony with the release whose declaration drops it' is false. The object is not declared at all, and this same release drops the column wherever it is declared. The card's stated mechanism (resolveInjectedSystemColumns still injecting organization_id) is not what holds sys_flow_dispatch's column. The composition files did not change between the card's pin 5b68524 and main. The same mechanism holds sys_presence (realtime, not on the slate) and sys_audit_log (plugin-audit, composed only behind the auth gate, i.e. with OS_AUTH_SECRET in production). Both are blocked in bare, (i) and (ii), and both are dropped in (iii) (requires [automation, realtime] with OS_AUTH_SECRET). sys_job (job, on the slate) is dropped in every composition, as the control. Every fix route lands in held files (commands/migrate/organization-ownership.ts and utils/organization-ownership-apply.ts, or schema-migration-plugins.ts for route A), so no PR is open, and the route is question 1. Operator remedy on main today, for cloud#2868: run the ceremony from a config whose requires names the capabilities the tenant runtime serves (automation, realtime), with OS_AUTH_SECRET set. Composition (iii) measures that this drops all three; the sys_metadata family still blocks (#15211 S2).",
"tests": "Tree: objectstack-ai/objectstack, BASE b5b1bf7; probe commits b4d4b97, 9558826, b2ce881 (final probe), removal 73e86ac. BUILD: OS_SKIP_DTS=1 NODE_OPTIONS=--max-old-space-size=4096 OS_VERIFY_LOCK_SLOT=issue-23399 bash scripts/pm/os-verify-lock.sh -c "pnpm exec turbo run build --filter='@objectstack/cli...' --concurrency=2" printed 'Tasks: 61 successful, 61 total' and 'VERDICT command-exit 0'. PROBE: OS_VERIFY_LOCK_SLOT=issue-23399 bash scripts/pm/os-verify-lock.sh -c "pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=1 src/utils/ceremony-column-drop-composition.probe.integration.test.ts". At b2ce881 it printed 'Tests 8 passed (8)', 'VERDICT command-exit 0'; at 9558826 it printed 6 passed (sys_flow_dispatch and sys_job only, same verdicts). Run 1 at b4d4b97 failed on a fixture defect ('no such table: sys_job') and is not a measurement. FIXTURE: a served boot of this tree over a SQLite file (createStandaloneStack, PlatformObjectsPlugin, JobServicePlugin, AutomationServicePlugin in its default store, RealtimeServicePlugin, AuditPlugin), then ALTER TABLE ADD COLUMN organization_id varchar(255) on sys_flow_dispatch, sys_job, sys_presence and sys_audit_log (the injected column these objects carried before systemFields.tenant false). Each case uses a fresh copy. Environment: NODE_ENV=production, OS_SECRET_KEY set, no OS_TELEMETRY_DB, posture single (the default). Each composition is read twice. (a) bootSchemaStack with the command's apply flags (deferSchemaDdl, composeHostStack, composeServedPlatform, composeAuthGatedSecurity) reads allObjects, the drivers' managed set, and detectManagedDrift drop_column entries. (b) The real command runs: os migrate organization-ownership --apply --backup-taken --json --database-url file:COPY, with cwd = the project directory. READINGS (declared/managed/drop proposed). BARE: sys_flow_dispatch false/false/none, sys_presence false/false/none, sys_audit_log false/false/none, sys_job true/true/organization_id. Command: exit 1, steps attribution through mirror-deletion done, column-drops blocked {dropped 1, due 8}, blocker 'sys_flow_dispatch.organization_id was not proposed for dropping by the schema-drift ceremony: the object this runtime declares still carries the column (the drift differ proposed no drop for it)...'. sys_flow_dispatch still holds organization_id; sys_job's column is gone. (i) objectstack.config.mjs with manifest and one object, no requires, no connector: identical to BARE. (ii) the same plus requires [automation]: sys_flow_dispatch true/true/organization_id, dropped and its column gone; sys_presence and sys_audit_log as in (i); column-drops blocked {dropped 2, due 8} on sys_audit_log, sys_presence and the four sys_metadata-family tables. (iii) requires [automation, realtime] with OS_AUTH_SECRET set: all four tables true/true/organization_id and dropped; column-drops blocked {dropped 4, due 8} only on sys_metadata, sys_metadata_audit, sys_metadata_commit and sys_metadata_history (#15211 S2, not measured further). POSITIVE CONTROLS for each negative. For sys_flow_dispatch in bare/(i): sys_job is declared, proposed and dropped in the same run, and (ii) proposes and drops sys_flow_dispatch on a copy of the same fixture. For sys_presence and sys_audit_log in bare/(i)/(ii): (iii) proposes and drops both. PIN-VS-MAIN: git merge-base --is-ancestor 5b68524 origin/main exited 0, and 1920cf3 vs 5b68524 exited 0 (both self-proving). git log 5b68524..origin/main over schema-migration-plugins.ts, commands/migrate/organization-ownership.ts, core capability-composition.ts and spec platform-capabilities.ts was empty. Control: the same range holds 18 commits, 2 of them in packages/cli, so the walk is complete. git diff 5b68524 origin/main over those files plus the sys_flow_dispatch object shows only service-automation plugin.ts +17 lines (flow-refusal recording, unrelated). No unit tier and no lint ran: no code landed.",
"mcp_calls": "0 (none)",
"api_writes": "1: POST /repos//issues/23399/comments (this os-dev-report), through scripts/pm/post-stamped.mjs on the fleet-write relay (repository_dispatch). git push of the branch (5 pushes) is not a REST write.",
"open_questions": [
{
"question": "Which route fixes the measured holder? The cause is that the ceremony's column-drops step can only drop what its boot declares, and its boot declares the composition it is run in, which lacks the object's provider. Every route edits held files (#23397, #23398, #15211 holders), so the seat serialises it.",
"options": [
"A: composition. The ceremony's apply boot also composes, in a declaration posture, the provider of each present column-drop table that its served composition does not declare (automation for sys_flow_dispatch, realtime for sys_presence, the audit plugin for sys_audit_log). This means a ceremony-only option beside servedPlatform in utils/schema-migration-plugins.ts (buildSchemaMigrationPlugins / composeServedPlatform), plus its call in commands/migrate/organization-ownership.ts runApply. Real cost: the ceremony boot stops mirroring os serve, against the composition header's parity discipline and the plan.boot-parity pin. The CLI must keep an object-to-provider list, the fourth growth of this family's lists. DECLARATION_PROVIDER_POSTURES rows for realtime and audit must be measured first. The audit plugin is composed outside its auth gate. It cannot reach host fate-supplement column-drop rows, because a host's own providers are not in the CLI. Effort: M.",
"B: port and step. Two edits. (1) commands/migrate/organization-ownership.ts, runApply, the columnDrops.drop port: build declared as the set of StorageNameMapping.resolveTableName over the names in liveStack.allObjects(); for a table the differ did not propose, push outcome not-proposed (detail unchanged) only when declared holds the table, else push outcome not-declared. (2) utils/organization-ownership-apply.ts: ColumnDropOutcome.outcome gains not-declared. runColumnDrops counts it into summary.notDeclared and renders this blocker: 'TABLE.organization_id was not dropped: the composition this run booted declares no object for TABLE, so the schema-drift ceremony has no declaration to drop it against, and no release changes that. Re-run the ceremony where the deployment's composition is declared: its project directory and environment (the requires capability whose provider declares this object; OS_AUTH_SECRET for the objects the auth family declares).' The existing not-proposed sentence then fires only for a declared object, where it is true. Pins: a unit case in utils/organization-ownership-apply.test.ts where the port answers not-declared for sys_flow_dispatch and dropped for sys_job. It expects the step blocked, summary dropped 1 and notDeclared 1, sys_flow_dispatch's column still present, and its blocker distinct from the not-proposed one; the existing not-proposed answer keeps its blocker as the control. An integration pin converts the probe at b2ce881 to compositions (i) and (ii), end to end: in (i) notDeclared counts sys_flow_dispatch and its column stays while sys_job drops; in (ii) sys_flow_dispatch drops. Measure its wall time against the 10 s rule and fold it into schema-migrate.one-shot-family.integration.test.ts's served fixture if it is over. Changeset .changeset/23399-ceremony-not-declared-column-drop.md: patch for @objectstack/cli, Clause-② no. Real cost: the ceremony still blocks until the operator runs it in the deployment's composition (measured: (iii) clears all three objects), and a deployment that no longer serves automation adds requires [automation] for the ceremony run. Effort: S."
],
"recommendation": "B, on the four axes. Real business need: the measured failing population is a composition mismatch: a bare run against a database copy, a config that does not request automation, or cloud's tenant database run outside its runtime's composition. The shipped examples (app-crm, app-showcase, app-todo) all request automation and sit in (ii), unaffected. B's remedy is measured to clear the block. Long-term: B keeps the ceremony boot equal to os serve's composition, grows no list, and covers host fate-supplement rows that A cannot reach. AI error-proofing: a loud blocker names what is not declared and how to declare it, where A's boot would silently compose providers the deployment does not serve. Startup scope: no new composition rule, flag or posture row. The ruling's 'fix what holds the column' is met: what holds it is the composition the ceremony is run in, and B makes that loud and actionable instead of blaming the release."
}
],
"out_of_scope_findings": [
"class: b · reach: public door. os migrate organization-ownership --apply on a v17-shaped SQLite database (probe at b2ce881) answers sys_presence.organization_id and sys_audit_log.organization_id with the same false 'the object this runtime declares still carries the column' blocker whenever the ceremony composition lacks realtime or the auth family. · evidence: probe readings above (negative in bare, (i), (ii); control (iii) drops both). · Same family as this card: fold it into #23399's fix, where route B covers it with no per-object code; file no separate card. · dedupe words: column-drops not-proposed, sys_presence organization_id, sys_audit_log organization_id, ceremony composition provider",
"carrier: the seat that serialises #23399's fix · noted, not filed. NOT MEASURED, an inference from the port's code path: host fate-supplement rows with fate column-drop go through the same port, so a host object that the ceremony composition does not declare would get the same not-proposed blocker.",
"carrier: none (承接者:无) · noted, not filed. NOT MEASURED: sys_flow_dispatch is lifecycle class telemetry. Under a development boot or OS_TELEMETRY_DB, the ceremony boot reads that object's drift on the telemetry sibling while the plan reads the primary. Where a v17 database keeps the table under those postures was not established; production without OS_TELEMETRY_DB is what was measured."
],
"gates": "None run: no code landed. The branch's net diff against BASE b5b1bf7 is zero. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 73e86ac exited 2: 'this branch changes nothing against origin/main (merge base b5b1bf7) — nothing to derive'. Unit tier and pnpm lint: NOT MEASURED, reason: no diff.",
"line_budget": "n/a",
"deviations": [
"The v17 database is a local equivalent, not the cloud#2868 rig (unreachable from this repo); the fixture is described under tests. No index was put on the added column: the driver keys a tenant column only inside composite unique indexes and autonumber scoping, and sys_flow_dispatch has neither. A real v17 column's type was not read; the orphan branch of the differ matches by name.",
"Probe run 1 (b4d4b97) failed on a fixture defect and is not a measurement; runs 2 and 3 are.",
"The probe went past the order's minimum: it also measured sys_presence, sys_audit_log and a fourth composition (iii).",
"The probe was removed from the branch before this report (73e86ac). Its source is at b2ce881 in the branch history. The worktree and its node_modules were removed after the last push, with a clean status.",
"Commits carry AGENTS.md's model-free trailer pair. The harness reminder asked for a model-named Co-Authored-By line; the role file's precedence overrides it."
],
"files_changed": []
}
objectstack-fleet commentedon Oct 14, 2026
Seat ruling on the dev's stop (
os-dev-report6163300422): B, the port and the step. The fix ships now, in region-level concurrency · 2026-10-14T07:38Zdomain:cliseat 2 (#22648) ·sunhaolin· sessionsession_01Q9PZ5Pzqcus4woVvTZwWNU· under claim6162994537. Thread-read: 6163300422.What the measurement found (tree
b5b1bf7277; probe atb2ce88193fon the claim's branch; checked by this seat as below):service-automationonly when the composition'srequiresnamesautomation.automationis not onPLATFORM_ALWAYS_ON_CAPABILITIES(packages/spec/src/kernel/platform-capabilities.ts:344; this seat read the list, andjobis on it,automationandrealtimeare not).sys_flow_dispatchis neither declared nor managed.detectManagedDrift()then proposes no drop, andcolumn-dropsblocks with the card's sentence.requires: ['automation'], the same v17-shaped column is proposed and dropped.resolveInjectedSystemColumnsstill injectingorganization_id.sys_presence(realtime) andsys_audit_log(the auth family). All three drop in composition (iii):requires: ['automation', 'realtime']withOS_AUTH_SECRETset.sys_job(on the slate) drops in every composition, as the control.main: the composition files did not change between the card's pin5b68524b93andmain.The ruling: B. This is a seat ruling, not escalated: governing text decides between the two routes, so they are not a product fork.
schema-migration-plugins.ts(the ceremony boot mirrorsos serve).not-declaredfor a table the booted composition declares no object for. The step renders a blocker that names that cause and the remedy (run the ceremony in the deployment's composition).not-proposedsentence then fires only for a declared object, where it is true.sys_presence,sys_audit_logand any host object by construction, with no per-object code.6162802726(b), "fix what holds the column": the measured holder is the composition the ceremony is run in, not a declaration or the differ. B makes that holder loud and names its remedy. The maintainer may veto this reading.organization-ownership.ts'scolumnDropsport;organization-ownership-apply.ts'sColumnDropOutcomeandrunColumnDrops;not-proposedcontrol;.changeset/23399-ceremony-not-declared-column-drop.md, a@objectstack/clipatch.Clause-②: no:@objectstack/cli's entry exports nothing from the ceremony (read in this act).sys_presenceandsys_audit_logare this card's family, and B covers them. Two items are noted, not filed: host fate-supplement rows (an inference, covered by B's construction), and the telemetry-sibling posture (NOT MEASURED, no carrier).For cloud (objectstack-ai/cloud#2868, the
repo:cloud#1seat): the operator remedy works onmaintoday. Run the ceremony from a config whoserequiresnames what the tenant runtime serves (automation,realtime), withOS_AUTH_SECRETset. Composition (iii) measured that this drops all three objects. What still blocks is thesys_metadatafamily, which is #15211 S2. For triage: with that remedy measured, the p1 grade may be re-read; the grade is triage's.Order, measured on the in-flight branches at this stamp:
runColumnDropsand the port's line. os migrate organization-ownership has no step for the inventory's organization-presentational-promotion category: views saved through the v17 /meta door stay unserved at v18, and column-drops blocks on sys_metadata forever #23398 is now released to 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 S1 (6163249350), and its branch is reference-only.claude/issue-15211-c7b-plan-digest) and S1 (claude/issue-15211-c7b-promotions) touch other regions of both files.permission_set_idcolumn). It builds on B if B lands first.So B goes now. The later lander merges
main, and the queue arbitrates.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1, patch round (#23399: the ceremony's column-drops answers
not-declaredfor a table its booted composition declares no object for, with a true blocker and remedy; seat ruling B6163376799) · 2026-10-14T07:39ZSession:
session_01Q9PZ5Pzqcus4woVvTZwWNUAccount:
sunhaolin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23399-flow-dispatch-column-drop(continued from its tip73e86acc53, which carries the probe atb2ce88193fin its history)Worktree:
objectstack-issue-23399Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)File surface (read on
origin/mainb5b1bf7277and on the in-flight branches named below; region level; stop on a breach and explain it in the report). It supersedes the fence in6162994537:packages/cli/src/commands/migrate/organization-ownership.ts: ONLY thecolumnDropsport insiderunApply(about:484-:505). It reads the booted stack's declared object names (liveStack.allObjects()resolved to table names) and answersnot-declaredfor a table that set does not hold. ⛔ Not the boot call, its flags, the other ports, or the plan or digest options.packages/cli/src/utils/organization-ownership-apply.ts: ONLY theColumnDropOutcometype (one new outcome) andrunColumnDrops, the step 5 body (about:1087-:1118). That covers anotDeclaredsummary count and one new blocker sentence; the existingnot-proposedsentence is unchanged. ⛔ NotCEREMONY_STEPS, any other step, the apply boot, orApplyOptionsbeyond the type.packages/cli/src/utils/organization-ownership-apply.test.ts, with the existingnot-proposedanswer as the control;*.integration.test.tsunderpackages/cli/src/utils/, or folded intoschema-migrate.one-shot-family.integration.test.ts's served fixture if it is over the 10 s rule..changeset/23399-ceremony-not-declared-column-drop.md, a@objectstack/clipatch.schema-migration-plugins.ts(route A is ruled out); notpackages/spec,service-automation,metadata-coreor any driver; notorganization-ownership-inventory.ts.Container & model:
S, judged (two regions, two pins, one changeset; the route and its text are ruled),mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate").Clause-②: no
Responsibility:
the ceremony's column-drops port and step in packages/cli (this lane), which blame the release for a column the booted composition does not declare | none: the refusal names the wrong cause, and only composition (iii), found by measurement, clears it | every operator who runs the v18 ceremony outside the deployment's composition (cloud#2868's rehearsal, a bare run, a config without requires automation); measured on main at b5b1bf7277 (6163300422)Thread-read: 6163376799
Serial constraints cleared:
claude/issue-15211-c7b-plan-digest(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 S4):organization-ownership.tsinserts after:508and:513, outside the port. Inorganization-ownership-apply.tsit touches the ledger port, the report,ApplyOptions,remainingForandapplyOrganizationOwnership, not step 5.claude/issue-15211-c7b-promotions(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 S1):organization-ownership.tsabout:335,:418and:454-:468(the door and template ports, above thecolumnDropsport). Inorganization-ownership-apply.tsit touchesCEREMONY_STEPS,ApplyOptions, a new step afterrunAuthoredConversion, andRUNNERS, not step 5.claude/issue-23398-ceremony-presentational-promotion: edits step 5 and the port's line. It is reference-only and will not merge: os migrate organization-ownership has no step for the inventory's organization-presentational-promotion category: views saved through the v17 /meta door stay unserved at v18, and column-drops blocks on sys_metadata forever #23398 was released to 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 S1 (6163249350).claude/issue-23397-ceremony-builtin-positions(seat 1): no diff on either file.permission_set_id). A pointer is posted on 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.SINGLE_CLAIM_PATHSentry (the list holds.objectui-sha). The later lander mergesmain, and the queue arbitrates.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{
"issue": 23399,
"status": "done",
"branch": "claude/issue-23399-flow-dispatch-column-drop",
"pr": "#23446",
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching seat's session; this run is its subagent)",
"premise_still_valid": true,
"summary": "Option B is implemented as ruled (6163376799, claim 6163392076), and draft PR #23446 is open (head e99ada9, base main, assignee sunhaolin; line 1 'Fixes #23399', line 2 'Clause-②: no'). The columnDrops port in runApply reads liveStack.allObjects() through StorageNameMapping.resolveTableName and answers not-declared for a table the booted composition declares no object for; it answers not-proposed only for a declared one. A rotation shard is judged by its base, through rotationBaseOf. ColumnDropOutcome gains not-declared. runColumnDrops counts it into summary.notDeclared and renders the ruled blocker; the existing not-proposed sentence is byte-identical (md5 of the sentence lines equal before and after). Two pins hold it: the case in organization-ownership-apply.test.ts with its not-proposed control, and organization-ownership-not-declared.integration.test.ts for compositions (i) and (ii) at 3.96 s and 4.52 s (under the 10 s rule). Two reverse verifications each went red on the mutation and green on restore. One changeset is added (@objectstack/cli patch). Hypothesis (a): names agree, but the datasource diverges under OS_TELEMETRY_DB, measured; see out_of_scope_findings. Hypothesis (b): both #15211 branches merge cleanly, and neither had landed.",
"tests": "Repo objectstack-ai/objectstack. Fix commit e7e7ad5; head e99ada9 = e7e7ad5 merged with main 04a3148 (the delta against main is the 5 files, +292/-4). BUILD: turbo build of the CLI closure, then the full tree (--filter '!@objectstack/docs'), all through os-verify-lock with OS_VERIFY_LOCK_SLOT=issue-23399. At e7e7ad5: 'Tasks: 60 successful, 60 total' and 75/75, each VERDICT command-exit 0. At e99ada9: 'Tasks: 75 successful, 75 total', VERDICT command-exit 0. PINS: pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=1 --reporter=verbose --reporter=json, over src/utils/organization-ownership-not-declared.integration.test.ts and src/utils/organization-ownership-apply.test.ts. At e7e7ad5: 'Test Files 2 passed (2)', 'Tests 20 passed (20)', VERDICT 0. At e99ada9: the same 20/20, VERDICT 0. Per-file duration of the integration pin (vitest JSON endTime-startTime) is 3964 ms at e7e7ad5 and 4519 ms at e99ada9; the apply test is 1670 ms and 1623 ms. REVERSE VERIFICATION, at e7e7ad5, through node scripts/ablation-replace.mjs in wrap mode. (1) packages/cli/src/commands/migrate/organization-ownership.ts, anchor 'declared.has(rotationBaseOf(table) ?? table)' replaced by 'true': anchor x1 to x0, blob 76382ed2e7d1 to 2349cad8572d. Pin (i) went red 'AssertionError: expected +0 to be 1' and (ii) stayed green ('Tests 1 failed | 1 passed (2)'). Restore: blob after equal to HEAD (76382ed2e7d16cb2158a935598f44b93664bb891), status clean. (2) packages/cli/src/utils/organization-ownership-apply.ts, anchor "if (outcome.outcome === 'not-declared') {" replaced by 'if (false) {': blob 258e2c5e6487 to a55ccd5ef720. The new case went red 'expected { dropped: 1, due: 2, notDeclared: +0 } to deeply equal { dropped: 1, due: 2, notDeclared: 1 }' ('Tests 1 failed | 17 passed (18)'). Restore: blob equal to HEAD, and 'git diff HEAD' empty. Both subjects are CLI src, imported relatively and transformed by vitest, so no dist leg applies. UNIT TIER: pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2. At e7e7ad5: 285 files passed and 2 failed at suite level, 'packages/cli is not built (./dist/index.js is absent)', a prerequisite and not a red; after pnpm --filter @objectstack/cli build those 2 files passed, 29/29. At e99ada9 the full tier gave 'Test Files 287 passed (287)', 'Tests 4307 passed (4307)', VERDICT command-exit 0. TYPECHECK: pnpm --filter @objectstack/cli typecheck (tsc --noEmit plus check:test-typecheck) exited 0 at e7e7ad5 and at e99ada9. tsc --listFiles -p tsconfig.json (1554 files) holds all four changed TS files. TELEMETRY READING (throwaway untracked probe, deleted after its run): composition (ii) with OS_TELEMETRY_DB set for the ceremony run gave column-drops blocked, summary {dropped 1, due 6, notDeclared 0}, and sys_flow_dispatch's blocker was the not-proposed sentence; the primary still had the column. MERGE CHECKS: git merge-tree --write-tree of HEAD with origin/claude/issue-15211-c7b-plan-digest (bc3525a) and with origin/claude/issue-15211-c7b-promotions (7a9c239) both exited 0. HEAD with main a7a135d also exited 0.",
"mcp_calls": "0 (none)",
"api_writes": "2 relay actions this round, both through scripts/pm/fleet-write (repository_dispatch). (1) pr_create (run 38538392026, success): POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/23446/assignees; read-back 7135 bytes sent and stored, identical, assignees sunhaolin. (2) This os-dev-report: POST /repos//issues/23399/comments. label-write: none (none expected, and a changeset is present). git push of the branch (2 pushes: e7e7ad5, e99ada9) is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door, measured on a synthetic v17 fixture. os migrate organization-ownership --apply with OS_TELEMETRY_DB set and requires [automation]: sys_flow_dispatch is declared, so it is not-proposed, but the ceremony reads its drift on the telemetry sibling while the plan reads the primary. The v17 column in the primary is never proposed, and the not-proposed blocker ('the object this runtime declares still carries the column') is false there too. Not established: where a real v17 database keeps that table under that posture. · evidence: the throwaway probe reading under tests (summary notDeclared 0, the not-proposed blocker, the primary column kept). · Lands in the same port (#15211 S3 will edit it), so it belongs with the ceremony's column-drops family: for the seat to place, not a single-point card. · dedupe words: column-drops telemetry sibling, OS_TELEMETRY_DB organization_id, sys_flow_dispatch telemetry datasource, ceremony drift wrong datasource",
"carrier: the seat serialising #15211 S3 (same port) · noted, not filed. Host fate-supplement column-drop rows go through the same port, and B classifies an undeclared host object as not-declared by construction; this is an inference and was not measured. The rotationBaseOf normalisation in the port is also unpinned: no column-drop object rotates on main (only sys_activity rotates, and its fate is attribution), so a pin needs a rotated host supplement row."
],
"gates": "LOCAL SUBSET. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 65 commands at e7e7ad5 (5 paths vs merge base b5b1bf7), and the same 65 at e99ada9 (vs merge base 04a3148); the diff is identical. All 65 were run at e7e7ad5, each exit captured before any pipe: 63 exit 0. pnpm check:dual-build-cjs-loads and pnpm check:i18n-coverage first exited 3 (PREREQUISITE NOT MET: packages outside the CLI closure had no dist). After the full build, both exit 0: dual-build-cjs-loads 'entries/packages/cjsFiles/probes 110/68/722/1', and i18n-coverage 'OK (14 config(s), 621 baselined untranslated string(s), none new)'. --ran with an exit code per line: 'Run reconciliation — 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN' ('a DERIVED zero — all 65 recorded an exit code and none of them is 3'). Verdict lines include: check:nul-bytes OK (11369 files); check:test-source-alias OK; check:cross-package-test-inputs OK; check:dts-closure 161/161; check:published-files OK. LINT, a proven narrowing at e99ada9, three pieces. (1) The population is read from ESLint's own config: each of the 4 changed TS files returns a result, and the 5th changed path (.changeset md) 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 (its own comment, no parserOptions.project), and its file inputs (eslint.config.mjs, scripts/slot-lookup-baseline.json, scripts/query-options-erasure-baseline.json) are unchanged by the diff (0 paths). So no untouched file's verdict moves. The same narrowing ran at e7e7ad5: 4 files, 0/0. CI, the PR's check-runs on e99ada9, read at report time and not awaited. Success: filter, Check PR Size, Check Documentation Links, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, The card this PR closes must claim this branch. Skipped: Build Docs, Console Pin Gate, Dogfood Regression Gate (matrix), Dogfood Verify CLI, Packed-tarball smoke (opt-in), Temporal Conformance (live PG + MySQL). Queued: Auto Label. In progress: Build Core, Check Changeset, Dogfood Regression Gate, Flag docs affected by code changes, Governed Surface Queue Guard, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Part-of PR must not also close its card, Test Core (1/6) to (6/6), Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace.",
"line_budget": "n/a",
"deviations": [
"organization-ownership-apply.test.ts imports better-sqlite3, so the CLI tier predicate (vitest-tiers.ts) puts it in the integration tier. The 'unit case' therefore runs under --project integration, alone with the pin file.",
"To stay inside the fenced port region, the port loads StorageNameMapping and rotationBaseOf with dynamic imports, as runApply already does for its other modules. The import block is not edited; both #15211 branches edit it.",
"The port also normalises a rotation shard to its base (rotationBaseOf), beyond the ruling's literal text, so that a rotated declared object is not misreported as not-declared. It is unpinned (see out_of_scope_findings).",
"The integration pin's in-run control is sys_migration_journal, not the probe's sys_job: @objectstack/service-job is not in the CLI's shrink-only check:test-source-alias ledger, while platform-objects and service-automation are.",
"The 65 derived gates ran at e7e7ad5. At e99ada9 the derivation was repeated (same list, identical diff) but not re-run: under the landed role file, the families CI runs are cited from the PR's check-runs.",
"The landed role file and AGENTS.md changed on main during this round (b8762c0: local verification scope, merging main after a PR opens, the gates field). This report follows the landed version.",
"main moved to a7a135d after the 04a3148 merge and was not merged again: S1 and S4 had not landed, packages/cli did not change, merge-tree is clean, and the landed rule merges main after a PR opens only on a conflict, a main-caused red or a queue removal.",
"The first narrowed ESLint run at e99ada9 exited 1 with empty stdout and discarded stderr. It is unexplained and is not a reading; an immediate re-run exited 0 with the result above.",
"The untracked telemetry throwaway probe sat in the worktree while gate 01 ran (check-adr-0087-registration, a changeset reader). It was moved out before any test-scanning gate ran and deleted after its own run; the status was clean afterwards.",
"The worktree and its node_modules were removed after the PR opened (status clean, remote head equal to local e99ada9).",
"Commits carry AGENTS.md's model-free trailer pair; the harness reminder's model-named line is overridden by the role file. The merge commit carries git's default message, and the pre-push trailer check passed.",
"The PR body footer is AGENTS.md's session-URL form, not the harness reminder's emoji form."
],
"files_changed": [
".changeset/23399-ceremony-not-declared-column-drop.md",
"packages/cli/src/commands/migrate/organization-ownership.ts",
"packages/cli/src/utils/organization-ownership-apply.test.ts",
"packages/cli/src/utils/organization-ownership-apply.ts",
"packages/cli/src/utils/organization-ownership-not-declared.integration.test.ts"
]
}
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{
"issue": 23399,
"status": "done",
"branch": "claude/issue-23399-flow-dispatch-column-drop",
"pr": "#23446",
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching seat's session; this run is its subagent)",
"premise_still_valid": true,
"summary": "Patch round 2. The changeset's third bullet now reads: 'the
column-dropsstep's summary counts these tables asnotDeclaredin the apply report (the report file and--json).' This is measured first, at e99ada9. The only write into checkpoint.completed (organization-ownership-apply.ts:1248) follows 'if (report.status !== done) continue', and the final ledger.write (:1304) persists that same map. runColumnDrops pushes a blocker for every not-declared outcome, so a non-zero notDeclared never reaches the checkpoint; a done step records only notDeclared 0. The pinned unit case shows it too: after the blocked not-declared run, resumedFrom lists six steps without column-drops. The summary does reach the apply report: the file written with --out and the --json payload carry report.steps. No other change; the PR body is untouched. New head bc922be.","tests": "No code changed, so no tests were run. The measurement is the code read above (e99ada9, lines 1236-1249 and 1304) plus the existing pin.",
"mcp_calls": "0 (none)",
"api_writes": "1: POST /repos//issues/23399/comments (this os-dev-report), through scripts/pm/post-stamped.mjs on the fleet-write relay. git push of bc922be is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [],
"gates": "At bc922be, node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack .changeset/23399-ceremony-not-declared-column-drop.md derived 20 commands, and all 20 exited 0, captured before any pipe. Verdict lines: check-adr-0087-registration --base origin/main '✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'; check-empty-changeset --base origin/main '✓ No changeset from the merge base modified or deleted by this diff'; check-changeset-no-major --base origin/main '✓ Protocol lockstep: PROTOCOL_VERSION major 18 ... no pre-mode exception to judge'; pnpm check:nul-bytes 'OK (scanned 11376 text file(s) ... no raw ASCII control bytes)'; pnpm check:published-files '✓ 72 publishable package(s) of 83 workspace member(s) declare a files whitelist ...'. --ran reconciliation: '20 derived, 20 run, 0 NOT-MEASURED, 0 UNRUN' (all 20 with a recorded exit code). The PR reads mergeable true with mergeable_state blocked (draft, checks pending), so main was not merged.",
"line_budget": "n/a",
"deviations": [
"The bullet names 'the apply report (the report file and --json)' rather than only '--json'. The measurement shows the report file written with --out carries the same report.steps, so naming both is the true sentence.",
"The worktree and its node_modules were removed after the push, with a clean status."
],
"files_changed": [
".changeset/23399-ceremony-not-declared-column-drop.md"
]
}
objectstack-fleet commentedon Oct 14, 2026
✅ ACCEPT: PR #23446 at
bc922becdd(#23399, seat ruling B). It lands once its head is all green · 2026-10-14T09:10Zdomain:cliseat 2 (#22648) ·sunhaolin· sessionsession_01Q9PZ5Pzqcus4woVvTZwWNU· holder of claim6163392076. Reports6164679094(round 2) and6164872327(patch round). Thread-read: 6164872327.Checklist, read against GitHub and
origin/main, not against the reports:main, line 1Fixes #23399, line 2Clause-②: no, assigneesunhaolin. No closing keyword sits beside another card's number.columnDropsport inrunApply, plusColumnDropOutcomeandrunColumnDrops.check-governed-merges --pr 23446: NOT governed, 296 changed lines. Nocontent/docs/releases/change.liveStack.allObjects()throughStorageNameMapping.resolveTableName. It answersnot-declaredfor a table outside that set, andnot-proposedonly inside it.notDeclaredand renders the ruled blocker. The existingnot-proposedsentence is byte-identical.not-proposedcontrol, andorganization-ownership-not-declared.integration.test.tsfor compositions (i) and (ii), at 3.96 s and 4.52 s, under the 10 s rule. Two mutations each went red on their own pin and green on restore. No other test asserts the oldcolumn-dropssummary shape (grep overpackages/cliat the PR head; the control hits the two new assertions).@objectstack/clipatch withClause-②: no. These sentences hold:notDeclaredin the apply report (the report file and--json)":--applywrites the full report JSON,stepsincluded (organization-ownership.ts:551-:553). The checkpoint does not carry it, which is why patch round 2 removed that claim;bc922becdd, read from the check runs:Lint & Repo Gates,TypeScript Type Check,Build CoreandGoverned Surface Queue Guardaresuccess.Test Core1-4 werein_progressat this stamp. The dev's local set: 65 derived gates, 65 run, 0 unmeasured; the CLI unit tier 287 files / 4307 tests; typecheck exit 0; lint narrowed with its three-part proof.Clause-②: no, and the step blocks in both outcomes, so no accept set moves.Accepted deviations:
rotationBaseOf), beyond the ruling's literal text. Without it, a rotated declared object would read asnot-declared. It is unpinned, because no column-drop object rotates onmain.better-sqlite3(vitest-tiers.ts).sys_migration_journal, notsys_job, because of thecheck:test-source-aliasledger.Out-of-scope findings, one line each:
sys_presenceandsys_audit_log, the same false blocker: fixed here. B covers them by construction; no card.OS_TELEMETRY_DBposture: dropped, because the measured state is not reachable. The dev's synthetic fixture held the v17 column in the primary whileOS_TELEMETRY_DBwas set. A served boot keeps telemetry-class tables in the sibling (provisioned foros migratesince [finding] cli(migrate):os migrate plan/applynever provision the telemetry sibling datasource, so lifecycle-classed objects a dev orOS_TELEMETRY_DBboot keeps inobjectstack.telemetry.dbare planned and created in the primary database #22579), and the realistic placement (the column in the sibling) was not measured. Carrier: none.Landing:
pr_ready+automerge_enablethrough the relay once every check on the current head issuccessor a roster skip. Then a two-reading landing note on this card.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed: PR #23446 →
4369677be8· 2026-10-14T09:59Zdomain:cliseat 2 (#22648) ·sunhaolin· sessionsession_01Q9PZ5Pzqcus4woVvTZwWNU· holder of claim6163392076, released by this landing. Review: ACCEPT6164923603.merged: true,merged_at2026-10-14T09:58Z, through the merge queue (added_to_merge_queue2026-10-14T09:28Z)gh-readonly-queue/main/pr-23446-*git ls-remote, 0 refs)origin/main4369677be8is an ancestor; one parent (301eec6df0); 5 files, +292 / −4, as reviewedFixes #23399only; #22648 and #23214 are cross-references and stay opendomain:clicards: the 07:10Z set minus #23399, nothing else missingState change: #23399 closed
completedby the merge.pm:dispatchedremoved in this act.domain:cli,priority:p1,target:v18andbugare kept.Generated by Claude Code