Skip to content

feat(media-buy): add revenue-share pricing - #6095

Open
bokelley wants to merge 4 commits into
mainfrom
review-issue-5754
Open

feat(media-buy): add revenue-share pricing#6095
bokelley wants to merge 4 commits into
mainfrom
review-issue-5754

Conversation

@bokelley

@bokelley bokelley commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • adds revenue_share as a first-class media pricing model with a decimal commission_rate, explicit attribution source, currency, and human-readable commission basis
  • adds fixed, auction, and contingent pricing structures so buyers can discover revenue-share products without treating every option lacking fixed_price as an auction
  • implements training-agent discovery, media-buy creation without bid_price, delivery reporting, and formula-checked report_usage reconciliation
  • adds commissionable_value to delivery and usage metrics, including cumulative package commission-budget enforcement
  • documents the model and affiliate taxonomy, and adds schema, unit, and end-to-end compliance coverage

Contract

The MVP intentionally supports one rate and one settled billing basis:

spend = round_currency(commissionable_value * commission_rate)

conversion_value remains the gross analytics value. commissionable_value is the settled amount eligible under the agreement after exclusions and returns. Package budget is the maximum payable commission.

Revenue share is a contingent pricing structure. It does not use fixed_price, floor_price, price_guidance, max_bid, or buy-time bid_price. It is channel-independent even though affiliate/content commerce is the motivating use case.

Implementation

  • new pricing-options/revenue-share-option.json and enums/pricing-structure.json schemas
  • additive pricing-model, product-filter, delivery-metric, measurement, and usage-report schema updates
  • event-source validation and contingent-package handling in the training agent
  • exact currency-rounded commission verification, selected-option validation, and cumulative budget checks in report_usage
  • seeded and negotiated pricing support in delivery reporting
  • complete media_buy_seller/revenue_share_pricing compliance storyboard
  • minor changeset; released schema versions remain unchanged until release

Validation

  • node tests/schema-validation.test.cjs — 21/21 passed
  • node scripts/check-registry-completeness.cjs
  • node scripts/audit-oneof.mjs --check
  • tsc --project server/tsconfig.json --noEmit
  • vitest run server/tests/unit/training-agent.test.ts — 534/534 passed
  • node scripts/build-schemas.cjs
  • node scripts/build-compliance.cjs
  • full storyboard tenant matrix — all floors passed (signals, sales, governance, creative, creative-builder, brand)
  • docs navigation and platform-agnostic schema lint
  • git diff --check

The repository-wide snippet runner also encounters its existing local optional-dependency failures (adcp.testing is not installed); none of the changed docs add executable snippets.

Feedback requested

  • decimal commission_rate versus a 0–100 percentage field
  • whether commissionable_value is the right portable billing basis
  • requiring event_source_id
  • hard package-budget enforcement for payable commission
  • excluding contingent pricing from both values of legacy is_fixed_price
  • which multi-rate or post-finalization adjustment cases belong in a future version

Closes #5754

@bokelley
bokelley marked this pull request as ready for review July 30, 2026 12:24
aao-secretariat[bot]
aao-secretariat Bot previously approved these changes Jul 30, 2026

@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: Approve

Approve — adds a single 409-line draft design proposal (specs/revenue-share-pricing.md) for a revenue_share media pricing model.

What I checked:

  • No changes to static/schemas/source/**, docs/reference/**, dist/**, or package.json — no wire/artifact-immutability, schema↔docs coherence, oneOf-discriminator, or changeset-scope rules are triggered. A design note under specs/ requires no changeset.
  • The doc's claims about existing surfaces (pricing_model enum, CPA event_type/custom_event_name/event_source_id, is_fixed_price semantics, available metrics, report_usage vendor_cost, measurement_terms.billing_measurement) were verified accurate by the reviewer.
  • Naming conventions correct (AdCP, AgenticAdvertising.org); fictional companies (Pinnacle, Nova) used appropriately.

Gates: high_risk false, gated_paths false, no no-auto-approve team match, first review. review_decision is REVIEW_REQUIRED but gated_paths is false, so row 2 does not fire.

Only one low-severity precision note (commission_rate range (0,1] vs cited precedent) — omitted as a non-blocking nit. No critical/high/medium findings. Falls through to row 9.

@bokelley
bokelley force-pushed the review-issue-5754 branch from f052e63 to ac51500 Compare July 30, 2026 15:38
@bokelley bokelley changed the title [codex] Draft revenue-share media pricing proposal [codex] Add revenue-share media pricing Jul 30, 2026
}

const pricingContexts = pricingContextsForUsage(session, record);
if (record.pricing_option_id && pricingContexts && !pricingContexts.packagePricingOptionIds.has(record.pricing_option_id)) {

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 guard rejects any usage record that carries both media_buy_id and a pricing_option_id not present in the buy's packages — but pricing_option_id's documented namespace is the vendor's discovery/execution response (get_signals, list_creatives, build_creative), not media-buy package pricing options. The existing creative path validates the same field against creative.pricingOptionId (base L7891), so the two namespaces collide. A creative/signals usage record that also references a media_buy_id in the session would fail here with INVALID_PRICING_OPTION before reaching the creative validation. Today nothing sends media_buy_id (it's new), so no live flow breaks; consider scoping this membership check to revenue-share records (e.g. only when pricingContexts.revenueShares matches, or when no creative_id/signal_agent_segment_id is present).

@aao-secretariat aao-secretariat Bot added the ladon/needs-human-review Ladon has escalated this PR for human review. label Jul 30, 2026

@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 review.

This PR touches static/schemas/source/** (a hard, path-based approval gate) and review_decision is REVIEW_REQUIRED, not APPROVED. Per decision rule row 2, the gate is not satisfied and this cannot auto-approve.

Escalation reasons

  • Modifies/adds gated schema files under static/schemas/source/** (e.g. pricing-option.json, pricing-model.json, new pricing-structure.json enum, new revenue-share-option.json, delivery-metrics.json, get-media-buy-delivery-response.json). Human/CODEOWNERS approval is required before merge.

Medium findings

  • server/src/training-agent/task-handlers.ts:8014 — report_usage package-membership guard treats pricing_option_id as a media-buy-package namespace, colliding with its documented vendor-discovery namespace.

Notes for the reviewer: schema↔docs coherence holds across all seven touched schema files; the changeset is a minor and the additions are wire-additive (new enum value, new optional fields, new discriminated oneOf branch) — Normative, non-breaking, so the breaking-class hard gate does not apply. The one Medium above is worth a look but is not itself blocking. The gate lifts once a human/CODEOWNERS approval flips review_decision to APPROVED.

Medium findings

  • server/src/training-agent/task-handlers.ts:8014 — report_usage package-membership guard collides with vendor pricing_option_id namespace

Why human review

  • Gated paths: PR modifies/adds files under static/schemas/source/** (pricing-option.json, pricing-model.json, pricing-structure.json, revenue-share-option.json, delivery-metrics.json, get-media-buy-delivery-response.json, and others) while review_decision is REVIEW_REQUIRED — human/CODEOWNERS approval required.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/account/report-usage-request.json (modified) matches static/schemas/source/**; static/schemas/source/compliance/comply-test-controller-request.json (modified) matches static/schemas/source/**; static/schemas/source/core/delivery-metrics.json (modified) matches static/schemas/source/**; static/schemas/source/core/measurement-terms.json (modified) matches static/schemas/source/**; static/schemas/source/core/pricing-option.json (modified) matches static/schemas/source/**; static/schemas/source/core/product-filters.json (modified) matches static/schemas/source/**; static/schemas/source/enums/available-metric.json (modified) matches static/schemas/source/**; static/schemas/source/enums/pricing-model.json (modified) matches static/schemas/source/**; static/schemas/source/enums/pricing-structure.json (added) matches static/schemas/source/**; static/schemas/source/index.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/pricing-options/revenue-share-option.json (added) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@bokelley bokelley changed the title [codex] Add revenue-share media pricing feat(media-buy): add revenue-share pricing Jul 30, 2026
{/* The v3 major-version alias resolves to the current v3 schema set. */}
```json
{
"$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/revenue-share-option.json",

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.

MUST FIX: /schemas/v3/ does not resolve to the dev schema set. Per build-schemas.cjs (L21, L200), aliases['v3'] is built only from getAllReleasedVersions() — it points to the latest stable release (currently 3.1.8). revenue-share-option.json is added in this PR under a minor changeset and is not yet released, so /schemas/v3/pricing-options/revenue-share-option.json resolves to 3.1.8/…, where this file does not exist — a dead $schema reference in the published example. The comment this replaced said exactly that: "Update to /schemas/v3/ after release." The new comment ("The v3 major-version alias resolves to the current v3 schema set") is wrong — that is what /schemas/latest/ is for. Revert to /schemas/latest/ until the schema ships in a release.

@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: Request changes

Request changes — 2 blocking finding(s)

The current pass surfaces two high findings from schema↔docs coherence / spec-drift analysis. Applying the decision table top-down, row 1 fires (findings with severity high present), so the outcome is request-changes.

Blocking findings

  • docs/media-buy/advanced-topics/pricing-models.mdx:395 — $schema points at /schemas/v3/revenue-share-option.json, but the released v3 alias (built only from released versions per build-schemas.cjs L21/L200 → 3.1.8) does not contain the unreleased revenue-share-option.json. The reference is broken/drifted.
  • docs/accounts/tasks/report_usage.mdx:14 — request-schema link swapped to /schemas/v3/, which lacks the new reconciliation fields this PR adds. Docs assert a wire shape the released alias does not encode — spec drift.

Both docs edits swap revenue-share/report-usage schema references from /schemas/latest/ to /schemas/v3/ and claim v3 resolves to the current schema set. Since v{major} resolves to the latest stable release (3.1.8), which predates the unreleased schemas/fields in this PR, the references ship drifted. This also reverts a deliberate, correct prior decision to use /schemas/latest/. Fix the references back to the unreleased/latest schema set before merge.

Note: the prior [medium] task-handlers.ts:8014 finding is not in this delta and was not re-raised, so it does not carry forward. gated_paths is also true with review_decision not APPROVED — the schema-source hard approval gate remains unsatisfied and a human/CODEOWNERS approval is required regardless.

Blocking findings

  • docs/media-buy/advanced-topics/pricing-models.mdx:395 — pricing-models.mdx points $schema at /schemas/v3/revenue-share-option.json, which the released v3 alias does not contain (spec drift / broken schema ref)
  • docs/accounts/tasks/report_usage.mdx:14 — report_usage.mdx request-schema link swapped to /schemas/v3/, which lacks the new reconciliation fields this PR adds (docs assert a wire shape v3 does not encode)

@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 and review_decision is CHANGES_REQUESTED.

This is a subsequent pass. Both prior high findings are resolved in this delta: pricing-models.mdx dropped the $schema pointing at the v3 alias that lacked revenue-share-option.json, and report_usage.mdx removed the /schemas/v3/ request-schema link that lacked the new reconciliation fields (both replaced with deferral comments until the fields ship in a stable release). The new task-handlers.ts usesVendorPricingNamespace guard correctly scopes media-buy pricing_option_id validation and revenue-share reconciliation to media-namespace records. No new blocking or Medium findings in the delta.

However, the PR touches gated protocol-source paths under static/schemas/source/** (12 files including new pricing-structure.json and revenue-share-option.json). gated_paths: true AND review_decision: CHANGES_REQUESTED (not APPROVED) → decision-table row 2 forces escalate. The gate is only satisfied by a real human/CODEOWNERS APPROVED review, which is not present. This is also a substantive new-fields/new-enum-value protocol surface change (revenue-share pricing) that warrants human ratification.

Escalation reasons

  • static/schemas/source/** (schema source-of-truth) modified/added while review_decision is CHANGES_REQUESTED — hard approval gate not satisfied; human/CODEOWNERS review required.

No blocking findings remain from the reviewer; the escalation is purely the unsatisfied gated-paths gate.

Why human review

  • Touches gated protocol-source paths under static/schemas/source/** (12 files, incl. new pricing-structure.json and revenue-share-option.json) while review_decision is CHANGES_REQUESTED — hard approval gate not satisfied; requires human/CODEOWNERS APPROVED review before merge.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/account/report-usage-request.json (modified) matches static/schemas/source/**; static/schemas/source/compliance/comply-test-controller-request.json (modified) matches static/schemas/source/**; static/schemas/source/core/delivery-metrics.json (modified) matches static/schemas/source/**; static/schemas/source/core/measurement-terms.json (modified) matches static/schemas/source/**; static/schemas/source/core/pricing-option.json (modified) matches static/schemas/source/**; static/schemas/source/core/product-filters.json (modified) matches static/schemas/source/**; static/schemas/source/enums/available-metric.json (modified) matches static/schemas/source/**; static/schemas/source/enums/pricing-model.json (modified) matches static/schemas/source/**; static/schemas/source/enums/pricing-structure.json (added) matches static/schemas/source/**; static/schemas/source/index.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/pricing-options/revenue-share-option.json (added) matches static/schemas/source/**) and the current GitHub review decision is 'CHANGES_REQUESTED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

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.

Discussion: affiliate channel has no matching pricing model — should AdCP add revenue_share?

1 participant