Repository navigation
[decision] Should the seed loader and #8686's backfill follow #13491's per-object tenancy classification, or stay on the namespace regexp? #14096
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3
on Sep 1, 2026 路由 ——
domain:engine·priority:p2·needs-user-decisiondomain:engine车道 PM,sessionsession_01F3jdziLbAPGeceVNmSox5L。由 #13637 的席位按我的派发裁定拆出。⭐ 卡的形状是对的:三个选项、⛔ 不给推荐,理由是「选择需要关于已部署装机的知识,而这个仓库提供不了」。⛔ 我不推翻这份克制,也不代裁。⭐ 但我要加一条:有一个可测量的前置,它会决定这是不是一个真问题
卡把分歧描述为「两条 seed 路径与运行时路径决定方式不同」。
⚠️ 那还不是最锋利的问法。运行时的判定来自一个人工裁定的台账(PLATFORM_OBJECT_TENANCY),⇒ 真正要问的是:⭐ 该台账把多少个对象判得与命名空间正则
/^(sys_|cloud_|ai_)/不同?- 若为零 ⇒ 三处对每一个真实对象给出相同答案,分歧纯属形式 ⇒ A(记录现状)代价为零,且本卡应降为 p3。
- 若非零 ⇒
⚠️ 对那些对象,seed 与 backfill 今天就在写运行时不会那样写的行 ⇒ 这不是文档不一致,是两个写者对活数据不同意;⇒ 立即改判 p1,且 A(什么都不做)需要一个比「省事」更强的理由。
⛔ 这个读数不需要任何裁决就能拿到(读台账 + 对每个键跑正则),⭐ 且它是可派发的:纯测量、不改行为、不碰存量数据。⇒ 与 #14080 的普查同形 —— 先量分母,再裁 A/B/C。
p2 的判据(⛔ 写明以便被推翻)
⛔ 不判 p1:
⚠️ 上面那个分母未测。在它为零的世界里,本卡只是把一个已存在的形式分歧记录下来。
⛔ 不判 p3:⚠️ 卡自己点出的那条最重要 ——#13491 再裁定的执行点 3(⛔ 永不静默改写行为)适用于每一个答案,包括「维持现状」。⇒ 不做也是一个必须被写下来的决定,而树里目前记录了分歧、却没有记录有人选择了它。
⇒ ⭐ 一个「未被裁决的状态」和一个「被裁决为维持现状的状态」在代码里长得一样,但对下一个作者的价值完全不同。这本身就值 p2。
对三个选项的一点补充(⛔ 仍不选边)
- A 的隐藏代价卡说全了:每个未来读者都要重问一遍。⭐ 但若上面的分母为零,A 是明显正确的,且应当连同那个读数一起记进代码。
- B 的代价卡也说准了:
⚠️ 同一个对象可能同时持有两种读法下打戳的行,而没有任何东西记录哪行是哪种。 ⇒ 这实际上把今天的分歧烙进数据里,比留在代码里更难查。 - C 撞人工地板最硬。⭐ 且卡援引的反向判例很有力:Seed loader writes untenanted rows while the REST path stamps an organization — one single-tenant install runs two autonumber scopes and mints duplicate business identifiers, silently (17.0.0 GA) #8686 自己的姿态是「已铸出的重复项只报告、从不重写」 ⇒ 同一条车道上的既有裁定不支持 C。
相邻但不重复
#13636(同一次再裁定无法表达的第三种租户状态,p1,未派)是相邻问题,⛔ 不是重复 —— 那张问的是分类体系本身不够用,本张问的是已有分类该不该扩散到另外两条写路径。
⚠️ 但两张若同时被裁,应一并看:若 #13636 的结论是分类体系要改,本卡的 B/C 就是在扩散一个即将变化的分类。
Generated by Claude Code
裁决:条件裁 —— 普查前置;零则 A 并入册,非零则改判 p1 回箱与 #13636 联裁(维护者 2026-09-01,总监批 #25)
项目总监席 · session
session_01KGtaLpkW1mycWgkbSb3H6t· 维护者对本批逐字:「同意」。- 普查先行(派发的第一交付物,纯测量零裁决):读
PLATFORM_OBJECT_TENANCY台账,对每个键跑/^(sys_|cloud_|ai_)/,量出台账与正则判得不同的对象数; - 零 ⇒ A:两条 seed 路径维持正则,把「维持现状」记录为被选择的决定 —— 两个正则站点各加一行注记引用本裁决(满足 design:
isSystem写入是否在租户审计控制范围内?——#13178 类级装置(A/B/C)的共同前置,从未被裁过 #13491 执行点 3「不做也是必须写下的决定」),卡关 completed,归档级降 p3; - 非零 ⇒ ⛔ 停手,带对象清单回决策箱改判 p1 —— 那时这不是形式分歧,是两个写者对活数据不同意;B/C 届时上桌,且与 design: a THIRD tenancy state the 批 #9 re-ruling cannot express — objects that are CONDITIONALLY tenant-scoped, where org-less is a property of the ROW, not the object #13636(分类体系第三态)联裁:⛔ 不在分类体系可能要改的当口扩散它;
- C 的反向判例入册:Seed loader writes untenanted rows while the REST path stamps an organization — one single-tenant install runs two autonumber scopes and mints duplicate business identifiers, silently (17.0.0 GA) #8686 自己的姿态「已铸出的只报告、从不重写」—— 同车道既有裁定不支持对存量行的重写,C 即便届时也需独立强理由;
- Clause-②:普查与注记均 no;若走到 B/C 再按 diff 复判。
状态转移(同笔)
needs-user-decision→pm:queue;priority:p2domain:engine不动。
Generated by Claude Code
- 普查先行(派发的第一交付物,纯测量零裁决):读
Maintainer ruling recorded — none of A / B / C; the question is absorbed by the total-organization-ownership record (PR #14976); the joint ruling with #13636 is void
Recorded by the ADR-0131 drafting PM session (
6679d191-11f4-465b-b322-0e0409d76793) under the maintainer direct-dispatch channel.Provenance. Maintainer, 2026-09-04, live chat, verbatim: 「同意」 — to the proposal: record on #14096 that A, B and C are all not adopted; the seed loader's
sys_/cloud_/ai_stamping exemption is withdrawn under the record's D3/D9 (card C1: every seed row is stamped with the resolved organization or the load refuses); existing rows are attributed to their real organization by the v18 manual migration ceremony under D10 (card C7, fate 3, with these eight objects as the inventory's first named members); the joint ruling with #13636 is void because #13636 is superseded by the same record (§1.6).Why the three options dissolve. The census (PR #15122, merged) measured that two seed paths cut the platform namespace by a regexp while the runtime cuts it per object. The record removes the category the regexp was protecting — there are no platform-global seeds, because code-declared metadata is never seeded and every seeded row is an organization's data — so the two paths do not need to consult a ledger (B is subsumed by "stamp everything"), existing NULL rows are repaired inside a planned, backed-up, per-table-gated ceremony rather than by a standalone data card (C's substance without its risk), and "record the divergence and keep it" (A) is exactly the state the record's D1 forbids.
The eight objects —
sys_file·sys_upload_session·sys_approval_request·sys_approval_action·sys_approval_approver·sys_automation_run·sys_notification_delivery·sys_record_share— enter the record's §8 C7 row by name as fate-3 members (attribution via parent anchor; undersinglethe Default Organization).17.3. Nothing lands under this card. The #13491 gate shipping in 17.3 already refuses an org-less system write on these eight under a walled posture and derives the sole organization under
single; the seed paths' regexp only decides what they pass, the engine decides what lands.State.
needs-user-decision→pm:blockedin one read-modify-write with comparative read-back;pm:blocking,priority:p1,domain:engineuntouched.
Blocked-by: #14976
Unlock-action: fold into the C1 / C7 cards cut from the merged record (this card closes asPart ofthem).🤖 Generated with Claude Code
- added a commit that references this issue
on Sep 4, 2026 Presented in decision batch #26 (2026-09-04) — the maintainer's reply: 「其他不处理」. Not ruled; not to be re-presented by the director seat without new facts.
Director seat, summon 14 (
session_01LsEjuNMPitCHwEfYftZ1um, GitHubos-warren). The card was put to the maintainer with the eight-object census result (comment 5534164987, PR #15122) and the recommendation D — hold until ADR-0131 (PR #14976) merges, no seed change, the eight objects handed to the ADR's §8 plan as a named input, fallback B. The maintainer declined to rule this round, verbatim: 「2 · objectui#7580 同意;objectstack#14927 同意;其他不处理」.State, stated so no seat reads silence as a ruling:
needs-user-decisionstays — the decision is pending.priority:p1·pm:blocking·domain:engineunchanged; unassigned.- The 2026-09-01 conditional ruling (5494594783) has run its course: the census was non-zero, the card returned to the inbox as that ruling directed, and the B / C choice it reserved for this case is what remains unruled. ⛔ No seed path and no row on disk moves.
- PR docs(audits): #14096 census — tenancy ledger disagrees with namespace regexp on 8 objects #15122 (the census record,
Part of #14096, draft) is the engine seat's to land as a docs-only record; it settles nothing on this card. - design: a THIRD tenancy state the 批 #9 re-ruling cannot express — objects that are CONDITIONALLY tenant-scoped, where org-less is a property of the ROW, not the object #13636 stays
pm:awaiting-maintainer; ADR-0131 (PR docs(adr): ADR-0131 — organization ownership is total: no NULL organization_id; declared metadata stays in code; rows only when an organization authored them (Refs #13564) #14976, draft) is the maintainer's own record and is not gated on this card.
The director seat will not re-present this card unless a seat brings a new fact (e.g. ADR-0131 merged, which would make option D executable as a hold-release rather than a ruling). Ledger: objectstack#12708, batch #26 entry.
Generated by Claude Code
Correction — my note above (5535694896) is superseded: this card was ruled three minutes earlier through the maintainer direct-dispatch channel.
Director seat, summon 14 (
session_01LsEjuNMPitCHwEfYftZ1um, GitHubos-warren). Comment 5535672867 (04:30:40Z, ADR-0131 drafting session, maintainer verbatim 「同意」) records the ruling: none of A / B / C; the seed stamping exemption is withdrawn under the record's D3/D9 (card C1), the eight objects enter the §8 C7 inventory by name, the joint ruling with #13636 is void (§1.6); statepm:blockedon #14976 withUnlock-actionfolding into C1 / C7. My "not ruled" note at 04:33:42Z was written without having read it and is withdrawn; the maintainer's 「其他不处理」 to the director seat meant "already handled elsewhere", not "pending". The state set by 5535672867 stands; nothing here changes it.Ledger: objectstack#12708, batch #27 entry.
Generated by Claude Code
Unblocked and re-aimed: the upstream this card waited on is merged, and the answer it asked for is in the record.
Blocked-by: #14976is discharged — ADR-0131 merged tomainon 2026-09-04, approved by the maintainer.The question this card asked
Should the seed loader and #8686's backfill follow #13491's per-object tenancy classification, or stay on the namespace regexp?
Neither. ADR-0131 D9 removes the question rather than choosing between its two options: there is no classification to consult, because there is no class of row that may arrive unstamped.
- The seed loader's exemption of
sys_/cloud_/ai_seeds from organization stamping is withdrawn — every seed row is stamped with the resolved organization or the load refuses. There are no platform-global seeds left to classify (D2: declared metadata is not seeded at all). - A missing stamp is refused in every posture; only
singlederives the owner, from the Default Organization, which becomes a boot invariant that runs before seed datasets load (D3, D11). - The design:
isSystem写入是否在租户审计控制范围内?——#13178 类级装置(A/B/C)的共同前置,从未被裁过 #13491 per-object ledger itself retires as machinery in the same major (D13/C8): a ledger that classifies which objects may hold organization-less rows has no reader once no table may.
So the namespace regexp and the per-object classification both go; the third option — no exemption at all — is the ruling.
Where the work went
The eight
sys_objects this card's census enumerated are covered by the execution cards, not by this one:- feat(objectql,plugin-auth): the Default Organization is load-bearing under
single; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195 (C1) — the seed-loader exemption withdrawal, the Default Organization boot invariant, andresolveSystemInsertOrganizationrefusing in every posture. - C7 (to be cut) — the four fates for existing rows, including the first-boot residue
seed-tenancy-backfill.tshandles today, as a manual operator ceremony rather than a boot step (maintainer, 2026-09-04: 「客户数据变化比较大,而且需要手工执行升级脚本」).
State
⛔ This card does not return to
pm:queue. Its decision is made and its implementation is elsewhere, so returning it would put a decided question back in a dispatch queue. It moves to the v18 gate as a pointer: closed as completed by ruling, with the execution tracked on #15194.Ruled by ADR-0131 (merged #14976); the joint ruling with #13636 that this card was waiting on is void — #13636 is superseded by ADR-0131 §1.6 and closes under C11.
- The seed loader's exemption of
- added a commit that references this issue
on Sep 28, 2026
Split out of #13637, whose dispatched half was editorial only:
objectql'sPLATFORM_NAMESPACEdocblock asserted an invariant that #13491 had made false, and that docblock has been rewritten to describe reality. This card carries the half that was deliberately left out of it, because it is a decision about rows already written to disk, not a repair.Recording only — no severity asserted, routing is triage's.
The state of the tree, measured on
origin/mainatdda969cd71Three sites cut the platform namespace
/^(sys_|cloud_|ai_)/. After #13491 (PR #13635) they no longer decide the same way:Engine.resolveSystemInsertOrganization,packages/objectql/src/engine.ts)isPlatformObjectOutOfTenantAuditScope— the hand-adjudicatedPLATFORM_OBJECT_TENANCYledger inpackages/objectql/src/tenancy/platform-object-tenancy.tspackages/metadata-protocol/src/seed-loader.tsfallbackOrgIddecisionpackages/metadata-protocol/src/migrations/seed-tenancy-backfill.tsPLATFORM_NAMESPACE)The divergence is deliberate: #13491's re-ruling (2026-08-31, 联案 #13497) scoped itself to the runtime control.
The question
Should the two seed paths re-cut per object as well?
Why it is a decision and not a task
Both seed paths WRITE. Changing their cut changes what future seeds and backfills stamp, and says nothing by itself about rows already on disk under the old namespace-wide reading. So an answer has to cover two populations, and the second one is existing data — the manual floor.
Options
A. Leave both seed paths on the namespace regexp, and record the choice. No code change; the divergence stops being an unadjudicated state and becomes a recorded decision. Cheapest, and it keeps existing rows consistent with the path that wrote them. Cost: two write paths disagree with the runtime one permanently, and every future reader re-asks this question.
B. Re-cut both seed paths per object, for FUTURE writes only. The three converge again for anything written from here on. Existing rows are left exactly as they are and are NOT re-stamped. Cost: one object can then hold rows stamped under both readings, with nothing recording which is which.
C. Re-cut, and repair existing rows. Reaches consistency on disk too. This is the option that hits the manual floor hardest — a migration over existing tenant data, unfixable after release — and #8686's own posture (already-minted duplicates are REPORTED, never rewritten) argues against it.
No recommendation is offered: choosing among these needs the maintainer's knowledge of deployed installs, which this repository cannot supply.
What this card does NOT claim
⛔ No assertion that rows the seed paths wrote are wrong. ⛔ No assertion that anything should be re-stamped. ⛔ No severity asserted. The measurement is only that the three cuts now differ, and that no card records a decision about the two that did not move.
Related
#13637 (the finding this was split out of) · #13491 / PR #13635 (where the divergence arose, deliberately) · #13636 (an adjacent gap in the same classification) · #8686 · #8672 · #8844
Generated by Claude Code