Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

ActionEngine.getActionsForLocation 把 CEL 信封塌回 legacy JS 求值路径,与每个渲染器的判决不一致 #3314

Closed

Description

@xuyushun441-sys

Filed unassigned,来自 objectstack#4811 / PR objectstack#4951 期间的只读排查(为给 null-guard 闸门定判据而读 ActionEngine)。只记录发现。

现象

同一条 action visible 谓词,经 ActionEngine 与经渲染器会走两个不同的求值引擎。

packages/react/src/hooks/useExpression.ts 的 toPredicateInput 明确保留 CEL 信封,好让 useCondition 路由到 @objectstack/formula(#2661 就是为此):

// #2661 — preserve a CEL-dialect envelope so `useCondition` routes it to the
// canonical `@objectstack/formula` engine (identical verdict to the server),
// instead of collapsing it to a `${source}` string on the legacy JS path.
if ((value as any).dialect === 'cel') return { dialect: 'cel', source: src };
return `\${${src}}`;

而 packages/core/src/actions/ActionEngine.ts 的 getActionsForLocation 自己把信封拆开、重新包成 ${…} 字符串:

} else if (typeof raw === 'object' && typeof raw.source === 'string') {
  const src = raw.source;
  if (!src) return true;
  expr = `\${${src}}`;          // ← dialect 被丢弃
}
…
return evaluator.evaluateCondition(expr as any, { throwOnError: true } as any);

ExpressionEvaluator.evaluateCondition 只在参数仍是 {dialect:'cel'} 对象时才转发给 evaluateCelCondition;收到字符串就走 legacy JS 路径。所以这里 #2661 的路由被绕过了。

方法自己的 doc comment 还写着它「matches the contract used by every individual action renderer (action-button, action-menu, action-bar, …)」—— 而那些渲染器走的是 toPredicateInput,恰恰是保留信封的那条路。

为什么这不是无关紧要的

上游 ExpressionInputSchema(objectstack spec)会把裸串 visible: 'record.x > 1' 规范成 { dialect: 'cel', source: … },所以已发布元数据基本都是 CEL 信封,这条分支是常走的。

两个引擎的 null 语义不同:CEL 里 null < null 无重载 → fault → fail-closed 隐藏;JS 里 null < null 是 false,不 fault。于是同一条谓词可能在渲染器里隐藏按钮、在引擎枚举里保留它(或反之)。CEL 专有能力(如 record.due_date == today(),objectstack#3205)在这条路径上也不可用。

顺带:ActionRunner 的 disabled 走的是 this.evaluator.evaluateCondition(action.disabled) —— 直接传对象,所以 disabled 走真 CEL 而同一个 action 的 visible 经引擎走 JS。

可能的修法(未验证,留给认领者判断)

把 getActionsForLocation 的归一化换成复用 toPredicateInput,不要自己拆信封 —— 一处归一化而不是两处,这也正是 toPredicateInput 的注释所主张的。

备注

objectstack 侧的 lint 闸门(objectstack#4811)因为另一个原因排除了 action 谓词面(客户端记录不是对已声明字段全量的,见 objectstack#4953),所以本问题不会被那道闸门捕获。

Activity

  1. added
    target:v17v17 发布窗口工作集(GA 前排查 2026-08-04)
    on Aug 4, 2026
  2. self-assigned this
    on Aug 5, 2026
  3. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    Contributor

    认领:objectui 分片 PM 循环第 1 轮(v17 C 组)
    会话:session_01GTRjn8xBqp75dk7kFupVRt
    分支:claude/issue-3314-actionengine-cel-envelope
    Worktree:objectui-issue-3314
    域:objectui 分片(core / react)
    文件面:packages/core/src/actions/ActionEngine.ts(主修)+ 只读复用 packages/react/src/hooks/useExpression.ts 的 toPredicateInput(如需搬移归一化逻辑,落点与形状由 dev 依包依赖方向裁定并在报告说明)+ 相应测试 + .changeset/*.md(越界即停,报告说明)

    前提已对 origin/main @ 68b6a28 核验:信封塌陷仍在(ActionEngine.ts:228/232)。


    Generated by Claude Code

  4. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    Contributor

    PM 验收:ACCEPT(返工 1 轮后)→ PR #3369 转 ready + auto-merge(CI 15/15 全绿,含此前唯一红的 Type Check/check:spec-symbols),跟到 MERGED。

    落地内容:getActionsForLocation 不再手拆 CEL 信封,归一化统一走 @object-ui/core 新增的 canonical helper(与渲染器侧语义逐项一致,14 形态归一化 parity + 9 用例判决 parity 钉住);判别用例锚在两引擎真实分歧处(CEL null 比较 fault→fail-closed vs legacy JS false);非 CEL 分支逐项行为保持。返工内容:本地类型改名 EvaluatorPredicateInput(先核 spec 的 PredicateInput 语义确异 —— 作者面谓词 vs 求值器归一化联合,互不可赋值;对 spec 16 subpath / 4264 导出名核过新名未被占用),循 renamed-dialect 先例(PageNodeRegion/RuntimeWidgetManifest)加双向 tripwire,未加 ALLOW 例外。

    衍生记账:finding #3367(归一化双实现收敛)、#3368(event-mapping 零调用方 + fail-open)已立,留分诊轮;#3288 补两条硬证据(包级 --filter test 假绿机制 + 独立门禁不在包级 type-check 里 —— 后者正是本次 CI 红的直接成因,PM 采纳并入 #3288 记录,不单独立项)。

    至此第 1 轮三单(#3316/#3317/#3314)全部代码侧闭环。

    objectui 分片 PM session_01GTRjn8xBqp75dk7kFupVRt


    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

Labels

pm:dispatchedtarget:v17v17 发布窗口工作集(GA 前排查 2026-08-04)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions