feat(schemas): package.committed_metrics — frozen reporting contract on create#3510
Merged
Conversation
…on create Adds package.committed_metrics and package.committed_vendor_metrics — frozen snapshots of the product's reporting_capabilities at create_media_buy response time. Closes #3481. Resolves the audit gap flagged on PR #3472: without per-package snapshots, missing_metrics is computed against the product's live state, so a 90-day-old buy is incorrectly judged "clean" when the seller quietly drops a metric they originally committed to. missing_metrics on get_media_buy_delivery now reconciles against committed_metrics when present, falling back to product.available_metrics when absent. Optional in v1 (sellers adopt incrementally; absence is conformant); MUST NOT change post-creation (update_media_buy cannot modify; renegotiation requires a new buy); snapshots full available_metrics, not the intersection with required_metrics. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, audit-gap Three review fixes per expert feedback on #3510: 1. Add minItems: 1 to both committed_metrics and committed_vendor_metrics. Protocol expert flagged that empty array vs absent created semantic ambiguity ("committed to nothing" vs "didn't snapshot"). Schema-level forbid eliminates the ambiguity — absence is the only "no snapshot" signal. 2. Tighten immutability prose: sellers MUST reject update_media_buy attempts to modify these fields with validation_error (suggested code IMMUTABLE_FIELD). Schema-level enforcement still requires a parallel change to package-update.json (filed as follow-up). 3. Acknowledge the fallback as a known audit gap. Both reviewers flagged that "optional with silent fallback" creates plausible deniability — the spec creates a contract that sellers can opt out of without buyers detecting it. Documented explicitly: sellers that omit committed_metrics accept the audit risk; buyers SHOULD treat absence as "no audit-grade contract" rather than "clean delivery." Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 29, 2026
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
…ymmetric audit Reshape package.committed_metrics and by_package.missing_metrics from string arrays into discriminated object arrays covering both standard and vendor-defined metrics. Each entry carries an explicit `scope` discriminator (standard | vendor) and committed_metrics entries carry `committed_at` timestamps so the array doubles as the contract amendment ledger. - Day-1 entries share `committed_at = confirmed_at` - Mid-flight additions appended via update_media_buy with their own committed_at, append-only (sellers MUST reject modify/remove with validation_error) - missing_metrics filters by `committed_at < reporting_period.end` so metrics committed mid-flight are audited only from commitment forward (matches IAB Open Measurement §4.3 precedent) - Standalone committed_vendor_metrics deleted; vendor entries live in the unified array with scope: "vendor" Vendor metric accountability is no longer scoped by metric type — the advisory-vs-accountable distinction now lives at the contract layer. Any metric stamped in committed_metrics is accountable, regardless of scope. Sellers who can't credibly attest to a vendor metric SHOULD NOT stamp it; absence keeps it advisory. Closes #3518 (every entry timestamped, so amendments are just new entries) and #3519 (vendor entries live in unified missing_metrics). Supersedes the parallel-array design from #3510 (merged hours ago, zero GA adopters; cleaner final shape lands before adoption hardens). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 30, 2026
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
Add committed_metrics_supported boolean to media-buy-features.json. Closes the buyer-side detection gap from #3510 — absence of committed_metrics on a buy response was indistinguishable between "seller didn't snapshot this buy" (missing infra) and "seller chose not to snapshot for adversarial reasons." With this flag, buyers can require sellers that commit to populating committed_metrics — and use it as a get_products required_features filter to narrow the catalog accordingly. One flag (not two) because the unified committed_metrics array (per #3576) covers both standard and vendor-defined entries — one snapshot capability, one flag. MUST timing is atomic with the flag — sellers declaring true MUST populate committed_metrics on create_media_buy responses AND honor append-only mid-flight additions on update_media_buy. Advisory-only flags leave the audit gap exploitable. Closes #3517. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 2, 2026
…ymmetric audit Reshape package.committed_metrics and by_package.missing_metrics from string arrays into discriminated object arrays covering both standard and vendor-defined metrics. Each entry carries an explicit `scope` discriminator (standard | vendor) and committed_metrics entries carry `committed_at` timestamps so the array doubles as the contract amendment ledger. - Day-1 entries share `committed_at = confirmed_at` - Mid-flight additions appended via update_media_buy with their own committed_at, append-only (sellers MUST reject modify/remove with validation_error) - missing_metrics filters by `committed_at < reporting_period.end` so metrics committed mid-flight are audited only from commitment forward (matches IAB Open Measurement §4.3 precedent) - Standalone committed_vendor_metrics deleted; vendor entries live in the unified array with scope: "vendor" Vendor metric accountability is no longer scoped by metric type — the advisory-vs-accountable distinction now lives at the contract layer. Any metric stamped in committed_metrics is accountable, regardless of scope. Sellers who can't credibly attest to a vendor metric SHOULD NOT stamp it; absence keeps it advisory. Closes #3518 (every entry timestamped, so amendments are just new entries) and #3519 (vendor entries live in unified missing_metrics). Supersedes the parallel-array design from #3510 (merged hours ago, zero GA adopters; cleaner final shape lands before adoption hardens). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 2, 2026
…ymmetric audit (#3576) * feat(schemas): unify metric accountability — timestamped contract + symmetric audit Reshape package.committed_metrics and by_package.missing_metrics from string arrays into discriminated object arrays covering both standard and vendor-defined metrics. Each entry carries an explicit `scope` discriminator (standard | vendor) and committed_metrics entries carry `committed_at` timestamps so the array doubles as the contract amendment ledger. - Day-1 entries share `committed_at = confirmed_at` - Mid-flight additions appended via update_media_buy with their own committed_at, append-only (sellers MUST reject modify/remove with validation_error) - missing_metrics filters by `committed_at < reporting_period.end` so metrics committed mid-flight are audited only from commitment forward (matches IAB Open Measurement §4.3 precedent) - Standalone committed_vendor_metrics deleted; vendor entries live in the unified array with scope: "vendor" Vendor metric accountability is no longer scoped by metric type — the advisory-vs-accountable distinction now lives at the contract layer. Any metric stamped in committed_metrics is accountable, regardless of scope. Sellers who can't credibly attest to a vendor metric SHOULD NOT stamp it; absence keeps it advisory. Closes #3518 (every entry timestamped, so amendments are just new entries) and #3519 (vendor entries live in unified missing_metrics). Supersedes the parallel-array design from #3510 (merged hours ago, zero GA adopters; cleaner final shape lands before adoption hardens). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * spec(schemas): add measurement-standard qualifier on standard-scope contract entries Adds an optional `qualifier` object on `committed_metrics` and `missing_metrics` standard-scope entries, with a single defined key in v1: `viewability_standard` (mrc | groupm). Without this, a contract entry like `{scope: "standard", metric_id: "viewable_rate"}` is ambiguous — MRC and GroupM viewability are materially different thresholds and not comparable, so the audit channel can't tell whether the seller delivered the standard the buyer expected. The qualifier lets a buyer commit to MRC and have a GroupM-only delivery report flagged as missing the MRC commitment. The qualifier object is closed (`additionalProperties: false`); future qualifiers (completion threshold, reach unit) get added explicitly in subsequent minors rather than via free-form keys. Came out of a field discussion where a partner proposed an `ext`-level viewability rollup at root `aggregated_totals` — the right place to handle standard-disambiguation is the per-package contract entry, not the root aggregate. Root aggregation is a separate question. Wired in: - core/package.json: add optional qualifier to standard-scope item in committed_metrics. Updated description and example. - media-buy/get-media-buy-delivery-response.json: add the same optional qualifier to the standard-scope item in missing_metrics, with a description noting it must match the corresponding committed_metrics qualifier. - docs/media-buy/task-reference/create_media_buy.mdx: document the qualifier in the reporting-contract worked example and how it affects reconciliation. - docs/media-buy/task-reference/get_media_buy_delivery.mdx: update the missing_metrics field doc with the qualifier symmetry. - .changeset/unified-metric-accountability.md: add the measurement-standard-qualifier section. Tests: schemas (7/7), examples (34/34), json-schema (255/255), composed (32/32) all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(schemas): fold #3578 — buyer-proposed committed_metrics on create request Add committed_metrics to package-request.json so buyers can propose the reporting contract on create_media_buy, parallel to how measurement_terms and performance_standards already work. Closes #3578 by folding into #3576's unified-metric-accountability scope rather than shipping separately — keeps the proposal/acceptance/audit surface coherent. Negotiation pattern matches the existing precedent: - Buyer proposes committed_metrics on the package request - Seller accepts → echoes on response with committed_at stamped - Seller rejects → TERMS_REJECTED with explanation of which entries were unworkable - Seller normalizes → echoes a different but compatible list; buyer retries with the normalized terms Request-side entries use the same {scope, metric_id, vendor?} discriminated shape as response-side, MINUS committed_at — that timestamp is stamped by the seller on accept, not proposed by the buyer. Constraints: each scope:standard entry's metric_id MUST be in the product's available_metrics; each scope:vendor entry's (vendor, metric_id) MUST appear in the product's vendor_metrics. Sellers SHOULD reject TERMS_REJECTED when the proposal exceeds product capability. Closes #3578. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(schemas): regenerate index.json after rebase on main Pre-push hook caught version mismatch — main's published version state required regenerating the schema registry. No semantic changes; pure build artifact catchup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
package.committed_metricsandpackage.committed_vendor_metrics— frozen snapshots of the product'sreporting_capabilities.available_metricsandvendor_metricsstamped atcreate_media_buyresponse time. Closes #3481.The audit gap
PR #3472 established that the product's
available_metricsbecomes the binding reporting contract carried into the resulting media buy. That holds only if the product is immutable AND the seller stores a snapshot at buy creation. Neither is guaranteed:available_metricsas their reporting infrastructure evolves)missing_metricsis computed against "what the product currently advertises" — a 90-day-old buy is incorrectly judged "clean" because the seller quietly dropped a metric they originally committed toFlagged on PR #3472 by the product expert as the primary sell-side audit gap.
Three open design questions resolved
Optional or required? Optional in v1. Forcing the snapshot would break existing implementations on first deployment. With a doc note that buyers SHOULD reconcile against
committed_metricswhen present and fall back to product live state when absent, sellers adopt incrementally. Expected to become required at the next major.What snapshots in? The product's full
available_metricsat the moment ofcreate_media_buy. NOT the intersection withrequired_metrics. The product committed to reporting all those metrics; reducing to the intersection would silently drop reporting on metrics the buyer didn't list but the seller has.requested_metrics(onreporting_webhook) remains the buyer's payload-optimization filter — separate concept.Mutation policy? Frozen at creation. MUST NOT change post-creation.
update_media_buycannot modify either field. Renegotiation requires a new buy. Cleanest contract; mutability with audit trail can be added later if real demand emerges.Reconciliation
missing_metricsonget_media_buy_delivery:package.committed_metricsis present → reconcile against the snapshotavailable_metrics(today's behavior)This makes the v1 behavior of #3472 work for sellers without snapshot infrastructure, and gives sellers WITH snapshot infrastructure the ability to honor a frozen contract for the package's lifetime.
Backwards compatibility
Optional and additive. Sellers without snapshot infrastructure see no change. Buyers incrementally upgrade to consume
committed_metricswhen present.Test plan
npm run build:schemas— cleannpm run test:schemas— 7/7npm run test:examples— 34/34npm run test:json-schema— 255/255npm run test:composed— 32/32npm run typecheck— cleanCloses #3481.
🤖 Generated with Claude Code