Platform-admin standing is moving from a stored grant row to config-derived, resolved at request time (#11663 re-anchor, ADR-0131). That migration is right, but it silently drops a property the stored row had: a durable record of who held administrator standing, and from when.
Filed at the maintainer's direction (2026-09-16T09:51Z).
What is lost, and why it is not obvious
|
stored grant row (today's anchor) |
config-derived standing (the target) |
| who holds it |
a row |
OS_PLATFORM_OWNER_EMAIL at request time |
| since when |
row timestamp |
⛔ nothing in the data |
| who granted it |
row writer |
⛔ nothing in the data |
| revocation leaves |
a deleted/changed row |
⛔ an env change + a restart |
⇒ After the migration, 「three months ago this account performed an admin action — what made them an administrator at that moment?」 is answerable only from the deployment's environment-variable history, which the product does not keep and an auditor cannot read.
⚠️ sys_audit_log still records the ACTIONS. What disappears is the basis for the authority behind them. Those are different questions, and only the first one currently has a writer.
Measured characteristics that make this concrete
Read on origin/main:
- standing is resolved per request from config (
resolvePlatformAdminEmails, memoized; resetPlatformAdminEmailMemo exists to clear it) ⇒ removing an administrator takes effect on config change + restart, leaving no data-side trace;
- boot already COMPUTES exactly the record that is wanted —
resolvePlatformAdminStanding(ql, platformAdminConfig) builds a per-entry summary (registered / verified / which userId holds standing) and logs it at info;
- ⇒ the information exists at boot and is thrown away into a log line.
Proposal
Write that resolved standing as an audit record at boot: per declared entry — the declared spelling, whether an account exists, whether it is verified, and which user id (if any) actually holds standing.
Why this shape:
- it gets ADR-0131's 「no NULL grant row」 and keeps a durable, queryable record of who could administer the deployment and from when;
- ⛔ it adds no new configuration key — the input is already parsed and the summary already assembled;
- it makes the walled-posture story complete: config declares, boot records, the audit ledger answers 「who, since when」.
⚠️ Open questions for whoever takes it, ⛔ not decided here: which object carries it; whether it writes on every boot or only when the resolved set CHANGES (a per-boot row on a frequently-restarted rig is noise); and how it is scoped for organization ownership under ADR-0131's own no-NULL rule — ⭐ note this record must not reintroduce the very NULL-organization shape the migration is removing.
Dedup words: platform admin standing audit record · config-derived standing no durable trace · who was admin and since when · resolvePlatformAdminStanding boot log · admin revocation leaves no trace
🔴 维护者速读 —— 已裁方案的验收条件做不到,需要你一个字母
由 domain:services 席位(objectstack#6021)session_01QGMBhvUoyD8t5zY8xHQhnP 写于 2026-09-17T16:14Z。dev 按令停手,零文件改动、零 PR;分支是空探针。⛔ 席位不自裁 —— 这需要放宽一条你已经批过的裁决条款。
改了什么 —— 还没改,一行代码都没写。
为什么卡住 —— 你在批次 #148 批了 A:开机时把「谁是平台管理员」的变化记一条审计。裁决里加了一条硬约束:这条记录不许是「无组织归属」的行。但实测下来,这条记录没有任何组织可以归属:
- 这棵树上根本不存在「平台组织」,而且是当初特意决定不要的 —— ADR-0131 §1.5 原话说它是「自然的修法,但是错的……存在的唯一意义就是给 NULL 换个名字」。全仓零命中。
- 更硬的一条:裁决要求的首次开机基线那一行,是在第一个组织还没被创建之前 1.3 秒写的 —— 那一刻世界上还没有任何组织 id 可以填。
- 填某一个租户的 id 是撒谎(这是全部署的事实,塞进一个租户的墙里),而且首次开机时那个 id 也不存在;每个组织写一行则和裁决自己说的「恰好一条」冲突。
⇒ 三个候选值全部走不通。不是实现难,是这条验收条件本身做不到。
风险与代价(含回滚) —— 现在零风险,因为什么都没做。代价是:平台管理员权限的变更今天仍然查不到(这正是本卡要修的审计缺口)。两个选项都可一次 revert 回退。
席位意见 —— 荐 A。理由不是图省事:这条记录按构造就是全部署级的,ADR-0131 §1.5 已经说过「给它发明一个主人」是错的方向,所以这里的 NULL 不是那条禁令针对的 NULL —— 那条禁令针对的是本该有主人的租户行。A 就是 ADR-0131 的 D7 将来会用「删掉这一列」正式确立的形状,只是提前用 NULL 表达。⚠️ 但这要放宽你批过的条款,所以必须你说了算。
你要做的(一个动作) —— 回一个字母:
A(这条记录允许无组织归属,现在就做)/ B(不放宽任何条款,本卡压后等 v18 线开、等 D7 删掉那一列)/ C(每个组织各写一行 —— ⛔ 席位不荐,且首次开机根本做不到)
四棱分析
① 实际业务需求(实测,⛔ 不是「读起来有用」) —— 审计缺口是真的:平台管理员从「存的授权行」变成「配置推导」之后,「谁持有过、从何时起」就没有留痕了,这是审计员的常规问题,裁决也引了 Salesforce Setup Audit Trail 作对照。但「必须有非空组织」这条要求的实测拉动是零:这棵树上的部署级审计行今天已经全部按 tenantId ?? null 落盘 —— 四个写入点 audit-writers.ts:1375、read-audit.ts:654、auth-event-audit.ts:314、config-change-audit.ts:197 一致如此。⇒ A 不是新造一个例外,是沿用这棵树既有的、唯一的部署级写法。
② 项目长远合理性 —— 指向 A / B 同向,⛔ 反对 C。ADR-0131 的方向是 D7:sys_audit_log 直接失去 organization 列,部署级的行不再假装有主人。A 是 D7 将来会正式确立的那个形状的提前表达;B 是等 D7 真的落地再做,结构上更干净但要等 v18 线开(而 #15193 这道闸门明写:在它关闭前,任何座位、任何仓都不得派发 ADR-0131 执行卡)。C 与 ADR 的「解混」方向正相反。
③ 防 AI 写代码犯错(尤其防写元数据) —— 这一棱指向 B,是唯一真正反对 A 的力量:A 写下的正是裁决字面禁止的形状,如果不把例外白纸黑字记在卡上和代码注释里,下一个作者读到裁决就会把它「修」回去。⚠️ 反过来,C 才是真正的陷阱形状 —— 给一个没有主人的事实发明一个主人,正是 ADR §1.5 说的「给 NULL 换个名字」,而且会让部署级事实泄漏进每个租户的审计视图。
④ 创业阶段不扩散 —— 两个选项都不扩面:不加对象、不加配置键、不加公开 API。B 把能力推迟到 v18;A 现在就补上审计缺口,成本是一条需要记录在案的例外。按「创业阶段不渐进」的既有指示,⛔ 不设分阶段过渡 —— 要么现在按 A 做完,要么按 B 整张压后,⛔ 不做「先写一半」。
四棱结论 —— ①④ 中立偏 A,② A/B 同向,③ 指向 B。不同向 ⇒ ⛔ 不满足代裁置信门;且本卡踩两条人工地板(安全/权限边界、已发布契约语义),⛔ 席位无权自裁。
Governing text: docs/adr/0131-total-organization-ownership-no-null-organization-id.md §1.5(逐字:「The first draft of this record gave deployment-level rows an owner … it is the natural repair and the wrong one … exists only to give NULL a new name」,本席在 origin/main 上复读)+ 同记录 D7 + 裁决批次 #148 项 2 + 闸门卡 #15193(open,明文禁止派发 ADR-0131 执行卡)。
⛔ 本块与上方速读由席位落在卡面正文(非仅评论),依「决策卡落卡必带四棱卡面块与其上的维护者速读」。dev 的终报在 issuecomment-5717560616,其中 P1/P2 两条把本席派发令里的两个判断都纠正了 —— 见席位评论。
Generated by Claude Code
Platform-admin standing is moving from a stored grant row to config-derived, resolved at request time (#11663 re-anchor, ADR-0131). That migration is right, but it silently drops a property the stored row had: a durable record of who held administrator standing, and from when.
Filed at the maintainer's direction (2026-09-16T09:51Z).
What is lost, and why it is not obvious
OS_PLATFORM_OWNER_EMAILat request time⇒ After the migration, 「three months ago this account performed an admin action — what made them an administrator at that moment?」 is answerable only from the deployment's environment-variable history, which the product does not keep and an auditor cannot read.
sys_audit_logstill records the ACTIONS. What disappears is the basis for the authority behind them. Those are different questions, and only the first one currently has a writer.Measured characteristics that make this concrete
Read on
origin/main:resolvePlatformAdminEmails, memoized;resetPlatformAdminEmailMemoexists to clear it) ⇒ removing an administrator takes effect on config change + restart, leaving no data-side trace;resolvePlatformAdminStanding(ql, platformAdminConfig)builds a per-entry summary (registered/verified/ whichuserIdholds standing) and logs it atinfo;Proposal
Write that resolved standing as an audit record at boot: per declared entry — the declared spelling, whether an account exists, whether it is verified, and which user id (if any) actually holds standing.
Why this shape:
Dedup words:
platform admin standing audit record·config-derived standing no durable trace·who was admin and since when·resolvePlatformAdminStanding boot log·admin revocation leaves no trace🔴 维护者速读 —— 已裁方案的验收条件做不到,需要你一个字母
由
domain:services席位(objectstack#6021)session_01QGMBhvUoyD8t5zY8xHQhnP写于 2026-09-17T16:14Z。dev 按令停手,零文件改动、零 PR;分支是空探针。⛔ 席位不自裁 —— 这需要放宽一条你已经批过的裁决条款。改了什么 —— 还没改,一行代码都没写。
为什么卡住 —— 你在批次 #148 批了 A:开机时把「谁是平台管理员」的变化记一条审计。裁决里加了一条硬约束:这条记录不许是「无组织归属」的行。但实测下来,这条记录没有任何组织可以归属:
⇒ 三个候选值全部走不通。不是实现难,是这条验收条件本身做不到。
风险与代价(含回滚) —— 现在零风险,因为什么都没做。代价是:平台管理员权限的变更今天仍然查不到(这正是本卡要修的审计缺口)。两个选项都可一次 revert 回退。
席位意见 —— 荐 A。理由不是图省事:这条记录按构造就是全部署级的,ADR-0131 §1.5 已经说过「给它发明一个主人」是错的方向,所以这里的 NULL 不是那条禁令针对的 NULL —— 那条禁令针对的是本该有主人的租户行。A 就是 ADR-0131 的 D7 将来会用「删掉这一列」正式确立的形状,只是提前用 NULL 表达。⚠️ 但这要放宽你批过的条款,所以必须你说了算。
你要做的(一个动作) —— 回一个字母:
A(这条记录允许无组织归属,现在就做)/ B(不放宽任何条款,本卡压后等 v18 线开、等 D7 删掉那一列)/ C(每个组织各写一行 —— ⛔ 席位不荐,且首次开机根本做不到)
四棱分析
① 实际业务需求(实测,⛔ 不是「读起来有用」) —— 审计缺口是真的:平台管理员从「存的授权行」变成「配置推导」之后,「谁持有过、从何时起」就没有留痕了,这是审计员的常规问题,裁决也引了 Salesforce Setup Audit Trail 作对照。但「必须有非空组织」这条要求的实测拉动是零:这棵树上的部署级审计行今天已经全部按
tenantId ?? null落盘 —— 四个写入点audit-writers.ts:1375、read-audit.ts:654、auth-event-audit.ts:314、config-change-audit.ts:197一致如此。⇒ A 不是新造一个例外,是沿用这棵树既有的、唯一的部署级写法。② 项目长远合理性 —— 指向 A / B 同向,⛔ 反对 C。ADR-0131 的方向是 D7:
sys_audit_log直接失去 organization 列,部署级的行不再假装有主人。A 是 D7 将来会正式确立的那个形状的提前表达;B 是等 D7 真的落地再做,结构上更干净但要等 v18 线开(而 #15193 这道闸门明写:在它关闭前,任何座位、任何仓都不得派发 ADR-0131 执行卡)。C 与 ADR 的「解混」方向正相反。③ 防 AI 写代码犯错(尤其防写元数据) —— 这一棱指向 B,是唯一真正反对 A 的力量:A 写下的正是裁决字面禁止的形状,如果不把例外白纸黑字记在卡上和代码注释里,下一个作者读到裁决就会把它「修」回去。⚠️ 反过来,C 才是真正的陷阱形状 —— 给一个没有主人的事实发明一个主人,正是 ADR §1.5 说的「给 NULL 换个名字」,而且会让部署级事实泄漏进每个租户的审计视图。
④ 创业阶段不扩散 —— 两个选项都不扩面:不加对象、不加配置键、不加公开 API。B 把能力推迟到 v18;A 现在就补上审计缺口,成本是一条需要记录在案的例外。按「创业阶段不渐进」的既有指示,⛔ 不设分阶段过渡 —— 要么现在按 A 做完,要么按 B 整张压后,⛔ 不做「先写一半」。
四棱结论 —— ①④ 中立偏 A,② A/B 同向,③ 指向 B。不同向 ⇒ ⛔ 不满足代裁置信门;且本卡踩两条人工地板(安全/权限边界、已发布契约语义),⛔ 席位无权自裁。
Governing text:docs/adr/0131-total-organization-ownership-no-null-organization-id.md§1.5(逐字:「The first draft of this record gave deployment-level rows an owner … it is the natural repair and the wrong one … exists only to give NULL a new name」,本席在origin/main上复读)+ 同记录 D7 + 裁决批次 #148 项 2 + 闸门卡 #15193(open,明文禁止派发 ADR-0131 执行卡)。⛔ 本块与上方速读由席位落在卡面正文(非仅评论),依「决策卡落卡必带四棱卡面块与其上的维护者速读」。dev 的终报在
issuecomment-5717560616,其中 P1/P2 两条把本席派发令里的两个判断都纠正了 —— 见席位评论。Generated by Claude Code