Skip to content

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

Merged
bokelley merged 1 commit into
mainfrom
claude/issue-3998-governance-denial-wire-placement-storyboard
May 11, 2026
Merged

fix(compliance): correct wire-placement assertions in governance denial storyboards#4004
bokelley merged 1 commit into
mainfrom
claude/issue-3998-governance-denial-wire-placement-storyboard

Conversation

@bokelley

@bokelley bokelley commented May 3, 2026

Copy link
Copy Markdown
Contributor

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

@bokelley bokelley added the claude-triaged Issue has been triaged by the Claude Code triage routine. Remove to re-triage. label May 3, 2026
…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
@bokelley

Copy link
Copy Markdown
Contributor Author

Audit pass complete (per request before merge). Surveyed every storyboard mentioning GOVERNANCE_DENIED or living under a governance_denied.yaml filename:

File Task Schema arms Case Status
protocols/media-buy/scenarios/governance_denied.yaml create_media_buy Success/Error/Submitted Case-2 (no rejection arm) fixed in this PR
specialisms/brand-rights/scenarios/governance_denied.yaml acquire_rights Acquired/PendingApproval/Rejected/Error Case-1 (rejection arm) fixed in this PR
specialisms/signal-marketplace/scenarios/governance_denied.yaml activate_signal Success/Error (no rejection arm) Case-2 already correct
protocols/media-buy/scenarios/governance_denied_recovery.yaml create_media_buy Success/Error/Submitted Case-2 already correct
specialisms/governance-aware-seller/index.yaml (narrative only) not affected

The two files this PR touches are the only governance-denial storyboards that needed wire-placement corrections.

Adjacent finding (out of scope): the acquire_expired_rights step in specialisms/brand-rights/index.yaml uses a generic check: error_code against acquire_rights which has both AcquireRightsError and AcquireRightsRejected arms. The test is for business-rule rejection (expired campaign dates), likely intentionally targeting the Error arm, not the Rejected arm — but the assertion doesn't pin which. Worth a separate look-over but not this PR.

@bokelley bokelley force-pushed the claude/issue-3998-governance-denial-wire-placement-storyboard branch from 15c4b96 to 2c02215 Compare May 11, 2026 17:30
@bokelley

Copy link
Copy Markdown
Contributor Author

Audit confirmed — the two files this PR touches are the only governance-denial storyboards that needed wire-placement corrections. Scope is clean.

Adjacent finding noted (acquire_expired_rightsacquire_rights assertion in specialisms/brand-rights/index.yaml not pinning Error vs Rejected arm). Out of scope here; worth a standalone issue to track the arm-disambiguation gap before the next compliance sweep.


Generated by Claude Code

@bokelley bokelley marked this pull request as ready for review May 11, 2026 17:34
@bokelley bokelley merged commit f4bd134 into main May 11, 2026
19 checks passed
@bokelley bokelley deleted the claude/issue-3998-governance-denial-wire-placement-storyboard branch May 11, 2026 17:34
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