feat(showcase): 动作显隐矩阵——四个动作面 × 三种 visible 写法 × requiredPermissions × 每种字段类型的谓词 - #6157
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
… 每种字段类型的谓词
给 `showcase_field_zoo` 加一整套可证伪的动作门控夹具,覆盖决定「按钮出不出现」的两件事:
`visible`(记录上的 CEL 谓词)与 `requiredPermissions`(ADR-0066 D4 能力门)。
两条种子记录是可证伪性的来源:Specimen — Full(字段全填)必须出现,
Specimen — Minimal(多数为 null)必须消失——写错的表现是按钮出现在错误的地方,
而不是一个悄悄变绿的测试。
- **四面一致性**:同一个动作声明在行内 kebab / 记录页头 / ⋯ 菜单,并被视图的
`bulkActions` 提升进批量栏。关系比关系(`f_lookup == f_lookups[0]`)是最利的
一条:两个操作数都是 lookup,谁被展开谁就变成对象,比较就静默为假。
`gated_columns` 视图把 `f_lookup` 设为列(于是被 `populate=`),默认列表两个都不展开
—— 同样的记录、同样的谓词,两个视图必须给出同一结论(objectui#3501)。
- **三种 visible 写法**:裸 CEL 字符串 / `P` 标签模板 / `{ dialect, source }` 信封,
同一条谓词并排钉住;三者若在同一条记录上分歧,就是某个渲染器把某一种当特例了。
布尔 visible 不在此列——`ExpressionInput` 不接受它,`objectstack build` 发不出这种
形状,由 objectui 自己的单测钉(objectui#3492)。
- **requiredPermissions**:持有 / 不持有 / 两个取 AND / 空声明四种,四个面同一结论。
新增 `showcase.restricted_ops` 能力:**定义了但不授予任何权限集**——门的可证伪一半,
正是批量栏当初整个忽略 `requiredPermissions` 时无人察觉的原因。
- **字段类型谓词园**:每种字段类型一条 `visible`,含 lookup(单/多/空)、master_detail、
tree、user、文本族、数值族、时间族、布尔/选项族、json/location/address/composite/
repeater/vector,以及 AND/OR/NOT/三元。全部对着真实 runtime 的 CEL 引擎实测过,
不是照着规范猜的。
顺带钉住三条实测得到的作者规则:必须 `record.` 前缀;遍历/调方法前必须判空
(`null` 没有成员也没有方法,不判空在 Minimal 上直接 fault);用 `contains()` / `matches()`
而不是 `startsWith()` / `endsWith()`(后者不是 CEL,objectui 会降级到遗留 JS 引擎并告警)。
`showcase_zoo_dialect_split` 是故意留下的**红色夹具**:同一个 `.contains()` 谓词在行内
kebab 里正常、在记录页头被判抛错并隐藏——页头不走 CEL 引擎(objectui#3521)。
按仓库规矩不粉饰,改成一屏可对比的现场。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
CI 两条红的都处理了: 1. Check Changeset —— 本 PR 的改动全部落在 2. TypeScript Type Check → 翻译而不是抬基线。 脚本抬头写明「the debt is FROZEN, and the build fails the moment it grows」; 补齐 基线仍是 451,一字未动。顺带修了上一笔批量替换在
🤖 Generated with Claude Code |
`check-i18n-coverage` 把本示例的未翻译计数冻结在 451,新声明的标签一旦跳过 zh-CN 就把计数顶上去、门变红 —— 我加了 ~56 个动作标签和 3 个视图标签却一条没译, CI 报 451 → 572。 翻译而不是抬基线:那个脚本的抬头写明「debt is FROZEN, and the build fails the moment it grows」,本文件里既有的两条注释也各自写着「Translated at birth ... this example is ratcheted at its current untranslated count」。抬基线正是它防的 那件事。 补齐 `zh-CN.objects.showcase_field_zoo` 下的 `_actions`(12 条门控动作 + 44 条 字段类型谓词,含 3 条 successMessage)与 `_views`(3 个视图标签)。标签本身是覆盖 夹具的说明文字,中文照写同一句技术陈述,不做意译。 顺带修 `field-zoo.view.ts` 里一处缩进(上一笔批量替换留下的)。 现在 `check-i18n-coverage: OK (none new)`,基线仍是 451。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
合入 main 后 CI 仍红 451 → 452,本地却绿 —— 差别在于 CI 跑的是**分支与 main 的 合并结果**,而分支落后 main 136 个提交,其中 b3c1f3c(#5164)改了 `_views` 的 键法:不带 `name` 声明的容器默认列表被 composer 注册为 `<object>.default`, bundle 的键就是这个裸运行时键。我写的 `_views.list` 是没有任何查找能到达的拼法, 正是 #5164 修掉的那个。 `ContactViews` 的注释里已经把这条定案写清楚了,我照它对齐。 顺带记下这次的教训并写进注释:**落后 main 的分支上「本地绿」不作数** —— 这条 只有在合并结果上才暴露。 合并后复跑:`check-i18n-coverage: OK (none new)`、`pnpm validate` exit 0、 `typecheck` 通过、146 tests passed。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
合 main 之后重新实测:这条谓词现在**校验通过**。#5378 已经把答案放进 `packages/spec/src/data/injected-system-columns.ts`,让作者时校验能推导出平台注入 列 —— 我原先注释里写的「校验器拒绝它,已上报」在合并后的树上是**假话**,不能就 这么随 PR 发出去。 于是不只是改注释,而是把这条谓词作为正式标本加回来(`showcase_zoo_owner_gate` + 类型园里的 `showcase_zoo_t_owner`,三个记录面 + 两个视图的 `bulkActions`)。 它值得独立一条,因为 `owner_id` 是注入列而非声明字段,同时压两个面: - 作者时:它就是 #5378 落地的活证据; - 运行时:列表 `$select` 按对象声明字段构造,消费方若不认识平台列就会把它当拼错 丢掉、让谓词在缺席键上 fault —— 即 objectui#3501 的 `PLATFORM_RECORD_COLUMNS`。 与既有的 `showcase_zoo_user_gate`(声明的 `user` 字段)成对:注入列与声明字段必须 门控一致。 标签当场补 zh-CN,棘轮仍是 451、`check-i18n-coverage: OK (none new)`; `pnpm validate` exit 0、`typecheck` 通过、146 tests passed。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
给
showcase_field_zoo加一整套可证伪的动作门控夹具,覆盖决定「按钮出不出现」的两件事:visible(记录上的 CEL 谓词)与requiredPermissions(ADR-0066 D4 能力门)。配套的 objectui 修复:objectstack-ai/objectui#3548(closes objectui#3492 / #3501)。本 PR 是那两个修复的实测夹具,也是它们的验收现场。
为什么需要它
一个门由四个各自过滤自己列表的面求值:列表工具栏 / 行内 kebab / 记录页头 / 批量选择栏。它们已经在两个方向上跑偏过:批量栏整个忽略
requiredPermissions(行 kebab 藏、勾选后又冒出来,objectui#3492);关系字段在一个面上绑成展开记录、另一个面上绑成外键,同一条谓词一真一假(objectui#3501)。任何只测单个面的用例都看不见这类 bug。两条种子记录是可证伪性的来源:Specimen — Full(字段全填)必须出现,Specimen — Minimal(多数为 null)必须消失 —— 写错的表现是按钮出现在错误的地方,而不是一个悄悄变绿的测试。
内容
src/ui/actions/predicate-matrix.action.tsshowcase_zoo_relation_gate是最利的一条:record.f_lookup == record.f_lookups[0],两个操作数都是 lookup,谁被展开谁就变成对象、比较就静默为假。gated_columns视图把f_lookup设为列(于是被populate=)、默认列表两个都不展开 —— 同样的记录、同样的谓词,两个视图必须给出同一结论。两个字段都不是默认列表的列,同时也把$select投影压上测试台。visible写法 —— 裸 CEL 字符串 /P`…`标签模板 /{ dialect, source }信封,同一条谓词并排钉住;三者若在同一条记录上分歧,就是某个渲染器把某一种当特例了。布尔不在此列:ExpressionInput不接受它,objectstack build发不出这种形状,由 objectui 自己的单测钉(不在这里假装覆盖)。requiredPermissions—— 持有 / 不持有 / 两个取 AND / 空声明四种,四个面同一结论。visible:lookup(单/多/空)、master_detail、tree、user、文本族、数值族、时间族、布尔/选项族、json / location / address / composite / repeater / vector,以及 AND / OR / NOT / 三元。全部对着真实 runtime 的 CEL 引擎实测过,不是照着规范猜的。src/security/capabilities.ts—— 新增showcase.restricted_ops:定义了但不授予任何权限集。这是 ADR-0066 三方分离里通常被省掉的、可证伪的那一半 —— 没有一个「谁都不持有」的能力,就没有东西能在某个面停止应用门控时报警,而那正是批量栏当初整个忽略requiredPermissions却无人察觉的原因。src/ui/views/field-zoo.view.ts—— 默认列表(列窄,被门控的字段都不是列)、gated_columns(把关系字段提为列,于是被展开)、inline_bulk_defs(bulkActionDefs+execution: 'aggregate',弹窗报告合格/跳过拆分)。默认列表另带一条关系字段的条件格式规则。顺带钉住三条实测得到的作者规则
record.前缀(裸字段在页头路径是未声明变量,抛错→fail-closed 隐藏);null没有成员也没有方法,不判空在 Minimal 上直接 fault,而 fault 在行/批量面 fail-closed、在宽松路径 fail-open;contains()/matches(),不要startsWith()/endsWith()—— 后者不是 CEL,objectui 会降级到遗留 JS 引擎并打 deprecation 警告,服务端引擎则根本不认。一个故意留下的红色夹具
showcase_zoo_dialect_split:同一条.contains()谓词,在行内 kebab 里正常、在记录页头被判抛错并隐藏 —— 页头不走 CEL 引擎(objectstack-ai/objectui#3521,实测控制台原话已附在该单里)。按仓库「绝不粉饰未交付的能力」的规矩,不改写成能过的写法,而是留成一屏可对比的现场;#3521 落地后这个按钮应当出现在页头,那就是它的验收条件。同样明说的取舍:归属谓词的自然写法
record.owner_id == os.user.id编译不过 —— 平台注入列不在发布的对象 metadata 里,作者时校验判unknown field。已报 #6113,showcase 里退到自己声明的user字段并在注释里写清原因,不绕过。测试
pnpm validateexit 0(无新增 error)、pnpm typecheck通过、pnpm test146 passed。真机实测(showcase :3100 + objectui console dev :5181):
$select实际带上谓词读的 34 个非列字段;三个记录面结论一致;四种requiredPermissions表现符合预期;gated_columns下populate=f_lookup,f_master_detail时关系比关系仍为真;聚合批量弹窗正确报「1 条所选记录不符合条件,将被跳过」。🤖 Generated with Claude Code