fix(app-shell,components): 动作 disabled 的「已声明」判定补上 !== '' 半边,disabled: '' 不再永久置灰 (#3842) - #3851
Merged
Merged
Conversation
… '' 不再永久置灰 (#3842) `disabled` 的「是否声明了门」停在 `!= null`,少了 `visible` 一族已收敛的 `hasDeclaredVisibilityGate`(`!= null && !== ''`)的另一半。于是 `disabled: ''` 被判成「声明了门」,verdict 交给求值入口 —— 而求值入口把空谓词读成 「没有条件 → true」(`toPredicateInput('')` → `undefined` → `evaluateCondition(undefined)` → `true`)。 方向是这半边成为缺陷、而 `visible` 那半边不是的原因:在 `visible` 上这个 `true` 意味着「显示」,过宽的「已声明」判定与宽松的空谓词互相抵消,`visible: ''` 两种写法 都渲染;在 `disabled` 上同一个 `true` 意味着「禁用」,两个错误叠加 —— 空谓词从 「没有门」变成「永久置灰」。同一个空谓词,两个 key 得到相反待遇。 两处落点改读同一处定义: - `app-shell` 的 `DeclaredActionsBar`:最烫的一处。动作是服务端声明的 (`objectDef.actions[]`),宿主是审批收件箱记录区,元数据里一个 `disabled: ''` (授权表单留空、模板求值成空串)渲染出谁都点不动的 Approve / Reject,且无法与 「元数据本意」区分。#3835 是同一surface 反方向的失败。 - `components` 的 `action:button`:改前先自行探针实证同形(issue 只按拼法逐字相同 推断,未单独实证):`disabled: ''` 时渲染出的按钮带 `disabled=""`,pin 测试红。 行为变化面刻意窄:只有 `disabled: ''` 改变 —— 从置灰变可点,即「没有谓词」本来的 含义。`disabled: true` 仍置灰,`disabled: false` 与未声明仍不置灰,任何表达式取值的 `disabled` 判定不变。一处连带后果写明:`action:button` 上空的 `disabled` 现在会 **穿透**到 legacy 的 `enabled` 回退腿,而不再在空谓词上短路,所以同时写了 `disabled: '' + enabled: true` 的动作变为可点。 `action:button` 的 legacy `enabled` 腿也改读同一定义,但那部分是按推演 **行为等价**、不是修复:该腿取反(`disabled = !isEnabled`),空谓词的 `true` 本来 就落成「不置灰」,与「没有门」同一结果;四形状在两种判定下逐一相同。推演表与 「为什么没有任何测试能区分这条腿的改动」一并写在 pin 旁边。 `hasDeclaredVisibilityGate` 在两处沿用历史名(#3842 派发裁定):谓词与 key 无关, 同一实现两个名字即轻度方言;两处落点各就地注释说明。
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
✅ 验收通过(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt)—— undraft + auto-merge。 核验:功能 diff 恰 3 行(两落点 + enabled 腿统一到 衍生三单:#3848(ActionRunner 与渲染器对空谓词判定分歧,点击被 runner 挡 —— 端到端另一半)、#3849(同族门位还剩 5 处,issue 只点了 7 中之 2 —— PR3851 落 main 后即派候选)、#3850(「空谓词」在三个定义里三种外延,契约裁决类,两轴分析在案)—— 立单边界合规,#3850 归分诊席判级。 Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 8, 2026 20:54
This was referenced Aug 8, 2026
Merged
Open
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 #3842
disabled的「是否声明了门」判定停在!= null,少了visible一族已收敛的hasDeclaredVisibilityGate(!= null && !== '')的另一半。两处落点改读同一处定义。机理:为什么这半边是缺陷,而
visible那半边不是disabled: ''被判成「声明了门」,verdict 交给求值入口 —— 求值入口把空谓词读成「没有条件 →true」(toPredicateInput('')→undefined→evaluateCondition(undefined)→true)。方向是全部:在
visible上这个true意味着「显示」,于是过宽的「已声明」判定与宽松的空谓词互相抵消,visible: ''两种写法都渲染(这就是 PR #3843 只能把''当文档、当不了变异探测器的原因,那边注释里写明了)。在disabled上同一个true意味着「禁用」,两个错误叠加:空谓词从「没有门」变成「永久置灰」。同一个空谓词,两个 key 得到相反待遇。两处落点前后对照
packages/app-shell/src/views/DeclaredActionsBar.tsx:212一带(最烫的一处:动作是服务端声明的objectDef.actions[],宿主是审批收件箱记录区):packages/components/src/renderers/action/action-button.tsx:164(两条腿):命名按 #3842 的派发裁定沿用历史名(谓词与 key 无关,同一实现两个名字即轻度方言),两处各就地注释说明。
action:button同形实证(改前先探针,不按拼法推断)issue 明示
action-button.tsx:164未单独实证。先写 pin 测试、在未改动的源码上跑,得到的红正是同形证据:同一次运行里,
disabled: true/false/ 未声明、以及enabled四形状全绿 —— 坏的只是「已声明」的判定,不是求值。enabledlegacy 腿:按推演行为等价,不是修复该腿取反(
disabled = !isEnabled),所以空谓词的true落成「不置灰」,与「没有门」同一结果。四形状在两种判定下逐一相同:enabled!= null(旧)hasDeclaredVisibilityGate(新)''!true= 不置灰false= 不置灰true!true= 不置灰!true= 不置灰false!false= 置灰!false= 置灰falsefalse所以没有任何测试能靠还原这条腿变红 —— 这一点直说,不包装成覆盖率。改它只为「已声明」在两条腿上只有一种拼法。四个
enabled钉子留着,钉的是推演所断言的语义(enabled: false必须仍然置灰),那才是将来重写这段链条会静默弄坏的东西。真正被这次收紧移动的是穿透一例:
disabled: ''不再是门之后,链条落到 legacy 腿而不是在空谓词上短路,所以disabled: '' + enabled: true从置灰变可点(改前红、改后绿,见上)。反向验证(方向先判后跑)
方向 A —— 两处落点还原
disabled腿的!= null。预判:红,共 4 例(每包 2 例)。''在本侧是真的变异探测器,与visible侧相反(#3843 的 M2 记账说明了为什么)。实跑:预判命中:正好这 4 例,无第五例。
方向 B —— 只还原
enabled腿的!= null。预判:零红(上表的推演结论)。实跑Tests 11 passed (11)。推演由实测确认,而不是只被断言。行为变化面(刻意窄)+ 一处诚实披露
只有
disabled: ''改变:从置灰变可点,即「没有谓词」本来的含义。disabled: true仍置灰,disabled: false与未声明仍不置灰,任何表达式取值的disabled判定不变。披露:实测
ActionRunner.execute的执行门(ActionRunner.ts:666,disabled != null && disabled !== false)对空谓词同样判「已禁用」,handler 一次不跑、回{"success":false,"error":"Action is disabled"}。所以本 PR 之后,disabled: ''的动作按钮能点了,点下去收到Action is disabled报错。比原来「永久置灰、无任何解释」是改善,但那半边不在 #3842 的范围内(PM 已裁范围为两处渲染落点),已另立 #3848,不在此扩范围。顺手发现(均已独立成单、未认领,不在本 PR)
ActionRunner.execute的同族半边(上面那条披露);另有渲染器/执行器对纯空白串判定相反的分歧(可点 vs 拦),ActionEngine.getActionsForLocation 把 CEL 信封塌回 legacy JS 求值路径,与每个渲染器的判决不一致 #3314 同类。!= null拼法还剩五处渲染落点:action-icon.tsx:97、action-group.tsx:106与:148、action-menu.tsx:98、plugin-detail/record-quick-actions.tsx:208。同一按钮上visible问「已声明」而disabled问!= null。''后的残留:disabled: { dialect: 'cel', source: '' }仍被判成已声明的门 → 置灰(实测)。修法要动visible一族共用的那处定义,是契约范围变更,附两轴分析,请先裁。(同一批探针还证伪了一个我原本的怀疑:纯空白串在渲染侧良性,机制是走字符串腿包成模板求出 falsy,不是被哪处判为空。)验证
固件扫面:全仓
grep无任何既有 fixture 拼disabled: ''/enabled: '',所以没有 fixture 被这次收紧重新判决(无需重拼 / 补声明 / 整例替换)。新增 11 例(
packages/components/.../__tests__/action-disabled-declared-gate.test.tsx)+ 6 例(DeclaredActionsBar.test.tsx,沿用 PR #3843 收紧后的importOriginal偏 mock 桩形态:求值入口是真的,hasDeclaredVisibilityGate从真源模块取)。DeclaredActionsBar侧多钉一例「空谓词的按钮真的能 DISPATCH」—— 「没有 disabled 属性」只是这个论断的一半,而这个组件的点击处理器就是发出 approve 调用的那个。Changeset:
.changeset/action-declared-disabled-gate-3842.md(@object-ui/app-shell+@object-ui/componentspatch,写明disabled: ''从永久置灰变可点、以及穿透到 legacyenabled腿这一连带后果)。Generated by Claude Code