# ADR 0026. Random Analytics Subject and Provider-Global Cohort (not alias)

## Status
Accepted.

Does not change [ADR 0014](0014-provider-operator-alias-scope.md): a
provider-global **alias** remains forbidden by default. This record allows a
provider-global **cohort** (Account counts, numbers only).

## Context
Partners want enrichment without correlating a person across Audiences.
Two tempting shortcuts both fail the identity invariants:

1. Hang partner analytics off `players.id` or expose UUID / `pp_plr_...` /
   another Audience’s alias on a partner read.
2. Mint one `pp_plr_prv_...` per provider so that provider can join the same
   person across operators. ADR 0014 already rejects that default.

A third need appears at Delete Account: gameplay facts may be retained, but
the live UUID join must disappear. A PlayerPal-only clustering key is required
so retained Activity is not re-attached to a person on a partner API, and so
studio KPIs can count distinct Accounts rather than aliases.

Deriving that key from UUID or `pp_plr_...` would make it reversible.
Summing per-alias serving rows would double-count a Player with two
integrations at the same provider.

## Decision
1. Mint a cryptographically random analytics subject (`pp_anl_...`) at Account
   activation. Never derive it from UUID, `pp_plr_...`, PII, or a partner
   identifier. Never return it on player or partner APIs (including
   `/v1/me`, link exchange, activity ingest, and studio reads).
2. Live loyalty processing may use Account id in memory. Persisted
   partner-facing analytics and serving rows must not expose Account id.
   Partner studio HTTP is machine-authenticated and provider-Party scoped
   (`x-party-id` as `pp_prv_...`), not ingest’s `x-integration-id`, and not a
   payload `operator_id`.
3. Per-alias studio lookup uses only a `pp_plr_prv_...` that provider was
   issued. A foreign alias is the same unknown as a missing alias. Two aliases
   for the same Account at that provider return the same Account-level numbers.
   “On you” is all of that provider’s integrations for the Account, not one
   casino. Response body is numbers only: `from`, `to`, `bets`, `wins`,
   `bet_eur`, `share_of_their_slot_turnover`, `rounds_on_you`,
   `rounds_elsewhere`.
4. Studio rollup counts distinct Accounts for that provider Party and is not
   implemented as a sum of per-alias serving rows. It includes `accounts`.
   Windows below a configurable Account floor are suppressed (production
   default 50; tests/demo may lower it). Per-alias lookup is n=1 by design.
5. Unlink freezes new partner-player-day rows for that alias; historical days
   remain. Delete Account deletes partner-player-day (and related serving)
   rows for that Player’s aliases, nulls `player_id` / `player_alias` on
   retained Activity, destroys the UUID↔analytics-subject mapping, and leaves
   `round_ref` and the analytics subject on retained facts. Live per-alias GET
   for those aliases is unknown.
6. v1 stores UUID↔analytics-subject in application storage.
   Crypto-shredding / KMS envelope encryption is out of scope. A restored
   pre-delete backup can still join; do not market otherwise.

## Consequences
- ADR 0014 still governs aliases. Open question 5 (which providers, if any,
  get a provider-global **alias** exception) stays open. Cohort is not that
  exception.
- Identity and loyalty topic pages must not say partner analytics hang off
  `players.id`. Internal ledger and identity tables still may.
- Closes open question 6 for **output** (numeric cohort and per-alias
  enrichment; never another Audience’s identifier) and open question 7
  (Unlink vs Delete Account retention). Legal joint-controller contracts stay
  a product-permission assumption, not an identifier rule.
- Glossary: `CONTEXT.md`. Day-to-day: [docs/loyalty.md](../loyalty.md),
  [docs/identity.md](../identity.md). Unlink skeleton: spec §36.6.
