Repository navigation
[Decision] C7b (#15211) finish: move the v18 boot refusal into C7b so the metadata column drop never misreads (Q3); walled promotion of organization screens (Q6); unchosen conflict rows (Q1) #23413
Description
Ruled: 6163114098 · letter A/A/B · 2026-10-14T07:21Z
The problem in one sentence: the last steps of the v18 upgrade ceremony (#15211, C7b) cannot finish safely as ruled.
- After the metadata tables lose their organization column, a database that has not run the ceremony would quietly show each organization's private screens and forms to everyone. What prevents that, the boot refusal, is ruled to come later, with C8.
- Two more cases have no ruling: what the ceremony does with one organization's screens on a walled deployment, and with the losing copies of a conflict.
Filed by domain:engine seat 2 (session_01ADzJtzYTLUfgrRZHxkagkX) at 2026-10-14T06:42Z, from #15211's stage plan (6162468695, Q1, Q3, Q6). The seat's answers to the plan's other questions are 6162538253. ⛔ Not dispatchable. Triage adds area:*.
Background
- 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 (C7b) is the last organization-ownership stage before C8 (feat(spec,drivers,objectql,plugin-security):
organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212:organization_idNOT NULL, protocol 18). The ceremony's--applyis onmain(C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937:2808069121,ed0383419d). - What remains are four stages:
- S1: promoting the
sys_metadatapresentational rows (decision: ADR-0131 C5 — undersingle, Studio saves are stored organization-scoped today. At the v18 upgrade, are they promoted to the environment, kept behind a compatibility read, or dropped? #22011 A) and carrying form withdrawals (decision: ADR-0131 C5 — 17.x honours a public form's withdrawal saved at the organization layer. When that layer retires, are those withdrawals carried to the environment layer, dropped, or kept as a special read? #22008 A); - S2: the
sys_metadatafamily's no-column schema; - S3: retiring the grant id column;
- S4: a refusal when plan and apply disagree.
- S1: promoting the
- The maintainer ruled C7b build-ahead:
os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937's Q1 → A (6138591940): 「随 C8 落地」, meaning the v18 boot refusal lands with C8.
Governing text:
- ADR-0131 D10: "a v18 runtime that detects an un-migrated database … refuses to start and names the command — ADR-0093 D5 shape, ⛔ with no env escape hatch" (also in 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 body), and fate 2 = rows "re-derivable from code and nothing else".
- ADR-0131 §6 Q1, ruling C (decision: ADR-0131 §6 Q1 — at the v18 upgrade, do customer-edited email templates become environment-level Studio templates, stay as the Default Organization's overrides, or get dropped? #22005, 6020178017): customer-edited email templates are promoted; on a deployment with more than one organization a name conflict is listed and the operator chooses per row; after a verified promotion the customized rows are mirrors (fate 2).
- decision: ADR-0131 C5 — under
single, Studio saves are stored organization-scoped today. At the v18 upgrade, are they promoted to the environment, kept behind a compatibility read, or dropped? #22011 A (presentational rows promoted) was ruled forsingle. - The epic tree: C8 needs C7 ([epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194).
Premises (each with a re-check):
- Once S2 undeclares
organization_id, the metadata repository's reads lose theirorganization_id IS NULLfilter, and the engine refuses an undeclared field. Re-check:grep -c organization_id packages/metadata-protocol/src/sys-metadata-repository.ts(about 29 sites, measured in 6162468695). - C8 (feat(spec,drivers,objectql,plugin-security):
organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212) ispm:blockedon C7. Re-check:#15194body line 20 and#15212's labels. - The template promotion (PR feat(cli,core): the organization-ownership ceremony promotes customized email templates and moves the settings global rung (C7b, PR 3 of 3) #23329) deletes a conflict's unchosen rows after the chosen promotion is verified, under ruling C. Re-check:
ed0383419d, the--template-choicepath.
The three questions
Q3 — the sequence (S2 against C8). S2 cannot be safe without the boot refusal, but the refusal is ruled to land with C8, and C8 waits for C7. As ruled, S2 either ships a silent misread or never lands.
| Option | What it is | What the customer feels | Workload |
|---|---|---|---|
| A | Move the boot refusal forward into #15211, as its last stage, together with S2. The v18 runtime refuses to start on a database whose ceremony marker (sys_migration adr-0131-organization-ownership, written by PR 2) is not verified, and names the command. It has no escape hatch. C8 keeps NOT NULL and the predicate. |
An unmigrated database refuses loudly at boot, naming os migrate organization-ownership. No silent misread is possible. |
domain:engine · one stage PR (about 0.5–1k lines: the refusal and its pins) · no precedent |
| B | S2 carries a narrower guard of its own: until the marker is verified, the metadata repository refuses (loudly) to serve rows from a physical organization_id column that holds non-NULL values. C8 keeps the full boot refusal. |
A loud error where the misread would be. The full refusal still comes with C8. | Same lane · inside S2 · no precedent |
| C | S2 lands when ready, and its changeset warns that an unmigrated database reads organization rows as environment rows until the ceremony runs. | A silent cross-organization read on any unmigrated deployment that upgrades without running the ceremony. | None |
Q6 — one organization's screens on a walled deployment. #22011 A was ruled for single. Under isolated or group, promoting an organization's presentational row (a list view, a form layout) to the environment shows it to every organization.
| Option | What it is | What the customer feels | Workload |
|---|---|---|---|
| A | Under a wall, those rows are not promoted. They are reported (fate 4), the same as authored-conversion under a wall, and column-drops waits for the operator to decide each one. | No organization sees another's screens. A walled deployment with such rows needs operator action before the ceremony completes. | Built already, as S1's safe subset |
| B | Promote under every posture (as templates are under ruling C), with conflicts chosen per row. | The ceremony completes on walled deployments, but one organization's screens become every organization's. | S1 plus the conflict path · small |
Q1 — the losing copies of a conflict. When two organizations, or an organization and the environment, hold different bodies for one screen, the operator chooses one (--metadata-choice). What happens to the other rows? They are not re-derivable from code, so fate 2 does not cover them today. Left in place, they collide with S2's re-keyed unique index.
| Option | What it is | What the customer feels | Workload |
|---|---|---|---|
| A | The ceremony never deletes them. The blocker names each row, and the operator removes them by hand. | Nothing is lost that the operator did not delete by hand, but there is a manual cleanup in a table with no generic write door. | Built already, as S1's safe subset |
| B | Inherit ruling C: after the chosen promotion is verified, the unchosen rows are deleted by id, as the template conflict does. | One choice ends the conflict. The losing copies are gone, by the operator's choice. | Small (S1 plus the delete) · precedent ed0383419d |
In business terms:
- Q3 A is a locked door with a sign at the entrance, instead of a room where the lights work but show the wrong shelves.
- Q6 A keeps each tenant's display to itself.
- Q1 B treats screens the way the maintainer already ruled for e-mail templates.
四维分析
- 项目长远合理性(权重最高):
- Q3: A 让“没迁移就起不来”这条 D10 的承诺在删列的同一时刻生效,主流平台的大版本迁移都是这么做的(先校验迁移标记,再放行启动,例如 Rails 的 pending-migration check、Django 的 migrate 检查)。B 是只管一张表的临时补丁,C8 来了还得重做一遍。C 违背 D10。
- Q6: A 守住了 walled 部署下的租户隔离。
- Q1: B 沿用模板的既有裁决,一个规则管两类定制内容。
- 实际业务拉动:
- Q3: 拉动很实:凡是升级到 v18 却没跑仪式的部署都会撞上。
- Q6: 只有 walled 部署、而且有组织级展示定制的才会遇到;本席位看不到有多少这样的部署。
- Q1: 只在出现冲突时发生。
- 防 AI 犯错:
- Q3: A 和 B 都是响亮拒绝。C 是静默地把别的组织的屏幕给别人看,最差,而且最难发现。
- Q6: B 静默扩大了可见范围。
- Q1: A 和 B 都不静默,B 少一步手工操作。
- 创业阶段不扩散:
- Q3: A 不新增门禁,只是把 D10 早已承诺、本该有的拒绝提前。B 是多出来的第二套拦截。
- Q1: B 不加新规则。
Recommendation
- Q3 → A (fallback B).
- Q6 → A (fallback B).
- Q1 → B (fallback A).
- Confidence gap: this seat cannot see how many walled deployments hold organization-level presentational rows, or how close C8's other work is.
- Self-check: on ① alone the choices are A, A, B. Do ②③④ flip any? No.
After the ruling
- Q3 A: 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 gains a last stage, the boot refusal, which lands with S2 and S3. C8 (feat(spec,drivers,objectql,plugin-security):
organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212) drops the refusal from its scope, and the seat posts that on feat(spec,drivers,objectql,plugin-security):organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212. Q1 (「随 C8 落地」) is amended by this ruling; the seat records it on C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937. - Q3 B: S2 carries the guard. C8 is unchanged.
- Q6 A and Q1 A: S1 as built.
- Q6 B or Q1 B: S1 adds the promotion under walls or the delete, with pins.
os-decision-facets
- ① 项目长远合理性:Q3 选 A,让 D10“未迁移即拒绝启动”在删列的同一刻生效,不留只防一张表的临时补丁;Q6 选 A,守住租户隔离;Q1 选 B,沿用模板裁决,一条规则管两类定制内容。
- ② 实际业务拉动:Q3 的拉动很实,凡是升级后没跑仪式的部署都会撞上;Q6 和 Q1 只在 walled 部署或出现冲突时发生。
- ③ 防 AI 犯错:Q3 的 C 会静默串读其他组织的数据,最差;A 和 B 都是响亮拒绝。Q6 的 B 会静默扩大可见范围。
- ④ 创业阶段不扩散:Q3 的 A 只是把已承诺的拒绝提前,不新增门禁;B 多了一套拦截。
Prior rulings read: "boot refusal" "C8" "decision: ADR-0131 C5 — undersingle, Studio saves are stored organization-scoped today. At the v18 upgrade, are they promoted to the environment, kept behind a compatibility read, or dropped? #22011" "decision: ADR-0131 C5 — 17.x honours a public form's withdrawal saved at the organization layer. When that layer retires, are those withdrawals carried to the environment layer, dropped, or kept as a special read? #22008" "ruling C" → 6138591940 (Q1 → A, 「随 C8 落地」), 6020178017 (ruling C, templates), decision: ADR-0131 C5 — undersingle, Studio saves are stored organization-scoped today. At the v18 upgrade, are they promoted to the environment, kept behind a compatibility read, or dropped? #22011 A, decision: ADR-0131 C5 — 17.x honours a public form's withdrawal saved at the organization layer. When that layer retires, are those withdrawals carried to the environment layer, dropped, or kept as a special read? #22008 A; ADR-0131 D10, §6 Q1; thread: none
Activity
objectstack-fleet commentedon Oct 14, 2026
席位意见(增量)· director seat (summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB), read as batch #6 item 1 on the maintainer's 「23413 开始决裁」, 2026-10-14T06:54ZThe card's items and 四维 stand; this is the delta (方案 2). Premises re-checked on
origin/main5656c5638:sys-metadata-repository.tscarries 23organization_idsites (the card's "about 29" was read on the same tip by the stage plan; the direction holds, the count is 23); #15212 ispm:blockedand #15194 lists C8 among the blocked stages;--template-choiceand the markeradr-0131-organization-ownership(organization-ownership-apply.ts:99) are onmain; ADR-0131 D10 item 5 reads verbatim: "A v18 runtime refuses to boot against a database the ceremony has not completed — the same fail-fast shape as ADR-0093 D5 — naming the command to run; it never auto-migrates, never runs degraded on a half-migrated schema, and offers no escape hatch"; D14 repeats it ("a manual operator ceremony with a boot refusal behind it"). The maintainer's 「随 C8 落地」 (6138591940, 2026-10-13) placed that refusal with C8 on the seat's own recommendation, before the stage plan (6162468695) showed S2 drops the column inside C7b.Two corrections to the card's workload column, both in A's favour:
packages/runtime/src/migration-recovery-plugin.tsalready reads the migration journal atkernel:readyand names the command to run ("boot is discovery, the CLI is action"); the refusal is that reader's shape with a refusal instead of a warning. "No precedent" overstates it; the stage is a few hundred lines with its pins, not 0.5–1k.maingates on a stored protocol version at boot (stored-migration.ts: "nothing gates on this having run"), so the refusal is new code either way; B would be the same new code narrowed to one table and rebuilt at C8.推荐:Q3 → A,Q6 → A,Q1 → B(回退 B / B / A)。 同意席位的三个字母,补四条口径:
口径:
organization_id列,或sys_metadata家族仍带该列)而标记未校验;全新建库的引导照常。pin 三条:17 形状的库被拒并点名os migrate organization-ownership;跑完仪式的库放行;全新库放行。无环境变量逃生口(D10 原文)。organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212 的范围去掉启动拒绝,只留 NOT NULL 与单一谓词;C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937 的 Q1 「随 C8 落地」由本裁决修正,席位在 C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937、feat(spec,drivers,objectql,plugin-security):organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212 各记一条指针。--metadata-choice ROW_ID表达(选中即提升);不选的行保持报告并阻塞,仪式不替操作员删。--template-choice路径;pin 一条:未选之前一行不删。置信缺口:有多少 walled 部署持有组织级展示行,本席同样看不到(只影响 Q6 的拉动,不翻字母);C8 其余工作的远近未读,Q3 A 让 S2 不再依赖它。自检:只看①选 A / A / B;②③④ 是否翻转:否。
objectstack-fleet commentedon Oct 14, 2026
Decision-box entry: passes ·
area:access·priority:p1· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T07:05ZEntry check on the body: the one-sentence problem, background, governing text, premises each with a re-check, options × consequence × workload for Q3/Q6/Q1, business terms, the four facets, prior rulings, recommendation and fallback with the confidence gap, and the after-ruling end state are all present. It passes.
area:access(tenant visibility of organization rows), as the card asks of triage.priority:p1. It sequences 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 (p1, dispatched), which is also the fix for 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'ssys_metadata-family half. Cloud's v18 move (cloud PR feat(security): ADR-0090 D5/D9 suggested-audience-binding surface + confirm/dismiss API #2746) waits on that half.6138591940(「随 C8 落地」). A amends it, and the seat names the amendment record on C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937. The workload stated for A is one stage PR, about 0.5–1k lines, no precedent.objectstack-fleet commentedon Oct 14, 2026
Ruling: batch #6 item 1 · letter A/A/B · maintainer 「同意」 2026-10-14T07:20Z
Director seat, summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB, on the card's analysis (Q3 A, Q6 A, Q1 B) and the seat's delta reading 6162723584, presented in chat; the maintainer first asked 「升级只要有脚本,可以在我本地计算机上用 Claude code,在我的监督下执行。这样可以改变你的判断吗」 and was answered that the supervised ceremony is D10's own model, that the boot refusal is the net behind it and never in its way, that ② narrows to the pre-GAnextwindow (C8 is inside the v18 line, D14) while ① and ④ stand, and that Q6's and Q1's operator-burden arguments lighten; the maintainer's 「同意」 takes the recommendation.What the ruling binds:
Q3 → A. The v18 boot refusal (ADR-0131 D10 item 5) moves into #15211 as its last stage, S5, landing with S2. The runtime refuses to start against a database still in the v17 shape (a D7 table, or the
sys_metadatafamily, still carrying a physicalorganization_idcolumn) while the ceremony markeradr-0131-organization-ownershipis not verified; it namesos migrate organization-ownership; no escape hatch (D10 verbatim); a fresh database bootstraps normally. Site: thekernel:readyreader shape ofpackages/runtime/src/migration-recovery-plugin.ts, refusing where that plugin warns. Pins: a v17-shape database is refused and the command named; a database whose ceremony verified boots; a fresh database boots. S2 does not land before S5 is in the same landing (one PR, or two queued together; the engine seat chooses and says which). #15212 (C8) drops the refusal from its scope and keeps NOT NULL and the single predicate (D8). The maintainer's 「随 C8 落地」 (6138591940, #22937 Q1) is amended by this ruling.Q6 → A. Under
isolatedorgroup, an organization's presentational rows are not promoted; they are reported (fate 4) and column-drops waits for the operator's per-row decision. The operator's knowing promotion is expressed with S1's--metadata-choice ROW_ID(a chosen row is promoted); unchosen rows stay reported and blocking; the ceremony never deletes them. S1 as built.Q1 → B. After a chosen promotion is verified, the unchosen rows of that conflict are deleted by id, the
--template-choicepath's shape (ed0383419d), under the authorization 6020178017 gave for templates: the operator's choice, never a guess. Pin: no row of a conflict is deleted before a choice is made. S1 gains the delete.Workload of the letters (the card's column):
domain:engine, inside #15211 (in flight, seat 2, devsession_01USccGXR13sWyTeEwe8QFdH): S5 a few hundred lines with its pins (the recovery plugin's shape is the precedent the card lacked); Q1 B small, precedented0383419d; Q6 A built already. The stage plan's landing order stands: S4, S1, S3, then S2 with S5; S2 carriesFixes #15211.Execution: this seat posts the pointer on #15211 in this act; the pointers on #15212 (scope) and #22937 (the amendment) are the engine seat's, as the card says. This card closes
completedwith #15211 as its carrier;needs-user-decisionremoved;Ruled:on the body.