Skip to content

fix(metadata,client): subscribeMetadata 交付真正的 MetadataEvent——生产者履约 + 边界校验 (#4602) - #4628

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4602-metadata-event-contract
Aug 2, 2026
Merged

fix(metadata,client): subscribeMetadata 交付真正的 MetadataEvent——生产者履约 + 边界校验 (#4602)#4628
os-zhuang merged 1 commit into
mainfrom
claude/issue-4602-metadata-event-contract

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4602

按 PM 裁决(方案 1,生产者履约)实施:MetadataEvent(@objectstack/spec/api)是 #4587 收敛后 realtime 元数据变更的唯一已声明合同,本 PR 让生产侧发布真正的 MetadataEvent,消费侧在边界处校验而不是硬铸。

生产侧(packages/metadata)

  • MetadataManager.register() / unregister() 构造完整的 MetadataEvent:生成 uuid id,metadataType/name/definition 顶层铺开,写路径声明了操作者时携带 userId,发布前用 MetadataEventSchema.parse 校验(malformed 在生产端响亮失败,不再把谎话送下游)。
  • 传输信封保持不变:RealtimeEventPayloadpayload 承载完整 MetadataEvent。仓内对 metadata.{type}.* 信封 payload 的唯一读者是 client SDK(webhook auto-enqueuer 只消费 data.record.*),因此无信封依赖被破坏——未发现需要 needs_decision 的反证。
  • register 同名覆盖现在发布 metadata.{type}.updated(与既有 added/changed watcher 分叉一致),此前 .updated 声明而无任何生产者。已 pin 测试。
  • MetadataEventType 是封闭枚举:枚举外的类型(如 translation)没有已声明的 realtime 事件合同,不发布(debug 日志)——发布一个每个合规消费者都必须拒绝的事件更糟(declared = enforced)。覆盖面是否扩枚举/收窄 subscribeMetadata 参数类型,已立案 MetadataEventType 是 13 类型的封闭枚举,但可注册的 metadata 类型远多于此——枚举外类型的 realtime 事件合同缺位,需裁决覆盖面 #4627 待裁决。

消费侧(packages/client,client-react 传递生效)

  • 删除 callback(event as any as MetadataEvent) 双铸;subscribeMetadata 在边界拆信封并 MetadataEventSchema.safeParse,不合合同的 payload 响亮拒绝(handler 抛错、回调不触发),绝不静默胁变或透传。
  • client-reactuseMetadataSubscription / useMetadataSubscriptionCallback 直接委托 client SDK,无自己的铸型,随之修复,无需改动。

合同 seam(packages/spec)

  • MetadataWriteOptions 新增可选 userId(操作者),让知道 actor 的写路径能把 userId 带进事件——否则 MetadataEvent.userId 永远是"声明了但没人能生产"。加法变更,现有调用方不受影响。spec 八项生成产物门禁全绿(check:generated 全过,无需重新生成)。

测试

  • packages/metadata/src/metadata-realtime-events.test.ts(9 例):信封 payload 用 spec schema 本体 parse(双方漂移即红);覆盖 created/updated/deleted、userId 携带/缺省、枚举外类型零发布、非 string packageId 剔除、id 唯一、publish 失败不影响写入。
  • packages/client/src/realtime-api.test.ts(5 例):订阅者收到顶层字段(pre-fix 树上必红——旧代码回调收到的是信封,event.nameundefined);pre-fix 生产者形状与错误字段类型均被响亮拒绝且回调不触发;packageId 过滤与退订不回归。
  • @objectstack/metadata test:14 files / 290 tests 全绿;@objectstack/client test:16 files / 209 tests 全绿;spec/client/client-react typecheck 全绿。

范围外发现(已立案,unassigned)

🤖 Generated with Claude Code

https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5


Generated by Claude Code

…— producer fulfils the declared contract (#4602)

MetadataManager now builds a schema-valid MetadataEvent (generated uuid id,
flattened top-level metadataType/name/definition, userId when the write
declares an actor via the new MetadataWriteOptions.userId seam), validates it
with MetadataEventSchema.parse before publishing, and carries it as the
RealtimeEventPayload envelope's payload. A register() overwrite now publishes
metadata.{type}.updated (mirroring the added/changed watcher split) instead of
a second .created. Types outside the closed MetadataEventType enum publish
nothing (declared = enforced) instead of an event every compliant consumer
must reject.

The client SDK's subscribeMetadata unwraps the envelope and validates with
MetadataEventSchema.safeParse at the boundary — off-contract payloads are
rejected loudly (callback never invoked), and the 'as any as MetadataEvent'
double-cast is deleted. client-react's metadata hooks delegate to it and are
fixed transitively.

Out-of-scope findings filed: #4626 (subscribeData/DataEvent twin defect),
#4627 (MetadataEventType enum coverage vs registrable types).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 2, 2026 12:10pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/l labels Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/client, @objectstack/metadata, @objectstack/spec.

112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via packages/client, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/client)
  • content/docs/api/environment-routing.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via packages/metadata, @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/client)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/client, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/metadata, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/client)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/client, @objectstack/metadata, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/metadata-service.mdx (via @objectstack/metadata)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/client)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/metadata, @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/metadata, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants