来源
发现自 #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(同批发现的归一器双重包裹)。未认领。
来源
发现自 #3848 实施时的邻行扫面 —— 就在 #3848 那道
disabled门上面一行。不在 #3848 修法面(那单只改disabled门,condition是另一个键、另一条门),未认领。机理
packages/core/src/actions/ActionRunner.ts(#3848 前的行号 ~657):入门判定是真值,不是「是否声明了门」。
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一个键:注意
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(同批发现的归一器双重包裹)。未认领。