Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
da00e20
feat(governance): add seller policy and change-right contracts
bokelley Aug 23, 2026
bf70312
docs(accounts): note that disclosed rejections reflect origins while …
bokelley Aug 23, 2026
2cb4652
Merge origin/main into seller-governance-policies
bokelley Aug 24, 2026
caa16da
test: update schema weight after governance merge
bokelley Aug 24, 2026
7142f8a
fix(governance): preserve message-only outcome errors
bokelley Aug 24, 2026
6060673
docs(governance): record seller governance decisions
bokelley Aug 24, 2026
0f7d9e0
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 24, 2026
27ff9e1
docs(governance): explain sell-side authority boundaries
bokelley Aug 24, 2026
1df8703
fix(docs): add sell-side governance SEO title
bokelley Aug 24, 2026
e7e14b2
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 25, 2026
0fa8107
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 25, 2026
bbcd0a5
fix(training-agent): honor immutable capability versions
bokelley Aug 25, 2026
7c88f9d
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 25, 2026
e6b3b10
chore(training-agent): remove unused version import
bokelley Aug 25, 2026
55fa62b
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 25, 2026
cc9061d
feat: add seller governance policy contracts
bokelley Aug 25, 2026
e448886
fix: keep MCP prompt profiles within budget
bokelley Aug 25, 2026
f58408d
test: align storyboard floor assertions
bokelley Aug 25, 2026
da02416
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 25, 2026
3fdf52c
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 25, 2026
e2fe4e7
fix: harden seller governance execution
bokelley Aug 25, 2026
c0d28cc
test: ratchet governance binding storyboard
bokelley Aug 25, 2026
8e76d2e
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 25, 2026
c32f5f5
fix: validate governance binding storyboard request
bokelley Aug 25, 2026
e23cb00
fix: align sales storyboard CI ratchet
bokelley Aug 26, 2026
76f5901
Merge remote-tracking branch 'origin/main' into seller-governance-pol…
bokelley Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .agents/sdk-shim-ledger.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,24 @@
"node_modules/@adcp/sdk/dist/lib/types/schemas.generated.mjs",
"schemas.generated.js"
]
},
{
"id": "current-change-terms-negotiation-verifier-overlay",
"title": "Current change-terms field in SDK proposal verifier",
"kind": "node_modules_sdk_cache_mutation",
"status": "temporary",
"owner": "media-buy",
"upstream": "adcontextprotocol/adcp-client#2664",
Comment thread
bokelley marked this conversation as resolved.
"problem": "The pinned SDK proposal verifier has an independent commercial_terms allowlist that predates the 3.2 change_terms field, so it rejects otherwise schema-valid same-PR refinement responses.",
"localBehavior": "During current-source storyboard runs, add change_terms to the installed SDK verifier allowlist and restore the original verifier after the matrix.",
"removalCondition": "Remove when the pinned @adcp/sdk release natively validates proposal-bound change_terms through its public negotiation verifier.",
"paths": [
"scripts/overlay-compliance-cache.sh",
"scripts/run-storyboards-matrix.sh"
],
"terms": [
"node_modules/@adcp/sdk/dist/lib/negotiation/verification.js",
"node_modules/@adcp/sdk/dist/lib/negotiation/verification.mjs"
]
}
]
5 changes: 5 additions & 0 deletions .changeset/seller-governance-policy-surfaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"adcontextprotocol": minor
---

Add registry-backed seller acceptance-policy discovery with reusable version-pinned platform profiles, proposal-bound media-buy change terms, structured seller disposition evidence on failed governance outcomes, and seller acceptance criteria for buyer-selected governance agents. Add typed change constraints and status scope, project accepted terms into current `available_actions` through `change_term_id`, and deprecate the released 3.1 `terms_ref` action-link overload for removal in 4.0 while retaining explicit 3.1 compatibility. Add compliance storyboards for product promises, proposal materialization, accepted-term persistence, service-mode routing, state transitions, latent rights, and cross-version action parsing. Harden all four surfaces against stale or ambiguous policy, authorization confusion, untrusted audit evidence, unsafe remote resolution, and rejected-binding persistence.
23 changes: 20 additions & 3 deletions docs/accounts/tasks/sync_governance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ An account binds to one governance agent that owns the full lifecycle. Authoriza

This uses **replace semantics** — each call replaces any previously registered agent on the specified accounts. Accounts not included in the request keep their existing configuration.

## Seller acceptance of governance agents

Sellers may publish seller-wide advisory criteria at `adcp.governance_enforcement.accepted_governance_agents` in [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities). `any_of[]` is a typed union: a candidate is acceptable when it satisfies at least one matcher.

- `agent_url` matches one explicit HTTPS endpoint.
- `verification` requires a fresh result from the named HTTPS registry for the declared agent role, AdCP version, allowed verification modes, and maximum result age.

URL matchers use the shared [AdCP URL canonicalization rules](/docs/reference/url-canonicalization), not raw string equality. They match the exact canonical endpoint; redirects, DNS aliases, and candidate assertions do not expand an allowlist. A verification matcher is satisfied only by a deterministic result from the seller-configured trusted registry. The candidate cannot nominate the registry, and its self-described role or verification state is never evidence. Registry resolution uses HTTPS, no redirects or forwarded credentials, public-network address checks, DNS pinning through connection, bounded responses, and authenticated registry records. If the registry or verification service cannot be resolved, the seller returns [`GOVERNANCE_UNAVAILABLE`](/docs/building/verification/compliance-catalog#error-code-governance-unavailable); it does not guess or silently turn an availability problem into rejection.

The capabilities declaration is seller-wide preflight guidance. The response to `sync_governance` is authoritative for each account because account contracts, jurisdictions, and product access may narrow the criteria. Verification evidence is evaluated for freshness at binding time and pinned to the accepted binding. Later capability or registry drift does not silently revoke or redirect an existing binding; an explicit account resynchronization creates a new decision. If the submitted agent does not satisfy the applicable criteria, the account result is `failed` with [`GOVERNANCE_AGENT_NOT_ACCEPTED`](/docs/building/verification/compliance-catalog#error-code-governance-agent-not-accepted). A seller may return either opaque details (`{"disclosure":"opaque"}` plus an optional local `rejection_ref`) or disclosed details containing the parsed HTTPS origin and authoritative criteria. The disclosed arm uses `attempted_agent_origin`, never the submitted URL: userinfo, path, query, and fragment are stripped after parsing. Buyers reconciling a disclosed rejection against their `any_of[]` matcher list should compare full matcher entries, not origins — an `agent_url` matcher may include a path and therefore be narrower than the disclosed origin. The seller MUST NOT persist or contact a rejected endpoint and MUST NOT echo credentials or a raw candidate URL in responses, logs, or error details.

Acceptance matchers MUST express objective operational, security, interoperability, or compliance requirements. A seller MUST NOT use a URL allowlist or verification criterion to exclude a functionally equivalent governance provider merely as commercial leverage. Sellers SHOULD prefer verification criteria over enumerating providers when the registry can express the requirement deterministically.

Absence of `accepted_governance_agents` means the seller does not restrict governance-agent identity through this mechanism and accepts any otherwise valid binding. Adding or changing a declaration does not retroactively invalidate existing bindings; the seller applies it when a buyer next binds or replaces an agent. Account-specific criteria returned on a failed sync override the advisory capability for that attempt.

**Response Time**: ~1s.

**Request Schema**: [`/schemas/v3/account/sync-governance-request.json`](https://adcontextprotocol.org/schemas/v3/account/sync-governance-request.json)
Expand Down Expand Up @@ -101,20 +116,20 @@ asyncio.run(main())
## Request Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
|-----------|------|----------|--------------|
| `accounts` | array | Yes | Per-account governance agent entries. Each pairs an account reference with governance agents for that account. |

**Each account entry:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
|-------|------|----------|--------------|
| `account` | object | Yes | [Account reference](/docs/building/by-layer/L2/accounts-and-agents#account-references): `{account_id}` for account-id namespaces or `{brand, operator}` for buyer-declared accounts. |
| `governance_agents` | array | Yes | Governance agent endpoint for this account. Array with exactly one entry (`minItems: 1`, `maxItems: 1`). |

**The governance agent:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
|-------|------|----------|--------------|
| `url` | string | Yes | HTTPS endpoint URL for the governance agent. |
| `authentication` | object | Yes | Credentials the seller presents when calling this agent. Contains `schemes` (array with one auth scheme) and `credentials` (token, min 32 characters). |

Expand All @@ -131,6 +146,8 @@ Returns an `accounts` array with per-account results. Individual entries may fai
| `governance_agents` | Governance agents now active on this account. Reflects persisted state. Only present when `status: "synced"`. |
| `errors` | Per-account errors. Only present when `status: "failed"`. |

For [`GOVERNANCE_AGENT_NOT_ACCEPTED`](/docs/building/verification/compliance-catalog#error-code-governance-agent-not-accepted), `errors[].details` conforms to `error-details/governance-agent-not-accepted.json`. It never contains authentication credentials.

**Error response:**

`errors` array with operation-level errors (auth failure, service unavailable). No `accounts` array is present.
Expand Down
4 changes: 3 additions & 1 deletion docs/governance/campaign/specification.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,9 @@ Identity is role-specific. On the buyer's intent check, `caller` is the authenti

Sellers MUST persist the accepted intent token and any lifecycle tokens they subsequently hold, keyed at minimum by `jti` with `iss`, `aud`, the opaque action binding in `sub`, `phase`, decision outcome, and the timestamp of acceptance. They MUST NOT treat `sub` as a plan identifier. Retention follows the seller's regulatory retention period. Without seller-side retention, the audit log is single-sourced from the governance agent; independent reconciliation between seller records and `get_plan_audit_logs` is the cross-check that catches a compromised or misbehaving governance agent.

Seller-side governance (if the seller itself has configured a governance agent on the account) is an **independent layer**. A buyer's successful `check_governance` does not obligate the seller to accept the request; the seller's own compliance policies MAY still reject the action via `PERMISSION_DENIED`.
A buyer's successful `check_governance` does not obligate the seller to accept the request. Seller acceptance policy is an independent commercial and compliance layer; the seller's internal automation, review, and escalation process is not protocol authority and need not be exposed. Use `POLICY_VIOLATION` when seller content or advertising policy rejects an otherwise authorized action, `ACTION_NOT_ALLOWED` when a media-buy change is outside the accepted or currently available rights, and `PERMISSION_DENIED` only when the authenticated caller, account scope, credential, or signed governance context lacks authority.

When an authorized action fails after approval, the buyer reports the seller error through `report_plan_outcome` with `outcome: "failed"`. The governance agent retains that buyer-attributed error on the outcome audit entry. This closes the unexplained-audit gap without granting the seller a second approval role or claiming that the copied error is seller-authenticated evidence.

An approved token carries an `exp` that is authoritative at verification time; a policy change inside the token's validity window is accepted residual risk of any signed-decision system. Tight `exp` values (intent tokens SHOULD expire within 15 minutes per the JWS profile) bound the window rather than closing it. Operators who cannot tolerate the window MUST set `reallocation_threshold` to `0` or `human_review_required: true` so every action goes through an internal human review regardless of caching.

Expand Down
22 changes: 18 additions & 4 deletions docs/governance/campaign/tasks/report_plan_outcome.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,25 @@ If the seller rejected the request, report it so the governance agent can update
"governance_context": "gc_mb_seller_456",
"outcome": "failed",
"error": {
"code": "PRODUCT_UNAVAILABLE",
"message": "Product premium_video_300k is no longer available."
"code": "POLICY_VIOLATION",
"message": "The requested campaign is not accepted for this inventory.",
"classification_source": "seller_response_copy",
"details": {
"origin": "seller",
"category": "political_advertising",
"seller_policy_ref": "restricted-category-rule-17"
}
}
}
}
```

The reporter preserves a structured seller decline on the existing `outcome: "failed"` path; there is no seller authority or new outcome state. `classification_source: "seller_response_copy"` attributes the record correctly: it is the buyer's copy of what the seller returned, not an independently authenticated seller statement. A buyer that inferred the classification uses `buyer_classification` instead. The governance agent stores the complete error on the outcome audit entry so an approved plan action that did not execute has an attributable reason.

Every field in this error remains reporter-supplied evidence. Governance agents MUST NOT use it to authorize an action, debit or release budget, alter seller reputation, or establish that the seller made a statement. Unknown fields cannot create provenance or attestation semantics. Treat messages, suggestions, details, and extensions as untrusted display data: isolate them from privileged prompts, escape them in operator interfaces and exports, and enforce transport and storage size limits. A future seller-attested receipt can add stronger provenance without upgrading historical buyer copies retroactively.

Sellers may disclose progressively. A [`POLICY_VIOLATION`](/docs/building/verification/compliance-catalog#error-code-policy-violation) can include a registry `policy_id`, category, rule IDs, and remediation when those are publishable, or an opaque `seller_policy_ref` when disclosure would reveal enforcement controls. Use [`ACTION_NOT_ALLOWED`](/docs/building/verification/compliance-catalog#error-code-action-not-allowed) when the proposed change was not a negotiated/current right. Reserve [`PERMISSION_DENIED`](/docs/building/verification/compliance-catalog#error-code-permission-denied) for caller identity, credential, account scope, or signed-governance authorization failures.

```json
{
"outcome_id": "out_003",
Expand Down Expand Up @@ -282,9 +294,11 @@ If the seller rejected the request, report it so the governance agent can update
| `delivery.seller_statement_id` | string | For seller copy | ID received with the seller statement. |
| `delivery.seller_statement_digest` | string | For seller copy | Digest received with the seller statement. |
| `delivery.impressions` | integer | No | Optional buyer-attributed impressions. |
| `error` | object | No | Error details. Required when outcome is `failed`. |
| `error.code` | string | No | Error code from the seller. |
| `error` | object | No | Buyer-attributed structured error evidence. Required when outcome is `failed`; retained on the audit entry. |
| `error.code` | string | No | Error code returned by the seller or assigned by the buyer. Optional for compatibility with 3.1 message-only evidence; reporters SHOULD include it when available. |
| `error.message` | string | No | Human-readable error description. |
| `error.details` | object | No | Structured error details preserved without forcing policy disclosure. [`POLICY_VIOLATION`](/docs/building/verification/compliance-catalog#error-code-policy-violation) details SHOULD use the standard policy-violation shape. |
| `error.classification_source` | enum | No | `seller_response_copy` or `buyer_classification`. Omission is permitted for compatibility. Additional seller fields remain forward-compatible but are size-, width-, and depth-bounded; prefer the bounded `error.ext` envelope. |
| `governance_context` | string | Conditional | Exact approved context paired with `check_id` and `plan_id`. Required for every outcome. |

### Response
Expand Down
Loading
Loading