ADR-0159: PEP tier scores and sanctions defaults are our judgement, recorded as such
Date: 2026-08-01 Status: Accepted Deciders: Adrian (Soft4U BV), Claude Opus 5
Context
pep_tiers.json and sanctions_defaults.json both carried:
"source": "ADR-008a Default Configuration",
"source_url": null
ADR-008a does not exist. This repository's ADR series is four-digit (ADR-0001 … ADR-0158);
grep 008a returns nothing. Neither file cited any public standard.
That matters more for pep_tiers than a config-tidy framing suggests. It is
decision-driving — eba_risk_matrix._score_pep_exposure reads it — and it is frozen
verbatim into the append-only reference snapshots (scoring/reference_snapshot.py,
ADR-0124). So every scoring run persists a content hash of a file whose stated provenance
resolves to nothing, and an auditor replaying that run is pointed at a document that has
never existed.
Two facts had been conflated under one source field:
- The taxonomy — who is a PEP, which sanctions lists exist. This is published law and published authority output. It has a real citation.
- The numbers — the 30/25/20/15 tier contributions and the 100/70/60/30 match weights. No public standard assigns these. AMLR defines who a PEP is and what enhanced measures apply; FATF R.12 likewise. Neither assigns a weight. These are our risk judgement.
Attributing our numbers to an external document — any external document — is the defect. Attributing them to a non-existent document is the same defect with the evidence removed.
Decision
Split the two facts and cite each honestly.
Taxonomy, verified against the ingested corpus rather than from memory. The PEP
definition is AMLR (Reg (EU) 2024/1624) Art. 2(1)(34), with family members at
Art. 2(1)(35) and known close associates at Art. 2(1)(36); obligations at Art. 44-46;
FATF Recommendation 12 alongside. Each of the eight tier keys now records the specific limb
it derives from, in a sibling amlr_limbs map.
The issue that scoped this work cited
Art. 2(1)(25). Reading the corpus showed the PEP definition is at(34). Writing from the issue text would have replaced a phantom citation with a wrong one — a real article that says something else, which is exactly the failure mode ADR-0121 exists to catch and which no referential check would flag.
Numbers, declared as ours. Both files carry scores_authored_by / weights_authored_by
= "Trust Relay", a rationale, and a pointer here. The ordinal reasoning: proximity to
sovereign decision-making power (head of state / senior government, 30) ranks above
legislative and senior-military office (25), which ranks above regional and judicial office
(20), which ranks above derived exposure through a relationship rather than an office
(family member / close associate, 15). This is a defensible ordering, not a measured one,
and it is recorded so an officer can disagree with it explicitly.
The payload shape is unchanged, deliberately. Provenance went into sibling keys
(amlr_limbs, list_authorities), not into data. Two reasons, both verified rather than
assumed:
eba_risk_matrix._score_pep_exposurereadspep_data[key]andfloat()s it directly — it does not go throughReferenceDataService.get_risk_score, which would have tolerated a nested{"score": N}. A first attempt at a nested payload broke it, caught bytest_risk_config_service.py::test_modified_pep_tiers_changes_computed_pep_exposure.test_reference_snapshot.py:35pinssanctions_defaultsas{"lists": [...ids...]}, and the ADR-0124 reference snapshots hash these payloads.
So every score and every list id is byte-identical to before. This ADR changes what the files SAY about themselves, not what they DO.
Consequences
Positive
- An auditor replaying a scoring run reaches a real citation.
- The line between published law and our judgement is explicit and per-field, so an officer can contest the numbers without contesting the taxonomy.
- A standing test (
test_reference_data_provenance_930.py) fails on any reference dataset citing a non-existent ADR — the class, not the instance.
Negative
- Provenance now lives in sibling keys rather than beside each value, which is less tidy than a nested payload. That is the price of not changing a hashed, directly-read payload.
- The tier ordering remains a judgement. It is now a recorded, contestable judgement, which is better, but it is not evidence.
Neutral
- No scoring behaviour changes. No migration.
Alternatives Considered
Alternative 1: correct "ADR-008a" to a real ADR number
- Rejected: the numbers were never derived from an ADR. Pointing them at a different document would preserve the false attribution and remove the evidence that it was false. The phantom citation is at least visibly broken.
Alternative 2: nest provenance inside data
- Rejected on measured grounds: it broke
_score_pep_exposure(directfloat()), and it would change the ADR-0124 snapshot hash for every historical comparison.
Alternative 3: drop the numbers and score PEP exposure as a flat binary
- Rejected: AMLR Art. 44-46 distinguish PEP categories in the measures they require, so a flat treatment would lose real signal. The gradation is defensible; only its attribution was wrong.