compliance(media-buy): add typed proposal negotiation storyboard matrix - #6586
Conversation
Add typed_proposal_negotiation.yaml exercising the AdCP 3.2 typed negotiation lifecycle through refine_proposals: - Capability discovery: proposal_refinement.supported_dimensions gate - Typed budget constraint: satisfied (revised) and unsatisfied (partial with constraint_unsatisfiable) - Product changes: include/omit map with purchase verification - Alternatives: exact count with unique terms_digest values - Unsupported dimension: task-level UNSUPPORTED_FEATURE rejection with registered error-details shape - Finalize: committed snapshot with inventory hold and expires_at - Idempotent replay: same idempotency_key returns cached response - Immutable lineage: parent_proposal_id equals source on every successor - Accept: digest-verified acceptance creating a MediaBuy - Amendment and cancellation successors from accepted proposal - Double-finalize: INVALID_STATE rejection for committed proposals - Multi-source batch: ordered results with independent outcomes Register the scenario in the media-buy seller index.yaml. Refs adcontextprotocol#6559
fe71acf to
f6bb086
Compare
There was a problem hiding this comment.
Ladon verdict: Request changes
Request changes — 1 blocking finding.
Blocking findings
static/compliance/source/protocols/media-buy/scenarios/typed_proposal_negotiation.yaml:509— Lifecycle tail (finalize/accept/amendment/cancellation/double-finalize/multi-source) depends on context outputs produced only inside atotal_budget-gated phase. A spec-valid seller lacking that dimension breaks the scenario or silently loses the storyboard's headline coverage — a reproducible bug on a load-bearing compliance path.
Medium findings
- None.
The rest of the PR is solid: schema↔docs coherence is clean (asserted fields, enums, error codes, and the singular-proposal vs array-proposals split all match refine-proposals-response.json, proposal-refinement.json, canonical-proposal.json, and the capabilities schema), and the refine/decline scoping exemption is correct since the split schemas forbid account. Three low-severity narrative/checkbox nits (lines 578, 760, and the changeset test-plan) are noted but non-blocking. Address the capability-gated dependency at line 509 so the tail either guards on the gating dimension or does not rely on outputs a conformant seller may never produce.
Blocking findings
- static/compliance/source/protocols/media-buy/scenarios/typed_proposal_negotiation.yaml:509 — Lifecycle tail depends on context outputs from a capability-gated (total_budget) phase; a spec-valid seller without that dimension breaks or silently loses headline coverage
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — prior High finding resolved, no current blocking or Medium findings.
This is a subsequent review. The prior run (request-changes) flagged one High finding: the finalize lifecycle tail depending on context outputs from a capability-gated phase (typed_proposal_negotiation.yaml:509). The developer has resolved it:
- finalize_idempotency_key generation moved to the ungated seed_proposals phase (L347-348)
- finalize now targets the ungated seed_proposal_id (L655/L682/L686)
- multi_source_batch's second source now uses the ungated amendment_proposal_id (L900)
The now-orphaned budget_revised_* captures are harmless since context-output lints validate path resolution only. Net improvement over the prior pass.
Decision-table walk: no critical/high findings (row 1 no). gated_paths is false, so row 2 does not apply despite review_decision being CHANGES_REQUESTED — that stale GitHub status predates the fix and row 2 only fires when gated_paths is true. high_risk is false (rows 3, 5 no). No medium findings (rows 4, 8 no). Prior decision was request-changes, not escalate (row 6 no). No no-auto-approve team match (row 7 no). Falls through to row 9 → approve.
Summary
typed_proposal_negotiation.yaml— a 15-phase compliance storyboard exercising the complete AdCP 3.2 typed proposal negotiation lifecycle throughrefine_proposalsindex.yamlCoverage
proposal_refinement.supported_dimensionsgaterevisedoutcome withtotal_budgetconstraint metpartial/unablewithconstraint_unsatisfiableandunsatisfied_constraintsinclude/omitmap checked againstcommercial_terms.purchasesterms_digestvaluesUNSUPPORTED_FEATUREwithunsupported-refinement-dimension.jsondetailsexpires_atinventory holdidempotency_keyreturns cachedreplayed: trueresponseproposal_terms_digest-verified acceptance creating a MediaBuyINVALID_STATErejection for committed proposalsparent_proposal_idequalssource_proposal_idon every successorDesign decisions
proposal_refinement.supported_dimensionspresence — legacy sellers without typed negotiation are not penalizedrequires_capabilitywithcontainsso sellers declaring a subset of dimensions skip inapplicable phasesskip_ifto avoid false failure when the seller does supportflightTest plan
refine_proposalssteps carryaccount { brand, operator }Refs #6559