发现于 #5928 的实施(PR 见该单)。观察级:今天没有调用方能触发,不是在飞缺陷。
事实
#4281 (hook-empty-target-not-wildcard)把「空目标不是无目标」关在了两处:packages/spec/src/data/hook.zod.ts 的 authorable HookSchema.object refine,以及 packages/objectql/src/hook-binder.ts 的 normalizeObjects。两处都在元数据路径 上。
engine.registerHook(event, handler, { object }) 这条代码路径 不经过其中任何一处。匹配逻辑对 object 做的是真值判断(#5928 之前 triggerHooks 是 if (entry.object),hasHooksFor 是 if (!entry.object) return true,#5928 收敛后的 hookMatchesObject 按行为守恒保留了同一判断),于是:
#5928 新增排除面后,同一类「注册期可静态判定为无意义」的形状还多了一个:
为什么判为观察级
全仓 registerHook 调用方传的都是字面量常量('sys_user'、'sys_comment'、DELEGATION_OBJECT 等),没有任何调用方传入可能为空的计算值,因此今天无人能触发。#5928 的 PR 里已把 object: '' 的现有读法原样钉住并注明是保留而非认可 (hook-exclude-objects.test.ts),避免后来者在无意中翻转它。
为什么不在 #5928 里顺手改
翻转 object: '' 会把一个「对所有对象触发」的 hook 静默变成「对任何对象都不触发」—— 方向相反的静默失效,且发生在一个讲另一个契约面的 PR 里。正确的做法与 #4281 一致:注册期响亮拒绝 (而不是改变匹配读法),这是一个独立的契约决定,值得单独评估。
可能的处置(供分诊)
在 registerHook 里对 object 施加与 excludeObjects 同款的拒绝(空名 / 空白成员),消息沿用 fix(spec,objectql)!: hook 的空目标不再被静默放大成通配符 #4281 的措辞;顺带处理 allow 与 exclude 完全抵消的永不触发形状。
维持现状,仅保留 hook 注册契约只能表达「命中这些对象」,无法表达「全局但排除这些对象」—— #5860 因此在 plugin-audit 内无法落地 #5928 已加的钉子。
倾向 1:该面新增拒绝对现有调用方零影响(全是字面量常量),且把两条 ADR-0078 形状一次关掉;但它改变了代码注册面的严格度,属于契约决定,应由维护者定。
Refs: #4281 、#4001 、#5928 、ADR-0078
发现于 #5928 的实施(PR 见该单)。观察级:今天没有调用方能触发,不是在飞缺陷。
事实
#4281(
hook-empty-target-not-wildcard)把「空目标不是无目标」关在了两处:packages/spec/src/data/hook.zod.ts的 authorableHookSchema.objectrefine,以及packages/objectql/src/hook-binder.ts的normalizeObjects。两处都在元数据路径上。engine.registerHook(event, handler, { object })这条代码路径不经过其中任何一处。匹配逻辑对object做的是真值判断(#5928 之前triggerHooks是if (entry.object),hasHooksFor是if (!entry.object) return true,#5928 收敛后的hookMatchesObject按行为守恒保留了同一判断),于是:registerHook('afterUpdate', h, { object: '' })——''为 falsy,允许面被整个跳过,注册结果是一个全局 hook。正是 fix(spec,objectql)!: hook 的空目标不再被静默放大成通配符 #4281 描述的失败模式(空白意图变成最大爆炸半径),只是发生在该裁决未覆盖的路径上。#5928 新增排除面后,同一类「注册期可静态判定为无意义」的形状还多了一个:
registerHook('afterUpdate', h, { object: 'account', excludeObjects: 'account' })—— 允许面被排除面完全抵消,该 entry 永不触发。hook 注册契约只能表达「命中这些对象」,无法表达「全局但排除这些对象」—— #5860 因此在 plugin-audit 内无法落地 #5928 的裁决只点名了三种拒绝(''/['']/ 排除面里的'*'),这一种按裁决字面不在范围内,故 PR 里刻意未做,在此记录。两者同属 ADR-0078「不得有静默失效的声明」。为什么判为观察级
全仓
registerHook调用方传的都是字面量常量('sys_user'、'sys_comment'、DELEGATION_OBJECT等),没有任何调用方传入可能为空的计算值,因此今天无人能触发。#5928 的 PR 里已把object: ''的现有读法原样钉住并注明是保留而非认可(hook-exclude-objects.test.ts),避免后来者在无意中翻转它。为什么不在 #5928 里顺手改
翻转
object: ''会把一个「对所有对象触发」的 hook 静默变成「对任何对象都不触发」—— 方向相反的静默失效,且发生在一个讲另一个契约面的 PR 里。正确的做法与 #4281 一致:注册期响亮拒绝(而不是改变匹配读法),这是一个独立的契约决定,值得单独评估。可能的处置(供分诊)
registerHook里对object施加与excludeObjects同款的拒绝(空名 / 空白成员),消息沿用 fix(spec,objectql)!: hook 的空目标不再被静默放大成通配符 #4281 的措辞;顺带处理 allow 与 exclude 完全抵消的永不触发形状。倾向 1:该面新增拒绝对现有调用方零影响(全是字面量常量),且把两条 ADR-0078 形状一次关掉;但它改变了代码注册面的严格度,属于契约决定,应由维护者定。
Refs: #4281、#4001、#5928、ADR-0078