Skip to content

feat(schemas): metric_aggregates partition — row-symmetric delivery aggregation (closes #3848)#3876

Merged
bokelley merged 2 commits into
mainfrom
bokelley/metric-aggregates
May 2, 2026
Merged

feat(schemas): metric_aggregates partition — row-symmetric delivery aggregation (closes #3848)#3876
bokelley merged 2 commits into
mainfrom
bokelley/metric-aggregates

Conversation

@bokelley

@bokelley bokelley commented May 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #3848 (and supersedes the already-closed #3631 / #3833). Adds aggregated_totals.metric_aggregates to get_media_buy_delivery — a qualifier-aware delivery rollup row-symmetric with package.committed_metrics and by_package[].missing_metrics.

The atomic unit is now identical across contract, diff, and delivery:

committed_metrics row : (scope, metric_id, qualifier, committed_at)
missing_metrics row   : (scope, metric_id, qualifier)
metric_aggregates row : (scope, metric_id, qualifier, value, ...components)

Reconciliation collapses to a row-level join on (scope, metric_id, qualifier).

Why

The taxonomy doc's "atomic unit" claim becomes load-bearing in code. Today, contract and diff use the row shape; delivery aggregation uses bespoke per-metric scalars. With this PR, all three surfaces share one shape — buyer reconciliation code becomes one path instead of three.

Solves the apples-to-oranges sum problem at the aggregate layer: MRC and GroupM viewability never combine into a single rate. Future qualifier-aware metrics (completion threshold; attention methodology if it standardizes) plug in with no schema change.

Design decisions (per #3848)

  • Component fields inlined as siblings of value, matching the per-buy viewability block's flat shape.
  • Granularity rule: one row per (metric_id, full-qualifier-set) at the finest available granularity. Buyers re-aggregate up.
  • Closed contract vocabulary, open delivery vocabulary: delivery qualifier is a deliberate superset reserving space for transparency disclosures buyers don't commit to (e.g., tracker_firing pending Tracker firing path (client / server / mixed) — is this a buyer concern AdCP needs to model, and where? #3832).
  • Unqualified metrics stay top-level (impressions, spend, media_buy_count); metric_aggregates only for metrics with non-empty qualifier sets.
  • Per-buy totals stays flat — each individual buy is single-qualifier by definition; only the aggregate spans qualifiers.
  • oneOf discriminated on metric_id deferred — current schema does not enforce per-metric required components in JSON Schema (would be 31+ branches). Required fields documented in the description; future minor can tighten via oneOf if conformance testing demands.

Files

  • static/schemas/source/media-buy/get-media-buy-delivery-response.json — adds metric_aggregates array
  • static/schemas/source/core/package.jsoncommitted_metrics description cross-links the new surface
  • docs/media-buy/task-reference/get_media_buy_delivery.mdx — adds "Aggregated metric partitions" section
  • .changeset/metric-aggregates.md — minor

Test plan

  • Schema validates (build:schemas during commit hook)
  • TypeScript build passes
  • Doc renders cleanly

Related

🤖 Generated with Claude Code

bokelley and others added 2 commits May 2, 2026 15:10
…-symmetric with committed_metrics

Closes #3848. Supersedes #3631 and #3833 (already closed).

The atomic unit is now identical across contract, diff, and delivery:
  committed_metrics row : (scope, metric_id, qualifier, committed_at)
  missing_metrics row   : (scope, metric_id, qualifier)
  metric_aggregates row : (scope, metric_id, qualifier, value, ...components)

Reconciliation collapses to a row-level join on (scope, metric_id,
qualifier). For each committed_metrics row, find the matching
metric_aggregates row; absent matches surface as missing_metrics.

Schema:
- get-media-buy-delivery-response.json: aggregated_totals.metric_aggregates
  is a discriminated array (scope: standard | vendor) with the qualifier
  shape from committed_metrics, value, and inlined component fields
  (measurable_impressions, viewable_impressions, impressions,
  completed_views, spend, conversions, conversion_value, clicks).
- core/package.json: committed_metrics description cross-links the new
  surface and articulates the row-symmetric model.

Granularity: one row per (metric_id, full-qualifier-set) at finest
available granularity; buyers re-aggregate up.

Qualifier vocabulary: closed at contract layer (viewability_standard
only); deliberate superset at delivery layer for transparency disclosures
buyers don't commit to (e.g., tracker_firing pending #3832).

Unqualified metrics stay top-level (impressions, spend, media_buy_count).
Per-buy totals stays flat — single-qualifier by definition; only the
cross-buy aggregate spans qualifiers.

Doc updates: get_media_buy_delivery.mdx adds an Aggregated metric
partitions section explaining the reconciliation join, granularity rule,
qualifier-vocabulary asymmetry, per-buy / aggregate divergence, and
value-typing dispatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three blockers from protocol-expert review on PR #3876, two doc
tightenings from product-expert review:

1. additionalProperties drift (high confidence): committed_metrics items
   use additionalProperties: false; metric_aggregates items used true.
   Real divergence beyond the documented value+components delta.
   Tightened to false on both branches for full row symmetry.

2. Per-metric component gap (high confidence): required: [scope,
   metric_id, value] admitted viewable_rate: 0.7 with no
   measurable_impressions/viewable_impressions. Added if/then
   enforcement for the four highest-traffic metrics (viewable_rate,
   completion_rate, cost_per_acquisition, roas). Catches 90% of misuse
   without combinatorial explosion. Other metrics rely on prose-
   described components; full oneOf-on-metric_id deferred.

3. "Superset" claim was misleading (high confidence): both surfaces are
   additionalProperties: false today with identical content. Reframed
   as "closed today, expected to diverge in future minors as
   transparency disclosures ship delivery-only" — describes future
   divergence, not current state.

4. Top-level / metric_aggregates mutual exclusion needed explicit MUST
   (product expert): for any metric_id appearing in metric_aggregates,
   the top-level scalar MUST be omitted (not zeroed). Sellers MUST NOT
   emit both. Added to schema description and doc.

5. Qualifier-set drift across reports needed a doc sentence (product
   expert): when a campaign gains a new qualifier mid-flight, prior
   periods' rows remain valid at their original granularity; buyers
   SHOULD NOT retroactively repartition. Added to schema description
   and doc.

Also softened the "solves apples-to-oranges" framing in the changeset
per product expert: this PR ships the structure; sellers actually
emitting partitioned rows requires a forcing function from the contract
surface plus seller adoption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant