Why
Both specs/catalog-change-feed.md (landed in PR #4767) and specs/registry-change-feed.md (shipped) define event payload shapes in markdown prose + examples only — there is no static/schemas/source/core/catalog-event.json or per-event-type schema for product.priced, signal.priced, *.removed, catalog.bulk_change, applies_to, removal_reason, previous_pricing_option_ids, etc.
Surfaced during second-pass review of PR #4767. Quote: "the capability stanza enumerates event types but no schema exists. Without a schema, conformance runs can't validate event payloads and removal_reason is unenforced."
The catalog feed and registry feed share this gap. Spec'ing them together keeps the two surfaces parallel.
Scope
Catalog feed (PR #4767 surface)
Define a discriminated core/catalog-event.json keyed on event_type:
product.created, product.updated, product.priced, product.removed
signal.created, signal.updated, signal.priced, signal.removed
catalog.bulk_change
Each event payload needs:
event_id (UUID v7), event_type, entity_type, entity_id, created_at
- Type-specific payload union, validating fields like:
applies_to: { scope: \"public\" | \"account\", account_ids?: string[] } — discriminator on scope
removal_reason enum: withdrawn / cancellation / expired / depublication / policy_takedown
previous_pricing_option_ids: string[]
changed_fields: string[]
effective_at: string (date-time)
- For
*.updated / *.priced: full or partial entity payload
- For
catalog.bulk_change: summary, affected_entity_types[], affected_count, recommendation
Conformance: response from GET /catalog/events validates against the schema; SDKs (@adcp/client's CatalogSync) consume the schema rather than parsing prose.
Registry feed (existing, parallel work)
Same treatment for the events in specs/registry-change-feed.md:
property.created, property.updated, property.merged, property.stale, property.reactivated
agent.discovered, agent.removed, agent.profile_updated, agent.compliance_changed
publisher.adagents_changed
authorization.granted, authorization.revoked
The registry feed's agent.compliance_changed and authorization.granted payloads are the most field-heavy (tracks, storyboards, full authorization scoping model) — those are the priority for schema treatment because consumers (TMP routers, RegistrySync clients) make routing decisions off them.
Acceptance
core/catalog-event.json + per-event-type variants live in static/schemas/source/core/
core/registry-event.json + per-event-type variants ditto
GET /catalog/events and GET /api/registry/feed responses cite the event schemas
- Conformance assertion: events in storyboard fixtures validate against schemas
- SDK type generation picks up the new schemas (TS first, then Go and Python)
Dependencies
Related
Why
Both
specs/catalog-change-feed.md(landed in PR #4767) andspecs/registry-change-feed.md(shipped) define event payload shapes in markdown prose + examples only — there is nostatic/schemas/source/core/catalog-event.jsonor per-event-type schema forproduct.priced,signal.priced,*.removed,catalog.bulk_change,applies_to,removal_reason,previous_pricing_option_ids, etc.Surfaced during second-pass review of PR #4767. Quote: "the capability stanza enumerates event types but no schema exists. Without a schema, conformance runs can't validate event payloads and removal_reason is unenforced."
The catalog feed and registry feed share this gap. Spec'ing them together keeps the two surfaces parallel.
Scope
Catalog feed (PR #4767 surface)
Define a discriminated
core/catalog-event.jsonkeyed onevent_type:product.created,product.updated,product.priced,product.removedsignal.created,signal.updated,signal.priced,signal.removedcatalog.bulk_changeEach event payload needs:
event_id(UUID v7),event_type,entity_type,entity_id,created_atapplies_to: { scope: \"public\" | \"account\", account_ids?: string[] }— discriminator onscoperemoval_reasonenum:withdrawn/cancellation/expired/depublication/policy_takedownprevious_pricing_option_ids: string[]changed_fields: string[]effective_at: string (date-time)*.updated/*.priced: full or partial entity payloadcatalog.bulk_change:summary,affected_entity_types[],affected_count,recommendationConformance: response from
GET /catalog/eventsvalidates against the schema; SDKs (@adcp/client'sCatalogSync) consume the schema rather than parsing prose.Registry feed (existing, parallel work)
Same treatment for the events in
specs/registry-change-feed.md:property.created,property.updated,property.merged,property.stale,property.reactivatedagent.discovered,agent.removed,agent.profile_updated,agent.compliance_changedpublisher.adagents_changedauthorization.granted,authorization.revokedThe registry feed's
agent.compliance_changedandauthorization.grantedpayloads are the most field-heavy (tracks, storyboards, full authorization scoping model) — those are the priority for schema treatment because consumers (TMP routers, RegistrySync clients) make routing decisions off them.Acceptance
core/catalog-event.json+ per-event-type variants live instatic/schemas/source/core/core/registry-event.json+ per-event-type variants dittoGET /catalog/eventsandGET /api/registry/feedresponses cite the event schemasDependencies
Related