The FactSet is built from the request payload plus three deterministic producers (OFAC screener, country-risk tier from the FATF list, travel-rule VASP registry). Layer 1 then evaluates every active rule as a pure function of the FactSet, emitting one per-rule signal. Layer 2 folds those signals into a single status using a fixed priority order. The pipeline is deterministic, fails closed on missing data, and carries no opaque scoring — every status is traceable to a rule id, a citation, and the facts that produced it.
Layer 1 is engine/rule_evaluator.py. Layer 2 is gating/composer.py. Producers run in engine/fact_builder.py.
Every transaction is evaluated by all active rules (Layer 1, pure per-rule signals), then a single composition function folds the signals into one status:
Layer 1 is ternary. Every predicate resolves to TRUE, FALSE, or UNKNOWN (fact missing). Each rule emits one of five signals that the composer above consumes:
NOT_APPLICABLE — the rule's applies_when scope is FALSE for this transaction.NOT_TRIGGERED — applicable, but the base condition is FALSE.TRIGGERED_NO_EXCEPTIONS — base TRUE and no exception applies; the rule's status contribution stands.TRIGGERED_WITH_EXCEPTION — base TRUE and a named exception is TRUE; the exception status applies and the exception name is recorded.INCOMPLETE_DATA — a required fact is UNKNOWN; missing fact names are surfaced on the flag.Fail-closed rule: UNKNOWN in a base condition ⇒ INCOMPLETE_DATA ⇒ INCOMPLETE status (the transaction cannot be approved on missing facts). UNKNOWN in an exception ⇒ the exception is rejected (a caller cannot unlock an exception path by omitting the fact that would prove it).
Source of truth: gating/composer.py::compose. The code below is fetched from the running engine at /v1/gating/source — it cannot drift from what executes.
loading…
Loaded live from /v1/rules?scope=transaction. Each rule is a versioned YAML file with full citation; the engine evaluates exactly these files. Use the buttons below each rule to record ADGM verdicts in real time.
Before Layer 1 runs, engine/fact_builder.py augments the request payload with derived facts from three deterministic producers. For security-critical namespaces (sanctions.*, investor.country_risk_tier) the producer wins — a caller-supplied value that conflicts with the producer is overridden and logged, so an integrating platform cannot blind the screen. Loaded live from /v1/facts/producers.
Each card POSTs its payload to /v1/check on this server when the page loads — results below are live engine output, not screenshots.
Of the total rule library, … rules are evaluated per transaction; the remaining … are parked (asset-scope, institution-onboarding scope, or pending citation verification). They live in rules/_parked/ and are listed live from /v1/rules?scope=parked; hover a reason for the full rationale.
| Rule ID | Jurisdiction | Title | Why parked |
|---|