ADR-0062: Typed SubjectEntity + PurposeProfile models
Status: Accepted Date: 2026-06-16 Supersedes: none Superseded by: none Deciders: Adrian (Soft4U), Claude Opus 4.8
Decision context:
- Latency: none — pure in-memory Pydantic models + a rule helper.
- Dependency surface: no new packages. Adds two model modules + a
purpose_requirementshelper. - Debuggability: typed fields + computed signals (
address_divergence,sdd_eligible) are explicit and unit-tested. - Reversibility: branch revert; additive (no existing model changed).
- Blast radius: additive; the existing
CompanyProfilefact-bag and JSONB layers are untouched. - Alternative considered: keep the untyped fact-bag (rejected — the gaps are structural, not config).
Context
- The onboarded business (AMLR §1 SubjectEntity) was an untyped
CompanyProfile.factsbag + scatteredCasecolumns, with no first-classregister_name, noprincipal_place_of_businessdistinct fromregistered_address(their divergence is a risk signal), and nois_regulated_entity/listed_on_regulated_marketflags (which drive SDD eligibility / UBO exemptions). - Purpose & intended nature (AMLR Art. 20(1)(c), §5 PurposeProfile) were assembled ad-hoc from loose
additional_dataJSONB at memo time;stated_purposewas free text, not a monitorable enum baseline.
Decision
Add two typed Pydantic models + small rule helpers:
SubjectEntity(subject_entity.py): first-classregister_name,registered_addressvsprincipal_place_of_business,is_regulated_entity,listed_on_regulated_market; computedaddress_divergence(risk signal) andsdd_eligible.PurposeProfile(purpose_profile.py):StatedPurposeenum,ExpectedActivity(volumes/values/ geos/counterparties),source_of_funds,source_of_wealth;missing_purpose_requirements(profile, risk_tier)enforces SoF at standard/high and SoW at high (configurable).
Consequences
Positive
- SubjectEntity + PurposeProfile are first-class typed models; address divergence and SDD eligibility are
computed signals;
stated_purposeis an enum baseline for transaction monitoring; SoF/SoW gaps are tier-aware.
Negative
- Two more models alongside the existing fact-bag/JSONB — intentional (typed > untyped for AMLR §1/§5);
populating them from the existing layers + risk-engine consumption of
address_divergenceare config follow-ups.
Neutral
- No persistence/migration; field-level requirement stays configurable (draft RTS Art. 28(1)).
Alternatives Considered
Alternative 1: Keep the untyped CompanyProfile fact-bag
Rejected — the issues are explicit that these are structural (typed) gaps, not config. An enum
stated_purpose and a first-class principal_place_of_business cannot be expressed as loose facts while
serving as a monitoring baseline / risk signal.