{
  "registry": "uiaf-enums",
  "edition": "3.1.1",
  "description": "Single vocabulary source for UIAF v3. Payload values are always these literals; prose shorthand (T0-T4) never appears in payloads. This registry is edition-versioned by the uiaf-enums.json snapshot/ledger; no runtime version field exists.",
  "emitter": [
    "client",
    "server"
  ],
  "resolution_method": [
    "cookie",
    "localstorage_recovery",
    "sessionstorage_recovery",
    "new",
    "ephemeral"
  ],
  "confidence": [
    "high",
    "medium",
    "low"
  ],
  "confidence_derivation": "Closed derivation from the identity-resolution outcome, never producer-chosen: high = authoritative cookie re-read matched or new server issuance (resolution_method cookie or new), and recovery adopted after the authoritative cookie crosscheck; medium = credential-verified localStorage/sessionStorage recovery adopted within this document BEFORE the authoritative crosscheck completes (resolution_method localstorage_recovery or sessionstorage_recovery only); low = ephemeral or indeterminate resolution (resolution_method ephemeral always carries low). The shared reference validator enforces this mapping as an identity-tuple invariant.",
  "data_quality": {
    "values": [
      "full",
      "stripped",
      "restricted"
    ],
    "derivation": "From effective capabilities and field omission, never from a tier: full = all four effective permissions allowed; stripped = analytics_storage allowed with at least one ad permission denied (ad-purpose field groups null-valued, keys present); restricted = a narrowly permitted send under a non-analytics grant. No payload exists for pending or all-denied dormant states."
  },
  "attribution_completeness": [
    "full",
    "utm_only",
    "request_context_only",
    "none"
  ],
  "attribution_completeness_derivation": "Exhaustive precedence derived from the normalized last_touch surface, never producer-chosen: full = at least one click ID present in last_touch.click_ids; else utm_only = at least one of source/medium/campaign/term/content non-null; else request_context_only = last_touch.referrer or last_touch.landing_url non-null; else none = every scalar null and no click IDs. The shared reference validator recomputes this derivation and rejects any mismatch.",
  "consent": {
    "signal_values": [
      "granted",
      "denied",
      "unknown",
      "not_applicable"
    ],
    "lifecycle_status_model": [
      "pending",
      "resolved",
      "not_applicable"
    ],
    "lifecycle_status_data_plane": [
      "resolved",
      "not_applicable"
    ],
    "effective_values": [
      "allowed",
      "denied"
    ],
    "pending_reasons_internal_only": [
      "timeout",
      "parse_failure",
      "cmp_error",
      "unavailable",
      "awaiting_user"
    ],
    "source_values": [
      "cmp_cookiebot",
      "cmp_onetrust",
      "cmp_didomi",
      "cmp_custom",
      "gcm",
      "site_policy"
    ],
    "notes": "granted is used only for observed-signal values; every effective-permission condition uses exactly allowed or denied. pending and its reason/provenance fields belong to the internal/control-plane representation only; the data-plane schema admits no pending payload. GPC is an independent overlay ({detected, applicable}), never a source. cmp_custom covers all unlisted adapters; no vendor enum additions within v3."
  },
  "click_id_params": [
    "gclid",
    "gbraid",
    "wbraid",
    "dclid",
    "fbclid",
    "msclkid",
    "ttclid",
    "li_fat_id",
    "epik",
    "twclid",
    "ScCid"
  ],
  "click_id_params_notes": "Canonical wire spellings are case-sensitive and exact (Maestro ruling: ScCid, not sccid). Parsing exact-matches and preserves the decoded query-parameter name spelling; there is no global key lowercasing. This registry is the single vocabulary source: the payload schema's click_ids propertyNames enum and the shared reference validator allowlist MUST remain in parity with this list (CI-asserted); unknown click-ID keys fail producer, endpoint, and shared validation.",
  "utm_params": [
    "utm_source",
    "utm_medium",
    "utm_campaign",
    "utm_term",
    "utm_content"
  ],
  "normalization_profiles": {
    "identifier_pattern": "^[a-z][a-z0-9_]{1,46}_v[0-9]{1,3}$",
    "max_length": 52,
    "initial_registry": [
      "email_basic_v1",
      "email_google_ads_v1",
      "phone_e164_v1",
      "customer_keyed_v1"
    ],
    "notes": "Version is part of the identifier; there is no second profile-version field. email_google_ads_v1 uses the dated primary Google rule including Gmail/Googlemail dot and plus-suffix handling. No Meta-specific profile ships until its primary platform rule is verified. Compatible additions within v3 are MINOR; a receiver whose snapshot does not know a pattern-valid profile accepts the enclosing payload structurally, marks that evidence entry unsupported (no matching, confidence, or identity-graph use), and never rejects the whole event for a compatible new profile."
  },
  "evidence": {
    "type": [
      "email",
      "phone",
      "customer"
    ],
    "algorithm": [
      "sha256",
      "hmac_sha256"
    ],
    "verification_state": [
      "authenticated",
      "verified"
    ],
    "verification_method": [
      "authenticated_session",
      "verified_link",
      "verified_otp",
      "payment"
    ]
  },
  "session_event_reasons": [
    "session_open",
    "identity_created",
    "identity_recovered",
    "attribution_touch",
    "consent_change"
  ],
  "runtime_ipc": {
    "description": "Closed registration of runtime inter-context coordination names (D2/IR4/IR15 + Maestro consent-IPC rulings). Runtime IPC primitives, not storage rows: no storage semantics attach and none of these names may appear in uiaf-storage-keys.json. uiaf-session-alloc:<session_id> is the LIFETIME per-session allocator Web Lock; uiaf-session-claim is the session advisory BroadcastChannel; uiaf-identity-issuance is a SHORT-LIVED origin-wide Web Lock held only around the authoritative re-read + sole-winner cookie-endpoint call + post-response cookie readback; uiaf-consent-wakeup is a WAKE-ONLY BroadcastChannel whose messages carry no UID, consent vector, reason, provenance, or policy state — receipt triggers an authoritative CMP/GPC/server consent-state re-read before any persistence or send decision; uiaf-retry-drain is the retry-queue drain Web Lock serializing offline-queue draining across contexts (best-effort lease fallback where the Web Locks API is unavailable). All five names are distinct and all five are coordination names only — none is ever carried on the wire. Behavioral rules (250ms AbortSignal, pagehide/pageshow.persisted) live in the section 04/09 normative text and the shared reference validator.",
    "web_lock_name_template": "uiaf-session-alloc:<session_id>",
    "broadcast_channel": "uiaf-session-claim",
    "identity_issuance_web_lock": "uiaf-identity-issuance",
    "consent_wakeup_broadcast_channel": "uiaf-consent-wakeup",
    "retry_drain_web_lock": "uiaf-retry-drain"
  },
  "pending_context_allowlist": {
    "description": "CR10-02/03/03a/04/04a pending-core attribution context contract. The pending-core context value is JSON null or a closed object {utm_source, utm_medium, utm_campaign} with additionalProperties false, all three keys required, each member a JSON string or null. Exact members are ONLY these three: utm_term, utm_content, referrer/referrer-domain, landing URL/path, click IDs, custom keys, identifiers, tokens, timestamps, user-specific decoration, and every other query value are excluded while pending. Confinement: server-injected current-request context and current-document memory only; never cookies/localStorage/sessionStorage/durable state; never serialized as the context object into a data-plane payload, retry body, log, report, history, cache, service worker, hydration state, or third-party-readable global. After lifecycle resolution an independently authorized normal touchpoint may receive only the admitted source/medium/campaign mappings.",
    "members": [
      "utm_source",
      "utm_medium",
      "utm_campaign"
    ],
    "normalization": "Frozen master pipeline, no new algorithm: parse the query exactly once with application/x-www-form-urlencoded; first decoded occurrence wins; absent or empty-after-trim becomes null; utm_source/utm_medium use Unicode-White-Space trim, NFC, Unicode Default Case Conversion lowercase, NFC; utm_campaign uses trim and NFC preserving case; the same pinned Unicode inputs apply.",
    "bounds": {
      "member_max_utf8_bytes": 128,
      "member_min_utf8_bytes": 1,
      "context_max_canonical_utf8_bytes": 1024,
      "failure": "Invalid, over-bound, unregistered, or prohibited input fails closed before injection and is never truncated. Whole-object canonical overflow or zero admitted members yields top-level null, never a partial context. Incoming members are evaluated independently: a failing member nulls only itself while valid siblings survive."
    },
    "vocabulary_config": {
      "shape": "Closed runtime object with exactly the required keys utm_source, utm_medium, utm_campaign; each maps to a finite JSON array of closed entry records exactly {value, review}. An empty array is valid and denotes an empty vocabulary, not a malformed key.",
      "value_rules": "Each value is a 1-128-byte JSON string that is already a byte-for-byte normalization fixed point under its key's pipeline; values are unique within the key after normalization. A normalized incoming value must exact-match a vocabulary member; there is no wildcard, regex-only, default, inferred-value, or raw-query passthrough mode. Pattern screens are optional defense-in-depth and never establish non-user-specific provenance.",
      "review_shape": {
        "record_id_pattern": "^[A-Za-z0-9._:-]{1,128}$",
        "reviewed_at": "integer Unix seconds 0..253402300799",
        "classification": "shared_campaign_metadata",
        "required_false_booleans": [
          "per_recipient_or_dynamic",
          "identifier_or_token",
          "free_text",
          "sensitive_or_special_category",
          "audience_membership_inference"
        ],
        "notes": "Controller assertions whose truth remains a controller obligation; validator success cannot convert prohibited data into campaign metadata. Registering a value known to violate an attestation is a configuration-validation failure, not an allowlist bypass. Attestation metadata is configuration/governance data only: it never enters the runtime context, any payload, retry body, log, or other sink."
      },
      "failure_tiers": "Exactly two tiers. TOP-LEVEL: configuration absent, JSON parse failure, non-object, or any extra top-level key beyond the required three yields all three vocabularies empty. PER-KEY: within an otherwise object-shaped configuration, a required key missing, a non-array value, or any malformed / unsupported-property / non-fixed-point / duplicate-after-normalization / unreviewed / bad-attestation entry empties only that key's vocabulary while valid sibling-key vocabularies remain usable.",
      "duplicate_member_names": "The configuration input is raw JSON text and MUST use a duplicate-name-rejecting parser or an equivalent lossless pre-scan before object materialization, with duplicate detection by equality on decoded JSON member names (escaped and literal spellings of the same name are equal); never first-wins or last-wins. A duplicate member name in the top-level configuration object (recognized or extra) is TOP-LEVEL and empties all three vocabularies. A duplicate member name inside a {value, review} entry object or its nested review object makes that entry malformed (PER-KEY): only the affected key empties and valid siblings survive.",
      "diagnostic": "One bounded diagnostic containing only the layer, the affected allowlist key where applicable, and the duplicate-name or configuration error class; never the duplicate decoded name, either raw spelling, any configured or request value, or any review metadata."
    }
  },
  "derived_tier_table": {
    "description": "Exact machine 16-row truth table over the four effective permissions, key order analytics_storage,ad_storage,ad_user_data,ad_personalization with a=allowed d=denied. Overrides: lifecycle not_applicable under configured T0 policy serializes tier 0 regardless of row. Tier is serialized shorthand only and never drives behavior.",
    "key_order": [
      "analytics_storage",
      "ad_storage",
      "ad_user_data",
      "ad_personalization"
    ],
    "rows": {
      "aaaa": 1,
      "aaad": 2,
      "aada": 2,
      "aadd": 2,
      "adaa": 2,
      "adad": 2,
      "adda": 2,
      "addd": 2,
      "daaa": 3,
      "daad": 3,
      "dada": 3,
      "dadd": 3,
      "ddaa": 3,
      "ddad": 3,
      "ddda": 3,
      "dddd": 4
    },
    "status_override": {
      "not_applicable": 0
    }
  },
  "ingress_trust": {
    "description": "IR66/IR68 ingress and initialization prerequisites (governance; not a payload field). Data-plane ingress is PUBLIC/untrusted by default: emitter=server is honored only on an authenticated server-to-server channel; a PUBLIC ingress server assertion is REJECTED (untrusted); a RELAY ingress authoritatively REWRITES emitter to client for browser-originated input (server assertions are replaced, not merely forced); an authenticated S2S channel preserves server and may additionally carry relayed client bodies. Same-realm prototype-poisoning immunity is achieved by capturing intrinsics at module load; this is only sound if the shared reference validator / producer runtime is INITIALIZED BEFORE any untrusted page code executes — sections 04 and 09 MUST state trusted-first initialization as a normative prerequisite (no vm/realm architecture is assumed here).",
    "channels": [
      "public",
      "relay",
      "server_s2s"
    ]
  }
}
