diff --git a/.changeset/measurement-audit-cleanups.md b/.changeset/measurement-audit-cleanups.md new file mode 100644 index 0000000000..f792cbd621 --- /dev/null +++ b/.changeset/measurement-audit-cleanups.md @@ -0,0 +1,18 @@ +--- +"adcontextprotocol": minor +--- + +Three small cleanups from the measurement schema audit (closes audit findings §3.8 and §3.10; finishes the prose-side work for #3863). + +**§3.8 — `attribution-window` dedup.** `optimization-goal.json` previously inlined a partial `attribution_window` shape with `post_click` and `post_view` but no `model`, with `post_click` required. The canonical `core/attribution-window.json` has `post_click`, `post_view`, and `model` with `model` required. Two surfaces describing the same concept with conflicting constraints. Fix: + +- `optimization-goal.json` `attribution_window` collapses to `$ref attribution-window.json` so there's one canonical shape. +- `attribution-window.json` `model` becomes optional (was required). Absence means the seller's default attribution model applies (typically `last_touch` per industry convention). Sellers SHOULD populate `model` when committing to a specific methodology. Buyers reading delivery reports get the seller's choice when set; fall back to default when not. + +**§3.10 — `dooh_metrics.calculation_notes` description tightening.** Previously a one-liner ("Explanation of how DOOH impressions were calculated") that read like a primary methodology surface. Tightened to clarify it's for **row-specific supplementary context** (a particular daypart's calculation, a venue-mix exception) — the canonical methodology declaration belongs on the measurement vendor's `get_adcp_capabilities.measurement.metrics[]` block where it's discoverable once and inherited across delivery rows. Doesn't deprecate the field — DOOH methodology genuinely has row-level exceptions worth carrying inline. + +**#3863 — `forecastable-metric.json` description drift fix.** The description previously claimed `audience_size`, `measured_impressions`, `grps`, `reach`, `frequency` were forecast-only deltas. **Wrong:** `grps`, `reach`, `frequency` are also in `available-metric.json` (have been since their introduction). The actual forecast-only deltas are `audience_size` and `measured_impressions`. Description corrected. Closes the prose-cross-reference half of #3863; the schema-level enforcement of overlap (build-script work, not schema work) is deferred. + +**Backwards compatibility.** All three changes are additive or relax existing constraints (the `attribution-window.model` requirement relaxation makes previously-failing payloads valid; previously-valid payloads remain valid). No breaking changes. + +Closes audit findings §3.8 and §3.10. Substantially closes #3863 (prose cross-references); build-script overlap enforcement deferred to a follow-up. diff --git a/static/schemas/source/core/attribution-window.json b/static/schemas/source/core/attribution-window.json index 0ab5dc6780..df3446bc8f 100644 --- a/static/schemas/source/core/attribution-window.json +++ b/static/schemas/source/core/attribution-window.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/core/attribution-window.json", "title": "Attribution Window", - "description": "Describes the attribution methodology and lookback windows used for conversion measurement. Enables cross-platform comparison by making attribution methodology transparent.", + "description": "Describes the attribution methodology and lookback windows used for conversion measurement. Enables cross-platform comparison by making attribution methodology transparent. Used as a `$ref` from `optimization-goal.json` (buyer's optimization-time attribution choice), `get-media-buy-delivery-response.json` (seller-declared attribution methodology in delivery reports), and similar surfaces. All fields are optional individually but at least one of `post_click`, `post_view`, or `model` SHOULD be populated; absence of `model` means the seller's default attribution model applies (typically `last_touch` per industry convention) — sellers SHOULD populate `model` explicitly when committing to a specific methodology.", "type": "object", "properties": { "post_click": { @@ -15,9 +15,8 @@ }, "model": { "$ref": "/schemas/enums/attribution-model.json", - "description": "Attribution model used to assign credit when multiple touchpoints exist" + "description": "Attribution model used to assign credit when multiple touchpoints exist. SHOULD be populated when committing to a specific model; when absent, the seller's default applies." } }, - "required": ["model"], "additionalProperties": true } diff --git a/static/schemas/source/core/delivery-metrics.json b/static/schemas/source/core/delivery-metrics.json index 24325ec2e9..1dae336345 100644 --- a/static/schemas/source/core/delivery-metrics.json +++ b/static/schemas/source/core/delivery-metrics.json @@ -207,7 +207,7 @@ }, "calculation_notes": { "type": "string", - "description": "Explanation of how DOOH impressions were calculated" + "description": "Per-row supplementary methodology notes for DOOH impression calculation (e.g., 'rotation-based; 6-second slot weighted by 70% audience overlap'). Free-form prose for context that doesn't fit the structured measurement-vendor surface. Canonical methodology declarations belong on the measurement vendor's `get_adcp_capabilities.measurement.metrics[]` block where they're discoverable once and inherited across delivery rows; this field is for row-specific context (a particular daypart's calculation, a venue-mix exception) rather than the seller's general methodology." }, "venue_breakdown": { "type": "array", diff --git a/static/schemas/source/core/optimization-goal.json b/static/schemas/source/core/optimization-goal.json index a36350040e..2e12782d01 100644 --- a/static/schemas/source/core/optimization-goal.json +++ b/static/schemas/source/core/optimization-goal.json @@ -157,20 +157,8 @@ ] }, "attribution_window": { - "type": "object", - "description": "Attribution window for this optimization goal. Values must match an option declared in the seller's conversion_tracking.attribution_windows capability. Sellers must reject windows not in their declared capabilities. When omitted, the seller uses their default window.", - "properties": { - "post_click": { - "allOf": [{ "$ref": "/schemas/core/duration.json" }], - "description": "Post-click attribution window. Conversions within this duration after a click are attributed to the ad (e.g. {\"interval\": 7, \"unit\": \"days\"})." - }, - "post_view": { - "allOf": [{ "$ref": "/schemas/core/duration.json" }], - "description": "Post-view attribution window. Conversions within this duration after an ad impression (without click) are attributed to the ad (e.g. {\"interval\": 1, \"unit\": \"days\"})." - } - }, - "required": ["post_click"], - "additionalProperties": true + "allOf": [{ "$ref": "/schemas/core/attribution-window.json" }], + "description": "Attribution window for this optimization goal — references the canonical `attribution-window` shape (post_click, post_view, model). Values must match an option declared in the seller's `conversion_tracking.attribution_windows` capability. Sellers MUST reject windows not in their declared capabilities. When the entire field is omitted, the seller uses their default window." }, "priority": { "type": "integer", diff --git a/static/schemas/source/enums/forecastable-metric.json b/static/schemas/source/enums/forecastable-metric.json index f7205c90b8..c8a2c471a1 100644 --- a/static/schemas/source/enums/forecastable-metric.json +++ b/static/schemas/source/enums/forecastable-metric.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/enums/forecastable-metric.json", "title": "Forecastable Metric", - "description": "Standard delivery and engagement metric names for forecasts. Most values mirror `available-metric.json` (the canonical closed delivery vocabulary) — `impressions`, `clicks`, `spend`, `views`, `completed_views`, `engagements`, `follows`, `saves`, `profile_visits`, `downloads`, `plays` are shared. Forecast-only deltas — `audience_size`, `measured_impressions`, `grps`, `reach`, `frequency` — exist here because forecasts can reasonably project them but delivery reporting (and `available-metric.json`) uses different paths (e.g., reach/frequency are top-level fields on `aggregated_totals`, not entries in `available-metric`). When the industry converges on adding `audience_size` or `measured_impressions` to delivery reporting, those values graduate into `available-metric.json` and this enum's deltas shrink. For outcome/conversion forecasts (purchases, leads, app installs, etc.), use event-type enum values as metric keys instead. The ForecastPoint metrics map accepts any string key, so both forecastable-metric and event-type values can be used together.", + "description": "Standard delivery and engagement metric names for forecasts. **Most values mirror `available-metric.json`** (the canonical closed delivery vocabulary) — `impressions`, `clicks`, `spend`, `views`, `completed_views`, `engagements`, `follows`, `saves`, `profile_visits`, `downloads`, `plays`, `reach`, `frequency`, `grps` are all shared. **Forecast-only deltas** are just `audience_size` and `measured_impressions` — these exist here because forecasts can reasonably project them but delivery reporting today does not surface them as `available-metric` entries (delivery uses other paths: e.g., `measured_impressions` flows through `vendor_metric_values` when a third-party vendor reports it). When the industry converges on adding either to delivery reporting, those values graduate into `available-metric.json` and this enum's deltas shrink. For outcome/conversion forecasts (purchases, leads, app installs, etc.), use event-type enum values as metric keys instead. The ForecastPoint metrics map accepts any string key, so both forecastable-metric and event-type values can be used together.", "type": "string", "enum": [ "audience_size",