Skip to main content

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

LabelMeaning
LiveOn in production by construction (additive, no flag) or behind a default-on flag. It runs on every applicable case now.
Dark-launchedCode 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.
DeferredA 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.Decimal under a fixed context, so identical graphs yield byte-identical aggregates and a reproducible golden_record_hash; THRESHOLD_EPSILON was 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. Every BeneficialOwnerResult carries a ThresholdSnapshot recording 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); the fetch_arrangements reader + 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 / issueEngine stateRead surface
Sanctioned ownership / control — the 50%-rule over the ownership graph, individually or collectively (Art. 20(1)(d))ADR-0127 / #535Dark-launched (sanctioned_ownership_control_enabled=false)GET /api/cases/{id}/sanctioned-ownershipLive, 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 / #536Dark-launched (immediate_designation_rescreen_enabled=false)GET /api/cases/{id}/rescreen-eventsLive
BO register-discrepancy report + Art. 24(2) derogation + 14-day lifecycleADR-0129 / #537Live — wired into cross_reference_evidence_activity; a self-healing escalation Temporal schedule is provisioned at startupGET /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 / #534Live — the record_no_bo_identified Temporal activity writes an immutable record when no natural-person BO is identifiedGET /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_register models all nine Art. 20(1) measures with a four-value state (satisfied / partial / not_assessed / not_applicable, the last strictly distinct from a gap), reusing interpret_cdd_coverage internally 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-closed not_assessed unless real evidence is supplied. The assistant consumer (#601) and the CDD-coverage UI panel (#600) are now live on the register, as is the GET /api/cases/{id}/cdd-coverage read 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_register is 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 back not_assessed on cases where the evidence existed and had been collected. cdd_register_input.evidence_caller_owned_measures is now the single evidencing step every caller runs before compute_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. The screening_ran predicate 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 CddCoverageInput and the persisted latest investigation; a field with no evidenced source honestly stays not_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_result maps 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 flagged in_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 canonical determine_erasure_action policy (so the clock can never be more permissive than the purge). An unknown or active clock is never eligible for erasure. Surfaced at GET /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 extends retain_until, clamped to statutory + 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_chain passes 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 proven survivorship.py (ADR-0024/0073) into the investigation path: typed per-source Claims with a centrally stamped source_trust, a deterministic winner order (no wall-clock), losers retained as alternatives, and a golden_record_hash. Retires the mutation_queue.
  • Deterministic, hash-audited risk scoring (ADR-0124, #515) — Dark-launched (hash_audited_scoring_enabled=false). A run_hash over the canonicalised scoring inputs + every lever (engine/matrix version, reference snapshots, matcher config, dataset snapshot, normalizer versions), append-only scoring_runs chained via ADR-0109, and the LLM risk_score demoted off the authoritative decision (the EBA composite is authoritative everywhere; the LLM value is labelled raw_synthesis_risk_score).
  • Declarative connector data-contracts + drift detection (ADR-0143, #518) — the validator, the loud connector_contract_drift alert, the drift_to_finding mechanism and the per-field trust manifest are built and unit-tested; wiring validate_response into 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_thresholds ceilings) 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 logs approval.insufficient_data.would_block and writes an immutable audit row with zero behaviour change; the Phase-2 flip to a hard 409 (overridable only via an audited, four-eyes-routed override_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:

  1. the AI evidence bundles (ADR-0021 — SHA-256 data_hash, confidence, provenance, chain-of-thought, model/prompt metadata);
  2. the four Wave-1 determinations (§2), each with its state, regulatory_basis, adr and provenance;
  3. 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

CapabilityADRIssueStateFlag / gate
Decimal ownership + Art. 54 + rights-type0130#540Liveadditive
Effective-dated BO threshold snapshot0133#542Liveadditive
Control-via-other-means (engine + graph)0134#538 / #630Live (concert/nominee graph population landed)additive
Look-through arrangements + Art. 600135#539 / #632Live (fetch_arrangements + trust/foundation graph population landed)additive
Art. 62 BO dataset shape0136#541Liveadditive
Art. 62 data-currency monitoring0136#541Dark-launchedbo_dataset_currency_monitoring_enabled
Art. 62 identity-field population0136#541 / #634Live (populated from verified identity data; honest gap when no source)additive
Sanctioned ownership / control (Art. 20(1)(d))0127#535Dark-launched (read surface live)sanctioned_ownership_control_enabled
Immediate-on-designation re-screen (Art. 26(4))0128#536Dark-launched (read surface live)immediate_designation_rescreen_enabled
Register-discrepancy report + lifecycle (Art. 24)0129#537Liveadditive
SMO exhausted-means + tipping-off (Art. 22(2))0126#534Liveadditive
Sanctions FP-suppression never-suppress0131#587Live (fail-closed check + raw bypass); force-invalidation on the re-screen flagpartial
Dashboard-assistant Wave-1 + non-downgradable readiness0132#580Liveadditive
Nine-measure CDD register (Art. 20(1))0137#544Live; assistant (#601) + UI panel (#600) consumers live; PDF/dossier consumer follow-upadditive
Per-actor identity dataset (Art. 22(1))0138#543 / #640Live compute + populationadditive
Annex I/II/III factor annotation (Art. 20(2)-(3))0141#551Live (read surface); goAML wiring folded into #608additive
Retention clock + Art. 77(1) assertion0139#548Live (derived)additive
Retention holds + Art. 77(2) cap + no-purge guard0140#549Live guard; purge sweep dark-launchedretention_purge_enabled
Crypto-shred (audit + evidence PII)0142#550Dark-launched; memory/portal sinks deferredcrypto_shred_enabled; #676 / #677
OSINT golden-record survivorship0123#512Dark-launchedgolden_record_resolver_enabled
Hash-audited risk scoring0124#515Dark-launchedhash_audited_scoring_enabled
Connector data-contracts + drift0143#518Mechanism built/tested; live-parse wiring deferred; trust hook dark-launchedconnector_contract_trust_enabled
Risk-appetite activation four-eyes0070 / 0092#626Livemaker_checker
Insufficient-data approval gate0125#532Dark-launched, log-firstapproval_insufficient_data_gate_enabled
AMLR evidence dossier (PDF + JSON)#592Liveadditive

Components

FileResponsibility
app/services/sanctioned_ownership.pyArt. 20(1)(d) 50%-rule over the ownership graph (ADR-0127) — dark-launched
app/services/immediate_designation_rescreen_service.pyArt. 26(4) designation-delta + immediate re-screen enqueue (ADR-0128) — dark-launched
app/services/register_discrepancy_service.pyArt. 24 discrepancy classification + 14-day lifecycle + self-healing escalation schedule (ADR-0129)
app/services/smo_fallback_record.pyArt. 22(2) exhausted-means record + SMO verification gate (ADR-0126)
app/services/amlr_section_c.pyNine-measure CDD register + identity-dataset compute + _partition_ubos (ADR-0137/0138)
app/services/cdd_register_input.pyShared 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.pyPer-actor Art. 22(1) identity-dataset production (ADR-0138)
app/services/amlr_annex_factors.pyVersioned Annex I/II/III factor loader + effective-dated signal mapping (ADR-0141)
app/services/retention_clock_service.pyArt. 77(1) clock + retained-set assertion + RetentionHold honour (ADR-0139/0140)
app/services/connector_contracts.pyDeclarative connector schema validation + drift detection + per-field trust (ADR-0143)
app/services/amlr_dossier_service.pyConsolidated PDF+JSON AMLR evidence dossier (#592)
app/api/amlr_wave1.pyGET …/sanctioned-ownership, GET …/rescreen-events (fail-closed reads)
app/api/amlr_bo.pyGET …/beneficial-owners — ownership/control/SMO + arrangement UBOs
app/api/amlr_cdd.pyGET …/cdd-coverage — nine-measure register + trigger + identity datasets
app/api/amlr_annex.pyGET …/annex-factors — Annex-factor annotation
app/api/register_discrepancy.pyGET/POST …/register-discrepancies… — report + derogation lifecycle
app/api/retention.pyGET …/retention — per-case Art. 77 clock + hold state
app/api/case_pack.pyGET …/amlr-dossier — consolidated dossier export