AMLR Readiness
Regulation (EU) 2024/1624 — the single EU Anti-Money-Laundering Regulation (AMLR) — is directly applicable from 10 July 2027. It replaces the directive-and-transposition model with one harmonised rulebook: prescriptive customer-due-diligence (CDD) measures (Art. 20), a computed beneficial-ownership test (Art. 51–63), targeted-financial-sanctions ownership rules (Art. 20(1)(d)), a BO-register discrepancy-reporting duty (Art. 24), and defined retention obligations (Art. 77).
This page is the map of the AMLR-readiness uplift — epic #528, delivered across four waves, on top of the OSINT-auditability substrate (epic #511). It records what is built and running, what is dark-launched (code landed and tested behind a default-off flag, awaiting a Calibration-Review sign-off to flip), and what is deferred to a tracked follow-up. The status matrix at the foot of the page is the authoritative summary; each section states the same facts in context.
:::info Honesty is the contract
Every claim on this page is labelled with its real state. A determination behind a
default-off flag is dark-launched, never described as live. A read surface that returns
not_assessed because the engine that would populate it has not been flipped on is
described exactly that way. This mirrors the platform's own fail-closed doctrine
(ADR-0067): the system may add
scrutiny but never suppress a signal, and it never reports "clear" for a check that did
not run.
:::
How to read the state labels
| Label | Meaning |
|---|---|
| Live | On in production by construction (additive, no flag) or behind a default-on flag. It runs on every applicable case now. |
| Dark-launched | Code and tests are merged; a *_enabled config flag defaults to false. It does not execute in production until a human flips it after a passing Calibration Review. Flag-off is byte-identical to before the change. |
| Deferred | A bounded follow-up issue is open — typically the population of a computed record from live OSINT/registry/verification data, or the wiring of a substrate into a runtime consumer. The engine exists and is tested; the data feed is not yet connected. |
The distinction between "the engine is dark-launched" and "the read surface is live but
returns not_assessed" matters and is kept explicit throughout: several read APIs
(GET /api/cases/{id}/…) are wired and shipping, while the determination engine that
would fill them remains dark-launched — so the surface honestly reports not_assessed
today.
1. Beneficial ownership — AMLR Art. 51–63
The beneficial-ownership engine received the largest single body of AMLR work. It is documented in full on UBO Determination; the AMLR-specific extensions are:
- Decimal ownership arithmetic + Art. 54 two-limb + rights-type
(ADR-0130, #540) — Live. The
ownership aggregation migrated from IEEE-754 float to
decimal.Decimalunder a fixed context, so identical graphs yield byte-identical aggregates and a reproduciblegolden_record_hash;THRESHOLD_EPSILONwas dropped for an exact>=/>boundary. A distinct Art. 54 pass reports coexisting ownership-and-control (limbs (a)/(b)), and non-share holdings (voting rights, profit share, Art. 52(1)) count toward the threshold and are labelled. - Effective-dated, category-aware BO threshold snapshot
(ADR-0133, #542) — Live.
resolve_threshold_snapshot(country, as_of, high_risk)applies the rule in force as of the computation date. Because the AMLR-harmonised entry is not in force until 2027-07-10, a computation dated today falls back to the contemporaneous AMLD predecessor (the same 25% inclusive, honestly cited) rather than silently applying a not-yet-in-force value. A genuinely high-risk case may apply the 15% override, but only when it lowers the threshold. EveryBeneficialOwnerResultcarries aThresholdSnapshotrecording which rule ran. - Control-via-other-means derivation (ADR-0134, #538) — Live (engine + model); the declared concert-group + nominee-edge graph population landed (#630). Four legible control mechanisms (veto rights, appoint/remove board, acting-in-concert Art. 53(3), first-class nominee Art. 53(4)), with 50%+1 modelled as a distinct third threshold separate from the 25% interest test.
- Look-through legal-arrangement UBO regimes + Art. 60 state machine
(ADR-0135, #539) — Live
(wired into
UBOComputationService.compute); thefetch_arrangementsreader + trust/foundation graph population landed (#632). Trust/foundation/CIU regimes (Art. 58/59/61), a fail-closed discretionary-party state machine (Art. 60), multi-layer look-through recursion, and a beneficiary-class gap row that is never a silent "no BO". - BO record as the Art. 62 regulated dataset + data-currency job
(ADR-0136, #541) — the Art. 62 dataset
shape (direct/indirect split, provenance, disclosed gaps) is stamped on every
determination (Live), but the 28-day / annual currency monitoring check is
dark-launched (
bo_dataset_currency_monitoring_enabled=false). Identity-field population (DOB / nationality / residence / TIN) landed (#634) — populated from verified, identifier-exact identity data where a source exists; absent one it stays a disclosed honest gap (never fabricated). - Sanctioned-ownership / control test (the EU 50%-rule) (ADR-0127, #535) — see §2. Dark-launched.
The nine-measure CDD register (Art. 20(1)(a)–(i)) and the per-actor identity dataset (Art. 22(1)) are covered in §3.
2. Wave-1 determinations & read surfaces
Four Wave-1 controls each have a dedicated, tenant/RLS-scoped, CASE_READ-gated,
fail-closed read surface — a genuine read failure raises 503, and an absent
determination returns an explicit not_assessed, never a silent empty a consumer could read
as "checked and clean".
| Determination (AMLR) | ADR / issue | Engine state | Read surface |
|---|---|---|---|
| Sanctioned ownership / control — the 50%-rule over the ownership graph, individually or collectively (Art. 20(1)(d)) | ADR-0127 / #535 | Dark-launched (sanctioned_ownership_control_enabled=false) | GET /api/cases/{id}/sanctioned-ownership — Live, returns assessed=false until the flag flips |
| Immediate-on-designation re-screen — diff newly-added sanctions listings and re-screen affected customers at once, not at the next cadence tick (Art. 26(4)) | ADR-0128 / #536 | Dark-launched (immediate_designation_rescreen_enabled=false) | GET /api/cases/{id}/rescreen-events — Live |
| BO register-discrepancy report + Art. 24(2) derogation + 14-day lifecycle | ADR-0129 / #537 | Live — wired into cross_reference_evidence_activity; a self-healing escalation Temporal schedule is provisioned at startup | GET /api/cases/{id}/register-discrepancies (+ POST …/report, …/derogation-outcome) — Live |
| SMO fallback / exhausted-means record + tipping-off abstention (Art. 22(2); Art. 63) | ADR-0126 / #534 | Live — the record_no_bo_identified Temporal activity writes an immutable record when no natural-person BO is identified | GET /api/cases/{id}/smo-abstention (+ POST) — Live (see UBO Determination) |
Two determinations here — sanctioned ownership and immediate re-screen — carry the
highest false-positive/false-negative and API-spend risk, so they are dark-launched: the
code is merged and tested, the flag defaults off, and flipping it (the #529-class prod
enablement) is gated on a passing Calibration Review and a live validation run. When flipped,
a confirmed sanctioned owner floors the authoritative EBA score to 90 via a dedicated
SANCTIONED_OWNERSHIP_CONFIRMED escalator (parity with NETWORK_SANCTIONS_CONFIRMED), and a
new designation matching a customer enqueues an immediate CRITICAL alert.
Both use the two-lane attribution discipline
(ADR-0073 R9 /
ADR-0078): a reg_no/identifier
match is the verified lane (may floor subject risk); a name-only match is a labelled
candidate that is never folded into subject risk on name alone and never dropped.
Sanctions FP-suppression under the never-suppress doctrine
ADR-0131 (#587) hardens the tenant-editable Tier-2
sanctions false-positive suppression (ADR-0045) against the "hide a signal" failure mode.
The fail-closed check_active_rule and the deterministic raw-matches bypass (a raw
sanctions view that ignores Tier-2 suppression, feeding the ADR-0127 ownership test and the
ADR-0128 designation-match evaluator) are always-on. The force-invalidation of a
suppression when its underlying record is re-designated rides the
immediate_designation_rescreen_enabled flag.
Dashboard-assistant Wave-1 tools & non-downgradable readiness
ADR-0132 (#580) — Live.
The CopilotKit dashboard agent gains four fail-closed determination tools (grounded, each
returning determination + regulatory_basis + adr + provenance), and — the load-bearing
rule — assess_audit_readiness folds a non-downgradable Wave-1 gate: the readiness verdict
is MAX(base, Wave-1 floors), so a confirmed sanctioned owner or an open_obligation
register discrepancy forces BLOCKED and a favourable base can never wash a blocker out. An
unread summary fails closed to not_assessed.
3. CDD dataset & Annex factors
- Nine-measure CDD register (Art. 20(1)(a)–(i))
(ADR-0137, #544) — Live (pure compute + read
surface).
compute_cdd_registermodels all nine Art. 20(1) measures with a four-value state (satisfied/partial/not_assessed/not_applicable, the last strictly distinct from a gap), reusinginterpret_cdd_coverageinternally so the legacy five-section map and the register can never diverge. The four measures the legacy model lacked — (d) targeted financial sanctions, (f) ongoing monitoring, (h) beneficiary, (i) representative-authority — are fail-closednot_assessedunless real evidence is supplied. The assistant consumer (#601) and the CDD-coverage UI panel (#600) are now live on the register, as is theGET /api/cases/{id}/cdd-coverageread surface; a dedicated PDF/dossier consumer of the nine-measure register remains a tracked follow-up. - The evidencing step is shared, not per-caller (#1004) — Live.
compute_cdd_registeris pure over its input, so the register is only as honest as what the caller evidences into it. That evidencing lived in the officer assistant alone: the API read surface and the dossier called the register with an un-enriched input, so measures (c) purpose, (d) targeted financial sanctions and (f) ongoing monitoring came backnot_assessedon cases where the evidence existed and had been collected.cdd_register_input.evidence_caller_owned_measuresis now the single evidencing step every caller runs beforecompute_cdd_register, and a call-site test parses the AST of each consumer to assert it actually invokes it — a shared function nobody calls is the same defect wearing a different shape. Thescreening_ranpredicate moved here unchanged rather than being re-derived, because a from-memory reimplementation credited statuses the platform never emits. - Per-actor-type identity dataset (Art. 22(1))
(ADR-0138, #543) — Live
compute; the per-actor population landed (#640). The typed record distinguishes the
legal-entity vs natural-person dataset per actor type (customer / director / BO /
beneficiary / representative) and carries a first-class four-value registry-grounding
state that makes ADR-0058 ("a central register is a cross-check only") enforceable at the
field level. The producer enumerates every actor lane from the shared
CddCoverageInputand the persisted latest investigation; a field with no evidenced source honestly staysnot_assessed(never fabricated). - AMLR Annex I/II/III factors as a versioned reference dataset (Art. 20(2)-(3))
(ADR-0141, #551) — Live read
surface; composes with, never changes, the EBA matrix.
annotate_eba_resultmaps fired platform signals to the statutory Annex factor they correspond to, effective-dated (the in-force AMLD predecessor before 2027-07-10, an AMLR-added factor honestly flaggedin_force=false). Fail-closed: PEP (Art. 42-44), a sanctions hit (Art. 20(1)(d)) and adverse media (an Annex I variable) have no Annex III factor and produce an honest no-mapping naming their real basis — never a fabricated citation.GET /api/cases/{id}/annex-factors(#608) is live.
4. Retention & data protection — AMLR Art. 77 × GDPR Art. 17
- Retention clock + Art. 77(1) retained-set assertion
(ADR-0139, #548) — Live
(derived). A typed per-case clock (
active_relationship/retained_in_period/retention_expired_eligible_for_erasure) and an affirmative "what is retained, until when, on what basis" assertion, both derived from the canonicaldetermine_erasure_actionpolicy (so the clock can never be more permissive than the purge). An unknown or active clock is never eligible for erasure. Surfaced atGET /api/cases/{id}/retention(#606). - Retention holds + Art. 77(2) +5-year cap + no-purge guard
(ADR-0140, #549) — the no-purge
guard is Live at every deletion boundary (a held case never reaches erasure); the
retention-purge sweep itself is dark-launched (
retention_purge_enabled=false, ADR-0108). An effective legal hold extendsretain_until, clamped tostatutory + 5y; an indefinite legal hold blocks with no end. - Crypto-shred — erasure of PII inside immutable stores
(ADR-0142, #550) —
Dark-launched (
crypto_shred_enabled=false). A KEK/DEK key hierarchy encrypts PII under a destroyable per-(subject, case)key before the ADR-0109 hash is computed, so "erase" = destroy the key while the row, its hash and its chain position survive (verify_tenant_chainpasses before and after). Proven end-to-end on a testcontainer DB. The architecture is documented in full on Privacy & Data-Protection → Crypto-shred. Two PII sinks are deferred: assistant-memory (Letta /signal_events/ dashboard-agent, #676) and customer-portal enrolment (uploaded-doc extracts / portal answers, #677); historical plaintext written before the encrypt-at-write cutover is an immutable residual named honestly, not waved away.
5. Determinism substrate (OSINT epic #511)
The AMLR determinations rest on a substrate that makes a compliance run reproducible — the same inputs must give the same, auditable answer (EU AI Act Art. 12).
- OSINT golden-record survivorship
(ADR-0123, #512) — Dark-launched
(
golden_record_resolver_enabled=false). Extends the provensurvivorship.py(ADR-0024/0073) into the investigation path: typed per-sourceClaims with a centrally stampedsource_trust, a deterministic winner order (no wall-clock), losers retained asalternatives, and agolden_record_hash. Retires themutation_queue. - Deterministic, hash-audited risk scoring
(ADR-0124, #515) — Dark-launched
(
hash_audited_scoring_enabled=false). Arun_hashover the canonicalised scoring inputs + every lever (engine/matrix version, reference snapshots, matcher config, dataset snapshot, normalizer versions), append-onlyscoring_runschained via ADR-0109, and the LLMrisk_scoredemoted off the authoritative decision (the EBA composite is authoritative everywhere; the LLM value is labelledraw_synthesis_risk_score). - Declarative connector data-contracts + drift detection
(ADR-0143, #518) — the validator, the
loud
connector_contract_driftalert, thedrift_to_findingmechanism and the per-field trust manifest are built and unit-tested; wiringvalidate_responseinto each connector's live parse and flipping the survivorship trust hook (connector_contract_trust_enabled=false) are deferred / dark-launched. A declared upstream field going missing surfaces as a loud drift finding, never a silent null.
6. Governance
- Risk-appetite / risk-config activation four-eyes (ADR-0070 / ADR-0092, #626) — Live.
A draft risk-configuration activation that would lower due-diligence scrutiny (the
risk-appetite /
tier_thresholdsceilings) is held until a second, distinct approver authorises it (assert_distinct_approver); a self-approval or ambiguous identity is rejected. The risk appetite is read only from the audited, versioned risk configuration, never from the mutable Letta layer (ADR-0092). - Entity-risk downgrade four-eyes (ADR-0089 Component E) — Live. The only path a persisted entity baseline is allowed to go down requires a distinct maker and checker, a written reason, and a dual-actor immutable audit event — and only after the material check that produced the lower number is confirmed complete. See AMLR Compliance Controls → §4.
- Maker-checker on high-risk decisions (ADR-0070,
maker_checker_enabled=true) — Live. - Fail-closed insufficient-data approval gate
(ADR-0125, #532) — Dark-launched, log-first
(
approval_insufficient_data_gate_enabled=false). Like the ADR-0074 RBAC rollout, it logsapproval.insufficient_data.would_blockand writes an immutable audit row with zero behaviour change; the Phase-2 flip to a hard409(overridable only via an audited, four-eyes-routedoverride_insufficient_data) is Calibration-Review-gated. - Semantic citation gate (ADR-0121) — Live. A deterministic citation-resolution gate runs on every PR; a rule→corpus citation that resolves but is legally wrong is caught by the LLM judge tier when a human is deciding. Referential soundness is not correctness — a real article cited for the wrong duty is a failure mode.
7. AMLR evidence dossier
The consolidated AMLR evidence dossier (#592) is one regulator-facing export per case,
served at GET /api/cases/{id}/amlr-dossier as both PDF and JSON rendered from one
canonical payload (so the two can never diverge). It consolidates:
- the AI evidence bundles (ADR-0021 — SHA-256
data_hash, confidence, provenance, chain-of-thought, model/prompt metadata); - the four Wave-1 determinations (§2), each with its state,
regulatory_basis,adrandprovenance; - the ADR-0109 per-tenant audit hash-chain verify verdict (intact vs the first detected break).
It is fail-closed (an unread determination renders as an explicit not_assessed section,
never omitted, never fabricated as clear; a chain-verify failure is surfaced loud) and
tamper-evident (the three inputs are sealed as members of a SHA-256 manifest with a
Merkle-style dossier_hash root, reusing the case-pack primitive). Because the Wave-1
determination engines for sanctioned ownership and immediate re-screen are dark-launched,
the dossier honestly renders those sections as not_assessed until their flags flip.
Status matrix
| Capability | ADR | Issue | State | Flag / gate |
|---|---|---|---|---|
| Decimal ownership + Art. 54 + rights-type | 0130 | #540 | Live | additive |
| Effective-dated BO threshold snapshot | 0133 | #542 | Live | additive |
| Control-via-other-means (engine + graph) | 0134 | #538 / #630 | Live (concert/nominee graph population landed) | additive |
| Look-through arrangements + Art. 60 | 0135 | #539 / #632 | Live (fetch_arrangements + trust/foundation graph population landed) | additive |
| Art. 62 BO dataset shape | 0136 | #541 | Live | additive |
| Art. 62 data-currency monitoring | 0136 | #541 | Dark-launched | bo_dataset_currency_monitoring_enabled |
| Art. 62 identity-field population | 0136 | #541 / #634 | Live (populated from verified identity data; honest gap when no source) | additive |
| Sanctioned ownership / control (Art. 20(1)(d)) | 0127 | #535 | Dark-launched (read surface live) | sanctioned_ownership_control_enabled |
| Immediate-on-designation re-screen (Art. 26(4)) | 0128 | #536 | Dark-launched (read surface live) | immediate_designation_rescreen_enabled |
| Register-discrepancy report + lifecycle (Art. 24) | 0129 | #537 | Live | additive |
| SMO exhausted-means + tipping-off (Art. 22(2)) | 0126 | #534 | Live | additive |
| Sanctions FP-suppression never-suppress | 0131 | #587 | Live (fail-closed check + raw bypass); force-invalidation on the re-screen flag | partial |
| Dashboard-assistant Wave-1 + non-downgradable readiness | 0132 | #580 | Live | additive |
| Nine-measure CDD register (Art. 20(1)) | 0137 | #544 | Live; assistant (#601) + UI panel (#600) consumers live; PDF/dossier consumer follow-up | additive |
| Per-actor identity dataset (Art. 22(1)) | 0138 | #543 / #640 | Live compute + population | additive |
| Annex I/II/III factor annotation (Art. 20(2)-(3)) | 0141 | #551 | Live (read surface); goAML wiring folded into #608 | additive |
| Retention clock + Art. 77(1) assertion | 0139 | #548 | Live (derived) | additive |
| Retention holds + Art. 77(2) cap + no-purge guard | 0140 | #549 | Live guard; purge sweep dark-launched | retention_purge_enabled |
| Crypto-shred (audit + evidence PII) | 0142 | #550 | Dark-launched; memory/portal sinks deferred | crypto_shred_enabled; #676 / #677 |
| OSINT golden-record survivorship | 0123 | #512 | Dark-launched | golden_record_resolver_enabled |
| Hash-audited risk scoring | 0124 | #515 | Dark-launched | hash_audited_scoring_enabled |
| Connector data-contracts + drift | 0143 | #518 | Mechanism built/tested; live-parse wiring deferred; trust hook dark-launched | connector_contract_trust_enabled |
| Risk-appetite activation four-eyes | 0070 / 0092 | #626 | Live | maker_checker |
| Insufficient-data approval gate | 0125 | #532 | Dark-launched, log-first | approval_insufficient_data_gate_enabled |
| AMLR evidence dossier (PDF + JSON) | — | #592 | Live | additive |
Components
| File | Responsibility |
|---|---|
app/services/sanctioned_ownership.py | Art. 20(1)(d) 50%-rule over the ownership graph (ADR-0127) — dark-launched |
app/services/immediate_designation_rescreen_service.py | Art. 26(4) designation-delta + immediate re-screen enqueue (ADR-0128) — dark-launched |
app/services/register_discrepancy_service.py | Art. 24 discrepancy classification + 14-day lifecycle + self-healing escalation schedule (ADR-0129) |
app/services/smo_fallback_record.py | Art. 22(2) exhausted-means record + SMO verification gate (ADR-0126) |
app/services/amlr_section_c.py | Nine-measure CDD register + identity-dataset compute + _partition_ubos (ADR-0137/0138) |
app/services/cdd_register_input.py | Shared evidencing step for the caller-owned measures (c)/(d)/(f) + the screening_ran predicate — every register consumer runs it (#1004) |
app/services/identity_dataset_producer.py | Per-actor Art. 22(1) identity-dataset production (ADR-0138) |
app/services/amlr_annex_factors.py | Versioned Annex I/II/III factor loader + effective-dated signal mapping (ADR-0141) |
app/services/retention_clock_service.py | Art. 77(1) clock + retained-set assertion + RetentionHold honour (ADR-0139/0140) |
app/services/connector_contracts.py | Declarative connector schema validation + drift detection + per-field trust (ADR-0143) |
app/services/amlr_dossier_service.py | Consolidated PDF+JSON AMLR evidence dossier (#592) |
app/api/amlr_wave1.py | GET …/sanctioned-ownership, GET …/rescreen-events (fail-closed reads) |
app/api/amlr_bo.py | GET …/beneficial-owners — ownership/control/SMO + arrangement UBOs |
app/api/amlr_cdd.py | GET …/cdd-coverage — nine-measure register + trigger + identity datasets |
app/api/amlr_annex.py | GET …/annex-factors — Annex-factor annotation |
app/api/register_discrepancy.py | GET/POST …/register-discrepancies… — report + derogation lifecycle |
app/api/retention.py | GET …/retention — per-case Art. 77 clock + hold state |
app/api/case_pack.py | GET …/amlr-dossier — consolidated dossier export |
Related
- UBO Determination — the full beneficial-ownership engine (Art. 51–63)
- AMLR Compliance Controls — immutability, dissolved-entity block, typed CDD model, the audited downgrade
- Privacy & Data-Protection — crypto-shred and retention detail
- Sanctions Screening — the screening the ownership test reads
- Continuous Monitoring — the cadence the re-screen and currency checks ride
- Known Gaps & Roadmap — the honest register of dark-launched flags and deferred work
- ADRs: 0123, 0124, 0126, 0127, 0128, 0129, 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, 0138, 0139, 0140, 0141, 0142, 0143