Skip to content

Commit a8471c4

Browse files
bokelleyclaude
andauthored
Enforce atomic operation semantics with success XOR error responses (#186)
* Enforce atomic operation semantics with success XOR error responses Task response schemas now use oneOf discriminators to ensure responses contain either complete success data OR error information, never both. This prevents dangerous partial success scenarios in advertising operations (e.g., buyer requests "US + Tuesdays only" but gets created campaign without Tuesday constraint). **Changes:** - create-media-buy-response.json: oneOf success (requires media_buy_id, buyer_ref, packages) vs error (requires errors array) - update-media-buy-response.json: oneOf success (requires media_buy_id, buyer_ref) vs error (requires errors array) - build-creative-response.json: oneOf success (requires creative_manifest) vs error (requires errors array) - provide-performance-feedback-response.json: oneOf success (requires success: true) vs error (requires errors array) - webhook-payload.json: Conditional validation (if/then with allOf) to validate result field against appropriate task response schema based on task_type 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * Add atomic response semantics to sync_creatives and activate_signal Extended oneOf pattern to remaining mutating operations: - sync-creatives-response.json: Distinguishes operation-level failures (auth, service down) from per-item failures (validation errors). Maintains best-effort batch semantics while enforcing atomic operation boundaries. - activate-signal-response.json: Success requires decisioning_platform_segment_id; error requires errors array. Prevents dangerous scenarios where buyers think signal is active but it's not deployed. All mutating operations across media-buy, signals, and creative protocols now enforce success XOR error semantics. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 17f3a16 commit a8471c4

File tree

8 files changed

+551
-232
lines changed

8 files changed

+551
-232
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
"adcontextprotocol": minor
3+
---
4+
5+
Enforce atomic operation semantics with success XOR error response pattern. Task response schemas now use `oneOf` discriminators to ensure responses contain either complete success data OR error information, never both, never neither.
6+
7+
**Response Pattern:**
8+
9+
All mutating operations (create, update, build) now enforce strict either/or semantics:
10+
11+
1. **Success response** - Operation completed fully:
12+
```json
13+
{
14+
"media_buy_id": "mb_123",
15+
"buyer_ref": "campaign_2024_q1",
16+
"packages": [...]
17+
}
18+
```
19+
20+
2. **Error response** - Operation failed completely:
21+
```json
22+
{
23+
"errors": [
24+
{
25+
"code": "INVALID_TARGETING",
26+
"message": "Tuesday-only targeting not supported",
27+
"suggestion": "Remove day-of-week constraint or select all days"
28+
}
29+
]
30+
}
31+
```
32+
33+
**Why This Matters:**
34+
35+
Partial success in advertising operations is dangerous and can lead to unintended spend or incorrect targeting. For example:
36+
- Buyer requests "US targeting + Tuesday-only dayparting"
37+
- Partial success returns created media buy without Tuesday constraint
38+
- Buyer might not notice error, campaign runs with wrong targeting
39+
- Result: Budget spent on unwanted inventory
40+
41+
The `oneOf` discriminator enforces atomic semantics at the schema level - operations either succeed completely or fail completely. Buyers must explicitly choose to modify their requirements rather than having the system silently omit constraints.
42+
43+
**Updated Schemas:**
44+
45+
All mutating operation schemas now use `oneOf` with explicit success/error branches:
46+
47+
**Media Buy Operations:**
48+
- `create-media-buy-response.json` - Success requires `media_buy_id`, `buyer_ref`, `packages`; Error requires `errors` array
49+
- `update-media-buy-response.json` - Success requires `media_buy_id`, `buyer_ref`; Error requires `errors` array
50+
- `build-creative-response.json` - Success requires `creative_manifest`; Error requires `errors` array
51+
- `provide-performance-feedback-response.json` - Success requires `success: true`; Error requires `errors` array
52+
- `sync-creatives-response.json` - Success requires `creatives` array (with per-item results); Error requires `errors` array (operation-level failures only)
53+
54+
**Signals Operations:**
55+
- `activate-signal-response.json` - Success requires `decisioning_platform_segment_id`; Error requires `errors` array
56+
57+
**Webhook Validation:**
58+
- `webhook-payload.json` - Uses conditional validation (`if/then` with `allOf`) to validate result field against the appropriate task response schema based on task_type. Ensures webhook results are properly validated against their respective task schemas.
59+
60+
**Schema Structure:**
61+
62+
```json
63+
{
64+
"oneOf": [
65+
{
66+
"description": "Success response",
67+
"required": ["media_buy_id", "buyer_ref", "packages"],
68+
"not": {"required": ["errors"]}
69+
},
70+
{
71+
"description": "Error response",
72+
"required": ["errors"],
73+
"not": {"required": ["media_buy_id", "buyer_ref", "packages"]}
74+
}
75+
]
76+
}
77+
```
78+
79+
The `not` constraints ensure responses cannot contain both success and error fields simultaneously.
80+
81+
**Benefits:**
82+
83+
- **Safety**: Prevents dangerous partial success scenarios in advertising operations
84+
- **Clarity**: Unambiguous success vs failure - no mixed signals
85+
- **Validation**: Schema-level enforcement of atomic semantics
86+
- **Consistency**: All mutating operations follow same pattern
87+
88+
**Batch Operations Pattern**
89+
90+
`sync_creatives` uses a two-level error model that distinguishes:
91+
- **Operation-level failures** (oneOf error branch): Authentication failed, service down, invalid request format - no creatives processed
92+
- **Per-item failures**: Individual creative validation errors (action='failed' within the creatives array) - rest of batch still processed
93+
94+
This provides best-effort batch semantics (process what you can, report what failed) while maintaining atomic operation boundaries (either you can process the batch OR you can't).
95+
96+
**Migration:**
97+
98+
This is a backward-compatible change. Existing valid responses (success with all required fields) continue to validate successfully. The change prevents invalid responses (missing required success fields or mixing success/error fields) that were technically possible but semantically incorrect.
99+
100+
**Alignment with Protocol Standards:**
101+
102+
This pattern aligns with both MCP and A2A error handling:
103+
- **MCP**: Tool returns either result content OR sets `isError: true`, not both
104+
- **A2A**: Task reaches terminal state `completed` OR `failed`, not both
105+
- **AdCP**: Task payload contains success data XOR errors, enforced at schema level

static/schemas/v1/core/webhook-payload.json

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,7 @@
6868
},
6969
"result": {
7070
"type": ["object"],
71-
"description": "Task-specific payload for this status update. For 'completed', contains the final result. For 'input-required', may contain approval or clarification context. Optional for non-terminal updates.",
72-
"oneOf": [
73-
{ "$ref": "/schemas/v1/media-buy/create-media-buy-response.json" },
74-
{ "$ref": "/schemas/v1/media-buy/update-media-buy-response.json" },
75-
{ "$ref": "/schemas/v1/media-buy/sync-creatives-response.json" },
76-
{ "$ref": "/schemas/v1/signals/activate-signal-response.json" },
77-
{ "$ref": "/schemas/v1/signals/get-signals-response.json" }
78-
]
71+
"description": "Task-specific payload for this status update. Validated against the appropriate response schema based on task_type."
7972
},
8073
"error": {
8174
"type": ["string", "null"],
@@ -84,6 +77,78 @@
8477
},
8578
"required": ["task_id", "task_type", "status", "timestamp"],
8679
"additionalProperties": true,
80+
"allOf": [
81+
{
82+
"if": {
83+
"properties": {
84+
"task_type": {"const": "create_media_buy"}
85+
}
86+
},
87+
"then": {
88+
"properties": {
89+
"result": {
90+
"$ref": "/schemas/v1/media-buy/create-media-buy-response.json"
91+
}
92+
}
93+
}
94+
},
95+
{
96+
"if": {
97+
"properties": {
98+
"task_type": {"const": "update_media_buy"}
99+
}
100+
},
101+
"then": {
102+
"properties": {
103+
"result": {
104+
"$ref": "/schemas/v1/media-buy/update-media-buy-response.json"
105+
}
106+
}
107+
}
108+
},
109+
{
110+
"if": {
111+
"properties": {
112+
"task_type": {"const": "sync_creatives"}
113+
}
114+
},
115+
"then": {
116+
"properties": {
117+
"result": {
118+
"$ref": "/schemas/v1/media-buy/sync-creatives-response.json"
119+
}
120+
}
121+
}
122+
},
123+
{
124+
"if": {
125+
"properties": {
126+
"task_type": {"const": "activate_signal"}
127+
}
128+
},
129+
"then": {
130+
"properties": {
131+
"result": {
132+
"$ref": "/schemas/v1/signals/activate-signal-response.json"
133+
}
134+
}
135+
}
136+
},
137+
{
138+
"if": {
139+
"properties": {
140+
"task_type": {"const": "get_signals"}
141+
}
142+
},
143+
"then": {
144+
"properties": {
145+
"result": {
146+
"$ref": "/schemas/v1/signals/get-signals-response.json"
147+
}
148+
}
149+
}
150+
}
151+
],
87152
"notes": [
88153
"Webhooks are ONLY triggered when the initial response status is 'submitted' (long-running operations)",
89154
"Webhook payloads include protocol-level fields (operation_id, task_type, status, optional task_id/context_id/timestamp/message) and the task-specific payload nested under 'result'",
@@ -104,7 +169,13 @@
104169
"context_id": "ctx_abc123",
105170
"message": "Campaign budget $150K requires VP approval to proceed",
106171
"result": {
107-
"buyer_ref": "nike_q1_campaign_2024"
172+
"errors": [
173+
{
174+
"code": "APPROVAL_REQUIRED",
175+
"message": "Budget exceeds auto-approval threshold of $100K. Awaiting VP approval before media buy creation.",
176+
"field": "packages[0].budget"
177+
}
178+
]
108179
}
109180
}
110181
},
@@ -145,4 +216,4 @@
145216
}
146217
}
147218
]
148-
}
219+
}

static/schemas/v1/media-buy/build-creative-response.json

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,42 @@
22
"$schema": "http://json-schema.org/draft-07/schema#",
33
"$id": "/schemas/v1/media-buy/build-creative-response.json",
44
"title": "Build Creative Response",
5-
"description": "Response containing the transformed or generated creative manifest, ready for use with preview_creative or sync_creatives",
5+
"description": "Response containing the transformed or generated creative manifest, ready for use with preview_creative or sync_creatives. Returns either the complete creative manifest OR error information, never both.",
66
"type": "object",
7-
"properties": {
8-
"creative_manifest": {
9-
"$ref": "/schemas/v1/core/creative-manifest.json",
10-
"description": "The generated or transformed creative manifest"
7+
"oneOf": [
8+
{
9+
"description": "Success response - creative manifest generated successfully",
10+
"type": "object",
11+
"properties": {
12+
"creative_manifest": {
13+
"$ref": "/schemas/v1/core/creative-manifest.json",
14+
"description": "The generated or transformed creative manifest"
15+
}
16+
},
17+
"required": ["creative_manifest"],
18+
"additionalProperties": false,
19+
"not": {
20+
"required": ["errors"]
21+
}
1122
},
12-
"errors": {
13-
"type": "array",
14-
"description": "Task-specific errors and warnings",
15-
"items": {
16-
"$ref": "/schemas/v1/core/error.json"
23+
{
24+
"description": "Error response - creative generation failed",
25+
"type": "object",
26+
"properties": {
27+
"errors": {
28+
"type": "array",
29+
"description": "Array of errors explaining why creative generation failed",
30+
"items": {
31+
"$ref": "/schemas/v1/core/error.json"
32+
},
33+
"minItems": 1
34+
}
35+
},
36+
"required": ["errors"],
37+
"additionalProperties": false,
38+
"not": {
39+
"required": ["creative_manifest"]
1740
}
1841
}
19-
},
20-
"required": [
21-
"creative_manifest"
22-
],
23-
"additionalProperties": false
42+
]
2443
}

0 commit comments

Comments
 (0)