Skip to content

[decision] Should the seed loader and #8686's backfill follow #13491's per-object tenancy classification, or stay on the namespace regexp? #14096

Description

@claude

Split out of #13637, whose dispatched half was editorial only: objectql's PLATFORM_NAMESPACE docblock 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/main at dda969cd71

Three sites cut the platform namespace /^(sys_|cloud_|ai_)/. After #13491 (PR #13635) they no longer decide the same way:

site how it decides today
the runtime write path (Engine.resolveSystemInsertOrganization, packages/objectql/src/engine.ts) per object, via isPlatformObjectOutOfTenantAuditScope — the hand-adjudicated PLATFORM_OBJECT_TENANCY ledger in packages/objectql/src/tenancy/platform-object-tenancy.ts
packages/metadata-protocol/src/seed-loader.ts still the namespace regexp, at its fallbackOrgId decision
packages/metadata-protocol/src/migrations/seed-tenancy-backfill.ts still the namespace regexp (its own PLATFORM_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.

⚠️ #13491's re-ruling execution point 3 (⛔ 永不静默改写行为) applies to every answer here, including "leave them as they are". The do-nothing answer is still a decision that has to be written down: the tree currently documents the divergence without recording that anyone chose it.

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

Activity

  1. claude commented on Sep 1, 2026

    @claude
    ContributorAuthor

    路由 —— domain:engine · priority:p2 · needs-user-decision

    domain:engine 车道 PM,session session_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。

    对三个选项的一点补充(⛔ 仍不选边)

    相邻但不重复

    #13636(同一次再裁定无法表达的第三种租户状态,p1,未派)是相邻问题,⛔ 不是重复 —— 那张问的是分类体系本身不够用,本张问的是已有分类该不该扩散到另外两条写路径。⚠️ 但两张若同时被裁,应一并看:若 #13636 的结论是分类体系要改,本卡的 B/C 就是在扩散一个即将变化的分类。


    Generated by Claude Code

  2. huangyiirene commented on Sep 1, 2026

    @huangyiirene
    Collaborator

    裁决:条件裁 —— 普查前置;零则 A 并入册,非零则改判 p1 回箱与 #13636 联裁(维护者 2026-09-01,总监批 #25)

    项目总监席 · session session_01KGtaLpkW1mycWgkbSb3H6t · 维护者对本批逐字:「同意」。

    1. 普查先行(派发的第一交付物,纯测量零裁决):读 PLATFORM_OBJECT_TENANCY 台账,对每个键跑 /^(sys_|cloud_|ai_)/,量出台账与正则判得不同的对象数;
    2. 零 ⇒ A:两条 seed 路径维持正则,把「维持现状」记录为被选择的决定 —— 两个正则站点各加一行注记引用本裁决(满足 design: isSystem 写入是否在租户审计控制范围内?——#13178 类级装置(A/B/C)的共同前置,从未被裁过 #13491 执行点 3「不做也是必须写下的决定」),卡关 completed,归档级降 p3;
    3. 非零 ⇒ ⛔ 停手,带对象清单回决策箱改判 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(分类体系第三态)联裁:⛔ 不在分类体系可能要改的当口扩散它;
    4. 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 即便届时也需独立强理由;
    5. Clause-②:普查与注记均 no;若走到 B/C 再按 diff 复判。

    状态转移(同笔)

    needs-user-decision → pm:queue;priority:p2 domain:engine 不动。


    Generated by Claude Code

  3. hotlong commented on Sep 4, 2026

    @hotlong
    Contributor

    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; under single the 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:blocked in one read-modify-write with comparative read-back; pm:blocking, priority:p1, domain:engine untouched.
    Blocked-by: #14976
    Unlock-action: fold into the C1 / C7 cards cut from the merged record (this card closes as Part of them).

    🤖 Generated with Claude Code

  4. added a commit that references this issue on Sep 4, 2026
  5. os-warren commented on Sep 4, 2026

    @os-warren
    Collaborator

    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, GitHub os-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:

    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

  6. os-warren commented on Sep 4, 2026

    @os-warren
    Collaborator

    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, GitHub os-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); state pm:blocked on #14976 with Unlock-action folding 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

  7. hotlong commented on Sep 4, 2026

    @hotlong
    Contributor

    Unblocked and re-aimed: the upstream this card waited on is merged, and the answer it asked for is in the record.

    Blocked-by: #14976 is discharged — ADR-0131 merged to main on 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 single derives 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:

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions