Skip to content

fix(compliance): correct wire-placement assertions in governance denial storyboards#4004

Draft
bokelley wants to merge 2 commits intomainfrom
claude/issue-3998-governance-denial-wire-placement-storyboard
Draft

fix(compliance): correct wire-placement assertions in governance denial storyboards#4004
bokelley wants to merge 2 commits intomainfrom
claude/issue-3998-governance-denial-wire-placement-storyboard

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 3, 2026

Closes #3998

Summary

The brand_rights/governance_denied storyboard scenario was asserting expect_error: true + check: error_code, value: GOVERNANCE_DENIED on acquire_rights. Per the GOVERNANCE_DENIED wire-placement rule (landed in #3929), tasks that define a structured rejection arm MUST use that arm — not errors[]. AcquireRightsRejected carries not: { required: [errors] } at the schema layer, so the prior storyboard was grading a schema-violating response shape as passing for any brand agent that had run the brand_rights specialism suite.

What changes:

  1. brand_rights/scenarios/governance_denied.yaml — fixes Case 1 (acquire_rights):

    • Removes expect_error: true and negative_path: payload_well_formed
    • Adds expected_arm: "rejected" for correct path-resolution lint scoping
    • Replaces check: error_code, value: GOVERNANCE_DENIED with rejection-arm assertions:
      • check: response_schema — enforces not: { required: [errors] } on the Rejected arm
      • check: field_value path: status value: rejected — arm discriminator
      • check: field_present path: reason — governance findings propagated
      • check: field_absent path: errors — explicit dual-emission guard
      • check: field_value path: context.correlation_id — context echo
  2. media-buy/scenarios/governance_denied.yaml — documents Case 2 (create_media_buy):

    • Updates expected: block to document the Case-2 wire-placement rule explicitly (no rejection arm → errors[].code + adcp_error.code, transport failure markers flip)
    • Updates error_code description to reference the two-layer model

Non-breaking justification: Both changes are purely in storyboard YAML assertions. They correct incorrect test expectations to match spec-required behavior; no schema, task definition, or protocol surface is changed.

Case 3 (negative/violation test) — deferred: The issue also requested a fixture that populates both the rejection arm AND errors[] and asserts schema validation rejects it. This cannot be implemented as a live storyboard step (the runner has no mechanism to force a seller to emit an invalid response shape). It belongs as a test vector in static/compliance/source/test-vectors/acquire-rights-response/negative/ and is tracked for a follow-up.

Pre-PR review:

  • ad-tech-protocol-expert: approved — wire-placement rule enforcement is protocol-correct; adcp_error absent on rejection-arm responses is normative per protocol-envelope.json; expected_arm: "rejected" added per authoring guide
  • code-reviewer: approved — YAML valid; field_absent is in authored_check_kinds; field_present: errors dropped from media-buy scenario in favor of the existing shape-agnostic error_code check

Triage-managed PR. This bot does not currently iterate on
review comments or PR conversation threads (only on the source
issue). To unblock:

  • Push fixup commits directly: gh pr checkout <num>
    fix → push.
  • Or re-trigger: comment /triage execute on the source
    issue.

See #3121
for context.

Session: https://claude.ai/code/session_01QW7uzhT3g9LEVMX9y6Ew1R


Generated by Claude Code

claude added 2 commits May 3, 2026 17:39
…al storyboards

The brand_rights/governance_denied scenario previously asserted
expect_error: true + error_code: GOVERNANCE_DENIED on acquire_rights.
Per the GOVERNANCE_DENIED wire-placement rule (landed in #3929), tasks
with a structured rejection arm must use that arm — not errors[]. The
AcquireRightsRejected arm is enforced at the schema layer via
not: { required: [errors] }, so the prior test was grading a
schema-violating response shape as passing.

Fix the scenario to assert the correct Case-1 shape:
- expected_arm: rejected (paths resolution scoped to Rejected arm)
- check: response_schema (catches not: { required: [errors] } violation)
- check: field_value status = rejected
- check: field_present reason
- check: field_absent errors
- Transport at HTTP 200 / MCP isError: false (not an error response)

Also document the Case-2 placement rule in the media_buy_seller/
governance_denied description: create_media_buy has no rejection arm, so
GOVERNANCE_DENIED surfaces via error_code (errors[].code or adcp_error.code).

Closes #3998

https://claude.ai/code/session_01QW7uzhT3g9LEVMX9y6Ew1R
Remove "adcp_error absent" from expected (no envelope_field_absent check
type in authored_check_kinds; schema + field_absent on errors covers the
wire-placement contract). Tighten response_schema description to not
overstate what schema validation alone guarantees (field_value + field_absent
carry the arm-specific assertions).

https://claude.ai/code/session_01QW7uzhT3g9LEVMX9y6Ew1R
@bokelley bokelley added the claude-triaged Issue has been triaged by the Claude Code triage routine. Remove to re-triage. label May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-triaged Issue has been triaged by the Claude Code triage routine. Remove to re-triage.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants