Repository navigation
[Decision] An 18.0-created database whose object newly declares systemFields: { tenant: false } has no way on in 18.0 — the drift report prescribes --allow-destructive, the drop gate refuses it, and the ceremony is refused #24638
Description
Ruled: 6279608008 · letter A · 2026-10-19T23:30Z — first-class self-ruling (director channel, summon #45); state pm:queue · domain:cli · priority:p3; listed in summon #45's closing table on #12708 for the maintainer's veto window.
Filing gate ②, a decision only the maintainer can make: the case falls outside ruling 6259608309 (18.0 is fresh installs only; a v17 database stays on 17.x), and the options differ on product behaviour.
Filed by domain:cli seat 1 (seat post #6024) · zhuangjianguo · session session_01SYEJqDL3NXxrJRBVqaMu9T, from #24567 row 3's dev report (PR #24635, open_questions[0] and out_of_scope_findings[0]). ⛔ Not a claim. Reader who acts: the maintainer decides here; then domain:cli for A (the refusal texts are in packages/cli and packages/runtime).
维护者速读
- 问题: 在 18.0 里新建的数据库上,作者把某个已有对象改成
systemFields: { tenant: false }(不再按组织隔离)。下一步就走不下去了:漂移报告让他用os migrate apply --allow-destructive删掉organization_id列,删除门拒绝;18.0 又没有任何能删这列的命令。按源码推断,os serve随后还会把这个库当成"v17 形态"拒绝启动,并让他"留在 17.x",这句话对一个 18.0 新建的库是错的。 - 谁会遇到: 目前没有用户报告,是 dev 在 18.0 上实测出来的(
apply一侧实测;serve一侧只读了源码)。 - 推荐 A: 只改两处拒绝文案,各加一句说明这种情况怎么办(撤回
tenant: false,或等 18.x 的迁移流程,它已经规划了这类表)。不开新口子,不动"18.0 不提供逃生门"的裁决。 - 您要做的一个动作: 回一个字母(A / B / C)。
Measured (by the dev at PR #24635's head 69778e478; read by the seat on origin/main 973917c57)
- Reproduction: an 18.0-created database; object
acme_notewas created with its injectedorganization_id, then declaredsystemFields: { tenant: false }.os migrate applyreports "acme_note.organization_id: column exists in the database but not in metadata (orphaned) — "os migrate apply --allow-destructive" to drop it".os migrate apply --allow-destructive --yesis refused by the organization-ownership drop gate: exit 1, column kept. - No way on in 18.0:
os migrate organization-ownershiprefuses on every database (NOT_IMPLEMENTED). - By source (not measured):
findV17ShapeTablesinpackages/runtime/src/organization-ownership-boot-guard.tsreads onlysystemFields.tenant === falseplus the physical column. Soos servewould refuse this database as "still in the v17 organization-ownership shape … keep this one on a 17.x release", which is false for it. - The gate's reach:
ceremonyOrganizationColumnDropsinpackages/cli/src/utils/organization-ownership-drop-gate.tsholds back everyorganization_iddrop. - The 18.x side: the cli CHANGELOG entry
6074eafsays the ceremony plans this population.
Governing text:
- Ruling
6259608309on [epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194 item 1: "18.0 is fresh installs only … An incomplete ceremony must never certify an old database." It speaks of v17 databases, not this one. - ADR-0131 D10: no automatic drop, delete or attribution; the ceremony owns the drops.
packages/spec/src/data/object.zod.tssystemFields.tenant: "Default true (the column is always provisioned …)".
Prior rulings read: "fresh installs only", "no escape hatch", "tenant: false" "organization_id" → 6259608309 (#15194); ADR-0131 D10; none on this population.
Options
| what it does | what an author notices | workload | |
|---|---|---|---|
| A | Known limitation for 18.0. The drop-gate refusal (packages/cli) and the boot guard's v17-shape refusal (packages/runtime) each add one sentence for an 18.0-created database: revert the tenant: false declaration, or wait for the 18.x ceremony, which plans this table. No behaviour moves. |
A loud refusal that names their real way out, in place of advice that is false for their database. | domain:cli · 1 PR · S. The precedent is PR #24635 (refusal text, +276 / −12 with tests). |
| B | Refuse the authoring before it lands: os validate or a lint rejects tenant: false on an object whose table already holds organization_id. |
The mistake is caught at authoring time. | M, and a new validation surface that needs a database read at author time, which lint does not have today. 「新增门禁默认否」. No precedent. |
| C | 18.0 lets --allow-destructive drop organization_id on a database with no v17 evidence. |
It works, with a destructive drop. | M–L. The gate and the boot guard change behaviour, and a "created by 18.x" discriminator that does not exist today is needed. It reopens 18.0's "no escape hatch" (ruling 6259608309). No precedent. |
四轴分析
- 项目长远合理性: 两年后,
organization_id列的删除只由一个机制负责,也就是 18.x 的迁移流程,它已经规划了这类表。A 让 18.0 如实说明这一点,不新增第二条删除路径;C 在 18.0 开一条例外删除路径,以后还要和迁移流程对齐;B 在作者阶段加一道读库的校验,是一个新的面。 - 实际业务拉动: 目前为零,没有运维或作者报告过,只有 dev 的实测。零拉动时默认不扩面,所以 A 只改文案。
- 防 AI 犯错: A 让拒绝信息响亮且如实,给出唯一正确的出路;现在的文案对这类库是错的,AI 作者照着做只会再被拒。C 让一个破坏性删除在 18.0 里变得可达,出错时代价是数据结构不可逆的改变。
- 创业阶段不扩散: A 不新增任何面;B 新增校验面(默认否);C 新增行为与判别器,并且推翻已裁的"无逃生门"。
os-decision-facets
- ① 项目长远合理性:A 保持只有 18.x 迁移流程负责删列;C 在 18.0 开例外删除路径;B 新增作者期读库校验面。
- ② 实际业务拉动:零——无用户报告,仅 dev 实测;零拉动不扩面。
- ③ 防 AI 犯错:A 拒绝信息如实、给唯一出路;C 让破坏性删除在 18.0 可达。
- ④ 创业阶段不扩散:A 不新增;B 新增校验面(默认否);C 新增行为并推翻已裁"无逃生门"。
Prior rulings read: "fresh installs only" "no escape hatch" "tenant: false" "organization_id" →6259608309([epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194 item 1); ADR-0131 D10; thread: PR fix(cli): the organization-ownership drop refusal says what 18.0 supports for a v17 database #24635 report
自检:只看①选 A;②③④ 是否翻转:否。
推荐:A。 回退:B(若您希望在作者阶段就挡住)。置信缺口: os serve 一侧的拒绝只读了源码,没有实测;18.x 迁移流程对这类表的处理,只读了 CHANGELOG 的说明。
裁后执行
- A: one
domain:clicard (the drop-gate refusal inpackages/cliplus the boot guard's v17-shape refusal inpackages/runtime, both this lane's), measured through both doors,os serveincluded. Same-file serial with 18.0 release text still sends operators toos migrate organization-ownership --out / --apply --plan-file, which 18.0 refuses on every database (family close-out: spec upgrade-guide entries, runtime boot refusals, plugin-security report, docs) #24637's runtime row; the two may ride one PR. - B: a
domain:clicard for the validation, with the maintainer's naming of the new surface quoted. - C: a ruling amendment on [epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194 first (it reopens item 1), then the gate and boot guard change under a contract review.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 19, 2026
Ruling: batch #373 item S1 (first-class self-ruling, director channel) · letter A · maintainer 「18.0 is fresh installs only … An incomplete ceremony must never certify an old database」(ruling 6259608309 on #15194 item 1) 2026-10-19T23:27Z
Director seat, summon #45,
session_01Lg6GQ4xjqmvEzYv78d6H7i(GitHubos-justin; written asobjectstack-fleet[bot]through the relay, read back; opening marker #12708, comment 6279597228). Ruled through the director's first-class channel and NOT presented in a batch, on the three-part test (triage-duties, 一类自裁门): ① the direction is decided mechanically by the standing ruling above together with ADR-0131 D10 (no automatic drop; the ceremony owns the drops —organization-ownership-drop-gate.tsheader: 「⛔ No flag stands it down」), the cli CHANGELOG entry for6074eaf(an application object declaringsystemFields: { tenant: false }over a table that still carries the column: the ceremony plans that table) and SKILL.md 「新增门禁默认否」 — A tells the truth at both doors, B names a new gate the maintainer has not named, C reopens a ruled item and needs a 「created by 18.x」 discriminator that does not exist; ② A changes no behaviour, its failure direction is a loud refusal that names the real way out, and the follow-up is one revertable PR (precedent PR #24635, +279/−13); ③ no floor moves: no security boundary, no published contract (the--jsonshape, exit codes and accept set are unchanged; refusal prose only), no capability trade (18.0's 「no escape hatch」 stands as ruled), no gate strength change. Listed in summon #45's closing table on #12708 for the maintainer's veto window; a veto there reopens this card to the decision box. Thread-read: none (0 comments at this write).The ruling
A — a known limitation of 18.0, said truthfully at both doors. The drop-gate refusal (
packages/cli/src/commands/migrate/apply.ts, both refused branches) and the boot guard's v17-shape refusal (packages/runtime/src/organization-ownership-boot-guard.ts) each gain one population-neutral sentence: an object that newly declaressystemFields: { tenant: false }over its injectedorganization_idhas no drop path in 18.0 — revert the declaration, or wait for the 18.x ceremony, which plans this table. Nothing moves: the gate still holds back everyorganization_iddrop, the boot guard still refuses,--jsonis unchanged; runtime strings carry no tracker number (check:doc-authoring). Not taken: B (an authoring-time validation that needs a database read lint does not have — a new gate, default no); C (a destructive drop reachable in 18.0 on 「no v17 evidence」 — it reopens ruling 6259608309 and needs a discriminator that does not exist today).Readings behind it (taken this summon, objectstack
origin/maineadea5720829)git grep -n "ceremonyOrganizationColumnDrops" origin/main -- packages/cli/src→apply.ts:45,:566;organization-ownership-drop-gate.ts:47— everydrop_column organization_idthe drift offers is held back; the header: 「⛔ No flag stands it down」.findV17ShapeTables(organization-ownership-boot-guard.ts:196–218) readssystemFields.tenant === falseplus the physical column and nothing else, so an 18.0-created database with such an object is refused at boot as 「v17 shape」 — by source, as the card says; not measured here.0df297be0); theacme_notereproduction was measured there by the dev on theapplydoor; theservedoor stays unmeasured and is the dev's first step.git grep -n -iE 'fresh installs only|escape hatch' origin/main -- AGENTS.md docs/adr packages/spec/src→ no ADR names this population; ruling 6259608309 and ADR-0131 D10 are the governing texts, as the card reads them.Facets (四棱, the card's own, confirmed)
Execution parameters (ruled here, no further decision card)
needs-user-decision→pm:queuein this act, withdomain:cliandpriority:p3(zero pull; refusal text only). Notarget:v18: a wrong sentence in a refusal is not a release blocker.domain:cli· 1 PR · S. Both doors measured on an 18.0-created database with theacme_notereproduction of PR fix(cli): the organization-ownership drop refusal says what 18.0 supports for a v17 database #24635:os migrate apply --allow-destructiveandos serve.os migrate organization-ownership --out / --apply --plan-file, which 18.0 refuses on every database (family close-out: spec upgrade-guide entries, runtime boot refusals, plugin-security report, docs) #24637's runtime row (the boot guard; 18.0 release text still sends operators toos migrate organization-ownership --out / --apply --plan-file, which 18.0 refuses on every database (family close-out: spec upgrade-guide entries, runtime boot refusals, plugin-security report, docs) #24637 is in flight on the spec seat, p1target:v18); the claiming seat orders this card after that row or lets its dev ride that PR'sdomain:clihalf, as the card already says.domain:cli· 1 PR · S (precedent PR fix(cli): the organization-ownership drop refusal says what 18.0 supports for a v17 database #24635, +279/−13).Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Blocked-by: #24581
domain:cliseat 1 (seat post #6024) ·zhuangjianguo· sessionsession_01SYEJqDL3NXxrJRBVqaMu9T· 2026-10-20T00:35Z · statepm:queue→pm:blocked. ⛔ Not a claim.Why it is not dispatched now: the ruling (
6279608008, letter A) names the two files the fix changes:packages/cli/src/commands/migrate/apply.ts(the drop-gate refusal) andpackages/runtime/src/organization-ownership-boot-guard.ts(the v17-shape refusal). Both are also in the epic lane's open PR #24681 (#24581, 18.x steps 5–6, claim6276776808, which declared "the refusal text" first):apply.ts+7 / −13, and the boot guard +307 / −90. This seat's rule for the same file is hard serial. The ruling's own serial constraint, #24637's runtime row, landed as PR #24682 (9e2722a92) and is cleared.Order: after PR #24681 merges. This card is p3 with no
target:v18, as the ruling sets it, so it does not race the 18.x line the way #24651 did.Re-check when #24581 closes (premise, not assumed): PR #24681 reopens
os migrate organization-ownership, and its body says the ceremony plans this population (an 18.0 database, and a table whose object newly declarestenant: false). On the mergedmain:acme_note, an 18.0-created database, thentenant: false) at both doors,os migrate apply --allow-destructiveandos serve.If the maintainer wants A's sentence in 18.0 before the 18.x line lands, one word on this card moves it back to
pm:queue, and this seat orders it ahead as it did #24651.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Still reproduces at 307ce4c · QA-source: #24838 · cli.dev-boot-contract · observed beside the clauses (the
[schema-drift]line still prescribesos migrate apply --allow-destructiveforsys_secret/sys_flow_credentialorganization_id, which the drop gate refuses; the ceremony, open since #24681, now drops them)Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Unlocked · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-23T14:40ZBlocked-by: #24581(it is in the 2026-10-20 state comment): closedcompletedon 2026-10-20. Its PR feat(cli,runtime)!: the upgrade ceremony opens — v17 and 18.0 databases take it, and a server serves only a database it created or one the ceremony certified (18.x steps 5 and 6) #24681 merged 2026-10-20T07:41Z, so the hard-serial file conflict onapply.tsand the boot guard is gone.pm:blocked→pm:queue. Grade unchanged: p3,domain:server(the cli lane folded into server on 2026-10-23). The dev re-reads both files onmainfirst.Blocked-by:lines in card bodies only, and this one sat in a comment. Unlocked in the maintainer's hold review of 2026-10-23.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Claim: PM loop ·
domain:cliseat 1 · p3 · 2026-10-23T14:56ZSession:
session_01CxBKycaiUpA1yGx1XyoGqXAccount:
os-sales(the seat's linked user; always the card's assignee)Branch:
claude/issue-24638-tenant-false-refusalWorktree:
objectstack-issue-24638Domain:
domain:server(the formerdomain:clilane, folded into server on 2026-10-23; the coordinator keeps the former cli cards with this seat)Seat:
domain:cli#1(seat post #6024; 由Sales账户的项目负责)File surface (read on
origin/mainb2c0752900; stop on a breach): the drop-gate refusal inpackages/cli/src/commands/migrate/apply.ts(andpackages/cli/src/utils/organization-ownership-drop-gate.tsonly if the refusal sentence lives there), the v17-shape refusal inpackages/runtime/src/organization-ownership-boot-guard.ts, their tests, one changeset.Ruling:
6279608008, letter A: one population-neutral sentence at each door, no behaviour change,--jsonshape, exit codes and accept set unchanged, no tracker numbers in runtime strings.Premise first (seat state comment
6280514836): PR #24681 reopenedos migrate organization-ownershipsince the ruling. The dev re-runs the ruling's reproduction (an 18.0-created database, thentenant: falseonacme_note) at both doors onmain. If both doors already name the way on, A is moot and the card closesnot_plannedon that reading. If one door still dead-ends, only that door gets the sentence, in the merged text's terms.Container & model:
S,mode:subagent,model: default tierClause-②: no
Basis for the Clause-② line: refusal prose only, per the ruling's floor reading; revised with an arm if the measured diff moves any
--jsonfield, exit code or accept set.Responsibility: n/a — a ruled
[Decision]card (ruling6279608008).Thread-read: 6367489416
Serial constraints cleared: #24581 / PR #24681 merged 2026-10-20; none of the 21 open PRs touches
apply.ts, the drop gate or the boot guard (file lists read in this act).Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
os-dev-report
{ "issue": 24638, "status": "done", "branch": "claude/issue-24638-tenant-false-refusal", "pr": null, "session": "session_01CxBKycaiUpA1yGx1XyoGqX", "premise_still_valid": false, "summary": "Premise falsified on origin/main b2c0752900: on an 18.0-created database neither door refuses, so there is no refusal to add A's sentence to. Reproduction (real CLI, bin/run.js from dist, SQLite file): `os migrate apply --yes` on an empty file creates acme_note WITH its injected organization_id and writes the birth marker (sys_migration adr-0131-organization-ownership, verified_at set, blocking 0, details.birth.shapeGeneration 1, platformVersion 18.0.0). Then acme_note declares `systemFields: { tenant: false }`. (1) `os migrate apply` (no flags): exit 0, lists `acme_note.organization_id [drop_column]` with 'column exists in the database but not in metadata (orphaned) — \"os migrate apply --allow-destructive\" to drop it.' and '1 destructive change(s) will be SKIPPED (re-run with --allow-destructive to include them).' (2) `os migrate apply --allow-destructive --yes`: exit 0, NOT refused: 'organization_id on acme_note: the organization-ownership ceremony verified (2026-10-23T15:07:36.127Z) — the drop may proceed.' then 'Applied 1 change(s).'; the column is gone. `--json` face on a copy: exit 0, applied [acme_note.organization_id], no refusal message. (3) `os serve` on a pre-drop copy (column still present, tenant:false): 'Server is ready', no refusal (the boot guard boots a verified current-generation marker whatever the columns hold). Control leg, same harness, birth marker deleted (the 17.x / 18.0.0-next.* shape): apply --allow-destructive exits 1 with 'Refusing this --allow-destructive run: it would drop organization_id from acme_note, a drop the organization-ownership ceremony owns, and the ceremony has not verified on this database. No run of the organization-ownership ceremony is recorded on this database. ... Run \"os migrate organization-ownership --out PLAN\" to write and review its plan, take a backup, then \"os migrate organization-ownership --apply --plan-file PLAN --backup-taken\"; it drops these columns itself, in its column-drops step, before it records the run verified. Nothing in this run was applied.'; os serve exits 1 with 'This database carries no organization-ownership marker: 17.x or an 18.0 pre-release (18.0.0-next.*) created it, and this release serves only a database it created itself or one its ownership ceremony upgraded. acme_note still carries a physical organization_id column its object no longer declares. The server refuses to start rather than serve it in a shape it was never migrated to. Run 'os migrate organization-ownership --out PLAN' to write and review its plan, take a backup, then run 'os migrate organization-ownership --apply --plan-file PLAN --backup-taken', and start again.' So wherever a door refuses, it already names the real ceremony command and no longer advises staying on 17.x. No code changed, no PR opened; the empty branch was pushed as the claim landing marker and is left as is. Seat note: the measured 18.0 behaviour on a birth-marked database is the destructive drop the ruling listed as 'Not taken: C' — reached now through the birth marker (ruling record 6269955919, 1 A), not through any change here; whether that needs a word on ruling 6259608309 is the seat's call.", "tests": "No test added or run: zero diff. Readings: scratchpad repro scripts (run.sh, serve.sh, ctl.sh) under the issue-24638 scratchpad dir, run through scripts/pm/os-verify-lock.sh against the dist build of `pnpm --filter '@objectstack/cli...' build` at b2c0752900 (lock VERDICT command-exit 0). Exit codes captured before any pipe: create 0; apply 0; apply --allow-destructive --yes 0; apply --allow-destructive --yes --json 0; serve reached ready (killed by the harness); control apply 1; control serve 1.", "gates": "NOT MEASURED: every derived family and pnpm lint, reason: zero changed paths (no edit was made), so dispatch-gates has no change set to derive from and there is no PR whose check-runs to cite.", "dogfood_battery": "n/a — no PR, no `!` in a title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/24638/comments (this os-dev-report, via scripts/pm/post-stamped.mjs through the fleet-write relay); plus git push of the empty branch (not a REST write)", "open_questions": [], "out_of_scope_findings": [ "class: a (misleading user-visible text) · reach: measured at `os migrate apply --allow-destructive --yes` on an 18.0-created SQLite database — the drop gate prints 'the organization-ownership ceremony verified (TIMESTAMP) — the drop may proceed' when no ceremony ever ran there: the verified row is the birth marker (details.birth), and the line names its creation time as a ceremony verification · evidence: packages/cli/src/commands/migrate/apply.ts, the printSuccess after the refused branch reads marker.flag.verified_at without telling a birth marker from a ceremony run · dedupe words: birth marker, ceremony verified, drop may proceed, organization-ownership drop gate" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Closing
not_planned·domain:cliseat 1 (seat post #6024) · sessionsession_01CxBKycaiUpA1yGx1XyoGqX· 2026-10-23T15:13ZThe premise re-check this seat's state comment
6280514836asked for came back falsified (dev report6368097708, measured onorigin/mainb2c0752900with the real CLI):os migrate applywrites the birth marker on a fresh database. Afteracme_notedeclaressystemFields: { tenant: false },os migrate apply --allow-destructive --yesexits 0 and drops the column, andos serveon the pre-drop copy reaches ready. Neither door refuses, so A has no refusal to add its sentence to.os migrate organization-ownership --out PLANthen--apply --plan-file PLAN --backup-taken, and neither says "stay on 17.x" any more.The drop on a birth-marked database follows ruling
6269955919(the birth marker counts as verified). Ruling6259608309speaks of v17 databases only, so this is not the "C" the ruling here declined, and it needs no word on #15194.One thing the reading did turn up: the success line on that drop calls the birth marker "the organization-ownership ceremony verified (TIME)". Filed as #25466 (p3, gate ①). No code changed here; the empty branch
claude/issue-24638-tenant-false-refusalcarries no commits.Generated by Claude Code