feat(protocol): the principal layer — unified webhooks, declarations, principal.changed - #7110
Conversation
Allow caller-level webhook subscribers to carry account-anchored event types with fire-time authorization scoping — each fire covers only accounts the caller is authorized for at fire time — plus an explicit include_future_event_types opt-in instead of a wildcard. Add the declarations section: buyer-declared consumption facts (async payload versions, verifiable webhook signing algorithms, experimental opt-ins) with a seller-computed accepted intersection that gives webhook fires the version-negotiation channel synchronous requests get from the envelope. Add the caller-anchored principal.changed invalidation webhook for seller-driven principal-state transitions, repaired through get_principal. Report the seller-resolved principal_kind (buyer_agent or operator) on every readback with an expected_principal_kind assertion fence, and add the agent-scoped operator_id destination label with a normative cross-principal non-alignment rule. Advertise caller_event_types in capabilities so buyers select from the offering instead of probing by rejection, and gate the destination storyboard phases on section support. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ontract State explicitly that the layer begins after authentication — AdCP defines no credential-registration protocol — and add the normative "Establishing a connection" lifecycle joining authentication, principal configuration, account binding, and readiness. Require the explicit all_authorized_accounts acknowledgment for account-anchored event types on caller-level subscribers, with authorization evaluated at each delivery attempt so revocation suppresses queued retries carrying that account's data. Restrict include_future_event_types to types classified invalidation-only, so the flag can never silently opt a caller into payload-bearing events. Persist the full declarations negotiation record: declared set, accepted intersection, the seller's selected_async_adcp_version, and per-value exclusion reasons. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…anged-webhook title - `scheduled`, `final`, `delayed`, `adjusted`, `window_update` fire through `reporting_webhook`, not `push_notification_config` — fix in notification-type.json description and sync_principal.mdx bullet - `impairment` explicitly called out on `push_notification_config` as a decision: dependency-state change, not a cadence report - `principal-changed-webhook.json` title corrected from "Connection Changed Webhook" to "Principal Changed Webhook"
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema-source paths require human/CODEOWNERS approval.
This PR modifies and adds files under static/schemas/source/**, which are under a hard, deterministic approval gate (gated_paths: true). The current review_decision is REVIEW_REQUIRED, not APPROVED, so decision-table row 2 fires: the gate is not satisfied and the PR cannot be approved or plain-commented.
The reviewer surfaced no Critical/High/Medium findings — the change is additive, internally coherent, correctly experimental, carries a minor changeset, verified schema↔docs coherence and oneOf discrimination integrity. The escalation is purely because the mandatory human/CODEOWNERS review on the protocol source-of-truth schema paths has not yet been recorded.
Escalation reasons
- Touches gated schema-source paths (e.g.
static/schemas/source/core/principal-declarations.json[added],static/schemas/source/protocol/sync-principal-response.json[modified], and 12 others) — human/CODEOWNERS review required;review_decisionisREVIEW_REQUIRED.
Why human review
- Gated paths under static/schemas/source/** are modified/added (14 files) and review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS review required before merge.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/agent-notification-config-state.json (modified) matches
static/schemas/source/**; static/schemas/source/core/agent-notification-config.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/agent-reporting-destination.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/principal-changed-webhook.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/principal-declarations-state.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/principal-declarations.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/principal-state.json (modified) matchesstatic/schemas/source/**; static/schemas/source/enums/notification-type.json (modified) matchesstatic/schemas/source/**; static/schemas/source/index.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/get-adcp-capabilities-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/get-principal-request.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/get-principal-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/sync-principal-request.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/sync-principal-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema paths require human/CODEOWNERS approval.
No blocking, Medium, or High findings. The reviewer found this to be a clean consistency refactor: two shared schema definitions (core/experimental-feature-id.json, enums/principal-kind.json, both x-status: experimental) are extracted and referenced via $ref, and the principal-declarations feature-id pattern is tightened to the documented canonical vocabulary. $ids resolve, no new oneOf introduced, changeset present, schema↔docs coherence holds, all on an experimental surface.
However, this PR modifies/adds 16 files under static/schemas/source/**, which is a hard, non-overridable approval gate (gated_paths: true). The current GitHub review_decision is REVIEW_REQUIRED, not APPROVED. Per decision-table row 2, the outcome must be escalate regardless of how clean the diff is — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded. This is consistent with the prior escalate decision; the gate persists purely on the unsatisfied review status, not on any new finding.
Escalation reasons
- Gated paths under
static/schemas/source/**(16 files modified/added) andreview_decisionisREVIEW_REQUIRED, notAPPROVED— human/CODEOWNERS review required before merge.
Why human review
- Gated paths under static/schemas/source/** (16 files modified/added) and review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS review required before merge.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/agent-notification-config-state.json (modified) matches
static/schemas/source/**; static/schemas/source/core/agent-notification-config.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/agent-reporting-destination.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/experimental-feature-id.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/principal-changed-webhook.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/principal-declarations-state.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/principal-declarations.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/principal-state.json (modified) matchesstatic/schemas/source/**; static/schemas/source/enums/notification-type.json (modified) matchesstatic/schemas/source/**; static/schemas/source/enums/principal-kind.json (added) matchesstatic/schemas/source/**; static/schemas/source/index.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/get-adcp-capabilities-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/get-principal-request.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/get-principal-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/sync-principal-request.json (modified) matchesstatic/schemas/source/**; static/schemas/source/protocol/sync-principal-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
Replaces #7020, which GitHub auto-closed when its stacked base branch was deleted after #6966 merged. The reviewed implementation was rebased onto
main; #7020 retains the original design discussion and Ladon delta reviews. Implements RFC #7015.The model
Principal (the transport-resolved party: buyer agent or operator) → standing configuration with one seller (webhooks, destinations, declarations) → independently authorized advertiser accounts. Nothing in this layer grants account authority.
What this adds
all_authorized_accounts: true. Authorization is re-evaluated at every delivery attempt, including retries; revocation suppresses queued account data.include_future_event_typescovers only future invalidation-only types.active: falseis the one-call endpoint kill switch.principal.changed. A caller-anchored invalidation for seller-driven state changes, repaired throughget_principal.principal_kindand assertion fence. The seller resolvesbuyer_agentoroperatorfrom authorization state; request content can only assert an expected kind and receivesCONFLICTon mismatch.operator_iddestination labels. Supports per-operator isolation and offboarding without treating labels as cross-principal identity.caller_event_typeslets clients select supported events without rejection-driven probing.The follow-up also preserves media-buy cadence reports on
reporting_webhook, keepsimpairmentonpush_notification_config, and corrects the webhook schema title to “Principal Changed Webhook.”Verification
Human/CODEOWNERS review remains required for the schema-source paths.