Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/close-3-2-governance-loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"adcontextprotocol": minor
---

Complete cross-role governance conformance coverage for AdCP 3.2 with exact task-and-mode capability gates, positive and negative rights and paid-creative proofs, a universal discoverability index, and matching reference-agent enforcement.
6 changes: 6 additions & 0 deletions docs/building/verification/conformance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ Every agent satisfies the universal layer. Each `supported_protocols` claim adds

Agents MUST NOT declare a capability whose storyboards they do not pass. See the [Compliance Catalog](/docs/building/verification/compliance-catalog) for the full taxonomy and [Validate Your Agent](/docs/building/verification/validate-your-agent) for how to run the suite locally.

The non-executable [cross-role governance index](/compliance/latest/universal/governance.yaml)
maps the shared governance contract to capability-gated media-buy, signals,
rights, and creative proofs. It is discoverability metadata, not an additional
universal storyboard; only an agent's exact
`adcp.governance_enforcement.tasks[]` claims select those role-specific tests.

## Universal conformance

Every agent MUST pass every storyboard below.
Expand Down
7 changes: 4 additions & 3 deletions docs/protocol/get_adcp_capabilities.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,10 @@ The declaration is part of the experimental `governance.campaign` surface, so an

Advertising a task also commits the service to deterministic applicability. It resolves the commercial account from the request or existing resource before the side effect; when it cannot do so, it returns [`ACCOUNT_REQUIRED`](/docs/building/verification/compliance-catalog#error-code-account-required). It never treats a missing token as evidence that the buyer is ungoverned. Conditional task annotations use `trigger_overrides_exemption`: any commitment-increasing part of a mixed atomic update requires governance.

Current conformance runners continue to grade the legacy `media_buy.governance_aware` create path. Task-scoped grading for this new block will land with role-specific observable workflows; until then, registries MUST NOT present the experimental declaration as a certified claim.

The legacy `media_buy.governance_aware` boolean remains the compatibility claim for the historically graded `create_media_buy` consultation path. Agents use the core block for explicit task-scoped claims.
AdCP 3.2 conformance selects role-specific governance workflows from exact
entries in `adcp.governance_enforcement.tasks[]`. The legacy
`media_buy.governance_aware` boolean remains a compatibility signal for older
runners, but it is not the 3.2 certification gate.

### adcp.attestations

Expand Down
20 changes: 20 additions & 0 deletions docs/reference/migration/cross-role-governance-enforcement.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,23 @@ Validate generated SDK signers and verifiers against the published [`governance-
Until an SDK release contains this schema bundle, its generated `check_governance` validators are stale. The training agent deliberately uses passthrough MCP tool schemas with framework request/response validation disabled for this experimental tenant, then validates in the source-aligned handler. Production integrations MUST regenerate or pin a compatible SDK rather than copying that compatibility overlay.

Pin experimental integrations to the matching 3.2 schema bundle until the surface graduates.

## Conformance coverage

The [cross-role governance index](/compliance/latest/universal/governance.yaml)
connects this contract to the executable proof for each service role. It is an
index, not one universal workflow: each service storyboard runs only when the
agent advertises the exact task and mode in
`adcp.governance_enforcement.tasks[]`.

| Service role | Governed task | Capability-gated proof |
|---|---|---|
| Media buy | `create_media_buy` | [signed approval plus tamper rejection](/compliance/latest/protocols/media-buy/scenarios/governance_approved.yaml); agents also claiming `online_execution_check` run the [conditions](/compliance/latest/protocols/media-buy/scenarios/governance_conditions.yaml) and [denied](/compliance/latest/protocols/media-buy/scenarios/governance_denied.yaml) paths |
| Signals | `activate_signal` | [signed activation and persisted-deployment proof](/compliance/latest/specialisms/signal-marketplace/scenarios/governance_approved.yaml), plus [unauthorized paid activation and no-upstream-call proof](/compliance/latest/specialisms/signal-marketplace/scenarios/governance_denied.yaml) |
| Brand rights | `acquire_rights` | [signed grant and persisted-state proof](/compliance/latest/specialisms/brand-rights/scenarios/governance_approved.yaml), plus [unauthorized acquisition and no-grant proof](/compliance/latest/specialisms/brand-rights/scenarios/governance_denied.yaml) |
| Creative | `build_creative` | [signed paid-render artifact proof](/compliance/latest/specialisms/creative-transformers/scenarios/governance_approved.yaml), plus [unauthorized paid render and no-upstream-call proof](/compliance/latest/specialisms/creative-transformers/scenarios/governance_denied.yaml) |

Governance providers and enforcing services remain separate roles. Provider
responses prove verdict semantics; service storyboards prove that signed intent
is checked before a role-specific side effect. Merely returning or echoing
`governance_context` is not enforcement evidence.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"test:governance-runtime-attestations": "node --test --test-force-exit --test-timeout=30000 tests/governance-runtime-attestations.test.cjs",
"test:governance-conditions-storyboard": "node --test --test-force-exit --test-timeout=30000 tests/governance-conditions-storyboard.test.cjs",
"test:signal-governance-storyboard": "node --test --test-force-exit --test-timeout=30000 tests/signal-governance-storyboard.test.cjs",
"test:governance-task-slices": "node --test --test-force-exit --test-timeout=30000 tests/governance-task-slices.test.cjs",
"test:audience-evidence": "node --test --test-force-exit --test-timeout=30000 tests/audience-evidence.test.cjs",
"test:status-as-of-vectors": "node --test --test-force-exit --test-timeout=30000 tests/media-buy-status-as-of-vectors.test.cjs",
"test:storyboard-scoping": "node --test --test-force-exit --test-timeout=30000 tests/lint-storyboard-scoping.test.cjs",
Expand Down Expand Up @@ -132,7 +133,7 @@
"audit:oneof": "node scripts/audit-oneof.mjs",
"test:schema-utf8": "node scripts/normalize-schema-utf8.mjs --check",
"fix:schema-utf8": "node scripts/normalize-schema-utf8.mjs",
"test": "npm run test:docs-nav && npm run test:owned-links && npm run test:release-docs-nav && npm run test:rewrite-dist-redirect-links && npm run test:rewrite-dist-links-idempotency && npm run test:docs-error-handling-copy && npm run test:schemas && npm run test:performance-feedback && npm run test:mcp-schema-projection && npm run test:dist-schema-version-ids && npm run test:examples && npm run test:extensions && npm run test:extension-schemas && npm run test:error-handling && npm run test:json-schema && npm run test:audio-radio && npm run test:adagents-catalog-only && npm run test:canonical-reference-resolver && npm run test:composed && npm run test:rejection-arm-mutex && npm run test:migrations && npm run test:hmac-vectors && npm run test:hmac-signer-conformance && npm run test:webhook-signing-vectors && npm run test:webhook-receiver-envelope && npm run test:oauth-setup-vectors && npm run test:a2a-profile-extension && npm run test:transport-errors && npm run test:targeting-overlay-vectors && npm run test:targeting-aware-discovery && npm run test:demographic-targeting && npm run test:language-targeting && npm run test:attestation-vectors && npm run test:rights-attestations && npm run test:governance-runtime-attestations && npm run test:governance-conditions-storyboard && npm run test:signal-governance-storyboard && npm run test:audience-evidence && npm run test:status-as-of-vectors && npm run test:storyboard-scoping && npm run test:storyboard-branch-sets && npm run test:storyboard-provides-state-for && npm run test:storyboard-fixture-resolution && npm run test:storyboard-contradictions && npm run test:storyboard-context-entity && npm run test:storyboard-auth-shape && npm run test:storyboard-test-kits && npm run test:compliance-packaged-refs && npm run test:compliance-source-authority && npm run test:storyboard-sample-request-schema && npm run test:storyboard-response-schema && npm run test:storyboard-context-output-paths && npm run test:storyboard-validations-paths && npm run test:storyboard-check-enum && npm run test:update-media-buy-affected-packages && npm run test:storyboard-advisory-expiry && npm run test:storyboard-raw-mode-required && npm run test:storyboard-upstream-traffic-paths && npm run test:refine-finalize-validation-ids && npm run test:run-storyboards-schema-root && npm run test:storyboard-doc-parity && npm run test:pagination-invariant && npm run test:version-envelope && npm run test:test-dynamic-imports && npm run test:sdk-shims && npm run test:format-identity-boundaries && npm run test:sdk-runner-capability-gates && npm run test:callapi-state-change && npm run test:sign-protocol-tarball && npm run test:chat-streaming-code-fences && npm run test:certification-demo-formatting && npm run test:build-schemas-hoist-enums && npm run test:build-schemas-hoist-marked && npm run test:build-schemas-async-response-refs && npm run test:release-workflow && npm run test:immutable-release-artifacts && npm run test:patch-3-0-compat-bundle && npm run test:error-codes && npm run test:compliance-snippets && npm run test:doc-compliance-drift && npm run test:substitution-vector-names && npm run test:platform-agnostic && npm run test:oneof-discriminators && npm run test:schema-utf8 && npm run test:unit && npm run test:server-unit && npm run test:openapi && npm run typecheck",
"test": "npm run test:docs-nav && npm run test:owned-links && npm run test:release-docs-nav && npm run test:rewrite-dist-redirect-links && npm run test:rewrite-dist-links-idempotency && npm run test:docs-error-handling-copy && npm run test:schemas && npm run test:performance-feedback && npm run test:mcp-schema-projection && npm run test:dist-schema-version-ids && npm run test:examples && npm run test:extensions && npm run test:extension-schemas && npm run test:error-handling && npm run test:json-schema && npm run test:audio-radio && npm run test:adagents-catalog-only && npm run test:canonical-reference-resolver && npm run test:composed && npm run test:rejection-arm-mutex && npm run test:migrations && npm run test:hmac-vectors && npm run test:hmac-signer-conformance && npm run test:webhook-signing-vectors && npm run test:webhook-receiver-envelope && npm run test:oauth-setup-vectors && npm run test:a2a-profile-extension && npm run test:transport-errors && npm run test:targeting-overlay-vectors && npm run test:targeting-aware-discovery && npm run test:demographic-targeting && npm run test:language-targeting && npm run test:attestation-vectors && npm run test:rights-attestations && npm run test:governance-runtime-attestations && npm run test:governance-conditions-storyboard && npm run test:signal-governance-storyboard && npm run test:governance-task-slices && npm run test:audience-evidence && npm run test:status-as-of-vectors && npm run test:storyboard-scoping && npm run test:storyboard-branch-sets && npm run test:storyboard-provides-state-for && npm run test:storyboard-fixture-resolution && npm run test:storyboard-contradictions && npm run test:storyboard-context-entity && npm run test:storyboard-auth-shape && npm run test:storyboard-test-kits && npm run test:compliance-packaged-refs && npm run test:compliance-source-authority && npm run test:storyboard-sample-request-schema && npm run test:storyboard-response-schema && npm run test:storyboard-context-output-paths && npm run test:storyboard-validations-paths && npm run test:storyboard-check-enum && npm run test:update-media-buy-affected-packages && npm run test:storyboard-advisory-expiry && npm run test:storyboard-raw-mode-required && npm run test:storyboard-upstream-traffic-paths && npm run test:refine-finalize-validation-ids && npm run test:run-storyboards-schema-root && npm run test:storyboard-doc-parity && npm run test:pagination-invariant && npm run test:version-envelope && npm run test:test-dynamic-imports && npm run test:sdk-shims && npm run test:format-identity-boundaries && npm run test:sdk-runner-capability-gates && npm run test:callapi-state-change && npm run test:sign-protocol-tarball && npm run test:chat-streaming-code-fences && npm run test:certification-demo-formatting && npm run test:build-schemas-hoist-enums && npm run test:build-schemas-hoist-marked && npm run test:build-schemas-async-response-refs && npm run test:release-workflow && npm run test:immutable-release-artifacts && npm run test:patch-3-0-compat-bundle && npm run test:error-codes && npm run test:compliance-snippets && npm run test:doc-compliance-drift && npm run test:substitution-vector-names && npm run test:platform-agnostic && npm run test:oneof-discriminators && npm run test:schema-utf8 && npm run test:unit && npm run test:server-unit && npm run test:openapi && npm run typecheck",
"test:all": "npm run test:schemas && npm run test:examples && npm run test:extensions && npm run test:error-handling && npm run test:snippets && npm run typecheck",
"precommit:server-unit": "node scripts/precommit-server-unit.cjs",
"precommit": "bash scripts/with-timeout.sh 180 npm run test:unit && npm run test:test-dynamic-imports && npm run test:format-identity-boundaries && npm run test:callapi-state-change && bash scripts/with-timeout.sh 240 npm run precommit:server-unit && npm run typecheck",
Expand Down
1 change: 1 addition & 0 deletions scripts/generate-compliance-snippets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const ERROR_CODE = /^[A-Z][A-Z0-9_]*$/;
const RECOVERY_VALUES = new Set(['correctable', 'transient', 'terminal']);
const UNIVERSAL_SUPPORT_ARTIFACTS = new Set([
'fictional-entities',
'governance',
'runner-output-contract',
'storyboard-schema',
]);
Expand Down
1 change: 1 addition & 0 deletions scripts/lint-storyboard-contradictions.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ function classifyOutcome(step) {
check === 'envelope_field_pattern' ||
check === 'field_value' ||
check === 'field_value_or_absent' ||
check === 'field_greater_than' ||
check === 'http_status' ||
check === 'http_status_in'
);
Expand Down
1 change: 1 addition & 0 deletions scripts/lint-storyboard-sample-request-schema.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ function placeholderFor(schema, depth = 0) {
if (Array.isArray(resolved.enum) && resolved.enum.length > 0) return resolved.enum[0];
if (typeof resolved.const !== 'undefined') return resolved.const;
if (resolved.pattern === '^sha256:[A-Za-z0-9_-]{43}$') return SHA256_DIGEST_PLACEHOLDER;
if (resolved.pattern === '^[A-Z]{3}$') return 'USD';
// Object variant nested inside a oneOf/anyOf at a location where the
// author's substitution will resolve to that shape at runtime. Synthesize
// the concrete shape instead of returning a string that fails required.
Expand Down
1 change: 1 addition & 0 deletions scripts/lint-storyboard-validations-paths.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const PATH_BEARING_CHECKS = new Set([
'field_absent',
'field_pattern',
'field_contains',
'field_greater_than',
'all_fields_in_context_array',
'envelope_field_present',
'envelope_field_absent',
Expand Down
2 changes: 1 addition & 1 deletion scripts/lint-universal-storyboard-doc-parity.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
* "Graded" means the YAML has a `phases:` array. Filters out non-graded
* fixtures (storyboard-schema.yaml, runner-output-contract.yaml,
* fictional-entities.yaml) which live alongside graded storyboards but
* fictional-entities.yaml, governance.yaml) which live alongside graded storyboards but
* aren't run by the suite.
*
* Slug forms differ across the two docs by design (existing convention,
Expand Down
2 changes: 2 additions & 0 deletions server/src/training-agent/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ function createSession(): SessionState {
creatives: new Map(),
signalActivations: new Map(),
buildVariantTargets: new Map(),
buildVariantGovernance: new Map(),
usageRecords: [],
complyExtensions: {
accountStatuses: new Map(),
Expand Down Expand Up @@ -501,6 +502,7 @@ function deserializeSession(data: Record<string, unknown>): SessionState {
proposalLifecycleLinks: asMap(hydrated.proposalLifecycleLinks, fresh.proposalLifecycleLinks),
proposalRefinementRecords: asMap(hydrated.proposalRefinementRecords, fresh.proposalRefinementRecords),
buildVariantTargets: asMap(hydrated.buildVariantTargets, fresh.buildVariantTargets),
buildVariantGovernance: asMap(hydrated.buildVariantGovernance, fresh.buildVariantGovernance),
usageRecords: Array.isArray(hydrated.usageRecords) ? hydrated.usageRecords : [],
complyExtensions: {
accountStatuses: asMap(hydratedComply.accountStatuses, fresh.complyExtensions.accountStatuses),
Expand Down
Loading
Loading