Filed by the domain:spec seat (session_017RbbUMnxkUnWhE4j94v8FE) from the contract review of PR #14775 (#13881 , isolated reviewer verdict 5518923117, "Decisions to file for the maintainer" item 1). Decision inbox card — needs-user-decision; no domain:* / type set by this seat (triage's). Depends on PR #14775 landing (the column itself); nothing here blocks that landing.
背景 #13881 的裁决(2026-09-01,A 路线)给 sys_user 加了一等列 locale(PR #14775 )。落地形状:该列 readonly,只有系统上下文(system context)能写 —— 不在 ADR-0092 D2 的用户自助编辑白名单 SYS_USER_PROFILE_EDIT_FIELDS = {name, image} 里,也不在 plugin-auth 的 MANAGED_EXTENSION_EDITABLE_FIELDS.sys_user 里;今天没有任何管理员界面写它。首个消费者 hotcrm#1185 可以在系统上下文里盖章,所以 PR 可以不等本决策先落地。剩下的问题是身份表写路径的安全边界 ,裁决没有覆盖,座位不能自裁。
带 re-check 命令的前提 白名单今天是 {name, image}:git grep -n "SYS_USER_PROFILE_EDIT_FIELDS" origin/main -- packages/plugins/plugin-auth/src/sys-user-writable-fields.ts 可编辑扩展字段表没有 sys_user 条目:git grep -n "MANAGED_EXTENSION_EDITABLE_FIELDS" origin/main -- packages/plugins/plugin-auth/src/managed-extension-fields.ts 列已落地且 readonly(PR feat(spec,platform-objects,service-messaging,plugin-auth): sys_user.locale + per-recipient notification locale (#13881) #14775 合并后成立):git grep -n -A3 "locale: Field.text" origin/main -- packages/platform-objects/src/identity/sys-user.object.ts better-auth 的 /update-user 走不到这列(它不是 better-auth 字段):git grep -n "additionalFields" origin/main -- packages/plugins/plugin-auth/src/auth-schema-config.ts(只有 invitation 模型有) 一句话问题 一个中文用户登录进来,想把自己的通知语言从英文改成中文 —— 今天没有任何地方让他自己改,只能等管理员或系统替他填。
选项 × 真实代价 选项 做什么 真实代价 A 保持现状 列 readonly,只有系统上下文写(hotcrm 之类的应用在系统上下文里盖章) 用户收到的通知语言由别人决定;平台自己的 Studio/Console 没有入口让用户改语言;每个应用都要自己造一条「系统替用户填语言」的通道 B 放开自助 白名单加 locale({name, image, locale}),MANAGED_EXTENSION_EDITABLE_FIELDS.sys_user 加 locale,去掉 readonly,身份写守卫的 pin 同步翻转 身份表多开一个用户可写字段(安全边界扩一格);用户填错格式由列的 BCP-47 形状校验响亮拒绝;objectui 侧还要一个「我的语言」表单项才真正可达(另开 ui 卡)
业务上:A = 像早期企业软件,语言是 IT 管理员配置的;B = 像 Salesforce / Slack 的个人设置页里的 Language 下拉,用户自己选。
四轴(从业务立场) 实际业务需求 :hotcrm 发布 4 种语言、16 个 notify 节点,zh-CN / ja-JP / es-ES 用户此前全收英文 —— 拉动已实测(hotcrm#1185)。但「谁来填这一列」在应用侧还是空的:A 下每个应用都得自己写一段系统上下文盖章逻辑;B 下用户第一次登录就能自己选。项目长远合理性 (权重 ≥50%):用户自己的语言是主流平台的一等个人属性,长远终态就是用户自助;A 是过渡态,过渡态在创业阶段不留。B 不扩大契约(列已在),只是把已有的白名单机制多列一项,特例没有增生。防 AI 犯错 :B 下 AI 写应用元数据时不需要为「怎么替用户填语言」发明系统上下文写路径(那正是 AI 最容易写错权限的地方);用户填错值由 LOCALE_TAG_SHAPE 校验响亮拒绝、回退到部署默认,不会静默死信。A 下 AI 会在应用侧造绕行。创业阶段不扩散 :B 的改动是三行(两个白名单各一项 + 去 readonly)+ 一个 pin,不新增能力面;A 反而会让每个应用各自扩散一套盖章逻辑。推荐 + 回退 + 置信缺口 推荐 B (长远终态领起,实测拉动在,防错与不扩散同向)。安全边界扩一格是本卡进决策箱的唯一原因:身份表的用户可写集从 2 个字段变 3 个。回退 A :列保持只读,由应用在系统上下文里盖章;hotcrm 照常可用。本分析看不见什么 :objectui 是否已有「个人设置」表单可以挂 locale(未测);cloud 仓是否有自己的用户语言设置面(未测,本容器不可达 objectstack-ai/cloud)。裁后我会怎么执行(你不用管) 裁 B :等 PR feat(spec,platform-objects,service-messaging,plugin-auth): sys_user.locale + per-recipient notification locale (#13881) #14775 合并后立一张执行卡(plugin-auth 落点,Blocked-by: 本卡关闭即解除):白名单 {name, image, locale}、MANAGED_EXTENSION_EDITABLE_FIELDS.sys_user += 'locale'、去 readonly、identity-write-guard 与 managed-extension-fields 的 pin 翻转;再向 objectui 席立「我的语言」表单卡。裁 A :本卡关闭,PR feat(spec,platform-objects,service-messaging,plugin-auth): sys_user.locale + per-recipient notification locale (#13881) #14775 的列声明与 changeset 文案已如实写「只有系统上下文写」;hotcrm#1185 按系统上下文盖章。Refs #13881 (裁决 5494464459)· PR #14775 (复审 5518923117)· hotcrm#1185 · #14641 (邀请邮件梯级)· #14762 (auth 短信/邮件读列)· ADR-0092 D2 / D4 · ADR-0105 D7
os-decision-facets
① 长远合理性:用户自选语言是终态;B 只在既有白名单机制上加一项,不扩大契约、不增特例 —— 荐 B。 ② 实际业务拉动:已实测(hotcrm 4 语言 × 16 节点 × 0 可本地化);A 下每个应用要各造一条系统盖章路径才能填列。 ③ 防 AI 犯错:B 让 AI 写应用时不必发明权限绕行;错值由 BCP-47 形状校验响亮拒绝并回退部署默认。 ④ 创业阶段不扩散:B = 三行 + 一个 pin,无新能力面;A 会让盖章逻辑在应用间扩散。 推荐:B (回退 A)。 置信缺口:objectui 个人设置表单是否存在、cloud 仓是否另有用户语言面 —— 均未测。 Generated by Claude Code
Filed by the
domain:specseat (session_017RbbUMnxkUnWhE4j94v8FE) from the contract review of PR #14775 (#13881, isolated reviewer verdict 5518923117, "Decisions to file for the maintainer" item 1). Decision inbox card —needs-user-decision; nodomain:*/ type set by this seat (triage's). Depends on PR #14775 landing (the column itself); nothing here blocks that landing.背景
#13881 的裁决(2026-09-01,A 路线)给
sys_user加了一等列locale(PR #14775)。落地形状:该列readonly,只有系统上下文(system context)能写 —— 不在 ADR-0092 D2 的用户自助编辑白名单SYS_USER_PROFILE_EDIT_FIELDS = {name, image}里,也不在 plugin-auth 的MANAGED_EXTENSION_EDITABLE_FIELDS.sys_user里;今天没有任何管理员界面写它。首个消费者 hotcrm#1185 可以在系统上下文里盖章,所以 PR 可以不等本决策先落地。剩下的问题是身份表写路径的安全边界,裁决没有覆盖,座位不能自裁。带 re-check 命令的前提
{name, image}:git grep -n "SYS_USER_PROFILE_EDIT_FIELDS" origin/main -- packages/plugins/plugin-auth/src/sys-user-writable-fields.tssys_user条目:git grep -n "MANAGED_EXTENSION_EDITABLE_FIELDS" origin/main -- packages/plugins/plugin-auth/src/managed-extension-fields.tsreadonly(PR feat(spec,platform-objects,service-messaging,plugin-auth): sys_user.locale + per-recipient notification locale (#13881) #14775 合并后成立):git grep -n -A3 "locale: Field.text" origin/main -- packages/platform-objects/src/identity/sys-user.object.ts/update-user走不到这列(它不是 better-auth 字段):git grep -n "additionalFields" origin/main -- packages/plugins/plugin-auth/src/auth-schema-config.ts(只有 invitation 模型有)一句话问题
一个中文用户登录进来,想把自己的通知语言从英文改成中文 —— 今天没有任何地方让他自己改,只能等管理员或系统替他填。
选项 × 真实代价
readonly,只有系统上下文写(hotcrm 之类的应用在系统上下文里盖章)locale({name, image, locale}),MANAGED_EXTENSION_EDITABLE_FIELDS.sys_user加locale,去掉readonly,身份写守卫的 pin 同步翻转业务上:A = 像早期企业软件,语言是 IT 管理员配置的;B = 像 Salesforce / Slack 的个人设置页里的 Language 下拉,用户自己选。
四轴(从业务立场)
LOCALE_TAG_SHAPE校验响亮拒绝、回退到部署默认,不会静默死信。A 下 AI 会在应用侧造绕行。readonly)+ 一个 pin,不新增能力面;A 反而会让每个应用各自扩散一套盖章逻辑。推荐 + 回退 + 置信缺口
locale(未测);cloud 仓是否有自己的用户语言设置面(未测,本容器不可达objectstack-ai/cloud)。裁后我会怎么执行(你不用管)
Blocked-by:本卡关闭即解除):白名单{name, image, locale}、MANAGED_EXTENSION_EDITABLE_FIELDS.sys_user += 'locale'、去readonly、identity-write-guard与managed-extension-fields的 pin 翻转;再向 objectui 席立「我的语言」表单卡。Refs
#13881(裁决 5494464459)· PR #14775(复审 5518923117)· hotcrm#1185 · #14641(邀请邮件梯级)· #14762(auth 短信/邮件读列)· ADR-0092 D2 / D4 · ADR-0105 D7
os-decision-facets
Generated by Claude Code