File size: 92,631 Bytes
d727210
6025832
d727210
 
 
6025832
 
 
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
 
 
 
 
 
 
 
 
 
 
 
d727210
 
 
 
 
667fa47
 
 
 
 
 
 
 
d7aea6e
667fa47
 
 
 
 
 
 
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
622e841
d727210
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
622e841
d727210
9c003f0
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
622e841
d727210
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d727210
 
 
 
ffea7f4
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d727210
 
 
 
ffea7f4
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d727210
 
 
 
ffea7f4
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c003f0
d727210
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d727210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436d56f
 
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436d56f
ffea7f4
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
 
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
436d56f
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
436d56f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffea7f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436d56f
 
d727210
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
"""
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."
            ),
        },
    ],
}