Part of objectstack-ai/hotcrm#779
现象
ctx.previous 在 beforeUpdate 上的唯一来源是内建 sys_fetch_previous_update(priority 5,object: '*',由 kernel 的 registerAuditHooks() 装载)——引擎主体只在 afterUpdate 分支里赋 hookContext.previous。而该内建第一行是:
if (hookCtx.input?.id && !hookCtx.previous) { … }
multi: true 的批量更新没有 input.id(目标由 where 圈定),内建取不到任何东西,ctx.previous 在批量路径上恒为 undefined。
证据(pinned 17.0.0-rc.2,真 object schema + 真 hook + 内建忠实复刻,priority 299 探针 hook 观测)
=== single-row update (category only) ===
[prev-fetch] input.id="crm_knowledge_article-…-1" previous=undefined
[knowledge-hook sees] previous.status="published" input={"id":"…","category":"api"}
=> last_reviewed_at refreshed: true
=== MULTI update (category only, every published row) ===
[prev-fetch] input.id=undefined previous=undefined
[knowledge-hook sees] previous.status=undefined input={"category":"troubleshooting"}
=> last_reviewed_at refreshed: false
=== MULTI update writing last_reviewed_at: null ===
[knowledge-hook sees] previous.status=undefined input={"last_reviewed_at":null}
=> A: last_reviewed_at=null
=> B: last_reviewed_at=null
应用侧影响(hotcrm 实测,详见 hotcrm#779)
hotcrm 有 15 个 hook 文件读 ctx.previous,几乎全用 previous?.x 可选链——批量路径上不抛错,而是静默空转或走进「当作首次转换」分支。已实测的两种后果:批量编辑不刷新知识文章的 last_reviewed_at(打破 stale 报表前提);批量写能把 readonly: true 的 last_reviewed_at 写成 null(单行路径 hook 会补戳盖掉,批量不会)。守卫语义的 hook 在批量路径上失效方向是放行(如 contract.hook 的 previous?.status === 'activated' 守卫)。
关键问题(设计定夺,不预设是 bug)
引擎在 needsPriorRecord(updateSchema) 为真时确实会取 priorRows(用于校验规则),只是没有把它喂给 hook。所以:
- 批量路径不供给 previous 是设计取舍还是遗漏?若是取舍,请在 hook 契约文档里写明「multi:true 上
ctx.previous 不可用」,应用侧好据此重排不变量(挪到校验规则/flow);
- 若愿意供给:per-row previous 的代价与语义(N 行 N 份、hook 逐行执行还是一次批量执行)需要平台定夺——
priorRows 既然已取,喂给 hook 的增量成本可能有限。
应用侧的修复方向(hook 逐个显式化 vs 不变量搬家)取决于这个答案,hotcrm#779 因此 Blocked-by 本单。
Part of objectstack-ai/hotcrm#779
现象
ctx.previous在beforeUpdate上的唯一来源是内建sys_fetch_previous_update(priority 5,object: '*',由 kernel 的registerAuditHooks()装载)——引擎主体只在afterUpdate分支里赋hookContext.previous。而该内建第一行是:multi: true的批量更新没有input.id(目标由where圈定),内建取不到任何东西,ctx.previous在批量路径上恒为 undefined。证据(pinned 17.0.0-rc.2,真 object schema + 真 hook + 内建忠实复刻,priority 299 探针 hook 观测)
应用侧影响(hotcrm 实测,详见 hotcrm#779)
hotcrm 有 15 个 hook 文件读
ctx.previous,几乎全用previous?.x可选链——批量路径上不抛错,而是静默空转或走进「当作首次转换」分支。已实测的两种后果:批量编辑不刷新知识文章的last_reviewed_at(打破 stale 报表前提);批量写能把readonly: true的last_reviewed_at写成 null(单行路径 hook 会补戳盖掉,批量不会)。守卫语义的 hook 在批量路径上失效方向是放行(如 contract.hook 的previous?.status === 'activated'守卫)。关键问题(设计定夺,不预设是 bug)
引擎在
needsPriorRecord(updateSchema)为真时确实会取priorRows(用于校验规则),只是没有把它喂给 hook。所以:ctx.previous不可用」,应用侧好据此重排不变量(挪到校验规则/flow);priorRows既然已取,喂给 hook 的增量成本可能有限。应用侧的修复方向(hook 逐个显式化 vs 不变量搬家)取决于这个答案,hotcrm#779 因此 Blocked-by 本单。