实现 #5670(对齐 skills / content/docs 三处的 ctx.input.ast 假句)时核实真值来源,在 packages/spec/src/data/hook.zod.ts 里发现同一条从未兑现的陈述还剩一处。PD #10 单独记录,不在 #5670 的 PR 里顺手改(那单派发面明确排除 packages/spec/**,理由是「#5273 已修」——实测只修了其中一处)。
事实(对 origin/main = 5c94f833c 核实)
HookEvent 枚举的写事件注释:
packages/spec/src/data/hook.zod.ts:135-140
// Write — before/after per mutation kind. These fire on BOTH single-id and
// bulk (`multi: true`) writes: a bulk update/delete runs the SAME
// `beforeUpdate`/`beforeDelete`/`afterUpdate`/`afterDelete` with the
// row-scoping predicate carried in `input` (there is no per-cardinality
// `*Many` event — one write event covers one row or many, Salesforce's
// bulk-first model). See #3195.
「row-scoping predicate carried in input」是假的,和 #5273 删掉的那句是同一条陈述,只是没写 .ast 后缀。引擎从不在写路径的 HookContext.input 上放谓词;它在引擎内部 OperationContext.ast(#2982)。
PR #5668(#5273 的落地)改的是同文件下方约 200 行的 HookContextSchema.input 那段契约表,并在 :340 起写下了正确措辞:
* The row-scoping predicate is NOT reachable from `input` at all. It lives
* on the engine-internal `OperationContext.ast` (#2982) so that the filters
* middleware composes onto it …
git log -L 135,144:packages/spec/src/data/hook.zod.ts 显示这段枚举注释最后一次变动是 b49ccfdfe(#5306),#5668 未触及。于是同一个文件现在自相矛盾:枚举注释说谓词在 input,200 行外的契约表说它「NOT reachable from input at all」。
真值来源(已在 main 上,无需新测)
packages/objectql/src/hook-input-shape-contract.test.ts —— PR #5668 新增。本次实测全绿(11/11),其中直接钉死本句的三条:
✓ [#5273] a bulk write carries no `ast` on `input` > POSITIVE CONTROL — a read DOES carry `input.ast`
✓ [#5273] a bulk write carries no `ast` on `input` > `beforeUpdate` on a bulk write has no `ast` key
✓ [#5273] a bulk write carries no `ast` on `input` > `beforeDelete` on a bulk write has no `ast` key
阳性对照(读路径确实带 ast)同批为绿,所以「写路径 input 上没有谓词」是一次测量,不是空过。
为什么不是「注释小事」
和 #5273 分诊轮的判断同源:input 是 z.record(z.string(), z.unknown()),Zod 对键的存在什么都不校验,所以这段散文就是作者(人或 AI)能读到的唯一规格。一个枚举注释与同文件契约表互相矛盾时,读到哪一段是随机的。
建议
把 carried in input`` 改成与 :340 那段已落地措辞一致的说法(谓词不在 `input`,在引擎内部 `OperationContext.ast`),主句(批量写触发同名事件、没有 `*Many` 事件)保留。纯注释订正,`.describe()` 未涉及,预计不动生成物 —— 但按 AGENTS.md 需跑 `check:generated` 确认。
关联
Generated by Claude Code
实现 #5670(对齐 skills / content/docs 三处的
ctx.input.ast假句)时核实真值来源,在packages/spec/src/data/hook.zod.ts里发现同一条从未兑现的陈述还剩一处。PD #10 单独记录,不在 #5670 的 PR 里顺手改(那单派发面明确排除packages/spec/**,理由是「#5273 已修」——实测只修了其中一处)。事实(对
origin/main=5c94f833c核实)HookEvent枚举的写事件注释:packages/spec/src/data/hook.zod.ts:135-140「row-scoping predicate carried in
input」是假的,和 #5273 删掉的那句是同一条陈述,只是没写.ast后缀。引擎从不在写路径的HookContext.input上放谓词;它在引擎内部OperationContext.ast(#2982)。为什么 #5668 没覆盖到它
PR #5668(#5273 的落地)改的是同文件下方约 200 行的
HookContextSchema.input那段契约表,并在:340起写下了正确措辞:git log -L 135,144:packages/spec/src/data/hook.zod.ts显示这段枚举注释最后一次变动是b49ccfdfe(#5306),#5668 未触及。于是同一个文件现在自相矛盾:枚举注释说谓词在input,200 行外的契约表说它「NOT reachable frominputat all」。真值来源(已在 main 上,无需新测)
packages/objectql/src/hook-input-shape-contract.test.ts—— PR #5668 新增。本次实测全绿(11/11),其中直接钉死本句的三条:阳性对照(读路径确实带
ast)同批为绿,所以「写路径input上没有谓词」是一次测量,不是空过。为什么不是「注释小事」
和 #5273 分诊轮的判断同源:
input是z.record(z.string(), z.unknown()),Zod 对键的存在什么都不校验,所以这段散文就是作者(人或 AI)能读到的唯一规格。一个枚举注释与同文件契约表互相矛盾时,读到哪一段是随机的。建议
把
carried ininput`` 改成与:340那段已落地措辞一致的说法(谓词不在 `input`,在引擎内部 `OperationContext.ast`),主句(批量写触发同名事件、没有 `*Many` 事件)保留。纯注释订正,`.describe()` 未涉及,预计不动生成物 —— 但按 AGENTS.md 需跑 `check:generated` 确认。关联
HookContext.input的契约注释声明批量写携带input.ast,引擎从不设置它(AST 只在 opCtx 上);同一张表也未描述 #5038 后 after 事件的按行形状 #5273 / PR docs(spec): HookContext.input 契约表改成引擎真正构造的形状 (#5273) #5668(同文件契约表那份已修)、hooks 技能与 API 文档仍在教「批量写的行级谓词在ctx.input.ast」——与 #5273 同一句假话,只是另外三个面 #5670(skills 与 content/docs 三个面)opCtx.ast的原因)、Hook event taxonomy is over-specified: collapse 18 events → 8, make findOne fire find hooks, formalize bulk semantics #3195(为什么没有*Many事件)Generated by Claude Code