Skip to content

ActionRunner.execute 的 condition 门用真值判定:condition: false(最明确的「永不执行」)照样执行(实测 handler 跑了) #3872

Description

@yinlianghui

来源

发现自 #3848 实施时的邻行扫面 —— 就在 #3848 那道 disabled 门上面一行。不在 #3848 修法面(那单只改 disabled 门,condition 是另一个键、另一条门),未认领。

机理

packages/core/src/actions/ActionRunner.ts(#3848 前的行号 ~657):

// Conditional execution
if (action.condition) {
  const shouldExecute = this.evaluator.evaluateCondition(action.condition);
  if (!shouldExecute) {
    return { success: false, error: 'Action condition not met' };
  }
}

入门判定是真值,不是「是否声明了门」。condition: false 落在 if (false) 上 —— 整段被跳过,evaluateCondition 根本没被问,动作照常执行。这正是 #3812 已经付过一次代价的形状:真值回答不了「有没有声明门」,而 false 是作者能写出的最明确的一句「永远不要执行这个」。

同源的还有 0:condition: 0 同样被跳过。

实测(worktree @ origin/main = f0a625aa7b00c93a48329e1c456b1aeacfceba43,一次性探针,未提交)

new ActionRunner({ record: { id: 1, status: 'active' }, user: { role: 'admin' } }),只改 condition 一个键:

condition: false                     | handler ran: true  | {"success":true}            ← 声明了「永不执行」却执行了
condition: true                      | handler ran: true  | {"success":true}
condition: ''                        | handler ran: true  | {"success":true}            ← 空谓词放行,正确
condition: 'user.role == "guest"'    | handler ran: false | {"error":"Action condition not met"}
condition: {dialect:'cel',source:'false'} | handler ran: false | {"error":"Action condition not met"}

注意 false 与 {dialect:'cel',source:'false'} 语义相同、结论相反 —— 布尔字面量放行,同义的 envelope 拦下。

与 disabled 门的区别(为什么这单不能顺手并进 #3848)

disabled 门的病是空谓词被 evaluateCondition 的「没有条件 → true」判成禁用(#3848,已修);condition 门在空谓词上恰好是对的('' 被真值判定跳过 → 执行 → 与「没有条件 → 执行」同解)。这里的病在反方向:显式 false 被真值判定当成「没声明」。两处的正确改法不同(disabled 要「有没有条件」,condition 要「有没有声明」+ 布尔短路),键也不同,故独立成单。

影响

  • 命中条件:元数据把 condition 写成布尔 false(或数字 0)。授权/模板产出 false 常见于「按开关关掉某个动作」。
  • 受众是所有走 ActionRunner.execute 的动作 —— core 的公共执行入口,不限渲染面。
  • 方向是过度放行:声明了不执行却执行,比过度拦截更危险(动作真跑了,可能落库)。

修法建议(未裁)

与 #3492 / #3842 家族同解:入门判定问「已声明」而不是真值,布尔在求值入口自然短路(evaluateCondition(false) 就是 false)。ActionEngine.getActionsForLocation 的 visible 过滤已经是这个写法(if (raw == null || raw === '' || raw === true) return true; if (raw === false) return false;),可就近取形。「空谓词算不算已声明」的范围问题请对齐 #3850 的裁决,别再添第 N 种拼法。

Related: #3812(真值回答不了「有没有门」的出处)、#3848(邻行的 disabled 门,本单发现出处)、#3492(不变量出处)、#3850(「空谓词」范围裁决)、#3871(同批发现的归一器双重包裹)。未认领。

Activity

  1. claude commented on Aug 8, 2026

    @claude
    Contributor

    Triage: pm:queue — concrete defect, live repro in the card, named location.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 9, 2026
  3. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    CollaboratorAuthor

    🔒 分诊 + 认领(objectui shard PM 亲跑分诊轮,session session_01GTRjn8xBqp75dk7kFupVRt)

    定级:concrete —— #3848 dev 立卡时带实测(condition: false handler 照跑返回 success,而同义 {dialect:'cel',source:'false'} 被拦),真值判定 if (action.condition) 是 #3812 形状、方向为过度放行(声明了不执行却执行,可能落库)。前序 PR3873 已落 main,同文件的 disabled 门已收敛 —— 本单是同一道门的 condition 键。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions