You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Successor to #6656 (closed), alongside #6749 (acceptance-policy discovery) and #6750 (proposal-bound change rights). This RFC proposes records, not authority: it accepts the campaign-governance ("spend approval") model as intentionally asymmetric and accepts that seller-internal review, HITL, and escalation are implementation details. What it adds is the typed record of the seller's decline, which is external by construction: it crosses the wire and lands in the audit trail.
Amended 2026-08-22 per triage and maintainer review (see comments): narrowed to a vocabulary clarification and audit-preservation proposal. The error vocabulary is re-scoped rather than extended (origin on the existing POLICY_VIOLATION details; no new error code); declines map to the existing outcome: "failed" (the originally proposed not_executed value was an error — it is not in outcome-type.json); the relayed record is framed honestly as a buyer-attributed copy; the seller_policy_review hold class and human_involved field are dropped; all disclosure fields are genuinely optional so the opaque form is truly opaque.
Motivation
Grounding: citations from the repo at 17cb0f59776a0ecc68dd912dc43b640442002d74 (main, 2026-08-21); re-verify at implementation time.
The seller's right to decline is normative; its record is not.docs/governance/campaign/specification.mdx § Seller enforcement:
"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."
PERMISSION_DENIED is the same code used for token failures in the preceding paragraph of that section — a buyer cannot distinguish "your token failed verification" from "our standing policy declined this buy". The error-code registry itself divides these concerns already: POLICY_VIOLATION ("Request violates the seller's content or advertising policies"), ACTION_NOT_ALLOWED (mutation not available), PERMISSION_DENIED (caller authorization) — the seller-enforcement text just points at the wrong one.
The audit trail already expects the seller's result — untyped.docs/governance/campaign/responsibilities.mdx: after a seller responds, the orchestrator calls report_plan_outcome, which "lets the governance agent reconcile the approved action with the seller's actual response"; the documented failure mode for omitting it is "Governance budget and audit state drift from what actually happened." A policy-based decline is part of what actually happened — but no structured shape carries it, so the reconciled record can say an approved action failed without preserving why.
Reconciliation is the spec's own defense mechanism — decline reasons are missing from it.specification.mdx § Seller enforcement requires sellers to persist accepted tokens with decision outcomes, because "independent reconciliation between seller records and get_plan_audit_logs is the cross-check that catches a compromised or misbehaving governance agent." The seller-side decline reasons that determine whether approved actions execute have no structured place on the governance-agent side of that reconciliation today.
The typed-detail convention already exists.static/schemas/source/error-details/policy-violation.json is the "Recommended details shape for POLICY_VIOLATION errors" — policy_id, policy_url, violated_rules, all optional, extensible. What it lacks is an origin (whose policy) and explicit semantics for policies the seller does not disclose.
Neighbours and why they don't cover this.#6749 gives sellers structured acceptance-policy discovery and says definitive policy failures "use existing structured rejection/outcome mechanisms and reference the applicable registry policy IDs when they can be disclosed" — leaving the undisclosed case undefined and the audit-record entry unspecified. #6750 binds negotiated change rights and deliberately keeps the seller's process opaque — correctly — but the outcome of that processing has no preserved record either. Both RFCs define what happens before and around the seller's decision; neither defines the record of the decision itself. Pending-processing communication is deliberately out of scope here: the generic async lifecycle and #6750's seller_managed route cover it.
Problem
Buyer's intent check approves an update_media_buy; signed governance_context issued; the seller's execution check passes.
On the wire this is PERMISSION_DENIED — indistinguishable from a token failure — or a generic error. In get_plan_audit_logs the plan shows an approved action that failed, with no preserved reason. In a regulated context, the seller's decision — which may need to be demonstrable — exists only in the seller's internal systems.
Reconciliation between seller records and the governance audit log — the spec's stated cross-check — cannot cover the one class of seller decision that changes whether money moves.
Scope
Three mechanisms, all additive and optional. No new error codes, no new outcome values, no seller authority, no workflow exposure, no disclosure obligation.
1. Error vocabulary clarification + origin
The normative seller-enforcement text stops recommending PERMISSION_DENIED for acceptance-policy rejection, and the existing vocabulary is mapped by concern:
No new error code is added. SELLER_POLICY_REJECTED remains unfiled unless the WG later concludes that POLICY_VIOLATION's recovery classification cannot represent an opaque, terminal-for-this-request refusal.
2. Progressive disclosure — the opaque form is genuinely opaque
All detail fields (policy_id, policy_url, category, violated_rules) are optional, per the existing schema (no required entries). For an undisclosed policy the guaranteed signal is the typed error alone:
A mandatory stable identifier — even an opaque one — is a correlation handle: a buyer could map a seller's confidential control surface across requests. Disclosure is therefore progressive and entirely at the seller's discretion, from bare origin up to full policy reference; disclosed policies (e.g. those published via #6749's acceptance-policy profiles) SHOULD reference their registry policy IDs, per #6749.
3. Audit preservation on the existing failed outcome
report_plan_outcome already supports outcome: "failed" and requires an error. Its error object is extended to carry the structured seller error details (inline or $ref to the policy-violation details shape), and the governance agent retains and exposes that error on the corresponding get_plan_audit_logs outcome entry. get_plan_audit_logs can then answer "why did the approved action fail" with a preserved, typed reason.
Provenance is stated honestly: because report_plan_outcome is buyer-called, this record is a buyer-attributed copy of the seller response — not an authenticated seller disposition. Seller-attested provenance (signed seller evidence or direct authenticated seller reporting) is named future work, not claimed here.
Non-goals
No seller authority over spend approval; no change to check_governance, token threading, or the one-agent binding.
No exposure of seller-internal review, HITL, or escalation workflow — including no hold-reason class and no human_involved field: the generic async lifecycle communicates pending work, and RFC: bind buyer change rights into accepted proposal terms #6750's seller_managed route carries the externally meaningful processing commitment.
No obligation to disclose policy content, identifiers, or categories: the opaque form is first-class.
No new error codes or outcome values; no seller-attested audit provenance (future work).
Alternatives considered
Do nothing. Seller declines remain PERMISSION_DENIED-shaped per the current normative text — indistinguishable from token failures — and the audit record keeps losing the reason approved actions fail.
A new SELLER_POLICY_REJECTED error code. Single enum match for agent loops, but adds to the stable error-code vocabulary for a concern the existing POLICY_VIOLATION + origin expresses; deferred unless recovery-classification needs emerge (per maintainer review).
A seller_disposition block on report_plan_outcome with a not_executed outcome(this RFC's originally filed shape). Rejected on review: not_executed is not in outcome-type.json (adding it contradicted the non-breaking claim), and a dedicated seller-attributed block overstated the provenance of a buyer-relayed record. The failed-outcome error extension carries the same information honestly.
A typed hold-reason class (seller_policy_review)(also originally filed). Dropped: pending-processing communication is covered by the generic async lifecycle and RFC: bind buyer change rights into accepted proposal terms #6750's seller_managed commitments, and a reason class begins modeling the internal workflow the protocol deliberately leaves alone. Revisitable with implementer evidence that policy holds are being mistaken for latency in practice.
Free-text reasons on existing errors. No attribution, no machine-actionable structure, nothing aggregatable in audit.
Compatibility impact
Non-breaking. All schema additions are optional fields on additionalProperties: true schemas; no new error codes or outcome values; the specification change removes incorrect guidance (PERMISSION_DENIED for policy rejection) with no behavioral change for conformant implementations. Changeset: patch on experimental surface, minor if the governance/ schemas are classed stable — either way additive (per triage, verify x-status: experimental on governance/ schemas and flag for backfill if missing).
Files affected (as of 17cb0f5 — re-verify at implementation)
static/schemas/source/governance/report-plan-outcome-request.json — failed-outcome error object extended to carry the structured seller details (inline or $ref)
docs/governance/campaign/specification.mdx § Seller enforcement — remove PERMISSION_DENIED as the policy-rejection recommendation; add the vocabulary mapping
docs/governance/campaign/tasks/report_plan_outcome.mdx, responsibilities.mdx — structured error on failed seller declines; buyer-attributed-copy framing; exposure in get_plan_audit_logs
Changeset file, schema index if touched, compliance storyboards as follow-up scope
Reviewer checklist
Motivation is clear and not redundant with existing functionality
Scope is specific enough to implement without further clarification
Alternatives section covers at least one non-obvious alternative
Compatibility impact accurately states breaking vs. non-breaking
Wire-format or schema snippet included (for schema or task changes)
Motivation
Grounding: citations from the repo at
17cb0f59776a0ecc68dd912dc43b640442002d74(main, 2026-08-21); re-verify at implementation time.The seller's right to decline is normative; its record is not.
docs/governance/campaign/specification.mdx§ Seller enforcement:PERMISSION_DENIEDis the same code used for token failures in the preceding paragraph of that section — a buyer cannot distinguish "your token failed verification" from "our standing policy declined this buy". The error-code registry itself divides these concerns already:POLICY_VIOLATION("Request violates the seller's content or advertising policies"),ACTION_NOT_ALLOWED(mutation not available),PERMISSION_DENIED(caller authorization) — the seller-enforcement text just points at the wrong one.The audit trail already expects the seller's result — untyped.
docs/governance/campaign/responsibilities.mdx: after a seller responds, the orchestrator callsreport_plan_outcome, which "lets the governance agent reconcile the approved action with the seller's actual response"; the documented failure mode for omitting it is "Governance budget and audit state drift from what actually happened." A policy-based decline is part of what actually happened — but no structured shape carries it, so the reconciled record can say an approved action failed without preserving why.Reconciliation is the spec's own defense mechanism — decline reasons are missing from it.
specification.mdx§ Seller enforcement requires sellers to persist accepted tokens with decision outcomes, because "independent reconciliation between seller records andget_plan_audit_logsis the cross-check that catches a compromised or misbehaving governance agent." The seller-side decline reasons that determine whether approved actions execute have no structured place on the governance-agent side of that reconciliation today.The typed-detail convention already exists.
static/schemas/source/error-details/policy-violation.jsonis the "Recommended details shape for POLICY_VIOLATION errors" —policy_id,policy_url,violated_rules, all optional, extensible. What it lacks is an origin (whose policy) and explicit semantics for policies the seller does not disclose.Neighbours and why they don't cover this. #6749 gives sellers structured acceptance-policy discovery and says definitive policy failures "use existing structured rejection/outcome mechanisms and reference the applicable registry policy IDs when they can be disclosed" — leaving the undisclosed case undefined and the audit-record entry unspecified. #6750 binds negotiated change rights and deliberately keeps the seller's process opaque — correctly — but the outcome of that processing has no preserved record either. Both RFCs define what happens before and around the seller's decision; neither defines the record of the decision itself. Pending-processing communication is deliberately out of scope here: the generic async lifecycle and #6750's
seller_managedroute cover it.Problem
update_media_buy; signedgovernance_contextissued; the seller's execution check passes.PERMISSION_DENIED— indistinguishable from a token failure — or a generic error. Inget_plan_audit_logsthe plan shows an approved action that failed, with no preserved reason. In a regulated context, the seller's decision — which may need to be demonstrable — exists only in the seller's internal systems.Scope
Three mechanisms, all additive and optional. No new error codes, no new outcome values, no seller authority, no workflow exposure, no disclosure obligation.
1. Error vocabulary clarification +
originThe normative seller-enforcement text stops recommending
PERMISSION_DENIEDfor acceptance-policy rejection, and the existing vocabulary is mapped by concern:POLICY_VIOLATIONACTION_NOT_ALLOWEDPERMISSION_DENIEDpolicy-violation.jsongains one optional field —origin(buyer_plan|registry|seller) — so a decline attributes whose policy fired:{ "error": "POLICY_VIOLATION", "error_details": { "origin": "seller", "policy_id": "streamhaus_budget_change_approval", "policy_url": "https://streamhaus.example/policies/budget-approval", "violated_rules": ["budget increase 34% exceeds 20% self-serve limit"] } }No new error code is added.
SELLER_POLICY_REJECTEDremains unfiled unless the WG later concludes thatPOLICY_VIOLATION's recovery classification cannot represent an opaque, terminal-for-this-request refusal.2. Progressive disclosure — the opaque form is genuinely opaque
All detail fields (
policy_id,policy_url,category,violated_rules) are optional, per the existing schema (norequiredentries). For an undisclosed policy the guaranteed signal is the typed error alone:{ "error": "POLICY_VIOLATION", "error_details": { "origin": "seller" } }A mandatory stable identifier — even an opaque one — is a correlation handle: a buyer could map a seller's confidential control surface across requests. Disclosure is therefore progressive and entirely at the seller's discretion, from bare
originup to full policy reference; disclosed policies (e.g. those published via #6749's acceptance-policy profiles) SHOULD reference their registry policy IDs, per #6749.3. Audit preservation on the existing
failedoutcomereport_plan_outcomealready supportsoutcome: "failed"and requires anerror. Itserrorobject is extended to carry the structured seller error details (inline or$refto the policy-violation details shape), and the governance agent retains and exposes that error on the correspondingget_plan_audit_logsoutcome entry.get_plan_audit_logscan then answer "why did the approved action fail" with a preserved, typed reason.Provenance is stated honestly: because
report_plan_outcomeis buyer-called, this record is a buyer-attributed copy of the seller response — not an authenticated seller disposition. Seller-attested provenance (signed seller evidence or direct authenticated seller reporting) is named future work, not claimed here.Non-goals
check_governance, token threading, or the one-agent binding.human_involvedfield: the generic async lifecycle communicates pending work, and RFC: bind buyer change rights into accepted proposal terms #6750'sseller_managedroute carries the externally meaningful processing commitment.Alternatives considered
PERMISSION_DENIED-shaped per the current normative text — indistinguishable from token failures — and the audit record keeps losing the reason approved actions fail.SELLER_POLICY_REJECTEDerror code. Single enum match for agent loops, but adds to the stable error-code vocabulary for a concern the existingPOLICY_VIOLATION+originexpresses; deferred unless recovery-classification needs emerge (per maintainer review).seller_dispositionblock onreport_plan_outcomewith anot_executedoutcome (this RFC's originally filed shape). Rejected on review:not_executedis not inoutcome-type.json(adding it contradicted the non-breaking claim), and a dedicated seller-attributed block overstated the provenance of a buyer-relayed record. Thefailed-outcome error extension carries the same information honestly.seller_policy_review) (also originally filed). Dropped: pending-processing communication is covered by the generic async lifecycle and RFC: bind buyer change rights into accepted proposal terms #6750'sseller_managedcommitments, and a reason class begins modeling the internal workflow the protocol deliberately leaves alone. Revisitable with implementer evidence that policy holds are being mistaken for latency in practice.Compatibility impact
Non-breaking. All schema additions are optional fields on
additionalProperties: trueschemas; no new error codes or outcome values; the specification change removes incorrect guidance (PERMISSION_DENIEDfor policy rejection) with no behavioral change for conformant implementations. Changeset: patch on experimental surface, minor if thegovernance/schemas are classed stable — either way additive (per triage, verifyx-status: experimentalongovernance/schemas and flag for backfill if missing).Files affected (as of
17cb0f5— re-verify at implementation)static/schemas/source/error-details/policy-violation.json— optionalorigin(buyer_plan|registry|seller); confirm all detail fields remain optionalstatic/schemas/source/governance/report-plan-outcome-request.json—failed-outcomeerrorobject extended to carry the structured seller details (inline or$ref)docs/governance/campaign/specification.mdx§ Seller enforcement — removePERMISSION_DENIEDas the policy-rejection recommendation; add the vocabulary mappingdocs/governance/campaign/tasks/report_plan_outcome.mdx,responsibilities.mdx— structured error onfailedseller declines; buyer-attributed-copy framing; exposure inget_plan_audit_logsReviewer checklist
Related
ACTION_NOT_ALLOWEDcovers its non-negotiated-change declines, and itsseller_managedroute covers processing commitments. Complementary.