""" Task bank for the PayOps environment — 30 tasks across 4 difficulty tiers. Difficulty tiers ---------------- easy – single clear signal; one-hop decision (6 tasks) medium – ambiguous or competing signals; reasoning required (8 tasks) hard – adversarial, conflicting, or edge-case patterns (10 tasks) critical – multi-step investigation chains; regulatory compliance stakes (6 tasks) Multi-step chains ----------------- Tasks with chain_total > 1 require the agent to issue investigation sub-actions (inspect / request_docs / verify_kyc / contact_sender) before a terminal decision. Each chain step reveals progressively more context via the appropriate _reveal field. The terminal decision is only scored on the final chain step. Action costs ------------ Each investigation sub-action incurs a budget cost. If the agent exhausts the budget (spend > budget_limit), a cumulative cost penalty is applied to the reward. inspect → cost 0.1 request_docs → cost 0.2 verify_kyc → cost 0.2 contact_sender → cost 0.3 file_sar → cost 0.1 (required for structuring/AML — free if correct) Reward structure per task ------------------------- Each task defines: correct_action – ground-truth terminal decision partial_credit_actions – {action: fraction_of_full_credit} requires_investigation – set of sub-actions ideally used before deciding regulatory_action – if True, file_sar is a required prerequisite for full credit """ from __future__ import annotations from dataclasses import dataclass, field from typing import Dict, List, Optional, Set @dataclass class PayOpsTask: # --- identity --- task_id: str difficulty: str # easy | medium | hard | critical description: str # --- transaction fields --- transaction_id: str amount: float currency: str sender: str receiver: str transaction_type: str # transfer | payment | withdrawal | refund | internal | loan_repayment | payroll # --- primary risk signals --- risk_score: float # 0.0–1.0 ml_confidence: float = 0.90 # model's confidence in its own risk_score flags: List[str] = field(default_factory=list) # --- sender behaviour --- velocity_1h: Optional[int] = None velocity_24h: Optional[int] = None avg_transaction_amount: Optional[float] = None account_age_days: Optional[int] = None # --- counterparty / geo --- country_risk: Optional[str] = None # low | medium | high | sanctioned kyc_status: Optional[str] = None # verified | pending | failed | none | expired kyc_expiry_days: Optional[int] = None previous_violations: Optional[int] = None previous_sars: Optional[int] = None counterparty_risk: Optional[str] = None # clean | unknown | watchlist | blacklist # --- grading --- correct_action: str = "approve" partial_credit_actions: Dict[str, float] = field(default_factory=dict) requires_investigation: Set[str] = field(default_factory=set) # Set of sub-actions the agent SHOULD use for best score. # Using them before the terminal decision grants a bonus (applied in grader). regulatory_action: bool = False # If True, filing a SAR is required to get full credit. # --- investigation reveals (each unlocked by the matching sub-action) --- inspect_reveal: Optional[str] = None docs_reveal: Optional[str] = None kyc_reveal: Optional[str] = None contact_reveal: Optional[str] = None # --- key diagnostic flags (used for flag-identification reward shaping) --- key_flags: List[str] = field(default_factory=list) # Subset of flags that are causally decisive for the correct_action. # An agent that calls inspect AND those flags are in the observation # receives an additional flag_identification bonus in the grader. # --- network graph (mule chain / correspondent bank relationships) --- network_graph: Optional[Dict[str, str]] = None # Optional dict mapping roles to entity IDs, e.g.: # {"sender": "acc_123", "mule": "acc_456", "offshore": "bank_789"} # Exposed in observation so agents can reason about chains. # --- multi-step chain metadata --- chain_total: int = 1 # chain_total > 1 means the task spans multiple investigation steps. # The grader only scores the terminal decision on the last chain step. @property def grader(self) -> dict: """Grader configuration for this task. Exposed as a computed property so platform validators that import ``TASKS`` from tasks.py can discover grader configs via ``task.grader``. """ return { "enabled": True, "type": "action_match", "correct_action": self.correct_action, "partial_credit": dict(self.partial_credit_actions), "requires_investigation": list(self.requires_investigation), "regulatory_action": self.regulatory_action, "key_flags": list(self.key_flags), } # ============================================================================= # EASY (4 tasks — single clear signal) # ============================================================================= EASY_TASKS: List[PayOpsTask] = [ PayOpsTask( task_id="EASY-001", difficulty="easy", description="Low-value domestic transfer between two long-standing verified users. No flags.", transaction_id="TXN-E001", amount=47.50, currency="USD", sender="alice@example.com", receiver="bob@example.com", transaction_type="transfer", risk_score=0.05, ml_confidence=0.97, flags=[], velocity_1h=1, velocity_24h=3, avg_transaction_amount=55.0, account_age_days=1_095, country_risk="low", kyc_status="verified", kyc_expiry_days=320, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="approve", partial_credit_actions={}, # no partial credit — clear approve, wrong actions penalised inspect_reveal="Sender account 3 yrs old. Consistent transaction history. KYC fully verified.", key_flags=[], ), PayOpsTask( task_id="EASY-002", difficulty="easy", description="Textbook fraud: unknown sender, offshore, sanctioned country, risk=0.97.", transaction_id="TXN-E002", amount=9_800.00, currency="USD", sender="unknown_user_x99", receiver="offshore_acct_442", transaction_type="transfer", risk_score=0.97, ml_confidence=0.95, flags=["high_value", "unknown_sender", "offshore_receiver", "sanctioned_country"], velocity_1h=12, velocity_24h=28, avg_transaction_amount=None, account_age_days=2, country_risk="sanctioned", kyc_status="none", kyc_expiry_days=None, previous_violations=3, previous_sars=1, counterparty_risk="blacklist", correct_action="reject", partial_credit_actions={"escalate": 0.4}, # flag removed — textbook fraud needs decisive action inspect_reveal="Receiver linked to 3 prior rejected transactions. IP on threat-intel blocklist.", key_flags=["sanctioned_country", "unknown_sender"], network_graph={"sender": "unknown_user_x99", "offshore": "offshore_acct_442"}, ), PayOpsTask( task_id="EASY-003", difficulty="easy", description="Standard refund to a verified customer. Tiny amount, no flags.", transaction_id="TXN-E003", amount=12.99, currency="USD", sender="shop@retailer.com", receiver="carol@customer.com", transaction_type="refund", risk_score=0.03, ml_confidence=0.98, flags=[], velocity_1h=1, velocity_24h=5, avg_transaction_amount=14.0, account_age_days=730, country_risk="low", kyc_status="verified", kyc_expiry_days=180, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="approve", partial_credit_actions={}, # no partial credit — tiny refund, no flags, flagging is wrong inspect_reveal="Standard return via web checkout. Matching order ID on file. No anomalies.", key_flags=[], ), PayOpsTask( task_id="EASY-004", difficulty="easy", description="ATM withdrawal burst — 15 withdrawals in 58 minutes across 4 ATMs. Velocity violation.", transaction_id="TXN-E004", amount=200.00, currency="USD", sender="david@accounts.com", receiver="atm_node_77", transaction_type="withdrawal", risk_score=0.78, ml_confidence=0.89, flags=["velocity_breach", "atm_burst"], velocity_1h=15, velocity_24h=17, avg_transaction_amount=80.0, account_age_days=540, country_risk="medium", kyc_status="verified", kyc_expiry_days=90, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="flag", partial_credit_actions={"escalate": 0.6, "hold": 0.5}, inspect_reveal="15 ATM withdrawals in 58 min across 4 ATMs. Pattern consistent with card clone.", key_flags=["velocity_breach"], ), PayOpsTask( task_id="EASY-005", difficulty="easy", description="Scheduled monthly mortgage repayment. Regular amount, verified on-time borrower, no flags.", transaction_id="TXN-E005", amount=1_247.00, currency="USD", sender="john.doe@homeowner.com", receiver="mortgages@national-bank.com", transaction_type="loan_repayment", risk_score=0.03, ml_confidence=0.99, flags=[], velocity_1h=1, velocity_24h=1, avg_transaction_amount=1_247.00, account_age_days=1_825, country_risk="low", kyc_status="verified", kyc_expiry_days=280, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="approve", partial_credit_actions={}, inspect_reveal="Loan account in good standing. Payment 22 of 360. Amount matches amortization schedule exactly.", key_flags=[], ), PayOpsTask( task_id="EASY-006", difficulty="easy", description="Suspected duplicate payment: same sender, receiver, and amount submitted twice within 4 minutes.", transaction_id="TXN-E006", amount=523.50, currency="USD", sender="vendor@supplies-ltd.com", receiver="buyer@procurement.com", transaction_type="payment", risk_score=0.55, ml_confidence=0.91, flags=["duplicate_transaction", "velocity_breach"], velocity_1h=2, velocity_24h=3, avg_transaction_amount=520.0, account_age_days=980, country_risk="low", kyc_status="verified", kyc_expiry_days=150, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="flag", partial_credit_actions={"hold": 0.6}, inspect_reveal="TXN-E006-prev processed 4 min ago for identical amount and beneficiary. Duplicate submission detected — flag for double-charge review before releasing.", key_flags=["duplicate_transaction"], ), ] # ============================================================================= # MEDIUM (8 tasks — ambiguous, multi-signal) # ============================================================================= MEDIUM_TASKS: List[PayOpsTask] = [ PayOpsTask( task_id="MED-001", difficulty="medium", description="Large B2B wire. Verified CFO. Cross-border to medium-risk EU jurisdiction.", transaction_id="TXN-M001", amount=85_000.00, currency="EUR", sender="cfo@globalcorp.com", receiver="vendor@eu-supplier.de", transaction_type="transfer", risk_score=0.52, ml_confidence=0.72, flags=["high_value", "cross_border"], velocity_1h=2, velocity_24h=4, avg_transaction_amount=45_000.0, account_age_days=2_190, country_risk="medium", kyc_status="verified", kyc_expiry_days=200, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="escalate", partial_credit_actions={"flag": 0.5, "hold": 0.4}, requires_investigation={"inspect"}, inspect_reveal="Contract on file for €85k milestone. Receiver is licensed EU entity. Quarterly vendor pattern.", key_flags=["cross_border", "high_value"], ), PayOpsTask( task_id="MED-002", difficulty="medium", description="Internal treasury transfer. Large amount. KYC pending renewal — risk is procedural, not fraud.", transaction_id="TXN-M002", amount=250_000.00, currency="USD", sender="treasury@holdco.com", receiver="subsidiary@holdco-us.com", transaction_type="internal", risk_score=0.41, ml_confidence=0.78, flags=["high_value", "kyc_expiry_90d"], velocity_1h=1, velocity_24h=2, avg_transaction_amount=200_000.0, account_age_days=3_650, country_risk="low", kyc_status="pending", kyc_expiry_days=5, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="hold", partial_credit_actions={"escalate": 0.6, "flag": 0.4}, requires_investigation={"verify_kyc"}, kyc_reveal="KYC renewal submitted 10 days ago. Both accounts share same UBO. Transfer aligns with Q1 plan.", key_flags=["kyc_expiry_90d"], ), PayOpsTask( task_id="MED-003", difficulty="medium", description="Subscription payment 3× historical average. Possible upgrade billing or card compromise.", transaction_id="TXN-M003", amount=449.97, currency="USD", sender="eve@subscriber.com", receiver="billing@saas-platform.com", transaction_type="payment", risk_score=0.44, ml_confidence=0.68, flags=["amount_spike", "pattern_deviation"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=149.99, account_age_days=820, country_risk="low", kyc_status="verified", kyc_expiry_days=150, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="flag", partial_credit_actions={"hold": 0.5, "escalate": 0.3}, inspect_reveal="Historical monthly charge $149.99. This charge = 3-month annual upgrade. Merchant confirmation pending.", key_flags=["amount_spike", "pattern_deviation"], ), PayOpsTask( task_id="MED-004", difficulty="medium", description="Payment to regulated crypto exchange. Moderate risk. Sender has clean history.", transaction_id="TXN-M004", amount=5_000.00, currency="USD", sender="frank@personal.com", receiver="exchange@cryptovault.io", transaction_type="payment", risk_score=0.58, ml_confidence=0.74, flags=["crypto_exchange", "high_value"], velocity_1h=1, velocity_24h=2, avg_transaction_amount=2_000.0, account_age_days=1_460, country_risk="medium", kyc_status="verified", kyc_expiry_days=270, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="flag", partial_credit_actions={"escalate": 0.5, "hold": 0.4}, inspect_reveal="Exchange licensed in sender's jurisdiction. Sender made 4 similar payments over 6 months. Within limits.", key_flags=["crypto_exchange"], ), PayOpsTask( task_id="MED-005", difficulty="medium", description="Expired KYC on a high-frequency corporate account. Routine transactions continue but KYC lapsed 12 days ago.", transaction_id="TXN-M005", amount=28_000.00, currency="GBP", sender="payments@logistics-uk.com", receiver="driver-pool@payroll.co.uk", transaction_type="payroll", risk_score=0.38, ml_confidence=0.82, flags=["kyc_expired", "high_value"], velocity_1h=1, velocity_24h=6, avg_transaction_amount=26_000.0, account_age_days=1_800, country_risk="low", kyc_status="expired", kyc_expiry_days=-12, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="hold", partial_credit_actions={"flag": 0.5, "escalate": 0.4}, requires_investigation={"verify_kyc"}, kyc_reveal="KYC expired 12 days ago due to administrative oversight. Re-submission in progress. No fraud indicators.", key_flags=["kyc_expired"], ), PayOpsTask( task_id="MED-006", difficulty="medium", description="Real estate advance payment. Large amount. First payment to this receiver but contract exists.", transaction_id="TXN-M006", amount=120_000.00, currency="USD", sender="buyer@realestate-client.com", receiver="escrow@property-agent.co", transaction_type="transfer", risk_score=0.56, ml_confidence=0.65, flags=["high_value", "first_time_payee", "large_first_transfer"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=5_000.0, account_age_days=2_555, country_risk="medium", kyc_status="verified", kyc_expiry_days=90, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="escalate", partial_credit_actions={"flag": 0.4, "hold": 0.5}, requires_investigation={"request_docs"}, docs_reveal="Signed purchase agreement found. Escrow agent licensed and registered. Standard conveyancing practice.", key_flags=["first_time_payee", "large_first_transfer"], ), PayOpsTask( task_id="MED-007", difficulty="medium", description=( "Supplier emails to say bank details have changed; first payment to the " "new account matches a large outstanding invoice. Classic BEC indicator." ), transaction_id="TXN-M007", amount=43_800.00, currency="EUR", sender="accounts@client-corp.de", receiver="supplier-newacct@euro-clearing.eu", transaction_type="payment", risk_score=0.63, ml_confidence=0.67, flags=["bank_detail_change", "first_time_payee", "high_value"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=44_000.0, account_age_days=1_200, country_risk="medium", kyc_status="verified", kyc_expiry_days=130, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="hold", partial_credit_actions={"escalate": 0.6, "flag": 0.4}, requires_investigation={"request_docs", "contact_sender"}, docs_reveal="Email requesting bank change was sent from supplier-noreply@euro-cIearing.eu (capital I, not l). No signed bank-change authorisation on file. Possible BEC.", contact_reveal="Called original supplier contact number on record. They confirmed they did NOT send a bank-change notice. BEC fraud confirmed — hold and alert client.", key_flags=["bank_detail_change", "first_time_payee"], ), PayOpsTask( task_id="MED-008", difficulty="medium", description=( "Buy Now Pay Later purchase: high-value electronics order, account opened " "3 weeks ago, thin credit file, elevated risk signals." ), transaction_id="TXN-M008", amount=2_899.00, currency="USD", sender="bnpl-platform@payin4.io", receiver="electronics@megastore.com", transaction_type="payment", risk_score=0.60, ml_confidence=0.71, flags=["thin_credit_file", "high_value_bnpl", "new_account"], velocity_1h=1, velocity_24h=2, avg_transaction_amount=None, account_age_days=21, country_risk="low", kyc_status="pending", kyc_expiry_days=None, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="flag", partial_credit_actions={"hold": 0.6, "escalate": 0.4}, requires_investigation={"verify_kyc"}, kyc_reveal="KYC check: identity document submitted but address verification failed — billing address does not match ID. BNPL fraud profile. Flag for manual identity review before releasing funds.", key_flags=["thin_credit_file", "new_account"], ), ] # ============================================================================= # HARD (10 tasks — adversarial, conflicting, edge-cases) # ============================================================================= HARD_TASKS: List[PayOpsTask] = [ PayOpsTask( task_id="HARD-001", difficulty="hard", description="ML model poisoning: risk_score=0.18 but manual flags scream fraud. Agent must override the model.", transaction_id="TXN-H001", amount=47_000.00, currency="GBP", sender="grace@newaccount.co.uk", receiver="payee@solicitor-uk.com", transaction_type="transfer", risk_score=0.18, # poisoned — deliberately underscored ml_confidence=0.31, # low confidence signals model uncertainty flags=["new_account_7d", "large_first_transfer", "solicitor_mule_pattern"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=None, account_age_days=7, country_risk="low", kyc_status="pending", kyc_expiry_days=None, previous_violations=0, previous_sars=0, counterparty_risk="watchlist", correct_action="escalate", partial_credit_actions={"flag": 0.6, "hold": 0.5, "reject": 0.3}, requires_investigation={"inspect"}, inspect_reveal=( "Account 7 days old. First outbound transfer. Receiver matches solicitor-impersonation " "mule pattern from last month's intelligence bulletin. ML underscored — new clean account." ), key_flags=["solicitor_mule_pattern"], ), PayOpsTask( task_id="HARD-002", difficulty="hard", description="APP scam: victim willingly sending money but has been social-engineered. Receiver is a mule.", transaction_id="TXN-H002", amount=12_500.00, currency="GBP", sender="henry@victim-bank.co.uk", receiver="mule_relay_2287@fastpay.io", transaction_type="transfer", risk_score=0.61, ml_confidence=0.77, flags=["app_scam_indicator", "mule_account_pattern", "first_time_payee"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=300.0, account_age_days=3_285, country_risk="medium", kyc_status="verified", kyc_expiry_days=180, previous_violations=0, previous_sars=0, counterparty_risk="blacklist", correct_action="reject", partial_credit_actions={"escalate": 0.6, "hold": 0.5, "flag": 0.3}, requires_investigation={"contact_sender"}, contact_reveal=( "Sender says they were called by someone claiming to be their bank. " "Instructed to move savings to a 'safe account'. Classic APP scam confirmed." ), key_flags=["app_scam_indicator", "mule_account_pattern"], network_graph={"victim": "henry@victim-bank.co.uk", "mule": "mule_relay_2287@fastpay.io"}, ), PayOpsTask( task_id="HARD-003", difficulty="hard", description="Structuring/smurfing: just-below-CTR-threshold payments from same beneficial owner across accounts.", transaction_id="TXN-H003", amount=9_450.00, # just below $10k CTR threshold currency="USD", sender="irene_acct_A@shadow.net", receiver="irene_acct_B@shadow.net", transaction_type="transfer", risk_score=0.71, ml_confidence=0.83, flags=["structuring_pattern", "same_ubo", "ctr_threshold_avoidance", "high_value"], velocity_1h=3, velocity_24h=9, avg_transaction_amount=9_200.0, account_age_days=120, country_risk="high", kyc_status="failed", kyc_expiry_days=None, previous_violations=2, previous_sars=1, counterparty_risk="watchlist", correct_action="reject", partial_credit_actions={"escalate": 0.5, "flag": 0.2}, requires_investigation={"inspect"}, regulatory_action=True, inspect_reveal=( "3 transactions in 24h: $9,450 + $9,200 + $9,100 from related accounts. " "Same UBO. KYC failed on inconsistent ID docs. Classic CTR structuring." ), key_flags=["structuring_pattern", "ctr_threshold_avoidance"], network_graph={"node_a": "irene_acct_A@shadow.net", "node_b": "irene_acct_B@shadow.net", "ubo": "irene_ubo"}, ), PayOpsTask( task_id="HARD-004", difficulty="hard", description="Legitimate FX settlement: huge amount, looks alarming — is a standard correspondent banking transfer.", transaction_id="TXN-H004", amount=4_200_000.00, currency="USD", sender="nostro@bank-a-swift.com", receiver="vostro@bank-b-swift.com", transaction_type="internal", risk_score=0.67, ml_confidence=0.58, # model uncertain on legitimate large transfers flags=["high_value", "cross_border", "fx_settlement"], velocity_1h=8, velocity_24h=24, avg_transaction_amount=3_900_000.0, account_age_days=7_300, country_risk="medium", kyc_status="verified", kyc_expiry_days=400, previous_violations=0, previous_sars=0, counterparty_risk="clean", correct_action="approve", partial_credit_actions={"escalate": 0.5, "hold": 0.4, "flag": 0.3}, requires_investigation={"inspect"}, inspect_reveal=( "Both SWIFT BIC-verified. Part of daily USD/EUR FX settlement cycle. " "8 similar settlements this month, all cleared. Nostro/vostro agreement on file." ), key_flags=["fx_settlement"], network_graph={"correspondent": "nostro@bank-a-swift.com", "settlement": "vostro@bank-b-swift.com"}, ), PayOpsTask( task_id="HARD-005", difficulty="hard", description=( "Insider threat: employee of the bank initiating an unauthorised wire " "to their personal account, disguised as a vendor payment." ), transaction_id="TXN-H005", amount=22_000.00, currency="USD", sender="staff_ops@bank-internal.com", receiver="jake.smith.personal@gmail.com", transaction_type="payment", risk_score=0.54, ml_confidence=0.61, flags=["internal_to_personal", "unusual_beneficiary", "after_hours"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=75_000.0, account_age_days=2_000, country_risk="low", kyc_status="verified", kyc_expiry_days=240, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="escalate", partial_credit_actions={"flag": 0.5, "hold": 0.4, "reject": 0.3}, requires_investigation={"inspect", "contact_sender"}, inspect_reveal=( "Initiation time: 11:47 PM. No vendor contract matches this beneficiary. " "Staff member placed on PIP last week. Receiver email matches staff home address." ), contact_reveal=( "Staff member claims it is a legitimate vendor. Unable to provide contract reference. " "Story changes on follow-up. Escalate to HR and Fraud immediately." ), key_flags=["internal_to_personal", "unusual_beneficiary"], ), PayOpsTask( task_id="HARD-006", difficulty="hard", description=( "Ghost account: receiver account was dormant for 5 years and suddenly " "received 20 inbound transfers this week. Possible account takeover." ), transaction_id="TXN-H006", amount=3_200.00, currency="EUR", sender="layla@verified-sender.eu", receiver="old_dormant_acct_889@bank.eu", transaction_type="transfer", risk_score=0.63, ml_confidence=0.69, flags=["dormant_receiver", "sudden_activity", "high_inbound_velocity"], velocity_1h=1, velocity_24h=3, avg_transaction_amount=800.0, account_age_days=890, country_risk="medium", kyc_status="verified", kyc_expiry_days=60, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="flag", partial_credit_actions={"hold": 0.6, "escalate": 0.5}, requires_investigation={"inspect"}, inspect_reveal=( "Receiver dormant 5 years. 20 inbound transfers this week totalling €64k. " "All immediately forwarded offshore. Classic money-mule account reactivation." ), key_flags=["dormant_receiver", "sudden_activity"], ), PayOpsTask( task_id="HARD-007", difficulty="hard", description=( "SIM-swap attack: account holder's phone number was ported to a new SIM " "6 hours ago; account now requesting a large crypto withdrawal to a new address." ), transaction_id="TXN-H007", amount=14_200.00, currency="USD", sender="crypto-user@exchange.io", receiver="external-wallet-9f3a@coldwallet.net", transaction_type="withdrawal", risk_score=0.74, ml_confidence=0.78, flags=["sim_swap_detected", "new_withdrawal_address", "high_value"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=500.0, account_age_days=620, country_risk="low", kyc_status="verified", kyc_expiry_days=195, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="reject", partial_credit_actions={"hold": 0.5, "escalate": 0.4, "flag": 0.3}, requires_investigation={"inspect", "contact_sender"}, inspect_reveal=( "Telecom provider confirmed: SIM swap on this account's registered phone " "completed 6 h 12 min ago. External wallet address first appeared in this " "session only. IP geolocation mismatch vs prior 90-day baseline." ), contact_reveal=( "Attempted call to registered phone: intercepted by new SIM holder, " "who provided all correct security answers (likely obtained via social " "engineering). Account holder unreachable on backup email. SIM-swap " "takeover confirmed — reject and lock account." ), key_flags=["sim_swap_detected", "new_withdrawal_address"], ), PayOpsTask( task_id="HARD-008", difficulty="hard", description=( "Romance scam / pig butchering: customer making 4th escalating transfer " "to an overseas 'romantic partner' met on a dating app 7 weeks ago. " "Amounts have doubled each time." ), transaction_id="TXN-H008", amount=8_000.00, currency="USD", sender="victim@personal-bank.com", receiver="crypto-invest@hongkong-fund.net", transaction_type="transfer", risk_score=0.66, ml_confidence=0.73, flags=["romance_scam_indicator", "escalating_transfers", "offshore_receiver"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=1_200.0, account_age_days=4_015, country_risk="high", kyc_status="verified", kyc_expiry_days=220, previous_violations=0, previous_sars=0, counterparty_risk="blacklist", correct_action="reject", partial_credit_actions={"hold": 0.5, "escalate": 0.4, "flag": 0.3}, requires_investigation={"contact_sender", "inspect"}, contact_reveal=( "Spoke with account holder. They believe they are investing with a " "partner they met on a dating app. Prior transfers: $500, $1,000, $4,000. " "Receiver domain registered 3 months ago. Pig-butchering romance scam confirmed." ), inspect_reveal=( "Transfer history: $500 → $1,000 → $4,000 → $8,000 over 7 weeks. " "Receiver account linked to 23 other inbound romance-scam reports in the " "financial intelligence database. All prior transfers drained offshore within hours." ), key_flags=["romance_scam_indicator", "escalating_transfers"], network_graph={"victim": "victim@personal-bank.com", "scammer_fund": "crypto-invest@hongkong-fund.net"}, ), PayOpsTask( task_id="HARD-009", difficulty="hard", description=( "Synthetic identity fraud: new business account with a flawless-looking " "profile (perfect score, clean history) requests a large first outbound " "wire within 10 days of opening." ), transaction_id="TXN-H009", amount=19_500.00, currency="USD", sender="newco-llc@startup-bank.com", receiver="vendor@offshore-services.biz", transaction_type="transfer", risk_score=0.35, ml_confidence=0.40, # low confidence: model flagging identity inconsistency flags=["new_account_10d", "perfect_score_anomaly", "large_first_wire"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=None, account_age_days=10, country_risk="medium", kyc_status="verified", kyc_expiry_days=360, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="escalate", partial_credit_actions={"hold": 0.5, "flag": 0.4, "reject": 0.3}, requires_investigation={"verify_kyc", "inspect"}, kyc_reveal=( "KYC documents re-examined: EIN was issued 11 days ago but the 'years " "in business' field on the application states 4 years. Registered address " "is a UPS store mailbox. Director SSN belongs to a real person with no " "knowledge of this LLC. Synthetic identity confirmed." ), inspect_reveal=( "Account ML confidence flagged as low: identity features are statistically " "perfect (no noise in credit history, address, DOB fields) — consistent " "with synthetically generated identity. Escalate for manual identity verification." ), key_flags=["new_account_10d", "perfect_score_anomaly"], ), PayOpsTask( task_id="HARD-010", difficulty="hard", description=( "Payroll diversion: HR system breach rerouted an employee's salary to " "a newly added bank account 2 days ago. Employee has not been notified." ), transaction_id="TXN-H010", amount=5_640.00, currency="USD", sender="payroll@employer-corp.com", receiver="new-acct-redirect@routing-bank.com", transaction_type="payroll", risk_score=0.58, ml_confidence=0.63, flags=["direct_deposit_changed", "recent_account_change", "payroll_diversion_indicator"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=5_640.0, account_age_days=2_920, country_risk="low", kyc_status="verified", kyc_expiry_days=180, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="reject", partial_credit_actions={"hold": 0.5, "escalate": 0.4, "flag": 0.3}, requires_investigation={"inspect", "contact_sender"}, inspect_reveal=( "HR system audit: direct deposit account changed 2 days ago via an " "unrecognized IP at 2:14 AM. Employee's primary device was offline at the " "time. No 2FA challenge was issued. Account change was unauthorized." ), contact_reveal=( "Reached employee by mobile: they confirm they did NOT change their " "direct deposit account. Did not receive any email or SMS about the " "change. HR system credentials were likely phished. Reject this payroll " "disbursement and revert to prior account." ), key_flags=["direct_deposit_changed", "payroll_diversion_indicator"], ), ] # ============================================================================= # CRITICAL (6 tasks — multi-step chains, regulatory stakes) # ============================================================================= CRITICAL_TASKS: List[PayOpsTask] = [ PayOpsTask( task_id="CRIT-001", difficulty="critical", description=( "Multi-step investigation: large wire to a new counterparty. " "Agent must inspect logs, then request supporting documents, " "then make a final decision. Chain of 3 steps." ), transaction_id="TXN-C001", amount=375_000.00, currency="USD", sender="deal-team@pe-firm.com", receiver="newco@acquisition-target.io", transaction_type="transfer", risk_score=0.59, ml_confidence=0.55, flags=["high_value", "first_time_payee", "cross_border", "new_counterparty"], velocity_1h=1, velocity_24h=2, avg_transaction_amount=50_000.0, account_age_days=3_000, country_risk="medium", kyc_status="verified", kyc_expiry_days=120, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="approve", partial_credit_actions={"escalate": 0.5, "hold": 0.4, "flag": 0.3}, requires_investigation={"inspect", "request_docs"}, chain_total=3, inspect_reveal="PE firm confirmed. Series B investment round. Deal announced in press last week.", docs_reveal="Signed SPA (Share Purchase Agreement) on file. Notarised. Receiver is the acquisition target.", key_flags=["first_time_payee", "new_counterparty"], ), PayOpsTask( task_id="CRIT-002", difficulty="critical", description=( "Fraud ring: three related accounts sending coordinated small payments " "that aggregate above the reporting threshold. Requires SAR filing." ), transaction_id="TXN-C002", amount=4_900.00, currency="USD", sender="ring_node_1@tempmail.com", receiver="collector_hub@offshore.net", transaction_type="transfer", risk_score=0.76, ml_confidence=0.80, flags=["fraud_ring_indicator", "coordinated_transfers", "threshold_avoidance", "high_value"], velocity_1h=5, velocity_24h=18, avg_transaction_amount=4_800.0, account_age_days=45, country_risk="high", kyc_status="failed", kyc_expiry_days=None, previous_violations=1, previous_sars=0, counterparty_risk="blacklist", correct_action="reject", partial_credit_actions={"escalate": 0.4, "flag": 0.2}, requires_investigation={"inspect"}, regulatory_action=True, chain_total=2, inspect_reveal=( "3 accounts (ring_node_1/2/3) sending $4,900 / $4,850 / $4,750 simultaneously. " "All created same day. Receiver account drained offshore within minutes. " "SAR filing required under BSA §5318(g)." ), key_flags=["fraud_ring_indicator", "coordinated_transfers"], network_graph={"ring_1": "ring_node_1@tempmail.com", "ring_2": "ring_node_2", "collector": "collector_hub@offshore.net"}, ), PayOpsTask( task_id="CRIT-003", difficulty="critical", description=( "Trade-based money laundering: over- and under-invoiced international trade payments. " "Documents don't match transfer amounts. Regulatory escalation required." ), transaction_id="TXN-C003", amount=680_000.00, currency="USD", sender="importer@trade-co.hk", receiver="exporter@goods-supplier.cn", transaction_type="payment", risk_score=0.72, ml_confidence=0.67, flags=["trade_finance", "invoice_mismatch", "cross_border", "high_value"], velocity_1h=1, velocity_24h=3, avg_transaction_amount=120_000.0, account_age_days=730, country_risk="high", kyc_status="verified", kyc_expiry_days=30, previous_violations=1, previous_sars=0, counterparty_risk="watchlist", correct_action="escalate", partial_credit_actions={"flag": 0.4, "hold": 0.35, "reject": 0.3}, requires_investigation={"request_docs", "inspect"}, regulatory_action=True, chain_total=3, docs_reveal="Invoice declares 500 units @ $1,360 each = $680k. Market price is $320/unit. 4× over-invoiced.", inspect_reveal="Shipping records show only 200 units dispatched. Payment/goods ratio 3.4× above market norm.", key_flags=["invoice_mismatch", "trade_finance"], network_graph={"importer": "importer@trade-co.hk", "exporter": "exporter@goods-supplier.cn"}, ), PayOpsTask( task_id="CRIT-004", difficulty="critical", description=( "Compromised corporate account: valid credentials but geo-impossible login. " "Someone in Nigeria logged into a US-only account 8 minutes after the CEO logged out in NY." ), transaction_id="TXN-C004", amount=198_000.00, currency="USD", sender="ceo@target-corp.com", receiver="urgent-wire@third-party-finance.com", transaction_type="transfer", risk_score=0.81, ml_confidence=0.85, flags=["geo_impossible_login", "account_takeover_indicator", "high_value", "urgency_flag"], velocity_1h=1, velocity_24h=1, avg_transaction_amount=15_000.0, account_age_days=4_380, country_risk="high", kyc_status="verified", kyc_expiry_days=365, previous_violations=0, previous_sars=0, counterparty_risk="unknown", correct_action="reject", partial_credit_actions={"escalate": 0.5, "hold": 0.4}, requires_investigation={"inspect", "contact_sender"}, chain_total=2, inspect_reveal=( "Last login: NY (US) at 14:23. This session: Lagos (NG) at 14:31. " "Physical travel impossible in 8 minutes. Likely credential compromise." ), contact_reveal="CEO confirms they did NOT initiate this transfer. Account takeover confirmed.", key_flags=["geo_impossible_login", "account_takeover_indicator"], ), PayOpsTask( task_id="CRIT-005", difficulty="critical", description=( "OFAC sanctions evasion: large USD payment routed through a UAE shell " "company chain. Investigation reveals the ultimate beneficial owner is " "on the OFAC Specially Designated Nationals (SDN) list." ), transaction_id="TXN-C005", amount=490_000.00, currency="USD", sender="uae-trading@al-rashid-fze.ae", receiver="intermediary@gulf-clearing-bv.nl", transaction_type="transfer", risk_score=0.69, ml_confidence=0.60, flags=["high_value", "cross_border", "shell_company_indicator", "sanctions_adjacent"], velocity_1h=1, velocity_24h=2, avg_transaction_amount=380_000.0, account_age_days=180, country_risk="high", kyc_status="verified", kyc_expiry_days=45, previous_violations=0, previous_sars=0, counterparty_risk="watchlist", correct_action="reject", partial_credit_actions={"escalate": 0.5, "hold": 0.4}, requires_investigation={"inspect", "verify_kyc", "request_docs"}, regulatory_action=True, chain_total=3, inspect_reveal=( "Corporate registry search: al-rashid-fze.ae incorporated 6 months ago. " "Sole director: a nominee agent used by 47 other FZE shells. Beneficial " "ownership records absent. Payment routed via 3 intermediate entities " "before reaching the UAE sender — classic sanctions-evasion layering." ), kyc_reveal=( "UBO lookup via SWIFT KYC Registry and Refinitiv: ultimate beneficial " "owner resolves to Tariq Al-Mansouri, listed on OFAC SDN list since 2021 " "(Ref: SDN-2021-04-19-TM). Transacting with this UBO violates 31 C.F.R. " "§ 594. Reject immediately and file SAR." ), docs_reveal=( "Provided trade invoice shows goods described as 'industrial equipment'. " "HS code used (8479.89) does not match the attached packing list. " "Likely misclassification to obscure dual-use goods export controls." ), key_flags=["shell_company_indicator", "sanctions_adjacent"], network_graph={ "sender_shell": "al-rashid-fze.ae", "intermediary": "gulf-clearing-bv.nl", "ubo_sdn": "Tariq Al-Mansouri", }, ), PayOpsTask( task_id="CRIT-006", difficulty="critical", description=( "Correspondent banking alert: a downstream correspondent partner bank " "was added to FinCEN's 311 Special Measures list yesterday. Payments " "routed through them are now legally restricted." ), transaction_id="TXN-C006", amount=215_000.00, currency="USD", sender="trade-desk@our-bank.com", receiver="correspondent@partner-bank-latam.bz", transaction_type="transfer", risk_score=0.71, ml_confidence=0.65, flags=["fincen_311_alert", "correspondent_risk", "high_value", "cross_border"], velocity_1h=3, velocity_24h=12, avg_transaction_amount=200_000.0, account_age_days=2_100, country_risk="high", kyc_status="verified", kyc_expiry_days=20, previous_violations=0, previous_sars=0, counterparty_risk="watchlist", correct_action="escalate", partial_credit_actions={"hold": 0.5, "reject": 0.4}, requires_investigation={"inspect", "request_docs"}, regulatory_action=True, chain_total=2, inspect_reveal=( "FinCEN Section 311 Special Measure imposed 2024-04-01 on partner-bank-latam.bz " "for 'primary money laundering concern'. Special Measure 5 prohibits US " "correspondent accounts and payable-through accounts. All in-flight wires " "must be escalated to Compliance before release — unilateral rejection " "also requires Compliance sign-off (may breach existing agreements)." ), docs_reveal=( "Correspondent agreement review: clause 12.3 requires 90-day wind-down " "notice before terminating the relationship. Legal and Compliance must " "jointly approve next steps. Escalate immediately — do not reject or " "approve without written senior Compliance authorisation." ), key_flags=["fincen_311_alert", "correspondent_risk"], network_graph={ "our_bank": "trade-desk@our-bank.com", "correspondent": "partner-bank-latam.bz", }, ), ] # ============================================================================= # Combine all tasks # ============================================================================= TASKS: List[PayOpsTask] = EASY_TASKS + MEDIUM_TASKS + HARD_TASKS + CRITICAL_TASKS TASKS_BY_ID: Dict[str, PayOpsTask] = {t.task_id: t for t in TASKS} # --------------------------------------------------------------------------- # Action cost table (investigation sub-actions consume budget) # --------------------------------------------------------------------------- ACTION_COSTS: Dict[str, float] = { "approve": 0.0, "reject": 0.0, "flag": 0.0, "escalate": 0.0, "hold": 0.0, "inspect": 0.10, "request_docs": 0.20, "verify_kyc": 0.20, "contact_sender": 0.30, "file_sar": 0.05, # intentionally cheap — incentivise regulatory compliance } # --------------------------------------------------------------------------- # Task variant pools — anti-memorisation & investigation-gating # --------------------------------------------------------------------------- # Each entry is a list of override dicts for alternative scenarios of that task. # Variant index 0 always means "use base task" (no overrides applied). # Variants 1..N override the fields listed, so the correct_action is only # determinable after performing the required investigation sub-actions. # The variant is selected deterministically from the episode seed in environment.py. # # Key design constraint: the BASE observation (pre-investigation) must be # genuinely ambiguous — investigation reveals ARE the decisive evidence. # --------------------------------------------------------------------------- TASK_VARIANTS: Dict[str, List[Dict]] = { # ── EASY ────────────────────────────────────────────────────────────── "EASY-001": [ # v1: account accessed from unknown device just before transfer → hold { "correct_action": "hold", "partial_credit_actions": {"flag": 0.6, "escalate": 0.3}, "flags": ["unusual_login_location", "device_change"], "risk_score": 0.61, "inspect_reveal": ( "Sender logged in from an unrecognized device in a different country " "18 minutes ago. Possible account takeover. All signals otherwise " "identical. Hold pending confirmation with account holder." ), }, # v2: amount exceeds sender's daily limit for new payee → flag { "correct_action": "flag", "partial_credit_actions": {"hold": 0.6, "approve": 0.2}, "flags": ["first_time_payee", "amount_spike"], "risk_score": 0.44, "inspect_reveal": ( "Bob is a new payee — first time ever on this account. System " "limit for first-time payees is $25. Transfer amount $47.50 exceeds " "new-payee daily cap. Flag for standard new-payee challenge-and-confirm." ), }, ], "EASY-002": [ # v1: signals are real but entity is a licensed MSB with OFAC authorisation → escalate { "correct_action": "escalate", "partial_credit_actions": {"reject": 0.4, "hold": 0.3}, "inspect_reveal": ( "Sender is a licensed Money Service Business (MSB) with a FinCEN " "registration number and a current OFAC sanctions-compliance programme. " "The 'sanctioned country' flag was triggered by destination country " "of receiver's beneficial owner, but this MSB holds a specific OFAC " "licence for this corridor. Escalate for senior review rather than " "unilateral reject." ), }, # v2: additional money mule node detected → reject + SAR { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5}, "regulatory_action": True, "inspect_reveal": ( "Receiver account linked to 6 prior confirmed fraud rejections this " "quarter, totalling $58k. New network node in a known mule ring. " "All prior transfers drained offshore within 30 minutes. Reject " "immediately and file SAR." ), }, ], "EASY-003": [ # v1: refund issued to a different card than the original purchase → flag { "correct_action": "flag", "partial_credit_actions": {"hold": 0.5, "escalate": 0.3}, "flags": ["refund_mismatch", "different_card"], "risk_score": 0.52, "inspect_reveal": ( "Original purchase was made on Visa ending 4412. Refund is being " "issued to Mastercard ending 7891 — a different card not on file for " "this order. Possible refund fraud. Flag for merchant verification " "before processing." ), }, # v2: refund claimed on a fraudulent chargeback dispute → reject { "correct_action": "reject", "partial_credit_actions": {"flag": 0.5, "escalate": 0.3}, "flags": ["chargeback_fraud", "dispute_open"], "risk_score": 0.68, "inspect_reveal": ( "Customer filed a chargeback claiming non-receipt. Courier tracking " "shows package delivered and signed for 3 days ago. Customer's " "claimed delivery address matches the signature. Friendly fraud — " "reject refund and respond to chargeback with delivery evidence." ), }, ], "EASY-004": [ # v1: card-cloning confirmed by bank intelligence → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "flag": 0.4}, "inspect_reveal": ( "Cross-check with card-fraud intelligence feed: the card number " "involved in this ATM burst matches a batch of 340 card numbers " "from a point-of-sale skimming incident reported yesterday. Confirmed " "card-clone attack. Reject all withdrawals and block card immediately." ), }, # v2: legitimate end-of-month cash distribution for a small cash-and-carry → approve { "correct_action": "approve", "partial_credit_actions": {"flag": 0.3}, "inspect_reveal": ( "Account holder is a licensed cash-and-carry wholesaler. Same " "end-of-month ATM burst pattern appears in 11 of the last 12 months — " "consistent with weekly cash float replenishment for their 4 shop " "locations. No fraud intelligence matches. Approve." ), }, ], "EASY-005": [ # v1: loan account in arrears, payment covers only partial balance → flag { "correct_action": "flag", "partial_credit_actions": {"hold": 0.5, "approve": 0.3}, "amount": 650.00, "flags": ["partial_payment", "loan_arrears"], "risk_score": 0.38, "inspect_reveal": ( "Loan account 2 months in arrears. Required minimum payment is " "$1,247 but sender submitted $650. Partial payment does not clear " "the overdue balance. Flag for borrower contact and payment plan " "communication before crediting." ), }, # v2: overpayment submitted — process normally but note discrepancy → approve { "correct_action": "approve", "partial_credit_actions": {"flag": 0.3}, "amount": 2_500.00, "flags": ["overpayment"], "risk_score": 0.08, "inspect_reveal": ( "Borrower submitted $2,500 vs scheduled $1,247. Overpayments are " "permitted under the loan agreement (clause 8.2) and reduce principal " "balance. No fraud indicators. Approve and apply excess to principal." ), }, ], "EASY-006": [ # v1: genuine re-submission after first instance failed → approve { "correct_action": "approve", "partial_credit_actions": {"flag": 0.4}, "flags": ["resubmission"], "risk_score": 0.18, "inspect_reveal": ( "First submission (4 min ago) failed with a bank routing error " "(error code: ROUTE-001 — invalid sort code format). This is a valid " "resubmission with corrected routing. No double-charge risk. Approve." ), }, # v2: vendor-side system error caused 8 duplicate submissions → hold { "correct_action": "hold", "partial_credit_actions": {"flag": 0.6}, "velocity_1h": 8, "flags": ["duplicate_transaction", "velocity_breach", "api_error_burst"], "risk_score": 0.72, "inspect_reveal": ( "Vendor payment API experienced a timeout loop at 14:02. 8 identical " "payment submissions in 5 minutes from the same API key. Only 1 " "should be processed. Hold all 8; contact vendor to identify the " "canonical intended payment before releasing any." ), }, ], # ── MEDIUM ──────────────────────────────────────────────────────────── "MED-001": [ # v1: CEO impersonation (BEC) — fraudulent wire instruction → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "hold": 0.4}, "flags": ["high_value", "cross_border", "bec_indicator"], "risk_score": 0.74, "inspect_reveal": ( "The CFO's email domain in the wire instructions is globalcorp.co " "(note: .co not .com). CFO's actual domain is globalcorp.com. " "Phone verification: real CFO is on holiday and did NOT authorise " "this transfer. Business Email Compromise confirmed — reject." ), }, # v2: contract and counterparty fully verified → approve { "correct_action": "approve", "partial_credit_actions": {"escalate": 0.3, "flag": 0.2}, "inspect_reveal": ( "Vendor contract on file, signed 3 months ago (ref VC-EUR-0712). " "Receiver is an EU-licensed entity (UID DE315441892). Payment matches " "milestone 2 of 4. CFO confirmed by phone. Cross-border and amount " "are within normal operating parameters. Approve." ), }, ], "MED-002": [ # v1: KYC re-check reveals UBO mismatch → escalate { "correct_action": "escalate", "partial_credit_actions": {"hold": 0.5, "flag": 0.3}, "kyc_reveal": ( "KYC renewal triggered a UBO re-check. Companies House filing shows " "the ultimate beneficial owner changed 3 months ago to a holding " "company domiciled in a secrecy jurisdiction, without bank " "notification. Material KYC change — escalate for enhanced due " "diligence before releasing any treasury transfers." ), }, # v2: board resolution authorises emergency liquidity transfer → approve { "correct_action": "approve", "partial_credit_actions": {"hold": 0.4}, "kyc_reveal": ( "KYC renewal: both entities share the same UBO (verified). Pending " "status is purely administrative — renewal submitted 6 days ago, " "no change of ownership. Board resolution ref BR-2024-Q1-08 " "authorises this emergency liquidity transfer. Approve." ), }, ], "MED-003": [ # v1: unauthorised recurring billing → hold { "correct_action": "hold", "partial_credit_actions": {"flag": 0.5, "escalate": 0.3}, "inspect_reveal": ( "Merchant billing records show 3 prior declined charges this month " "from different cards linked to the same subscriber IP. Pattern is " "consistent with credential stuffing / unauthorised recurring charge. " "Freeze payment and contact customer before releasing." ), }, # v2: confirmed annual plan upgrade → approve { "correct_action": "approve", "partial_credit_actions": {"flag": 0.3}, "inspect_reveal": ( "Customer service log: subscriber upgraded to annual plan via phone " "this morning. $449.97 = 3 × monthly fee (annual discount applied). " "Merchant has confirmed the charge. Safe to approve." ), }, ], "MED-004": [ # v1: structuring pattern across exchanges → escalate { "correct_action": "escalate", "partial_credit_actions": {"flag": 0.5, "hold": 0.3}, "inspect_reveal": ( "Sender has made 7 crypto exchange payments in 5 days totalling $28k " "across 3 different platforms. Amounts stay just below exchange " "reporting limits. Pattern matches structuring. Escalate for AML review." ), }, # v2: clean recurring investment → approve { "correct_action": "approve", "partial_credit_actions": {"flag": 0.3}, "inspect_reveal": ( "Sender's 3-year payment history shows consistent quarterly crypto " "purchases of similar size. Exchange is fully regulated and KYC-verified. " "Amount within personal investment limits. No structuring indicators. Approve." ), }, ], "MED-005": [ # v1: KYC discrepancy found → escalate { "correct_action": "escalate", "partial_credit_actions": {"flag": 0.5, "hold": 0.3}, "kyc_reveal": ( "KYC expired because the compliance team found discrepancies between " "the business registration address and Companies House records. The " "case has been flagged for investigation by the KYC team. Escalate — " "do not approve or hold without senior sign-off." ), }, # v2: KYC renewed, routine payroll → approve { "correct_action": "approve", "partial_credit_actions": {"hold": 0.4}, "kyc_reveal": ( "KYC renewal was submitted 10 days ago and completed processing " "yesterday. All checks passed. The lapse was an administrative " "oversight only. Payroll has an 18-month history with no anomalies. " "Approve and process." ), }, ], "MED-006": [ # v1: forged purchase agreement → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.4, "flag": 0.3}, "docs_reveal": ( "Purchase agreement is a forgery: the notary seal serial number " "does not exist in the notary registry; the property title number " "returns no match in Land Registry; the 'escrow agent' is not " "registered with the Council for Licensed Conveyancers. " "Classic conveyancing fraud — reject immediately." ), }, # v2: title dispute — hold pending resolution { "correct_action": "hold", "partial_credit_actions": {"escalate": 0.5, "flag": 0.3}, "docs_reveal": ( "SPA is authentic and escrow agent is licensed. However, a co-owner " "has filed a title dispute on the property. The buyer's solicitor " "advises funds should be held in escrow until the dispute is resolved " "(expected 3–4 weeks). Hold pending legal clearance." ), }, ], "MED-007": [ # v1: genuine bank account change, properly authorised → approve { "correct_action": "approve", "partial_credit_actions": {"hold": 0.5, "flag": 0.3}, "docs_reveal": ( "Signed bank-change authorisation letter provided on supplier " "letterhead, with notarised director signature matching KYC records. " "Called supplier finance director on known contact number — confirmed " "the account change. New account is registered to the same company. " "Approve." ), "contact_reveal": ( "Original supplier contact verified the bank change was genuine — " "they switched banks last month due to a merger. Reference number " "SBC-2024-0312 matches their internal change management log." ), }, # v2: multiple lookalike invoice redirect emails sent to same client → escalate { "correct_action": "escalate", "partial_credit_actions": {"hold": 0.5, "reject": 0.4}, "docs_reveal": ( "Client reports receiving 3 lookalike invoice redirect emails in 2 " "weeks from slightly different supplier domains. Two payments to " "prior redirect accounts were already processed (total €87k). This " "is a systematic BEC campaign targeting this client relationship. " "Escalate to fraud and financial crime team immediately." ), "contact_reveal": ( "Genuine supplier finance director: they have sent zero bank-change " "notices this year. The lookalike domain was registered 3 weeks ago. " "Client's email system has been compromised. Escalate." ), }, ], "MED-008": [ # v1: KYC passes, address verified — legitimate BNPL purchase → approve { "correct_action": "approve", "partial_credit_actions": {"flag": 0.4}, "kyc_reveal": ( "KYC recheck: address verification passed on second attempt — " "customer had entered a unit number incorrectly. Identity document " "authentic. Credit bureau confirms thin file is consistent with " "a first-time credit applicant (age 22). No fraud indicators. " "Approve BNPL transaction." ), }, # v2: device fingerprint linked to BNPL fraud network → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "hold": 0.4}, "kyc_reveal": ( "Device fingerprint matches 14 prior BNPL fraud applications across " "3 platforms in the last 6 weeks. All 14 prior accounts defaulted on " "first instalment. Identity documents were synthetically generated. " "Reject and blacklist device fingerprint." ), }, ], # ── HARD ────────────────────────────────────────────────────────────── "HARD-001": [ # v1: deeper forensics confirm active fraud ring → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "flag": 0.3}, "inspect_reveal": ( "Cross-database query: same receiver account (payee@solicitor-uk.com) " "appeared in 4 prior rejected fraud wires this quarter, total £190k. " "Funds clear to offshore within 45 min each time. ML score was " "deliberately poisoned via clean-account seeding. Reject immediately." ), }, # v2: flags are false positives, ML score correct → approve { "correct_action": "approve", "partial_credit_actions": {"escalate": 0.4, "flag": 0.3}, "inspect_reveal": ( "Manual flag investigation: 'solicitor_mule_pattern' rule mis-fired " "on a legitimate SRA-registered firm (reg. SRA-443210) that rebranded " "last month, causing a new account. 'New_account_7d' flag triggered " "by the rebrand. ALL flags are false positives. ML risk score 0.18 is " "accurate. Safe to approve." ), }, ], "HARD-002": [ # v1: new legitimate supplier, flags were incorrect → approve { "correct_action": "approve", "partial_credit_actions": {"flag": 0.4, "hold": 0.3}, "contact_reveal": ( "Sender confirmed: mule_relay_2287@fastpay.io is their new supplier " "in Singapore. The VPN flagged by geo-risk monitoring is the company's " "standard corporate security policy. Purchase order #PO-44821 is on " "file matching the amount. Safe to approve." ), }, # v2: inconsistent story, AML review needed → escalate { "correct_action": "escalate", "partial_credit_actions": {"reject": 0.4, "flag": 0.3}, "contact_reveal": ( "Contacted sender: story is vague and inconsistent across two " "follow-up calls. Cannot confirm supplier identity. No purchase order " "provided. The receiver's business registration cannot be located. " "Cannot approve or reject — escalate to AML for investigation." ), }, ], "HARD-003": [ # v1: confirmed structuring but scale needs MLRO → escalate { "correct_action": "escalate", "partial_credit_actions": {"reject": 0.5, "flag": 0.3}, "inspect_reveal": ( "Pattern strongly resembles CTR structuring. However, the total " "aggregate ($27,750) and the involvement of 3 related entities " "means this crosses the threshold for mandatory MLRO referral under " "internal policy. Escalate rather than unilaterally reject." ), }, # v2: same UBO transfers are authorised corporate restructuring → hold { "correct_action": "hold", "partial_credit_actions": {"reject": 0.4, "escalate": 0.5}, "inspect_reveal": ( "Same-UBO transfers are documented in a board resolution dated last " "week authorising an internal corporate restructuring. KYC failure " "was due to an ID document resubmission still in queue. Structuring " "indicators are coincidental. Hold pending KYC re-verification." ), }, ], "HARD-004": [ # v1: unrecognised SWIFT BIC, potential impersonation → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "flag": 0.3}, "inspect_reveal": ( "SWIFT BIC vostro@bank-b-swift.com resolved to a bank that does NOT " "appear in the correspondent banking agreement registry. The BIC is " "visually similar to a legitimate bank (one character substituted). " "Likely impersonation of a correspondent partner — reject and alert." ), }, # v2: ghost employees detected → flag for payroll audit { "correct_action": "flag", "partial_credit_actions": {"approve": 0.5, "hold": 0.4}, "inspect_reveal": ( "FX settlement accounts are SWIFT-verified and legitimate. However, " "the initiating staff workflow shows this $4.2M wire was split from " "a larger batch that included 2 non-existent internal accounts. " "Flag for payroll/treasury audit before releasing." ), }, ], "HARD-005": [ # v1: confirmed insider fraud, no response from staff → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "flag": 0.3}, "contact_reveal": ( "Staff member's phone is disconnected; email bounced. HR confirms " "the employee was placed on garden leave 2 days ago pending " "investigation. Receiver account drained offshore within hours of " "prior similar payments. Insider fraud confirmed — reject and freeze." ), }, # v2: authorized vendor payment, staff working late → approve { "correct_action": "approve", "partial_credit_actions": {"escalate": 0.4, "flag": 0.3}, "contact_reveal": ( "Staff member confirmed: personal Gmail was added as a forwarding " "alias last month when the vendor switched to a personal invoicing " "service. Contract reference VC-2024-189 verified by procurement. " "After-hours initiation matches remote work schedule. Approve." ), }, ], "HARD-006": [ # v1: confirmed active mule network → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "flag": 0.4}, "inspect_reveal": ( "Receiver account confirmed to be receiving from 12 other dormant " "accounts this week (total €78k). All funds exit to the same offshore " "cluster within 2 hours. Active money-mule aggregation network. " "Reject and file SAR immediately." ), }, # v2: estate probate settlement, legitimate reactivation → escalate { "correct_action": "escalate", "partial_credit_actions": {"flag": 0.6, "hold": 0.4}, "inspect_reveal": ( "Account was frozen under a probate order that was lifted 3 days ago. " "Estate settlement documentation is on file. Amount (€3,200) is " "consistent with a partial distribution from the estate. Legitimate " "reactivation — escalate for senior approval given the reactivation flag." ), }, ], "HARD-007": [ # v1: SIM was ported by legitimate account holder (travelling) → hold { "correct_action": "hold", "partial_credit_actions": {"escalate": 0.5, "reject": 0.4}, "inspect_reveal": ( "Telecom records: SIM swap was initiated via the carrier's verified " "app using biometric authentication. Account holder is overseas and " "replaced a broken SIM abroad. No third-party involvement detected. " "New withdrawal address is a cold storage wallet previously linked " "to the account. Hold briefly for confirmation call." ), "contact_reveal": ( "Reached account holder on a backup email. They confirm the SIM swap " "and the withdrawal are legitimate. They are in Japan and broke their " "phone. Hold approved — confirm by secure callback before releasing." ), }, # v2: no response from account holder, third-party SIM swap confirmed → reject { "correct_action": "reject", "partial_credit_actions": {"hold": 0.4, "escalate": 0.5}, "contact_reveal": ( "Account holder unreachable on all registered contact methods. " "Carrier fraud team confirmed: SIM swap was initiated via a social " "engineering call to a customer service agent — not by the account " "holder. No biometric authentication used. Third-party SIM swap " "confirmed. Reject and lock account." ), }, ], "HARD-008": [ # v1: customer is aware it's high-risk investment but not a scam → escalate { "correct_action": "escalate", "partial_credit_actions": {"reject": 0.4, "hold": 0.4}, "contact_reveal": ( "Customer is fully aware of the risk and has a history of crypto " "investing. The 'romantic partner' is actually a friend from a " "verified online investment forum. Prior transfers were legitimate " "investments that returned profit. However, the escalating pattern " "and offshore destination warrant senior review. Escalate." ), "inspect_reveal": ( "Transaction history: prior 3 payments were invested and partially " "returned. Receiver fund has a verifiable online presence (2 years). " "However, the most recent dormancy of returns and amount escalation " "match late-stage pig-butchering conversion. Escalate for human review." ), }, # v2: scam confirmed, customer is distressed victim → reject { "correct_action": "reject", "partial_credit_actions": {"hold": 0.4, "escalate": 0.3}, "contact_reveal": ( "Customer broke down on the call. They have now been blocked by the " "'romantic partner' and cannot reach them. They realise they have been " "scammed. All prior funds ($5,500) are lost. Reject this transfer, " "file SAR, and refer customer to scam recovery support." ), }, ], "HARD-009": [ # v1: new tech entrepreneur, thin file is legitimate → approve { "correct_action": "approve", "partial_credit_actions": {"hold": 0.5, "escalate": 0.4}, "kyc_reveal": ( "KYC deep-dive: EIN issued 11 days ago but LLC was pre-registered " "90 days ago (formation date vs EIN issuance differ — normal for new " "companies). Registered address is a co-working space (common for " "startups). Director SSN cross-check passsed. Wire is to a verified " "software vendor (AWS partner). Thin file is genuine, not synthetic. " "Approve." ), "inspect_reveal": ( "ML confidence flag was fired due to thin data, not anomalous data. " "No features match synthetic identity profile (all values have " "natural variance). The 'perfect score anomaly' flag was a false " "positive from the rule engine. Safe to approve." ), }, # v2: identity confirmed as synthetic via government database check → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.4, "hold": 0.3}, "kyc_reveal": ( "IRS TIN matching service: EIN does not match the business name in " "the IRS database. SSN belongs to a deceased individual. Address is " "a virtual office with no physical occupancy. Synthetic identity " "confirmed. Reject all transactions and report to FinCEN." ), }, ], "HARD-010": [ # v1: employee did change their account (legitimate, notified HR verbally) → approve { "correct_action": "approve", "partial_credit_actions": {"hold": 0.5, "flag": 0.4}, "inspect_reveal": ( "HR audit: account change was requested by the employee at HR's " "'walk-in' desk 3 days ago. The change was logged manually by an " "HR admin and entered into the system at 2:10 AM during the overnight " "batch. Unusual timestamp is from the batch processor, not an " "attacker. Legitimate change confirmed." ), "contact_reveal": ( "Employee confirms: they switched to a new bank and submitted the " "form in person. Was not aware any notification would be sent. " "Batch processing time was confusing but legitimate. Approve." ), }, # v2: HR credentials were phished, second employee already victimised → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "hold": 0.4}, "inspect_reveal": ( "HR system audit: same rogue IP (185.220.101.x — known Tor exit node) " "changed direct deposit accounts for 3 employees overnight. Two " "employees have already confirmed they did not make changes. Payroll " "diversion campaign in progress. Reject all affected disbursements " "and freeze HR system access." ), }, ], # ── CRITICAL ────────────────────────────────────────────────────────── "CRIT-001": [ # v1: deepfake deal, BEC fraud → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.4, "hold": 0.35, "flag": 0.3}, "inspect_reveal": ( "PE firm name verified but domain is a typo-squatting lookalike " "(pe-flrm.com vs pe-firm.com). No SEC filing exists for this deal. " "The 'press announcement' URL leads to a page created 6 days ago. " "Classic BEC / CEO impersonation fraud." ), "docs_reveal": ( "'Signed SPA' is a doctored template — notarization certificate " "serial number LN-2024-00512 does not exist in the notary registry. " "Acquisition is fabricated. Reject and alert security team." ), }, # v2: legitimate deal, wrong destination account → hold { "correct_action": "hold", "partial_credit_actions": {"approve": 0.4, "escalate": 0.5}, "inspect_reveal": ( "PE firm and deal confirmed as genuine (SEC filing #0001234567). " "Press announcement verified with three external sources. Acquisition " "is legitimate. However, the destination account does not match the " "independent escrow account specified in clause 7.3 of the SPA." ), "docs_reveal": ( "SPA authenticated by legal team. Per clause 7.3, all milestone " "payments must route to independent escrow account " "escrow@trustco-escrow.com, not directly to the acquisition target. " "Hold and redirect to the correct escrow account." ), }, ], "CRIT-002": [ # v1: same SME multiple business accounts — coincidental threshold → hold { "correct_action": "hold", "partial_credit_actions": {"reject": 0.5, "escalate": 0.4}, "inspect_reveal": ( "The 3 flagged accounts all belong to the same registered SME " "(Companies House reg. 09876543) using separate business accounts " "for different cost centres. The $4,900 pattern matches their " "internal expense approval limit — not structuring intent. " "KYC re-verification recommended before releasing. Hold." ), }, # v2: ring is larger (9 accounts), MLRO escalation needed → escalate { "correct_action": "escalate", "partial_credit_actions": {"reject": 0.5, "flag": 0.3}, "inspect_reveal": ( "Network analysis reveals the ring involves 9 accounts total (not 3), " "all created on the same day, all funnelling to the same offshore " "collector. Aggregate total is $42k. Volume and complexity require " "MLRO involvement and a formal SAR — escalate rather than unilaterally reject." ), }, ], "CRIT-003": [ # v1: legitimate specialized goods, market price correct → approve { "correct_action": "approve", "partial_credit_actions": {"escalate": 0.5, "hold": 0.3}, "docs_reveal": ( "Independent commodity valuation confirms: these are custom precision " "industrial components for semiconductor manufacturing. Verified market " "price: $1,310–1,420 per unit. Invoice price of $1,360 is within " "normal range. No mismatch. Documentation is clean." ), "inspect_reveal": ( "Full shipping manifest verified: 500 units dispatched per bill of " "lading BoL-HK-2024-8821. Customs clearance certified in both " "Hong Kong and the destination country. No discrepancies." ), }, # v2: criminal-scale TBML → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.5, "flag": 0.3}, "docs_reveal": ( "Invoice declares 500 units @ $1,360 = $680k. Independent valuation: " "market price is $250/unit. Over-invoiced by 5.4×. Matches a " "trade-based money laundering typology filed with FATF last year. " "Criminal-level TBML — reject and file SAR immediately." ), "inspect_reveal": ( "Shipping records show only 100 units dispatched vs 500 invoiced. " "Invoice-to-goods ratio 5.4× above market. Direct evidence of false " "customs declarations. Reject immediately." ), }, ], "CRIT-004": [ # v1: CEO delegated to CFO who was in Lagos → hold for confirmation { "correct_action": "hold", "partial_credit_actions": {"escalate": 0.5, "reject": 0.4}, "inspect_reveal": ( "Geo-impossible login flagged. However, a delegation email from the " "CEO was sent 2 hours ago via their verified corporate account granting " "the CFO emergency signing authority for this exact transfer. " "CFO was in Lagos for a board meeting (flight records available). " ), "contact_reveal": ( "CEO confirmed via secure callback: CFO had full authority to initiate " "this transfer. Delegation is documented in the board minutes. " "Hold briefly to verify the papertrail, then release." ), }, # v2: CEO unreachable, audit trail ambiguous → escalate { "correct_action": "escalate", "partial_credit_actions": {"reject": 0.5, "hold": 0.4}, "inspect_reveal": ( "Account takeover strongly suspected. CEO's registered number is " "unreachable (OOO message). However, the transfer went through the " "standard dual-approval workflow with two internal approvers (both " "records look genuine). Outcome ambiguous — escalate to Fraud " "Operations for emergency investigation." ), "contact_reveal": ( "Cannot reach CEO. Deputy CFO states they approved the transfer but " "cannot confirm whether the CEO or an impersonator initiated it. " "Escalate immediately to Fraud Ops and place a temporary hold." ), }, ], "CRIT-005": [ # v1: shell layers cleared, legitimate commodity trade → approve { "correct_action": "approve", "partial_credit_actions": {"escalate": 0.5, "hold": 0.4}, "inspect_reveal": ( "UAE FZE registration traced through 3 layers: ultimate beneficial " "owner is a listed European industrial group (Frankfurt Stock Exchange: " "DXI.DE). No SDN or PEP matches. Shell structure is standard for " "legitimate UAE free-zone trading entities. All FATF risk factors " "cleared by AML team. Approve." ), "kyc_reveal": ( "UBO lookup: al-Rashid FZE is a subsidiary of Deutsche Xport Industries " "AG, a publicly listed entity. No sanctions exposure. KYC pack complete " "and verified. Trade invoice amount matches published commodity prices." ), "docs_reveal": ( "Trade invoice and shipping manifest verified. HS code 8479.89 matches " "industrial mixing equipment — no dual-use export restrictions apply. " "All documentation clean. Approve." ), }, # v2: SDN match confirmed at deeper UBO level → reject { "correct_action": "reject", "partial_credit_actions": {"escalate": 0.4, "hold": 0.3}, "inspect_reveal": ( "Shell chain traced: al-rashid-fze.ae → Gibraltar holding → BVI " "nominee → Tariq Al-Mansouri (OFAC SDN since 2021, Ref SDN-2021-04-19). " "3-layer obfuscation structure is classic sanctions evasion. Reject " "and report to OFAC within 10 business days." ), "kyc_reveal": ( "UBO confirmed as Tariq Al-Mansouri via FinScan premium SDN database " "cross-reference. Transacting with this UBO violates 31 C.F.R. § 594. " "Reject immediately and file SAR with FinCEN." ), }, ], "CRIT-006": [ # v1: FinCEN notice is a false positive — different bank with same name → approve { "correct_action": "approve", "partial_credit_actions": {"hold": 0.5, "escalate": 0.4}, "inspect_reveal": ( "FinCEN 311 action reviewed: the targeted bank is 'Global Partner Bank " "of Belize', ABA 082901987. Our correspondent is 'Partner Bank of Latin " "America, Belize', ABA 082901462 — a distinct entity. The alert was " "triggered by a partial name match. Our correspondent is not subject " "to the 311 measure. Approve with a compliance memo on file." ), "docs_reveal": ( "SWIFT BIC PTBZBZBZ confirmed as Partner Bank of Latin America — " "no sanctions linkage. Legal confirmed no restriction applies to " "this specific correspondent relationship. Approve this payment and " "update the name-match rule to prevent future false positives." ), }, # v2: FinCEN 311 applies, must cease all correspondent transactions → escalate { "correct_action": "escalate", "partial_credit_actions": {"hold": 0.5, "reject": 0.4}, "inspect_reveal": ( "FinCEN 311 Special Measure 5 confirmed as applicable to our " "correspondent (partner-bank-latam.bz, SWIFT PTBZBZBZ). All US " "dollar correspondent and payable-through accounts must be closed " "within 30 days. In-flight transactions require Compliance authorisation. " "Escalate immediately — unilateral rejection may breach our " "contractual wind-down obligations." ), "docs_reveal": ( "Correspondent agreement clause 12.3 requires 90-day written wind-down " "notice. FinCEN has set a 30-day compliance deadline creating a " "legal conflict. Legal and Compliance must jointly manage the " "resolution. Escalate with full documentation." ), }, ], }