Skip to content

bug(objectql/docs): hook condition 的 CEL 作用域只绑定 record —— 文档教的 previous.x / ctx.record 根本不存在,过渡型条件写不出来 #4784

Description

@xuyushun441-sys

TL;DR

声明式 hook 的 condition 求值时只绑定一个根 record:

// packages/objectql/src/hook-wrappers.ts
const r = ExpressionEngine.evaluate< boolean >(expr, { record: record ?? {} });

但两处已发布的 skill 文档教作者写 previous.*(以及一个不存在的 ctx.record)。写了就静默失效:previous.statusNo such key: previous → 被 catch 成 false → hook 不触发,只留一条 warn。

现场

  1. skills/objectstack-formula/SKILL.md §5「Update hook condition — previous vs record」直接给出:

    P`previous.status != 'escalated' && record.status == 'escalated'`

    这个例子对 validation rule 的 condition 是对的(rule-validator.ts 确实传 previous binding),对 hook 的 condition 是错的 —— 而小标题写的是 hook。同一页的转换表还把 ISCHANGED(x) 映射成 previous.x != record.x,作者会照着往 hook 里抄。

  2. skills/objectstack-automation/SKILL.md 的 time-word 速查表:

    Surface Event-time record Pre-event record Live record
    Object hook (ctx) ctx.record (write payload) ctx.previous

    HookContext没有 record 这个键(packages/spec/src/data/hook.zod.ts:input / result / previous / session / …)。ctx.previous 是真的,ctx.record 不是。

为什么现在值得单独立一条

#4770(hook 条件改为对 stored ⊕ payload 求值)刚把条件的语义从「本次 diff」变成「记录状态」:record.done == true 现在对每一次已完成任务的 update 都为真,而不只是那次把它置为 done 的 update。想表达「刚刚变成 done」这件事,唯一的写法就是比较 previous —— 而 hook 条件恰恰拿不到它。审计类 hook 的原意(showcase_audit_task_completion 的 description 写的是 "after a task transitions to done")因此仍然表达不出来。

两条路,需要定夺(所以本条不带实现)

  • A. 给 hook condition 的作用域补上 previous(以及可能的 user),与 validation 谓词对齐。 好处:文档里已经这么教了,作者心智一致,过渡语义可写。代价:condition 的可写表达面变大,属于公开契约扩展 —— 而且 previous 只在引擎取到 prior row 时才有(bulk predicate update 没有),要先定「拿不到时 previous 是什么」。
  • B. 不扩作用域,把两处文档改成只讲 record,并明确「hook 条件无法表达过渡,需要过渡语义请用 record-change flow trigger 的 start condition」。 好处:不动运行时。代价:skills/objectstack-formulaISCHANGED 迁移路径在 hook 上就是断的。

无论选哪条,文档与运行时现在是对不上的,属于 declared ≠ delivered。

复现

一个 afterUpdate hook,condition: "previous.done != true && record.done == true",更新任一字段:

WARN [hook] condition evaluation failed; treating as false
  {"hook":"...","condition":"previous.done != true && record.done == true",
   "error":"No such key: previous..."}

发现于 #4770 的实现过程中(PR: hook 条件合并 record),按 AGENTS.md Prime Directive #10 记录,未在该 PR 内修复。

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions