Conversation
…storyboard alignment (closes #3998) The wire-placement guidance on GOVERNANCE_DENIED (shipped via #3929 to main and #3996 to 3.0.x) is normative MUST-language: when a task response defines a structured rejection arm (AcquireRightsRejected, CreativeRejected), the arm IS the canonical denial shape — sellers MUST NOT additionally emit the error code in errors[] or adcp_error. The schema enforces this with not: required: errors on each rejection arm. Until now the rule was asserted only in prose. This change adds executable conformance: - tests/rejection-arm-mutual-exclusion.test.cjs — new schema-validation conformance check. Asserts both directions: canonical rejection-arm shape accepts; rejection-arm with errors[] populated rejects. Catches regression on the not block before storyboards do. Wired into the aggregate npm test run via test:rejection-arm-mutex. - brand_rights/governance_denied storyboard — was asserting check: error_code, value: GOVERNANCE_DENIED on a task whose canonical denial shape is status: rejected + reason. Now asserts field_value status=rejected plus field_present reason. Closes the storyboard portion of #3914 (storyboard was rejecting spec-correct adopter responses). - media_buy_seller/governance_denied storyboard — narrative tightened to make Case-2 of the rule explicit (no rejection arm → errors[] + adcp_error populated; transport markers flipped). Cross-references brand-rights as the Case-1 counterpart. Wire format unchanged. Schema constraints unchanged. Pure conformance + documentation: the schema rule was already in place; this change makes it discoverable from a failing test and aligns the existing storyboards with the rule. Closes #3998 (rejection-arm mutex conformance). Closes #3914 (storyboard portion — storyboard was non-spec, now matches the rejection-arm path). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The wire-placement guidance on `GOVERNANCE_DENIED` (shipped to `main` via #3929 and to 3.0.x via #3996) is normative MUST-language: when a task response defines a structured rejection arm (`AcquireRightsRejected`, `CreativeRejected`), the arm IS the canonical denial shape — sellers MUST NOT additionally emit the error code in `errors[]` or `adcp_error`. The schema enforces this with `not: { required: ["errors"] }` on each rejection arm.
Until now the rule was asserted only in prose. This PR adds executable conformance and aligns the existing storyboards with the rule.
Changes
`tests/rejection-arm-mutual-exclusion.test.cjs` — schema-validation conformance check (new)
Asserts both directions of the rule for both rejection arms:
Catches regression on the constraint before any storyboard does. Wired into the aggregate `npm test` run via `test:rejection-arm-mutex`.
`brand_rights/governance_denied` storyboard — assertions corrected
Was asserting:
```yaml
value: "GOVERNANCE_DENIED"
```
…on a task whose canonical denial shape is the `AcquireRightsRejected` arm (`status: "rejected"` + `reason`). Adopters returning the spec-correct shape were failing the storyboard. Now asserts:
```yaml
path: "status"
value: "rejected" # discriminator on the rejection arm
path: "reason" # governance findings propagated verbatim
```
Also drops the `expect_error: true` and `negative_path: payload_well_formed` flags — Case-1 of the rule explicitly says transport-level success markers MUST NOT be flipped (HTTP 200 / MCP `isError: false` / A2A `succeeded`). The narrative is updated to spell out that the schema's `not: { required: ["errors"] }` rule is what enforces the no-dual-emission discipline.
Closes the storyboard portion of #3914 (the storyboard was non-spec; adopters returning the rejection arm were failing it).
`media_buy_seller/governance_denied` storyboard — narrative tightened
The assertions were already correct (Case-2 of the rule: `create_media_buy` has no rejection arm, so `errors[].code: GOVERNANCE_DENIED` + `adcp_error.code: GOVERNANCE_DENIED` + flipped transport markers is the canonical denial shape). Updated the `expected` prose to make Case-2 explicit and cross-reference the brand-rights scenario as the Case-1 counterpart, so a reader sees both arms of the rule from either entry point.
Wire-format impact
None. Schema constraints unchanged. Pure conformance + documentation: the schema rule was already in place; this PR makes it discoverable from a failing test and aligns the existing storyboards with the rule.
Test plan
Refs
expect_error: code: GOVERNANCE_DENIEDis non-spec —AcquireRightsRejectedis the canonical denial shape #3914 — storyboard portion closed by this PR (was filed for the brand_rights/governance_denied assertion drift)🤖 Generated with Claude Code