fix(app-shell): /data 面的「New」读取完整 CRUD affordance 矩阵 (#5164) - #5172
Merged
Conversation
…atrix (#5164) `ObjectDataPage` — the parameterized bare data surface (ADR-0055, #2251, route `/apps/:appName/:objectName/data`) — gated its "New" on `can(objectDef.name, 'create')` and nothing else. `resolveEffectiveCrudAffordances` was never called in that file, so all four layers below the principal's grant were missing at once and one authored object got a different affordance here than on the object-list page next door: - the `managedBy` bucket default was ignored (`append-only` / `engine-owned` / `better-auth` all resolve `create: false`, yet /data offered "New"); - the object-level `userActions: { create: false }` opt-out did not close it; - the #3391 effective-API-operation intersection was absent, so the toolbar could offer a create the server would 405; - and `createPredicates` (#5153's layer) was unread here as a consequence. Resolved exactly as `ObjectView` resolves it: the spec's bucket/`userActions` matrix intersected with the server-resolved effective operations, then the toolbar-scope predicate layer on top. Predicate binding and failure posture are the family's verbatim — `visibleWhen` fails CLOSED with declared-ness by `?? true`, `disabledWhen` fails SOFT with its `!= null` gate outside the evaluation, and a `null` scope record so a `record.*` read fails closed per the spec's documented toolbar binding. The layers only narrow: the pre-existing permission gate is not replaced, it is one conjunct of the new one. Fixes #5164 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
PM 验收:ACCEPT(objectui 分片 PM,session 实物核验(merge-base
CI 亲读:21 项中 14 项终态全 success/skipped 零失败(Build & E2E、Bundle Analysis、Changeset 三门、Control Byte 等均绿),Test shard 1-4 / Type Check / Lint 在跑,按尾格零失败先例交 auto-merge 门(绿才合红不合)。席位本地已过 app-shell 全量 270 文件 2668 用例 + 包级双 tsc。 处置:undraft + auto-merge(SQUASH)。交接注记:本 PM 会话处于下班收尾,若后续 CI 转红,由后继 PM 按标准返工流程处理。 Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5164
背景
ObjectDataPage—— 参数化裸数据面(ADR-0055、#2251,路由/apps/:appName/:objectName/data)—— 的「New」按钮此前只有can(objectDef.name, 'create')一个门。该文件从头到尾没有调用过resolveEffectiveCrudAffordances,所以主体授权之下的四层同时缺席,同一份对象元数据在这个面上得到的 affordance 与隔壁对象列表页不一致:managedBybucket 默认被忽略 ——append-only/engine-owned/better-auth都解析出create: false,而/data仍然给出一个指向../new的「New」;userActions: { create: false }退出开关关不掉按钮;{id}被原样发出 #3391 有效 API 操作交集缺失,工具条可以给出服务端会 405 的 create;createPredicates谓词层在此未被读取 —— 即 Object-list toolbar's "New" (and its mobile FAB) does not consumecreatePredicates— the create half is honoured on related lists only #5153 的缺陷,但在这里是上述更大缺口的后果而非缺口本身。服务端始终是执行点,所以这是 UI 真实性缺陷而非提权:按钮被给出,写入照样被拒。但今日用户可达,且与控制台其余部分渲染所依据的 affordance 矩阵自相矛盾。
改动
门形完全照
ObjectView的现形(#5153 / PR #5165、#5142 / PR #5154 同族),不作二次发明:visibleWhenfail-CLOSED,声明性由?? true判定而非由真值判定 —— 所以字面visibleWhen: false(objectui#3492 的形)是隐藏按钮,而不是读作「未设门」;默认值是布尔,求值器直接短路,未声明谓词的对象不付任何求值代价。disabledWhenfail-SOFT,!= null的声明性判定放在求值之外 —— 所以disabledWhen: ''读作「无条件」而非「禁用」,不可求值的谓词也永远不会把按钮永久置灰。useRowPredicate(..., null, ...)是刻意的 —— 读record.*的谓词无从绑定,按 fail-closed 规则隐藏按钮,这正是 spec 写明的绑定,不是本 harness 的缺口。os.user.*/features.*正常绑定,是这里有意义的形。半径严格限定在
ObjectDataPage.tsx+ 测试 + changeset,未触碰ObjectView.tsx。其它 CRUD 入口普查(只测量,未夹带)
按派发要求普查了本文件是否还有同形缺门的 edit/delete 入口,结论为无,故未开 finding 卡:
isAdmin设门,写的是view元数据记录,不是该对象的 CRUD affordance,不属本矩阵;list-viewschema 不声明rowActions,也不传onEdit/onDelete,行 affordance 由plugin-list/plugin-grid内部解析,而那两处已各自调用resolveEffectiveCrudAffordances/isObjectInlineEditable;RecordDetailView是另一张面,有自己的门(此处onEdit传的是空函数);另:卡面已核实、此处复核一次 —— 文件内
userActions:(schema 组装处)是视图工具条词汇(search/sort/filter/rowHeight/group/hideFields,#2890),与对象级 CRUDuserActions不是同一键空间,未混用。测试
新增
packages/app-shell/src/views/ObjectDataPage.createAffordances.test.tsx,29 钉,按四层组合的顺序钉,并把两个 CONTROL 留在旁边,使「整条合取式被删」与「单个合取项被删」可区分:append-only/engine-owned/better-auth)+ 谓词不得复活已关 bucket + 已关 bucket 上的显式create: trueopt-in 仍开;create: false/enabled: false(带谓词)/ 纯布尔臂;{id}被原样发出 #3391 有效操作:排除create关门、包含create开门、空集「什么都不暴露」;visibleWhen真/假、字面visibleWhen: false、disabledWhen置灰(渲染且 disabled,隐藏与置灰保持可区分)、disabledWhen不成立、disabledWhen: ''、record.*的visibleWhenfail-closed、record.*的disabledWhenfail-soft;/data上与对象列表页取得一致的关门结论。反向验证
先书面预判,再跑变异。 预判:把门改回改动前的裸
can(objectDef.name, 'create')(不解析 affordances、不加谓词层、不传disabled),应有 18 钉转红、11 钉保持绿;绿的那 11 钉是对照而非覆盖 —— 它们本就走不到本次新增的层。观测:
红的 18 钉逐条与预判名单一致:三个 bucket 钉、bucket 不被谓词复活、对象级 opt-out、
enabled: false、有效操作排除create、空有效集、visibleWhen假、字面visibleWhen: false、disabledWhen置灰、record.*fail-closed、谓词不得重开有效操作,以及 parity 五钉。绿的 11 钉:两个 CONTROL、已关 bucket 上的 opt-in、纯布尔臂、有效操作含create、visibleWhen真、disabledWhen不成立、disabledWhen: ''、record.*的disabledWhenfail-soft、谓词不得重开主体授权、parity 开门钉。预判与观测完全吻合(matched),无反转、无「更多诊断」情形。还原后 29/29 复绿。
Generated by Claude Code