#4587(spec 双源 C2)判真源扫描时的范围外发现,按第十条军规立案,unassigned。
现象
@objectstack/spec/api 的 MetadataEvent 声明顶层字段 id(uuid,必填)、metadataType、name、definition?、userId?。但客户端 SDK 实际交付给订阅者的对象是 RealtimeEventPayload 信封,形状对不上:
- 生产侧
packages/metadata/src/metadata-manager.ts(register/unregister 路径)发布的是:
{ type: `metadata.${type}.created`, object: type,
payload: { metadataType, name, definition, packageId }, timestamp }
—— metadataType/name/definition/packageId 都嵌在 payload 里,且从不生成 id/userId。
- 消费侧
packages/client/src/realtime-api.ts:72 把这个信封直接 callback(event as any as MetadataEvent);packages/client-react 的 useMetadataEvent 同源。
结果:订阅者写 event.name / event.metadataType / event.definition,类型系统说是 string,运行时全是 undefined;必填的 event.id 也不存在。as any as 双铸就是这个谎的遮羞布。另注意 MetadataManager 只发布 created/deleted,metadata.{type}.updated 无生产者(register 同名覆盖也走 created)。
为什么现在值得修
#4587 已把 MetadataEvent 的裸名唯一属主收敛到 ./api——它现在就是 realtime 元数据变更事件的唯一合同,而这份合同当前对每个消费者都在说谎。AI 按类型写出的订阅代码(if (event.name === …))编译全绿、运行时静默走错分支,恰是 lenient-consumer 藏错的形态。
处置方向(contract-first,二选一,需裁决)
- 让生产者履约:
MetadataManager 发布真正的 MetadataEvent(生成 id,顶层铺开字段,补 userId),client 侧删掉 as any as 铸型,用 MetadataEventSchema.parse(或 safeParse)在边界处验证。
- 让合同如实:若 wire 信封就该是
RealtimeEventPayload,则 subscribeMetadata 回调类型改为信封形状(或提供显式的解包层),MetadataEvent 的字段布局改为与信封一致。
同族先例:#3197(声明无消费的事件枚举)、#4579(openApi31 declared ≠ enforced)。
发现于 #4587 三仓 import 级扫描;本条不属于该 PR 范围(该 PR 只消歧命名归属,不改 realtime 行为)。
#4587(spec 双源 C2)判真源扫描时的范围外发现,按第十条军规立案,unassigned。
现象
@objectstack/spec/api的MetadataEvent声明顶层字段id(uuid,必填)、metadataType、name、definition?、userId?。但客户端 SDK 实际交付给订阅者的对象是RealtimeEventPayload信封,形状对不上:packages/metadata/src/metadata-manager.ts(register/unregister 路径)发布的是:metadataType/name/definition/packageId都嵌在payload里,且从不生成id/userId。packages/client/src/realtime-api.ts:72把这个信封直接callback(event as any as MetadataEvent);packages/client-react的useMetadataEvent同源。结果:订阅者写
event.name/event.metadataType/event.definition,类型系统说是string,运行时全是undefined;必填的event.id也不存在。as any as双铸就是这个谎的遮羞布。另注意MetadataManager只发布created/deleted,metadata.{type}.updated无生产者(register 同名覆盖也走created)。为什么现在值得修
#4587 已把
MetadataEvent的裸名唯一属主收敛到./api——它现在就是 realtime 元数据变更事件的唯一合同,而这份合同当前对每个消费者都在说谎。AI 按类型写出的订阅代码(if (event.name === …))编译全绿、运行时静默走错分支,恰是 lenient-consumer 藏错的形态。处置方向(contract-first,二选一,需裁决)
MetadataManager发布真正的MetadataEvent(生成id,顶层铺开字段,补userId),client 侧删掉as any as铸型,用MetadataEventSchema.parse(或 safeParse)在边界处验证。RealtimeEventPayload,则subscribeMetadata回调类型改为信封形状(或提供显式的解包层),MetadataEvent的字段布局改为与信封一致。同族先例:#3197(声明无消费的事件枚举)、#4579(
openApi31declared ≠ enforced)。发现于 #4587 三仓 import 级扫描;本条不属于该 PR 范围(该 PR 只消歧命名归属,不改 realtime 行为)。