diff --git a/.changeset/4616-strawman-system-reference-d1.md b/.changeset/4616-strawman-system-reference-d1.md new file mode 100644 index 0000000000..8c5e0aeb3e --- /dev/null +++ b/.changeset/4616-strawman-system-reference-d1.md @@ -0,0 +1,38 @@ +--- +"adcontextprotocol": minor +--- + +Add `system-reference` primitive + `system-reference-fidelity` enum + `system-reference-conversion` schema — strawman for decision D1 of the signal-AND-measurement epistemic-model umbrella (#4616). + +Adds three reusable schema primitives that future row-level RFCs can adopt without re-litigating the shape per-RFC. After @bokelley's 2026-05-17 third-sibling framing (#4616 issuecomment-4470049814), D1 covers BOTH the signals track (#4472 PBA audience_model, @lszczesiak's pending ID-graph RFC) AND the measurement track (#2041 measurement source attribution, with #3885 / #3652 / #3877 as already-shipped publishing/authorization-half prior art) AND the product-level audience-construction-metadata row surfaced by @tescoboy on 2026-05-19. + +**Per @bokelley's 2026-05-24 WG-acceptance comment (issuecomment-4526559566):** D1 is scoped to newly-modeled rows ONLY — it does NOT replace per-dimension schemas that already work (`geo_metros`, `geo_postal_areas`). The primitive shape alone does NOT create interoperability — consuming row-level schemas MUST constrain or document which `system` values are meaningful for that row. + +**Round 5 normative tightening per @bokelley:** + +1. **Version semantics REVERSED:** omitted `version` now means UNKNOWN / unpinned, NOT a wildcard. Exact equality requires `(system, value, version)` to all match for versioned systems. Row-level schemas MAY declare a system version-insensitive; otherwise omitted version is a buyer-decision point. +2. **`converted` fidelity tightened:** reserved for deterministic AND row-semantics-preserving mappings. Deterministic ≠ lossless preservation; sellers MUST NOT advertise `converted` when the conversion changes the row's meaningful semantics even if per-record mapping is deterministic. +3. **`upscaled` and `crosswalk` cautions:** `upscaled` should typically pair with `approximated` fidelity (undefined inverse → lost granularity); only `converted` if the row explicitly says granularity does not matter. Same caution for `crosswalk` — deterministic mapping is not automatically lossless semantic preservation. +4. **Interop caveat:** explicit note in `system-reference.json` description that the primitive alone doesn't create interop; row-level schemas MUST constrain or document recognized systems. + +**Round 6 — ads.txt-pattern anchor added per the @bokelley / @lukasz-pubx / @SimonaNemes / Addie thread on #4616:** + +5. **`method_doc_url?`** added to `system-reference-conversion.json` — optional URI pointing at an attested methodology document (vendor's identity-graph methodology page, published crosswalk specification, etc.). Picks up Addie's "collapse to a pointer" recommendation at the primitive layer so downstream row-level RFCs have a canonical place to anchor link-out fields. Strictly informational on the wire; buyer agents MAY follow the link out-of-band to verify but MUST NOT branch on its content programmatically. Description notes that consuming row-level schemas MAY require this field in their row's binding if methodology disclosure matters for the row. + +Plus a description-only note that consuming row-level schemas adopting this primitive MAY add their own row-level `last_updated` field on the row itself to surface signal-record freshness (which IS verifiable, even if underlying methodology freshness isn't — per Simona's framing). + +**Round 7 — value-prop sharpening + single-party scope clarification per @bokelley's 2026-05-25 line reviews on PR #4622:** + +6. **`system-reference.json` description** rewritten to lead with the **union-axis value proposition**: the primitive earns its keep on rows where a single field can carry any of several external systems with the same comparator semantics (identity substrate, measurement source, PBA taxonomy). For single-axis rows where only one system applies, inline per-dimension fields remain simpler — the primitive is overhead. Explicitly does NOT replace working per-dimension schemas like `geo_metros` / `geo_postal_areas` / existing `ramp_id`. Per @bokelley's "RampID is defined elsewhere" comment: yes, and that's fine — the primitive's value is in giving union-axis rows ONE comparator vs. N parallel inline shapes, not in wrapping single-system rows that already have a shape. + +7. **`system-reference-conversion.json` description** rewritten to clarify **single-party observable scope** per @bokelley's comment that real programmatic chains have multiple hops (publisher / SSP / DSP / agency / vendor) and no single party observes the full chain. The structure describes ONE party's observable conversion (signals seller's in-agent translation, measurement vendor's projection), NOT the multi-hop chain. Downstream conversions are out of scope, observed by other parties; the protocol intentionally doesn't pretend the seller can speak to them. + +Plus naming note in the primitive description acknowledging @lukasz-pubx's `system → type` suggestion but keeping `system` — the connotation of "named external reference frame with its own lifecycle" is what we want vs. `type` which is overloaded across AdCP for general type discrimination on discriminated unions. + +- `core/system-reference.json` — the canonical `{system, value, version?, name?}` shape for a value defined against an external identity, taxonomy, geographic, or measurement system. `system` is intentionally an open string at the primitive level; per-use constraints live in consuming schemas. Field named `value` (not `id`) because the primitive is cross-axis: identity systems issue IDs, taxonomies issue values/terms, measurement systems issue methodology labels. +- `enums/system-reference-fidelity.json` — `exact | converted | approximated | unsupported` for deployment-side fidelity. `converted` covers the case where the destination uses a different system but the conversion is deterministic and lossless (e.g. Nielsen DMA → Comscore Market via crosswalk, UID2 → ID5 via identity graph). Generalizes #4475's `market_fidelity` mechanism to all reference-system axes. +- `core/system-reference-conversion.json` — `{from, to, method, method_provider?, method_details?}` structure describing how a deployment converts between systems. REQUIRED when fidelity is `converted`, OPTIONAL when `approximated`. `method` enum covers `id_graph | name_match | crosswalk | upscaled | inferred | projected | custom`. **`inferred` vs `projected` sharpening per @SimonaNemes (#4616 issuecomment-4493606229):** `inferred` is **entity-level attribution** ("given clues, who/what is this entity?" — uncertainty in per-record correctness); `projected` is **population-level estimation** ("given this sample, what should we expect at scale?" — uncertainty in the estimate, not individuals). Same underlying data can drive both; the distinction is the level at which uncertainty operates. `method_provider?` surfaces vendor identity (e.g. `LiveRamp`, `ID5`, `IAB`) as an opaque-by-convention string so buyer agents can branch on well-known providers without parsing free-text. + +**Non-normative on its own.** None of the primitives is referenced by any existing schema in this PR. Adoption happens row-by-row in the follow-up RFCs (#4472 / #4475 / identity-substrate) against whatever D1 shape the WG settles on. If the WG counter-proposes a different shape, this PR is three files + one changeset — close and re-draft. No sunk cost. + +Discussion: see https://github.com/adcontextprotocol/adcp/issues/4616 diff --git a/static/schemas/source/core/system-reference-conversion.json b/static/schemas/source/core/system-reference-conversion.json new file mode 100644 index 0000000000..5aaf452157 --- /dev/null +++ b/static/schemas/source/core/system-reference-conversion.json @@ -0,0 +1,127 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/core/system-reference-conversion.json", + "title": "System Reference Conversion", + "description": "Describes **one party's observable conversion claim** — typically the signals seller's view (in signal deployments) or the measurement vendor's view (in delivery reports). Per @bokelley's #4616 review: real programmatic chains have multiple parties (publisher / SSP / DSP / agency / vendor) each potentially performing their own identity-graph, taxonomy, or geographic conversions; no single party observes the full chain. This structure does NOT model the multi-hop conversion chain — it models ONE party's observable hop, the one that party can describe truthfully. Buyer agents reading this should understand they're seeing one party's conversion claim, not the full provenance from origin to activation. Downstream conversions (DSP graph, SSP graph, agency graph) are out of scope; they happen, they're real, and the protocol intentionally does not pretend the seller can speak to them.\n\nUsed by two surfaces: (1) signal deployments — when the **signals seller** remaps a signal's market / identity / taxonomy / measurement reference inside its own agent before activation; (2) delivery measurement — when the **measurement vendor** projects, infers, or upscales from one methodology source to another (e.g. set-top-box panel → P18-49 currency). REQUIRED when the surrounding fidelity is `converted`; OPTIONAL surfacing when fidelity is `approximated` (lets consumers see WHY a deployment or report is approximated, not just that it is). Lets buyer agents and measurement consumers reason about source, destination, and conversion method without parsing free-text.\n\nThe conversion claim itself (`from` system → `to` system, by `method`) is self-reported by the seller / measurement vendor; third-party verification of the claim is a future attestation concern (decision D3 in #4616; see #3877 `completion_source` for shipped prior art on the seller-attested vs vendor-attested split). **Per the #4616 thread consensus (@bokelley / @lukasz-pubx / @SimonaNemes / Addie):** the protocol carries structured-where-verifiable fields (source / destination / method) and link-out fields (`method_doc_url`) for claims that need external attestation; consuming row-level schemas that adopt this primitive MAY add their own row-level `last_updated` field on the row itself to surface signal-record freshness (which IS verifiable: the seller published this record on this date, even if the underlying methodology freshness isn't).", + "type": "object", + "properties": { + "from": { + "$ref": "/schemas/core/system-reference.json", + "description": "The signal's original reference system as defined by the seller. Identifies what the seller advertises in the signal definition before conversion." + }, + "to": { + "$ref": "/schemas/core/system-reference.json", + "description": "The system the destination natively uses. After conversion, the activated value lives in this system." + }, + "method": { + "type": "string", + "enum": [ + "id_graph", + "name_match", + "crosswalk", + "upscaled", + "inferred", + "projected", + "custom" + ], + "enumDescriptions": { + "id_graph": "Translation via an identity-graph link (e.g. UID2 → ID5 via LiveRamp, TTD, or similar graph provider). Lossless when the graph link is direct; buyer should inspect graph coverage for edge cases (universe shrinkage, link freshness, regional gaps). Vendor SHOULD be surfaced in `method_provider`.", + "name_match": "Match based on a shared external identifier present in both systems (e.g. hashed email common to both sides). Match rate is finite — buyer-decision point on accepted match-rate floor.", + "crosswalk": "Published mapping between two reference systems (e.g. Nielsen DMA → Comscore Market via the canonical county crosswalk; IAB Audience Taxonomy v2 → v3 via the published migration mapping). Mapping may be 1:1 or many-to-one; sellers SHOULD specify the granularity in `method_details`. **A deterministic crosswalk is NOT automatically lossless semantic preservation:** even when the per-record mapping is deterministic, the source and destination systems may define their values differently in ways that matter for the row's downstream consumers (e.g. DMA vs Comscore Market boundaries share counties but apply different methodologies; IAB v2 → v3 may merge nodes that downstream consumers treated as distinct). Sellers SHOULD pair `crosswalk` with `converted` fidelity only when the row's use case is unaffected by these semantic differences; otherwise pair with `approximated`.", + "upscaled": "Aggregation of finer-grained units into a coarser system (e.g. zip codes → DMA, counties → state, postcodes → MSA). Deterministic forward — every fine-grained unit is fully accounted for in the coarser unit — but **the inverse is undefined**: the upscaled value cannot recover the original granularity. **The deployment's `fidelity` SHOULD typically be `approximated`, NOT `converted`**, because the row's downstream consumers usually depend on the finer granularity. The narrow exception is rows where the consuming schema explicitly declares that the lost granularity does not matter for the row's use case; in those rows, `converted` may be appropriate. Buyers MUST NOT assume `upscaled` + `converted` means semantically lossless without inspecting the row's explicit rule.", + "inferred": "**Entity-level attribution** from observed signals (ML model or rule-based). Answers: *given these clues, who/what is this entity?* Uncertainty lies in the correctness of attributes assigned to individual entities — each `from` value is mapped to a `to` value with finite per-record confidence (e.g. predicting a single buyer's IAB taxonomy node from browsing behavior; predicting demographic membership from device-graph signals). Same input data may also be used for `projected`; the distinction is the level at which uncertainty operates. `method_provider` SHOULD identify the model or vendor; `method_details` SHOULD describe the confidence floor or per-record uncertainty.", + "projected": "**Population-level estimation** from a sample to a population (panel / subset → universe). Answers: *given this sample, what should we expect at scale?* Uncertainty lies in the population-level estimate itself — NOT in any individual entity's attributes (e.g. panel-projected reach, currency-projected impression counts, audience extrapolation from a measured sub-sample). Used primarily for measurement currencies and modeled audiences. Same underlying data may also be used for `inferred`; the distinction is the level at which uncertainty operates. `method_details` SHOULD describe sample size, weighting methodology, and confidence intervals.", + "custom": "Vendor-specific mapping not covered by the named methods. Buyer should consult the destination's documentation for the conversion's semantics, coverage, and edge cases. Buyer agents SHOULD treat `custom` as the weakest preservation claim and inspect `method_provider` + `method_details`." + } + }, + "method_provider": { + "type": "string", + "description": "Optional vendor or organization providing the conversion method. Opaque string by convention — consumers MAY branch on well-known provider names (e.g. `LiveRamp`, `ID5`, `IAB`, `Nielsen`, `Comscore`) but the field is not constrained to a closed enum, since new providers enter the ecosystem additively. Useful for ID-graph and inference methods where the vendor IS the meaningful identifier; less useful for crosswalk methods where the published mapping is canonical regardless of who hosts it.", + "minLength": 1, + "maxLength": 128, + "examples": [ + "LiveRamp", + "ID5", + "IAB", + "Nielsen", + "Comscore", + "TTD" + ] + }, + "method_doc_url": { + "type": "string", + "format": "uri", + "description": "Optional URI of an attested document describing the conversion methodology (e.g. the seller's published mapping reference, the vendor's identity-graph methodology page, the published crosswalk specification). The 'link out to attested document' anchor per @bokelley's #4616 framing and the ads.txt-pattern recommendation that emerged from the @bokelley / @lukasz-pubx / @SimonaNemes thread: structured-where-verifiable, link-to-doc-where-it's-a-claim. Strictly informational on the wire — buyer agents MAY treat its presence as a transparency signal and follow the link out-of-band to verify the seller's conversion claim, but MUST NOT branch on its content programmatically. Consuming row-level schemas that want to require methodology disclosure SHOULD mark this field required in their row's binding; the primitive keeps it optional so it doesn't burden adopters who don't need it.", + "examples": [ + "https://docs.liveramp.com/abilitec-graph/methodology", + "https://iabtechlab.com/wp-content/uploads/audience-taxonomy/v2-to-v3-migration.pdf", + "https://www.nielsen.com/measurement/local-tv/dma-comscore-crosswalk-2024.pdf" + ] + }, + "method_details": { + "type": "string", + "description": "Free-text vendor-specific elaboration on the method (e.g. graph version, crosswalk publication date, match-rate floor, model name, sample size). Strictly informational; buyer agents MUST NOT branch on this field. Complements `method_doc_url` — use `method_doc_url` to point at the canonical methodology document; use `method_details` for short inline elaboration buyers can read at triage time without following the link.", + "maxLength": 1024 + } + }, + "required": ["from", "to", "method"], + "additionalProperties": false, + "examples": [ + { + "from": { "system": "nielsen_dma", "value": "501", "version": "2024", "name": "New York" }, + "to": { "system": "comscore_market", "value": "NYC-100", "version": "2025-Q1" }, + "method": "crosswalk", + "method_provider": "IAB", + "method_details": "Canonical county-union mapping per Nielsen DMA 2024 spec + Comscore Market 2025-Q1 definition." + }, + { + "from": { "system": "uid2", "value": "AAAA..." }, + "to": { "system": "ramp_id", "value": "RA-9876" }, + "method": "id_graph", + "method_provider": "LiveRamp", + "method_doc_url": "https://docs.liveramp.com/abilitec-graph/methodology", + "method_details": "LiveRamp graph v2026.05. Coverage ~94% in US, ~71% in EU." + }, + { + "from": { "system": "iab_audience", "value": "4-1-2-3", "version": "v2" }, + "to": { "system": "iab_audience", "value": "4-1-2-3", "version": "v3" }, + "method": "crosswalk", + "method_provider": "IAB", + "method_details": "IAB published v2→v3 migration map. Direct 1:1 for this node; some sibling nodes are merged in v3." + }, + { + "from": { "system": "us_zip", "value": "10001" }, + "to": { "system": "nielsen_dma", "value": "501", "version": "2024", "name": "New York" }, + "method": "upscaled", + "method_details": "Zip 10001 lies entirely within DMA 501. Aggregation is deterministic forward; inverse is undefined." + }, + { + "from": { "system": "device_graph", "value": "device_abc123" }, + "to": { "system": "iab_audience", "value": "4-1-2-3", "version": "v3" }, + "method": "inferred", + "method_provider": "Lotame", + "method_details": "ML model v2026.04. Entity-level attribution: this particular device assigned to IAB node 4-1-2-3 with per-record confidence 0.78." + }, + { + "from": { "system": "device_graph", "value": "device_population_sample" }, + "to": { "system": "iab_audience", "value": "4-1-2-3", "version": "v3" }, + "method": "projected", + "method_provider": "Lotame", + "method_details": "Same underlying device-graph data as the `inferred` example above — but operating at population level. Projects sample-level membership counts to a universe estimate for IAB node 4-1-2-3 with 95% CI ±0.4pp. Uncertainty is in the population estimate, not in any individual device's assignment." + }, + { + "from": { "system": "publisher_panel", "value": "panel_A_18-49" }, + "to": { "system": "nielsen_p_18_49", "value": "P18-49" }, + "method": "projected", + "method_provider": "Nielsen", + "method_details": "Publisher's panel reweighted to Nielsen P18-49 distribution via post-stratification. Sample n=4,800, design effect 1.3." + }, + { + "from": { "system": "measurement_source", "value": "set_top_box", "name": "Samba STB universe" }, + "to": { "system": "nielsen_p_18_49", "value": "P18-49", "version": "2025" }, + "method": "projected", + "method_provider": "Samba TV", + "method_details": "STB tuning data projected to Nielsen P18-49 currency via Nielsen-licensed reweighting; methodology blend with census-level demos. Measurement-side analog of the signal-side panel projection — #2041 row use case." + } + ] +} diff --git a/static/schemas/source/core/system-reference.json b/static/schemas/source/core/system-reference.json new file mode 100644 index 0000000000..a9e2f1c26c --- /dev/null +++ b/static/schemas/source/core/system-reference.json @@ -0,0 +1,85 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/core/system-reference.json", + "title": "System Reference", + "description": "Reference to a value within a named external system. **Primary value: a uniform shape for union-axis rows** — rows where a single field can carry any of several external systems with the same comparator semantics. The motivating cases (per the #4616 thread): an **identity-substrate** field that may carry UID2 OR RampID OR ID5 OR hashed-email OR MAID; a **measurement-source** field (#2041) that may carry set_top_box OR ACR OR census OR panel; a **PBA taxonomy** field (#4472) that may carry IAB Audience Taxonomy v3 OR vendor-X taxonomy OR a custom ranking reference. Without the primitive, each union-axis row independently invents `oneOf` discriminators across N inline shapes; with it, one comparator (`a.system === b.system && a.value === b.value && (a.version ?? null) === (b.version ?? null)`), one extension story, one schema slot.\n\n**Not a replacement for per-dimension schemas.** Per @bokelley's WG framing: where AdCP has working per-dimension schemas (`geo_metros`, `geo_postal_areas`, the existing `ramp_id` / taxonomy schemas), they are the right shape — inline per-dimension fields are simpler than wrapping `RampID` in `{system: \"ramp_id\", value: \"...\"}` for a row that only ever carries RampID. D1 applies to **union-axis rows that don't yet have a row shape** (identity substrate, taxonomy / ranking references, measurement source / methodology, product-level seller-built audience metadata); it does NOT churn surfaces that already work.\n\nThe `system` axis is intentionally an open string at the primitive level; per-use constraints (closed enums, vendor allowlists) belong in the consuming schema's `oneOf` or `enum`, not here. **Important: the primitive shape alone does NOT create interoperability — consuming row-level schemas MUST constrain or document which `system` values are meaningful for that row.** D1's value is consistent SHAPE across rows, not a universal cross-row vocabulary.\n\n**Naming note:** `system` (not `type`) — `system` carries the connotation of a named external reference frame with its own lifecycle (Nielsen, IAB, LiveRamp), vs. `type` which is overloaded across AdCP for general type discrimination on discriminated unions. Decision D1 from the signal-and-measurement epistemic-model umbrella (see issue #4616).", + "type": "object", + "properties": { + "system": { + "type": "string", + "description": "Identifier for the reference system. Open string — consumers SHOULD use kebab_case or snake_case stable identifiers (e.g. `nielsen_dma`, `iab_audience`, `uid2`, `ramp_id`, `comscore_market`, `omb_msa`, `nielsen_p_18_49`, `measurement_source`). Custom or vendor-specific values are permitted but interoperate only with parties that recognize the same `system` string.", + "minLength": 1, + "examples": [ + "nielsen_dma", + "iab_audience", + "uid2", + "ramp_id", + "comscore_market", + "omb_msa", + "nielsen_p_18_49", + "measurement_source" + ] + }, + "value": { + "type": "string", + "description": "The value within the system that this reference points to. Format is system-dependent — Nielsen DMA codes are numeric strings (e.g. `501`), IAB Audience Taxonomy nodes are dotted paths (e.g. `4-1-2-3`), UID2 values are opaque tokens, measurement currencies use methodology labels. Named `value` rather than `id` because the primitive is cross-axis: identity systems issue IDs, taxonomies issue values/terms, measurement systems issue methodology labels. The pair `(system, value)` is the durable wire reference; consumers MUST compare on both axes.", + "minLength": 1 + }, + "version": { + "type": "string", + "description": "Optional version of the reference system this `value` is defined against. RECOMMENDED whenever the system has a versioned definition history where boundaries, values, or semantics drift between versions (e.g. `nielsen_dma` boundaries differ between 2020 and 2024 releases; IAB Audience Taxonomy v2 ≠ v3). **Omitted `version` means UNKNOWN / unpinned — NOT a wildcard.** Buyer-side comparators MUST NOT treat omitted version as 'matches any version.' For versioned systems, exact equality requires `(system, value, version)` to all match. Consuming row-level schemas MAY declare that a particular `system` is version-insensitive (e.g. version-less identity systems like UID2 / RampID), in which case omitted version is the only valid form for that system and comparators ignore the field. Otherwise omitted version is a buyer-decision point — the buyer MAY accept the reference, reject it, or treat the missing version as a freshness signal. The 'unknown' framing exists specifically to avoid recreating the fuzzy matching problem D1 is supposed to prevent.", + "minLength": 1, + "examples": [ + "2024", + "v3", + "2.1.0" + ] + }, + "name": { + "type": "string", + "description": "Optional human-readable label for UI display (e.g. `\"New York\"` for `nielsen_dma:501`). Strictly informational — never used for equality or routing; the canonical reference is `(system, value, version?)`.", + "minLength": 1 + } + }, + "required": ["system", "value"], + "additionalProperties": false, + "examples": [ + { + "system": "nielsen_dma", + "value": "501", + "name": "New York" + }, + { + "system": "nielsen_dma", + "value": "501", + "version": "2024", + "name": "New York" + }, + { + "system": "iab_audience", + "value": "4-1-2-3", + "version": "v3", + "name": "Affluent Households" + }, + { + "system": "uid2", + "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + { + "system": "comscore_market", + "value": "NYC-100", + "version": "2025-Q1" + }, + { + "system": "nielsen_p_18_49", + "value": "P18-49", + "version": "2025", + "name": "Persons 18-49" + }, + { + "system": "measurement_source", + "value": "set_top_box", + "name": "Set-top box" + } + ] +} diff --git a/static/schemas/source/enums/system-reference-fidelity.json b/static/schemas/source/enums/system-reference-fidelity.json new file mode 100644 index 0000000000..5da4875db6 --- /dev/null +++ b/static/schemas/source/enums/system-reference-fidelity.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/enums/system-reference-fidelity.json", + "title": "System Reference Fidelity", + "description": "How faithfully a deployment (or delivery measurement attribution) can honor a value defined against an external reference system (see /schemas/core/system-reference.json). Surfaces the cross-system fidelity question at the destination layer — buyer agents read this BEFORE activating a signal, and measurement consumers read it BEFORE accepting a delivery report, so they can make an informed decision about geographic, taxonomic, identity, or measurement-methodology drift introduced by the destination's mapping. Generalizes the `market_fidelity` mechanism proposed in #4475 to all reference-system axes — markets, ID graphs, taxonomies, measurement currencies, and methodology sources (the third-sibling measurement track per #4616). Used as a property on `deployments[]` entries or analogous per-destination / per-measurement-row structures; not used on the signal or measurement definition itself (the source's reference is canonical; fidelity is a function of where it's activated or measured). When fidelity is `converted` or `approximated`, the deployment / report SHOULD also surface a structured conversion descriptor (see /schemas/core/system-reference-conversion.json) so consumers can reason about the source, destination, and method without parsing free text.", + "type": "string", + "enum": [ + "exact", + "converted", + "approximated", + "unsupported" + ], + "enumDescriptions": { + "exact": "Destination uses the same `system` (and `version` when both are present) — activation preserves the reference frame byte-for-byte.", + "converted": "Destination uses a different `system` from the signal AND the seller asserts the conversion is **deterministic AND row-semantics-preserving** — the conversion preserves the meaningful interpretation of the value within the consuming row's use case, not merely that the per-record mapping is mathematically deterministic. **Deterministic ≠ lossless preservation.** A crosswalk may be mathematically deterministic but lose semantic information that the row's downstream consumers depend on (e.g. Nielsen DMA boundaries vs Comscore Market boundaries share counties but apply different cross-market reach methodologies; IAB v2 → v3 may merge nodes that downstream consumers treated as distinct). Sellers MUST NOT advertise `converted` when the conversion changes the row's meaningful semantics, even if the per-record mapping is deterministic — use `approximated` in that case. Examples that typically qualify for `converted`: UID2 → ID5 via direct identity-graph link, IAB v2 → v3 for nodes with explicit 1:1 published migration. Examples that typically do NOT qualify (use `approximated`): geographic upscaling via the `upscaled` method, currency crosswalks where definitions diverge, taxonomy migrations where v2 nodes split or merge in v3. Buyer agents that need to inspect the conversion chain (source / destination / method) read the deployment's `conversion` descriptor.", + "approximated": "Destination maps to a different `system` or a different `version` of the same system; the resulting activation is geographically, taxonomically, or identity-wise close but NOT byte-equivalent. Buyer agents SHOULD treat `approximated` as a buyer-decision point — for modeled audiences whose statistical validity depends on the reference frame, `approximated` activation may invalidate the model.", + "unsupported": "Destination cannot resolve the `system` at all. Activation against this destination will fail or silently degrade. Buyer agents MUST NOT proceed without an explicit opt-in to whatever fallback behavior the seller advertises." + } +}