Skip to content

HookContext.input 的契约注释声明批量写携带 input.ast,引擎从不设置它(AST 只在 opCtx 上);同一张表也未描述 #5038 后 after 事件的按行形状 #5273

Closed

Description

@os-zhuang

在 #5038(批量写按行语义)实现过程中发现,PD #10 单独记录。落在 packages/spec/**(spec 车道所有),#5038 未触碰该文件。

事实(对 origin/main 核实)

packages/spec/src/data/hook.zod.ts 里 HookContext.input 的契约注释逐条列出每种操作的 input 形状:

   - find (also fires for findOne): { ast: QueryAST, options: DriverOptions }
   - insert: { doc: Record, options: DriverOptions }
   - update (single id): { id: ID, data: Record, options: DriverOptions }
   - update (bulk, multi:true): { ast: QueryAST, data: Record, options: DriverOptions }
   - delete (single id): { id: ID, options: DriverOptions }
   - delete (bulk, multi:true): { ast: QueryAST, options: DriverOptions }

并在下方重申「the row-scoping predicate is carried in input.ast」。

packages/objectql/src/engine.ts 里 input: { ast: ... } 只出现两处,都是读路径(beforeFind / afterFind)。写路径构造的是:

  • update():input: { id, data: opCtx.data, options: opCtx.options }
  • delete():input: { id, options: opCtx.options }

批量分支消费的 AST 是 opCtx.ast(#2982 为让中间件合成的行级过滤真正绑定 driver 而设的那条缝),它从不进 hookContext.input。所以照契约写 ctx.input.ast 的 hook 作者在批量写上拿到的是 undefined —— 而这恰恰是注释点名让人去读的那个字段。

hook-wrappers.ts 的 installFlatInput 把 ast 当作 wrapper key 特判(不下放到 data),说明这个形状曾被当真过;今天在写路径上它是一个没有生产者的消费口。

第二处漂移(#5038 之后)

同一张表把 update (bulk, multi:true) 描述为单一形状。#5038 落地后,批量写的 after 型事件按匹配行派发,每行的 input 是单记录形状({ id, data, options });仍然整批触发一次的只有 before*。下方那句「A bulk (multi: true) update/delete fires the SAME beforeUpdate/beforeDelete events as a single-id write」本身仍然成立(它只谈 before),但这张表读起来会让人以为 after 事件在批量写上也没有 per-row id。

Zod 契约本身不受影响 —— input 是 z.record(z.string(), z.unknown()),开放形状,#5038 因此无需改 spec 即可实现按行语义(这一点已在 PR #5270 里核实过)。纯属注释与实现的漂移,不是 schema 变更。

分级说明

标 finding:今天没有用户会撞上它——写路径 hook 作者拿 AST 的实际写法是 ctx.input.options / 引擎内部,仓内没有任何 ctx.input.ast 的写路径消费者(已 grep 确认)。但它是一条明确写下来、却从未兑现的契约陈述,正是 ADR-0049 「declared = enforced」路线要清掉的那一类;而且它会主动误导下一个照文档写 hook 的人(含 AI 作者)。

建议

由 spec 车道一次改掉两处:把批量写的 input 描述改成引擎实际给的形状({ id?, data?, options },并说明 after 事件在批量写上按行携带 id),删掉「the row-scoping predicate is carried in input.ast」这句,或者反过来让引擎真的把 opCtx.ast 放进批量写的 input(后者要评估是否有人会改它 —— input 是 mutable 的,把安全过滤后的 AST 交给 hook 改写需要单独想清楚,因此倾向改注释而不是改引擎)。

关联

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 4, 2026
  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊轮:晋级 pm:queue(摘 finding,domain:spec 沿用)。理由:一条明确写下、从未兑现且主动误导 hook 作者(含 AI 作者)的契约陈述,正是 ADR-0049 declared=enforced 要清的形状;修向采正文倾向 —— 改注释不改引擎(把批量写 input 描述改成引擎实际形状、说明 #5038 后 after 事件按行携带 id、删掉 input.ast 那句;把过滤后 AST 交给 mutable input 的安全评估不在本单)。落点 packages/spec/src/data/hook.zod.ts 注释面,零 schema 变更,spec 车道串行队列消化;hook-wrappers.ts 对 ast 的 wrapper 特判是否顺带清理由 dev 按 main 现状判。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    分诊轮判级(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D,2026-08-05):晋级(摘 finding 挂 pm:queue)。理由:契约注释声明引擎从不兑现的形状(input.ast)+ #5038 后 after 事件形状缺描述 —— 契约散文对 AI 作者就是规格,假散文即缺陷;修正面小(注释/TSDoc + 可行处补 pin)。排存量批次。


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 5, 2026
  5. self-assigned this
    on Aug 5, 2026
  6. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    PM 认领(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D,2026-08-05)


    Generated by Claude Code

  7. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    验收通过(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06)—— PR #5668。

    转 ready + auto-merge,队列落地。


    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

domain:specDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions