Skip to content

Add a storyboard exercising rejection-arm vs errors[] mutual-exclusion for GOVERNANCE_DENIED / CREATIVE_REJECTED #3998

@bokelley

Description

@bokelley

Background

Surfaced during expert review of #3996 (3.0.6 cherry-picks). The wire-placement guidance for `GOVERNANCE_DENIED` (and the parallel rule for `CreativeRejected`) is now MUST-language in `enumDescriptions` and `error-handling.mdx`:

Case 1. Task response defines a structured rejection arm. The arm IS the canonical denial shape. The seller populates `reason` and does NOT additionally emit `GOVERNANCE_DENIED` in `errors[]` or `adcp_error`. The rejection arms enforce this at the schema layer: e.g., `AcquireRightsRejected` and `CreativeRejected` both declare `not: { required: [errors] }`, so dual-emission is already a schema violation.

Case 2. Task response has no rejection arm. The seller populates `errors[].code: GOVERNANCE_DENIED` in the payload AND `adcp_error.code: GOVERNANCE_DENIED` on the envelope.

Today the only mechanism enforcing "rejection arm AND `errors[]` is a violation" is the schema-layer `not: { required: [errors] }` constraint. Conformant agents are protected at validation time, but no storyboard exercises both arms of the rule end-to-end. A seller that relaxes `additionalProperties` (or that runs ahead of the schema constraint via a hand-rolled response shape) can drift without detection until a buyer's runtime trips on the dual emission.

What this issue tracks

Add a storyboard that exercises the wire-placement rule on both arms:

  1. Case-1 path (`acquire_rights` with `AcquireRightsRejected`) — the agent returns the rejection arm. Storyboard asserts:

    • `status: 'rejected'` discriminator present
    • `reason` populated
    • `errors[]` absent (or empty)
    • `adcp_error` absent
    • Transport-level success markers retained (HTTP 200 / MCP `isError: false` / A2A `succeeded`)
  2. Case-2 path (`create_media_buy` with no rejection arm — denial returned via `errors[]` + `adcp_error`) — the agent returns a failed task. Storyboard asserts:

    • `errors[].code: GOVERNANCE_DENIED` present
    • `adcp_error.code: GOVERNANCE_DENIED` present
    • Transport-level failure markers flipped (HTTP 4xx / MCP `isError: true` / A2A `failed`)
  3. Negative case (intentional violation) — a fixture variant that populates BOTH the rejection arm AND `errors[].code: GOVERNANCE_DENIED`. Storyboard asserts the agent's schema validation rejects the response.

Why now

The wire-placement guidance just shipped to `main` (#3929) and is being cherry-picked to 3.0.6 via #3996. Without a storyboard, the rule is asserted only in prose — adopters reading the long-form `GOVERNANCE_DENIED` description have no executable conformance check, and `@adcp/sdk` storyboard runners can't surface drift to a seller during onboarding.

Refs

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.compliance-suite

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions