Skip to content

feat(media-buy): add shared budgets and bidding policy - #6088

Open
bokelley wants to merge 3 commits into
mainfrom
flexible-package-budgets
Open

feat(media-buy): add shared budgets and bidding policy#6088
bokelley wants to merge 3 commits into
mainfrom
flexible-package-budgets

Conversation

@bokelley

@bokelley bokelley commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a discriminated BudgetAllocation model with backward-compatible fixed allocation and seller-optimized shared budgets
  • add a reusable BiddingPolicy at media-buy and package scope for manual bids, auction ceilings, average-cost controls, and ROAS controls
  • define aggregate/package pacing, complete-block bidding inheritance, authored-scope-preserving readback, atomic placement validation, update clearing, and deterministic legacy migration
  • add focused schema tests and capability-gated conformance scenarios for shared budgets and bidding-policy placement

Motivation

AdCP 3.1 requires explicit package budgets and splits buyer price intent across package bid_price, pricing-option max_bid, and monetary optimization_goals targets. Platforms that optimize one campaign budget and strategy across multiple ad sets or line items cannot express that behavior without vendor extensions.

This gives 3.2 one portable placement story: objective functions stay in optimization_goals; budget allocation, pacing, and bidding execution policy live at the scope where they are authored.

Budget semantics

  • total_budget is the hard aggregate lifetime cap
  • package budget remains a hard cap, never a current allocation
  • min_spend_target is soft
  • media-buy pacing governs aggregate spend; package pacing is subordinate
  • allocation goals operate across packages; package goals operate within a package
  • continuous seller allocation is delivery behavior and does not mutate revision/history
  • seller-optimized proposals use minimum/maximum percentages rather than exact allocation percentages

Bidding semantics

  • media-buy bidding is a complete inherited default; package bidding is a complete override, never a field merge
  • bid_amount is a manual auction bid; max_bid is the only hard per-auction ceiling
  • cost_per distinguishes average cap from target; roas distinguishes floor from target
  • cost_per resolves against the primary optimization goal, while bid_amount/max_bid resolve against the selected pricing option's auction unit
  • shared-budget campaign policy is authoritative where the provider requires campaign placement; unsupported overrides fail atomically with BIDDING_PLACEMENT_CONFLICT
  • canonical and legacy policy cannot coexist on one effective package; mixed input fails with AMBIGUOUS_BIDDING_POLICY
  • pricing-option max_bid, package bid_price, and monetary goal targets are deprecated with deterministic normalization rules

Validation

  • schema and compliance builds
  • 650-schema structural/reference/example validation
  • 131 composed-schema tests
  • documentation JSON validation: 284/284 annotated examples
  • storyboard sample request/response lint
  • oneOf audit, immutable-release, platform-agnostic, diff, and changeset checks
  • dynamic-import/state-change lint and TypeScript typecheck
  • aggregate unit run exercised 4,640 tests; two unrelated suite-order failures passed in isolation (23/23 and 5/5)

Notes

Daily/hourly caps remain out of scope; the new budget is a lifetime media-buy total. The bidding block represents numeric portfolio policy but not shared provider portfolio identity or lifecycle across media buys; that requires a future provider-neutral resource/reference.

@bokelley
bokelley marked this pull request as ready for review July 30, 2026 10:20
| `package_id` | Seller's package identifier |
| `product_id` | Product identifier this package is purchased from. For packages created from an explicit `create_media_buy` package request, sellers MUST echo the request package's `product_id` on every response package object that represents that requested package. |
| `budget` | Hard lifetime package spend cap. In seller-optimized mode this is not a current allocation and may be absent. |
| `min_spend_target` | Soft lifetime package spend target accepted for seller-optimized allocation. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: This PR adds packages[].pacing to get-media-buys-response.json (base carried only pacing_index), but the package response table here documents only budget and min_spend_target. The read surface now echoes a package-level pacing field the docs don't describe — schema↔docs drift on the authoritative read surface. Add a pacing row noting it is subordinate to media-buy aggregate pacing.

@@ -45,7 +45,12 @@
},
"budget": {
"type": "number",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: budget is dropped from this schema's required (L221). create-media-buy-request.json's new anyOf re-requires it for fixed-mode explicit packages, but update-media-buy-request.json's new_packages[] $refs this same shape with no equivalent guard. An add_packages update against a fixed-allocation buy can now omit budget and pass schema validation where 3.1 required it — the fixed-mode budget invariant is enforced only on the create path, not on mid-flight package additions.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Escalate to human review

Escalate — gated schema paths require human/CODEOWNERS approval.

This PR modifies published protocol source under static/schemas/source/** (a deterministic hard approval gate) and review_decision is REVIEW_REQUIRED, not APPROVED. Per decision rule row 2, the outcome must be escalate until a real human/CODEOWNERS approval lifts the gate.

No blocking (critical/high) findings were reported. The reviewer classified the change as Normative (non-breaking): additive BudgetAllocation discriminated model, aggregate budget/pacing, package caps, a new capability flag, and a new update_budget_allocation action, with wire changes that are additive or relaxations, a correct minor changeset, and a properly discriminated oneOf. Two Medium coherence/validation gaps remain (see below) but do not on their own change the outcome — the gated-paths gate is the controlling trigger.

Gated files requiring human/CODEOWNERS review include: static/schemas/source/core/budget-allocation.json (added) and the modified media-buy.json, package.json, proposal.json, planned-delivery.json, product-allocation.json, optimization-goal.json, media-buy-features.json, index.json, plus the media-buy request/response schemas.

Medium findings for the human reviewer to weigh:

  • docs/media-buy/task-reference/get_media_buys.mdx:84 — packages[].pacing added to response but undocumented in the package table (schema↔docs coherence gap).
  • static/schemas/source/media-buy/package-request.json:47 — budget no longer required on new_packages[] for fixed-allocation buys.

Once a CODEOWNERS/human approval sets review_decision to APPROVED, this can fall through to the normal table (no blocking findings present).

Medium findings

  • docs/media-buy/task-reference/get_media_buys.mdx:84 — packages[].pacing added to get-media-buys-response but undocumented in get_media_buys package table
  • static/schemas/source/media-buy/package-request.json:47 — budget no longer required on new_packages[] for fixed-allocation buys

Why human review

  • Touches gated protocol source under static/schemas/source/** (e.g. budget-allocation.json, media-buy.json, proposal.json, package.json) — hard approval gate; review_decision is REVIEW_REQUIRED, not APPROVED. Human/CODEOWNERS review required.
  • Two medium schema↔docs coherence/validation gaps to verify: get_media_buys.mdx:84 (packages[].pacing undocumented) and package-request.json:47 (budget no longer required on new_packages[] for fixed allocation).
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/budget-allocation.json (added) matches static/schemas/source/**; static/schemas/source/core/media-buy-features.json (modified) matches static/schemas/source/**; static/schemas/source/core/media-buy.json (modified) matches static/schemas/source/**; static/schemas/source/core/optimization-goal.json (modified) matches static/schemas/source/**; static/schemas/source/core/package.json (modified) matches static/schemas/source/**; static/schemas/source/core/planned-delivery.json (modified) matches static/schemas/source/**; static/schemas/source/core/product-allocation.json (modified) matches static/schemas/source/**; static/schemas/source/core/proposal.json (modified) matches static/schemas/source/**; static/schemas/source/enums/media-buy-valid-action.json (modified) matches static/schemas/source/**; static/schemas/source/index.json (modified) matches static/schemas/source/**; static/schemas/source/media-buy/create-media-buy-request.json (modified) matches static/schemas/source/**; static/schemas/source/media-buy/create-media-buy-response.json (modified) matches static/schemas/source/**; static/schemas/source/media-buy/get-media-buy-delivery-response.json (modified) matches static/schemas/source/**; static/schemas/source/media-buy/get-media-buys-response.json (modified) matches static/schemas/source/**; static/sc

…(truncated)…

@aao-secretariat aao-secretariat Bot added the ladon/needs-human-review Ladon has escalated this PR for human review. label Jul 30, 2026
@bokelley bokelley changed the title feat(media-buy): add seller-optimized package budgets feat(media-buy): add shared budgets and bidding policy Jul 30, 2026
@aao-secretariat

Copy link
Copy Markdown
Contributor

Ladon cannot review this PR until merge conflicts are resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ladon/needs-human-review Ladon has escalated this PR for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant