Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/proposal-mode-storyboard-fixture-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"adcontextprotocol": patch
---

compliance(storyboard): fix proposal-mode fixture authoring in `sales_proposal_mode` and `media_buy_seller/proposal_finalize`

Two pre-existing storyboard authoring issues, surfaced by `@adcp/sdk` PR #1603 (which made `create_media_buy` actually exercise proposal-mode end-to-end instead of silently sending `packages` regardless):

1. **`sales_proposal_mode`** authored `proposal_id: "balanced_reach_q2"` as a literal in two places (refine step + create_media_buy step). The training-agent's seed proposals don't include that id (it seeds `pinnacle_cross_channel`, `viewpoint_multi_screen`, `sparq_social_amplification`, `novamind_ai_audience`). Switched both to `$context.proposal_id` so the storyboard dynamically references whichever proposal the brief returned, matching the pattern `media_buy_seller/proposal_finalize` already uses.

2. **Both storyboards** now include `io_acceptance` on the `create_media_buy` fixture. AdCP 3.0+ proposals with guaranteed inventory carry an `insertion_order` with `requires_signature: true` after finalization; sellers reject `create_media_buy` against such proposals without `io_acceptance`. The finalize step's `context_outputs` captures `proposals[0].insertion_order.io_id`, and the create_media_buy step references it via `$context.io_id`.

3. **`sales_proposal_mode`** previously jumped straight from refine to create_media_buy, which kept the proposal in `draft` status. Added a `finalize_proposal` phase between them (matching the pattern in `media_buy_seller/proposal_finalize`) so the proposal transitions to `committed` before acceptance.

Forward-compatible with both pre-#1603 and post-#1603 SDK behavior — all six tenant matrix runs pass against both. /sales lifts from 258 → 259 steps (the new finalize step counts).

Patch-eligible per the conformance-additive rule (additive scenarios / fixture corrections that bring storyboards into alignment with the spec's own normative proposal-lifecycle).
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ phases:
domain: "acmeoutdoor.example"
operator: "pinnacle-agency.example"

context_outputs:
- path: "proposals[0].insertion_order.io_id"
key: "io_id"

validations:
- check: response_schema
description: "Response matches get-products-response.json schema"
Expand Down Expand Up @@ -244,6 +248,10 @@ phases:
currency: "USD"
start_time: "2026-04-01T00:00:00Z"
end_time: "2026-06-30T23:59:59Z"
io_acceptance:
io_id: "$context.io_id"
accepted_at: "2026-03-15T14:30:00Z"
signatory: "ops@acmeoutdoor.example"

idempotency_key: "$generate:uuid_v4#media_buy_seller_proposal_finalize_accept_proposal_create_media_buy"
validations:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ phases:
buying_mode: "refine"
refine:
- scope: "proposal"
proposal_id: "balanced_reach_q2"
proposal_id: "$context.proposal_id"
ask: "Shift 60% of budget to CTV. Drop the display product and redistribute that budget to video."
- scope: "request"
ask: "All products must support frequency capping at 3 per day."
Expand Down Expand Up @@ -293,6 +293,58 @@ phases:
- check: field_present
path: "products[0].format_ids[0].id"
description: "Format IDs include id — must be accepted back in sync_creatives"
- id: finalize_proposal
title: "Finalize the proposal"
narrative: |
Before accepting, the buyer requests finalization. This transitions the proposal from
draft (indicative pricing) to committed (firm pricing with inventory hold). For
guaranteed inventory, finalization also produces an insertion order the buyer
countersigns when calling create_media_buy.

steps:
- id: get_products_finalize
title: "Finalize the refined proposal"
narrative: |
The buyer requests finalization on the refined proposal. The seller commits
firm pricing and (for guaranteed inventory) returns an insertion order with
a signature requirement.
task: get_products
schema_ref: "media-buy/get-products-request.json"
response_schema_ref: "media-buy/get-products-response.json"
doc_ref: "/media-buy/task-reference/get_products"
comply_scenario: full_sales_flow
stateful: true
expected: |
Return the finalized proposal with committed status:
- proposals[0].proposal_status: committed
- proposals[0].expires_at: timestamp for the inventory hold window
- For guaranteed inventory, proposals[0].insertion_order with io_id
- The proposal is ready to accept via create_media_buy

sample_request:
buying_mode: "refine"
refine:
- scope: "proposal"
proposal_id: "$context.proposal_id"
action: "finalize"
account:
brand:
domain: "acmeoutdoor.example"
operator: "pinnacle-agency.example"

context:
correlation_id: "sales_proposal_mode--get_products_finalize"

context_outputs:
- path: "proposals[0].insertion_order.io_id"
key: "io_id"

validations:
- check: response_schema
description: "Response matches get-products-response.json schema"
- check: field_present
path: "proposals"
description: "Response contains the finalized proposal"
- id: accept_proposal
title: "Accept the proposal"
narrative: |
Expand Down Expand Up @@ -334,12 +386,16 @@ phases:
brand:
domain: "acmeoutdoor.example"
operator: "pinnacle-agency.example"
proposal_id: "balanced_reach_q2"
proposal_id: "$context.proposal_id"
total_budget:
amount: 50000
currency: "USD"
start_time: "2026-04-01T00:00:00Z"
end_time: "2026-06-30T23:59:59Z"
io_acceptance:
io_id: "$context.io_id"
accepted_at: "2026-03-15T14:30:00Z"
signatory: "ops@acmeoutdoor.example"

idempotency_key: "$generate:uuid_v4#sales_proposal_mode_accept_proposal_create_media_buy"
context:
Expand Down
Loading