发现于 #6585 的实现(PR #6711 ),不在该单范围 ,按纪律另立存档交分诊定级。
事实
字段级 visibleWhen / readonlyWhen / requiredWhen 实测只绑三个根,三处独立证据一致:
服务端:rule-validator.ts:577(readonlyWhen)绑 { record, previous, extra: { parent } },:1403(requiredWhen)绑 { record, previous, ...parentScope };
客户端:evalFieldPredicate 绑 record + previous + 调用方 scope,而 objectui 全部五个字段级调用点(form.tsx ×3、WizardForm.tsx、GridField.tsx)传的 scope 只可能是 undefined 或 { parent: contextRecord };
作者端:objectui 的 FIELD_RULE_ROOTS = ['record', 'previous', 'parent'](ObjectFieldInspector.tsx:127),注释明写 "nothing else"。
也就是说,真相是一张三项白名单 。
但 packages/lint/src/validate-expressions.ts 的 checkFieldRuleUserRoot 是一张黑名单 :#6584 里一项(current_user),#6711 之后三项(current_user / user / ctx)。@objectstack/formula 的 SCOPE_ROOTS 共约 26 个成员,减去白名单的三项与黑名单的三项,还剩约 20 个根 在字段级同样未绑定、同样 fault、同样 fail-open —— 且全部静默 :它们都在 SCOPE_ROOTS 里,所以裸引用检查从不报它们,而黑名单又不认它们。
具体名单(cel-engine.ts:54-79):input、output、os、vars、variables、automation、context、args、item、env、step、result、trigger、event、payload、data、params、config、settings、features、current。
其中几个是高度可信的作者笔误 ,而不是理论可能性:
os.user.id —— ADR-0068 D1 认定的第四种 用户拼写。fix(lint): 字段级 *When 的用户根拒绝覆盖 ADR-0068 的全部三种拼写 (#6585) #6711 收了三种,os 这一支没收(收了会连 os.env / os.org 一起判,那是另一次面级测量);
data.status == 'x' —— data 是元数据表单 里同一个 visibleWhen 槽位的合法根(见 view.zod.ts:1440 的 .describe():"Root: record … in runtime forms, or data in metadata forms"),两种表单同一个键、不同的根,作者极易串;
context / vars / trigger —— flow 谓词的根,从 flow 复制粘贴过来就是这几个。
后果
与 #6146 / #6585 同一条失败链,方向也一样:未绑定根 ⇒ fault ⇒ 可见性 fallback 为 true ⇒ 本想藏起来的字段对所有人恒可见,无任何构建期信号。差别只在于:#6585 是「三个等价拼写里挑错了一个」,本单是「挑了一个压根不属于这个面的根」。
现状与范围(实测)
可能的修法(留给分诊,不自选)
黑名单翻成白名单 —— checkFieldRuleUserRoot 改为「字段级 *When 的根不在 {record, previous, parent} 内即拒」。与三处实测绑定、与 objectui 作者端白名单完全对齐,且未来 SCOPE_ROOTS 新增成员时自动覆盖(黑名单则每次都漏)。代价:文案需要按根分档 —— 现有处方是用户向 的(移到选项级 / 权限集 FLS),对 data / vars 这类根答非所问,得给出通用处方(「改写成 record 谓词」)并保留用户根的专门处方;
只把 os 补进现有黑名单 —— 只补 ADR-0068 的第四种用户拼写,把 字段级 *When 的用户根拒绝只认 current_user —— ADR-0068 的两个别名 user / ctx.user 静默放行,写哪个拼写决定拿不拿得到诊断 #6585 的别名故事补完整,其余 ~20 个根继续静默。改动最小,但把同一类洞留在原地;
不动 —— 记录为已知边界。
个人倾向 1(理由:黑名单在这个面上结构性地追不上 SCOPE_ROOTS,而白名单的三项已被三处独立证据钉死),但文案分档是真实工作量,且 parent 那一项本身还有 #6457 的稀疏问题未了,请分诊定夺。
Refs: #6585 、#6584 (PR)、#6711 (PR)、#6146 、#6457 、ADR-0068 D1、objectui#1582
发现于 #6585 的实现(PR #6711),不在该单范围,按纪律另立存档交分诊定级。
事实
字段级
visibleWhen/readonlyWhen/requiredWhen实测只绑三个根,三处独立证据一致:rule-validator.ts:577(readonlyWhen)绑{ record, previous, extra: { parent } },:1403(requiredWhen)绑{ record, previous, ...parentScope };evalFieldPredicate绑record+previous+ 调用方scope,而 objectui 全部五个字段级调用点(form.tsx×3、WizardForm.tsx、GridField.tsx)传的scope只可能是undefined或{ parent: contextRecord };FIELD_RULE_ROOTS = ['record', 'previous', 'parent'](ObjectFieldInspector.tsx:127),注释明写 "nothing else"。也就是说,真相是一张三项白名单。
但
packages/lint/src/validate-expressions.ts的checkFieldRuleUserRoot是一张黑名单:#6584 里一项(current_user),#6711 之后三项(current_user/user/ctx)。@objectstack/formula的SCOPE_ROOTS共约 26 个成员,减去白名单的三项与黑名单的三项,还剩约 20 个根在字段级同样未绑定、同样 fault、同样 fail-open —— 且全部静默:它们都在SCOPE_ROOTS里,所以裸引用检查从不报它们,而黑名单又不认它们。具体名单(
cel-engine.ts:54-79):input、output、os、vars、variables、automation、context、args、item、env、step、result、trigger、event、payload、data、params、config、settings、features、current。其中几个是高度可信的作者笔误,而不是理论可能性:
os.user.id—— ADR-0068 D1 认定的第四种用户拼写。fix(lint): 字段级 *When 的用户根拒绝覆盖 ADR-0068 的全部三种拼写 (#6585) #6711 收了三种,os这一支没收(收了会连os.env/os.org一起判,那是另一次面级测量);data.status == 'x'——data是元数据表单里同一个visibleWhen槽位的合法根(见view.zod.ts:1440的.describe():"Root:record… in runtime forms, ordatain metadata forms"),两种表单同一个键、不同的根,作者极易串;context/vars/trigger—— flow 谓词的根,从 flow 复制粘贴过来就是这几个。后果
与 #6146 / #6585 同一条失败链,方向也一样:未绑定根 ⇒ fault ⇒ 可见性 fallback 为
true⇒ 本想藏起来的字段对所有人恒可见,无任何构建期信号。差别只在于:#6585 是「三个等价拼写里挑错了一个」,本单是「挑了一个压根不属于这个面的根」。现状与范围(实测)
examples/(53 处*When)、packages/(1173 处,非测试)与下游objectui(527 处)读取任一上述根的字段级*When均为零 —— 这是覆盖洞,不是在线故障,与 字段级*When的用户根拒绝只认current_user—— ADR-0068 的两个别名user/ctx.user静默放行,写哪个拼写决定拿不拿得到诊断 #6585 定性一致;可能的修法(留给分诊,不自选)
checkFieldRuleUserRoot改为「字段级*When的根不在{record, previous, parent}内即拒」。与三处实测绑定、与 objectui 作者端白名单完全对齐,且未来SCOPE_ROOTS新增成员时自动覆盖(黑名单则每次都漏)。代价:文案需要按根分档 —— 现有处方是用户向的(移到选项级 / 权限集 FLS),对data/vars这类根答非所问,得给出通用处方(「改写成record谓词」)并保留用户根的专门处方;os补进现有黑名单 —— 只补 ADR-0068 的第四种用户拼写,把 字段级*When的用户根拒绝只认current_user—— ADR-0068 的两个别名user/ctx.user静默放行,写哪个拼写决定拿不拿得到诊断 #6585 的别名故事补完整,其余 ~20 个根继续静默。改动最小,但把同一类洞留在原地;个人倾向 1(理由:黑名单在这个面上结构性地追不上
SCOPE_ROOTS,而白名单的三项已被三处独立证据钉死),但文案分档是真实工作量,且parent那一项本身还有 #6457 的稀疏问题未了,请分诊定夺。Refs: #6585、#6584(PR)、#6711(PR)、#6146、#6457、ADR-0068 D1、objectui#1582