Issue: add universal/governance.yaml cross-cutting normative obligation
Repo: adcontextprotocol/adcp
Type: spec gap (compliance / cross-cutting)
Severity: high (silently passing implementations are non-compliant)
Target: 3.x minor (purely additive — codifies behavior already expected)
Problem
The cross-cutting requirement "any mutating request whose account has registered governance agents MUST consult them before mutating" is encoded only inside individual specialism storyboards (e.g., compliance/specialisms/brand-rights/scenarios/governance_denied.yaml).
There is no universal/governance.yaml parallel to universal/idempotency.yaml. An agent author reading only the brand-rights chapter (or only the seller chapter, or only the signals chapter) cannot discover that this obligation exists. The first time most implementations hit it is when the storyboard fails — and even then the failure mode (missing GOVERNANCE_DENIED error code on a path the implementor didn't realize was governance-gated) is hard to triage without already knowing the rule.
Compare:
idempotency_key is normative cross-cutting → has universal/idempotency.yaml, listed in the protocol-wide block of every spec preamble, called out in MUTATING_TASKS constants, has its own conformance baseline.
- Governance consultation is normative cross-cutting → has nothing equivalent. It's encoded as an emergent property of one storyboard scenario.
Real-world impact: in our skill matrix, agents that pass every other test still fail governance_denied scenarios because they never wire checkGovernance() against registered governance agents. The rule was never visible to them.
Proposal
Add compliance/universal/governance.yaml with the following normative shape:
id: universal/governance
title: Governance Consultation
applies_to: any mutating tool on accounts that have registered governance agents
description: |
When an account has registered governance agents (via sync_governance or sync_accounts),
every mutating request against that account MUST consult the registered governance agent(s)
before performing the mutation. If the governance agent returns status 'denied',
the request MUST be rejected with error code GOVERNANCE_DENIED, with `findings` propagated
verbatim from the governance agent's response.
normative_requirements:
- mutating tools (per MUTATING_TASKS): create_media_buy, update_media_buy,
sync_creatives, acquire_rights, activate_signal, log_event, ...
- registration source: sync_governance OR sync_accounts.governance_agents[]
- check via: checkGovernance() helper from @adcp/client (or equivalent)
- on denial: return adcpError('GOVERNANCE_DENIED', { findings: plan.findings })
- on conditions: implementations MAY apply conditions or deny in strict mode
- on approved: proceed with mutation
scenarios:
- happy_path_no_governance: account has no registered governance agents → mutation proceeds normally
- approved: governance returns 'approved' → mutation proceeds, governance_context echoed in response
- conditions: governance returns 'conditions' → mutation MAY proceed with conditions attached, OR MAY deny
- denied: governance returns 'denied' → mutation rejected with GOVERNANCE_DENIED + findings
cross_references:
- protocol-wide block in 3.0 GA preamble (already names idempotency, signed-requests; should also name governance consultation)
- check_governance request/response schema
- sync_governance / sync_accounts.governance_agents[] registration flow
Mirror the structure of universal/idempotency.yaml so the conformance runner already knows how to grade it. Add a baseline check: every mutating-tool storyboard step picks up an additional implicit assertion ("if this account has registered governance agents in the seed fixture, the agent must have called check_governance before responding").
Protocol-wide block update
The 3.0 GA preamble lists:
Two requirements apply to every mutating AdCP operation regardless of specialism:
idempotency_key ...
- RFC 9421 HTTP Signatures ...
Add a third bullet:
- Governance consultation — if the account has registered governance agents, the implementation MUST consult them before performing the mutation. See
universal/governance.yaml for the normative obligation. Optional only when no governance agents are registered.
Files affected
compliance/universal/governance.yaml (new file, modeled on idempotency.yaml)
docs/v3/protocol/protocol-wide-requirements.md (add bullet)
- llms.txt (add cross-reference under each mutating tool's "see also" section)
- Specialism storyboards that already test the obligation get a
references: [universal/governance] annotation so it's machine-discoverable.
Source
Surfaced during AdCP @adcp/client skill matrix runs. Out of 8 storyboard pairs tested across multiple cycles, every implementation that wires governance does so by accident (after copying from a worked example) rather than by reading a normative requirement. Confirmed by protocol expert review with grade C+ for "cross-cutting obligation discoverability" (idempotency exemplary; governance scattered or implicit).
Issue: add
universal/governance.yamlcross-cutting normative obligationRepo:
adcontextprotocol/adcpType: spec gap (compliance / cross-cutting)
Severity: high (silently passing implementations are non-compliant)
Target: 3.x minor (purely additive — codifies behavior already expected)
Problem
The cross-cutting requirement "any mutating request whose account has registered governance agents MUST consult them before mutating" is encoded only inside individual specialism storyboards (e.g.,
compliance/specialisms/brand-rights/scenarios/governance_denied.yaml).There is no
universal/governance.yamlparallel touniversal/idempotency.yaml. An agent author reading only the brand-rights chapter (or only the seller chapter, or only the signals chapter) cannot discover that this obligation exists. The first time most implementations hit it is when the storyboard fails — and even then the failure mode (missingGOVERNANCE_DENIEDerror code on a path the implementor didn't realize was governance-gated) is hard to triage without already knowing the rule.Compare:
idempotency_keyis normative cross-cutting → hasuniversal/idempotency.yaml, listed in the protocol-wide block of every spec preamble, called out inMUTATING_TASKSconstants, has its own conformance baseline.Real-world impact: in our skill matrix, agents that pass every other test still fail
governance_deniedscenarios because they never wirecheckGovernance()against registered governance agents. The rule was never visible to them.Proposal
Add
compliance/universal/governance.yamlwith the following normative shape:Mirror the structure of
universal/idempotency.yamlso the conformance runner already knows how to grade it. Add a baseline check: every mutating-tool storyboard step picks up an additional implicit assertion ("if this account has registered governance agents in the seed fixture, the agent must have called check_governance before responding").Protocol-wide block update
The 3.0 GA preamble lists:
Add a third bullet:
Files affected
compliance/universal/governance.yaml(new file, modeled onidempotency.yaml)docs/v3/protocol/protocol-wide-requirements.md(add bullet)references: [universal/governance]annotation so it's machine-discoverable.Source
Surfaced during AdCP
@adcp/clientskill matrix runs. Out of 8 storyboard pairs tested across multiple cycles, every implementation that wires governance does so by accident (after copying from a worked example) rather than by reading a normative requirement. Confirmed by protocol expert review with grade C+ for "cross-cutting obligation discoverability" (idempotency exemplary; governance scattered or implicit).