Skip to content

declared ≠ enforced:DataEventType 的 data.field.changed 全仓没有生产者(ADR-0049 第四态) #4673

Description

@os-zhuang

#4639 的裁决中拆出(明确不做 #4639 的 rider),unassigned。

现象

packages/spec/src/api/events.zod.tsDataEventType 声明四个值:

export const DataEventType = z.enum([
  'data.record.created',
  'data.record.updated',
  'data.record.deleted',
  'data.field.changed',   // ← 无生产者
]);

全仓 grep data.field.changed 只有三处出现:上面这行、packages/spec/src/api/events.test.ts、以及文档 content/docs/references/api/events.mdx没有任何代码发出它——ObjectQL engine 的 publishDataEvent / publishBulkDataEvent 只发 data.record.{created,updated,deleted} 与(#4639 起)data.records.{updated,deleted}

content/docs/references/api/realtime.mdx 自己已经承认了这件事:

not yet enforced — the runtime emits data.record.* event names instead, and field.changed is never emitted; see #3197

这是教科书式的 ADR-0049 第四态(parsed / unmarked / unenforced),同族 #3197

处置方向(ADR-0049 enforce-or-remove 三选一)

  1. 实现:在 engine 的 update 路径按 changes 逐字段发 data.field.changed。要先回答值不值:逐字段事件在宽表上是一次写入 N 条事件,且现有 DataEvent 的 schema 没有 field / oldValue / newValue 字段可承载它——真要做需要自己的 schema(参照 declared ≠ enforced:引擎 multi:true 谓词写入没有 data.record.* 事件,webhook / 知识同步对其静默(REST 批量端点不受影响) #4639 给批量写单独立契约的做法)。
  2. experimental:仅当确实打算近期实现。
  3. 移除:从 DataEventType 删掉。按 .claude/skills/spec-property-retirement 走完整套件(ADR-0087 转换层、liveness ledger、生成基线、文档、pin 测试),别手工重建。

倾向 3:没有需求方,DataEvent 的形状也不适合承载逐字段语义;真需要时按 #4639 的先例新立一个诚实的契约,比让一个空 enum 值占着位置强。

参考

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions