@1aboveio/skills 0.20.2 → 0.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -6
- package/package.json +1 -1
- package/runtime/skills/distribution/generated/recipes.json +220 -25
- package/runtime/skills/distribution/scripts/bundles.mjs +167 -48
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -0
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +1 -1
- package/skills/compliance/compliance-audit/LICENSE +3 -0
- package/skills/compliance/compliance-audit/SKILL.md +466 -0
- package/skills/compliance/compliance-audit/references/findings-schema.md +290 -0
- package/skills/compliance/compliance-audit/references/vars-checklist.md +1068 -0
- package/skills/compliance/compliance-audit/references/vars-findings-schema.md +395 -0
- package/skills/compliance/compliance-audit/scripts/reconcile_tracker.py +454 -0
- package/skills/compliance/compliance-audit/scripts/test_validate_tracker.py +90 -0
- package/skills/compliance/compliance-audit/scripts/validate_tracker.py +752 -0
- package/skills/compliance/doc-authenticity/LICENSE +3 -0
- package/skills/compliance/doc-authenticity/SKILL.md +158 -0
- package/skills/compliance/doc-authenticity/references/agreement.md +66 -0
- package/skills/compliance/doc-authenticity/references/bank-statement.md +150 -0
- package/skills/compliance/doc-authenticity/references/forensic-signals.md +157 -0
- package/skills/compliance/doc-authenticity/references/image-signals.md +114 -0
- package/skills/compliance/doc-authenticity/references/invoice.md +66 -0
- package/skills/compliance/doc-authenticity/references/issuer-profiles.md +86 -0
- package/skills/compliance/doc-authenticity/references/verdict-and-report.md +95 -0
- package/skills/compliance/doc-authenticity/references/verdict.schema.json +81 -0
- package/skills/compliance/doc-authenticity/scripts/image_forensics.py +294 -0
- package/skills/compliance/doc-authenticity/scripts/pdf_forensics.py +401 -0
- package/skills/compliance/doc-authenticity/scripts/validate_codes.py +224 -0
- package/skills/compliance/doc-authenticity/scripts/validate_verdict.py +308 -0
- package/skills/compliance/report-writing/LICENSE +3 -0
- package/skills/compliance/report-writing/SKILL.md +206 -0
- package/skills/compliance/report-writing/references/audit-report-template.md +121 -0
- package/skills/compliance/report-writing/references/external-assessment-template.md +167 -0
- package/skills/compliance/report-writing/references/management-report-template.md +145 -0
- package/skills/compliance/report-writing/references/vars-report-template.md +224 -0
- package/skills/document-utils/doc-to-md/LICENSE +3 -0
- package/skills/document-utils/doc-to-md/SKILL.md +77 -0
- package/skills/document-utils/doc-to-md/scripts/convert.py +157 -0
- package/skills/document-utils/reg-parser/LICENSE +3 -0
- package/skills/document-utils/reg-parser/SKILL.md +201 -0
- package/skills/document-utils/reg-parser/references/checklist-schema.md +223 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-amlo-checklist.json +1306 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-amlo-checklist.md +469 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.json +634 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.md +233 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-mso-guideline-checklist.json +2194 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-mso-guideline-checklist.md +771 -0
- package/skills/document-utils/reg-parser/references/checklists/kyc-aml-checklist.md +258 -0
- package/skills/document-utils/reg-parser/references/checklists/visa-vars-checklist.md +1058 -0
- package/skills/document-utils/reg-parser/references/mandates/hk-amlo-cap615.md +389 -0
- package/skills/document-utils/reg-parser/references/mandates/visa-vars.md +205 -0
- package/skills/document-utils/reg-parser/scripts/compare_checklist.py +228 -0
- package/skills/document-utils/reg-parser/scripts/dump_checklist.py +348 -0
- package/skills/document-utils/reg-parser/scripts/preprocess-mandate.sh +171 -0
- package/skills/document-utils/reg-parser/scripts/test_quarantine.sh +102 -0
- package/skills/document-utils/reg-parser/scripts/validate_checklist.py +532 -0
- package/skills/document-utils/reg-parser/scripts/validate_mandate.py +242 -0
- package/skills/engineering/engineering-runtime/coherence/workflow.json +16 -16
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +1 -1
- package/skills/payment/similar-domain/LICENSE +3 -0
- package/skills/payment/similar-domain/SKILL.md +178 -0
- package/skills/payment/similar-domain/references/entities.md +58 -0
- package/skills/payment/similar-domain/references/fingerprints.md +82 -0
- package/skills/payment/similar-domain/references/pivots.md +66 -0
- package/skills/payment/similar-domain/references/report-template.md +76 -0
- package/skills/payment/similar-domain/references/scoring.md +61 -0
- package/skills/payment/similar-domain/references/terminology.md +37 -0
- package/skills/payment/similar-domain/references/workflow.md +79 -0
- package/skills/payment/tld-detection/SKILL.md +76 -21
- package/skills/payment/tld-detection/references/clean.md +1 -1
- package/skills/payment/tld-detection/references/clustering.md +21 -6
- package/skills/payment/tld-detection/references/decision.md +7 -2
- package/skills/payment/tld-detection/references/fx-conversion.md +229 -0
- package/skills/payment/tld-detection/references/report-template.md +63 -27
- package/skills/payment/tld-detection/references/streams.md +90 -0
- package/skills/payment/tld-detection/references/terminology.md +15 -7
- package/skills/payment/tld-detection/references/workflow.md +1 -1
- package/skills/payment/tld-detection/scripts/detect_streams.py +542 -0
- package/skills/engineering/ensure-coverage/evals/evals.json +0 -125
- package/skills/engineering/rush-issues/evals/evals.json +0 -65
- package/skills/engineering/rush-release/evals/evals.json +0 -58
- package/skills/fullstack/shadcn/evals/evals.json +0 -90
- package/skills/payment/fraud-analysis/evals/evals.json +0 -40
- package/skills/payment/tld-detection/evals/evals.json +0 -107
- package/skills/payment/tld-detection/tests/test_detect_ladders.py +0 -132
|
@@ -21,6 +21,11 @@ Risk uses independent post-membership evidence:
|
|
|
21
21
|
- transaction-date and BIN-country-local-hour differences versus that baseline
|
|
22
22
|
- externally evidenced MCC or merchant-business mismatch; never infer this
|
|
23
23
|
from a suggestive descriptor name alone
|
|
24
|
+
- stream evidence from [streams.md](streams.md): a wallet currency that is not
|
|
25
|
+
the card country's legal tender, a frozen-rate stream beside live-rate
|
|
26
|
+
groups on the same rungs and days, one attempt per card with no retry after
|
|
27
|
+
decline, burst concentration, a whole country off-menu, a retail constant
|
|
28
|
+
with no state variation
|
|
24
29
|
|
|
25
30
|
Declare portfolio-specific thresholds for a materially different rate,
|
|
26
31
|
distribution, or descriptor share in the run methodology before assigning
|
|
@@ -39,7 +44,7 @@ country-slice coverage = cluster attempts in country /
|
|
|
39
44
|
all attempts in the same descriptor and country
|
|
40
45
|
```
|
|
41
46
|
|
|
42
|
-
An exact **100% single-BIN-country cluster** is a standout characteristic.
|
|
47
|
+
An exact **100% single-BIN-country cluster** is a standout characteristic in the canonical original-currency run. In the BIN-country base-currency sensitivity, BIN country constructs the target currency and is therefore not independent evidence; report 100% concentration descriptively but do not use it to raise the FX risk tier.
|
|
43
48
|
Country-slice coverage explains how large that concentration is inside the
|
|
44
49
|
descriptor-country population, but it has no fixed threshold and contributes
|
|
45
50
|
no independent risk points. Values such as 20% or 33.8% are descriptive, not
|
|
@@ -52,7 +57,7 @@ Assign exactly one tier to every identified currency-amount pair:
|
|
|
52
57
|
|
|
53
58
|
| Tier | Use when |
|
|
54
59
|
|---|---|
|
|
55
|
-
| High |
|
|
60
|
+
| High | In the canonical run, an exact 100% single-country standout has at least one independent corroborating risk characteristic, or several independently strong non-country characteristics converge; FX concentration cannot raise tier |
|
|
56
61
|
| Medium | At least two independent characteristics materially differ from the same-descriptor baseline, but High is not justified |
|
|
57
62
|
| Low | The structural cluster gate is met, but observed corroborating evidence is limited or not material |
|
|
58
63
|
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# BIN-country base-currency FX sensitivity
|
|
2
|
+
|
|
3
|
+
Use this supplemental path when the user asks to convert transaction amounts
|
|
4
|
+
to the card-origin currency, or when daily FX drift may conceal fixed local-
|
|
5
|
+
currency anchors. Run it after canonical original-currency membership is
|
|
6
|
+
complete. It is sensitivity evidence, not a replacement clustering universe.
|
|
7
|
+
|
|
8
|
+
## Separation contract
|
|
9
|
+
|
|
10
|
+
- Reuse the canonical cleaned authorization rows and descriptor normalization.
|
|
11
|
+
- Keep canonical `C*` results unchanged.
|
|
12
|
+
- Name supplemental families `FXC1`, `FXC2`, ... and publish separate files
|
|
13
|
+
such as `<scope>_bin_base_currency_clusters.md` and
|
|
14
|
+
`<scope>_bin_base_currency_methodology.md`.
|
|
15
|
+
- Do not combine `C*` and `FXC*` attempts into one exposure total because the
|
|
16
|
+
same authorization may belong to both views.
|
|
17
|
+
|
|
18
|
+
## Target currency
|
|
19
|
+
|
|
20
|
+
Map normalized BIN country to the legal tender in effect on the transaction
|
|
21
|
+
date. Use the ISO 4217 Maintenance Agency's current and historic lists as the
|
|
22
|
+
preferred source for currency and minor unit, and record the complete run-
|
|
23
|
+
specific country-to-currency map and source.
|
|
24
|
+
Do not infer currency from language, descriptor, terminal country, shopper IP,
|
|
25
|
+
or transaction currency. For countries with several legal tenders or a
|
|
26
|
+
currency transition during the window, declare and justify the dated mapping;
|
|
27
|
+
exclude ambiguous rows rather than silently choosing one.
|
|
28
|
+
|
|
29
|
+
Missing BIN country remains in the canonical run but cannot be assigned a BIN-
|
|
30
|
+
origin currency. Count it as an FX exclusion under
|
|
31
|
+
`missing country-currency mapping`.
|
|
32
|
+
|
|
33
|
+
## Frankfurter v2 rates and cache contract
|
|
34
|
+
|
|
35
|
+
Frankfurter v2 is mandatory for this sensitivity. Its public API requires no
|
|
36
|
+
API key or other authentication. Before retrieval, choose and disclose either
|
|
37
|
+
`default blended` or one or more named provider keys. The default blend is a
|
|
38
|
+
real provider-selection decision: omit the `providers` filter from the URL and
|
|
39
|
+
state that Frankfurter blended all eligible providers. For a named selection,
|
|
40
|
+
append `&providers=<comma-separated-provider-keys>` and record those exact
|
|
41
|
+
filter keys. In both modes, `expand=providers` is mandatory so the response
|
|
42
|
+
identifies the providers that actually contributed to each non-peg row.
|
|
43
|
+
|
|
44
|
+
Every request uses this exact endpoint and includes all five required query
|
|
45
|
+
parameters, even for a one-day window. A named-provider request also includes
|
|
46
|
+
the optional `providers` filter:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
GET https://api.frankfurter.dev/v2/rates?from=<YYYY-MM-DD>&to=<YYYY-MM-DD>&base=<P>"es=<comma-separated-currencies>&expand=providers
|
|
50
|
+
GET https://api.frankfurter.dev/v2/rates?from=<YYYY-MM-DD>&to=<YYYY-MM-DD>&base=<P>"es=<comma-separated-currencies>&expand=providers&providers=<comma-separated-provider-keys>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`from` and `to` are the inclusive UTC transaction-date bounds, `base` is the
|
|
54
|
+
declared pivot currency `P`, and `quotes` is the sorted, deduplicated set of
|
|
55
|
+
source and target currencies needed by the run. `expand=providers` requests
|
|
56
|
+
row-level contributing-provider keys; it does not select a provider and does
|
|
57
|
+
not replace the optional `providers` filter. Do not use the v1 nested response,
|
|
58
|
+
`/latest`, or an implicit latest date. A returned row means that its `rate` is
|
|
59
|
+
quote-currency units per one unit of its `base` currency: with `base=P`,
|
|
60
|
+
`rate(d, P→Q)` is units of `Q` per one unit of pivot `P`.
|
|
61
|
+
|
|
62
|
+
Write the response to
|
|
63
|
+
`reports/tld-detection/<scope>_frankfurter_v2_rates.cache.json`. Create this
|
|
64
|
+
run-specific cache once, do not overwrite it, and record its SHA-256 in the
|
|
65
|
+
methodology. The cache schema is versioned and contains at least:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"schema_version": 1,
|
|
70
|
+
"api": "Frankfurter",
|
|
71
|
+
"api_version": "v2",
|
|
72
|
+
"endpoint": "https://api.frankfurter.dev/v2/rates",
|
|
73
|
+
"cache_created_at_utc": "<ISO-8601 UTC timestamp ending Z>",
|
|
74
|
+
"requests": [
|
|
75
|
+
{
|
|
76
|
+
"request_url": "<complete run-specific request URL>",
|
|
77
|
+
"retrieved_at_utc": "<ISO-8601 UTC timestamp ending Z>",
|
|
78
|
+
"provider_selection": {"mode": "default blended", "providers": []},
|
|
79
|
+
"query": {"from": "<date>", "to": "<date>", "base": "<P>", "quotes": ["<Q>"], "expand": ["providers"]},
|
|
80
|
+
"returned_facts": [
|
|
81
|
+
{"date": "<date>", "base": "<P>", "quote": "<Q>", "rate": "<positive decimal>", "providers": ["<contributing-provider-key>"]}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Preserve every returned `date`, `base`, `quote`, and unrounded `rate` fact and,
|
|
89
|
+
when Frankfurter returns it, the complete `providers` key array exactly as
|
|
90
|
+
returned. An omitted `providers` member means Frankfurter marked that fact as
|
|
91
|
+
peg-derived; it is not unknown provenance. Preserve the omission rather than
|
|
92
|
+
writing `[]`, `null`, a guessed provider, or the request's selected providers.
|
|
93
|
+
For named-provider requests, `provider_selection` and the optional URL/query
|
|
94
|
+
filter record what was requested; add `"providers": ["<provider-key>"]` to the
|
|
95
|
+
cached `query`. Each fact's `providers` array records which of those providers
|
|
96
|
+
actually contributed; peg-derived facts still omit the member. The methodology
|
|
97
|
+
repeats the cache path, complete request URL or
|
|
98
|
+
URLs, UTC retrieval timestamp or timestamps, provider selection, query
|
|
99
|
+
bounds/pivot/quotes/expansion, row-level provider identities and peg-derived
|
|
100
|
+
omissions, schema version, and cache SHA-256. Never rely on a later live refetch
|
|
101
|
+
to reproduce a run.
|
|
102
|
+
|
|
103
|
+
Fail closed before conversion when the cache is missing; `schema_version` is
|
|
104
|
+
missing or unsupported; a legacy/v1 cache is supplied; required metadata or a
|
|
105
|
+
`date`/`base`/`quote`/`rate` fact is absent; the endpoint, complete URL, parsed
|
|
106
|
+
query (including `expand=providers` and any provider filter), provider
|
|
107
|
+
selection, retrieval timestamp, requested window, pivot, or quotes do not match
|
|
108
|
+
the run; a present fact-level `providers` member is not an array of provider
|
|
109
|
+
keys; a returned provider key was not preserved; a rate is non-positive; or
|
|
110
|
+
duplicate facts conflict. Do not repair a cache mismatch by refetching during
|
|
111
|
+
analysis and do not treat an invalid cache as ordinary row-level FX exclusions.
|
|
112
|
+
|
|
113
|
+
## Conversion
|
|
114
|
+
|
|
115
|
+
For each authorization on UTC transaction date `d`, with source currency `S`,
|
|
116
|
+
target BIN-origin currency `T`, source amount `a`, and API quotes expressed as
|
|
117
|
+
currency units per pivot currency `P`:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
base_amount = a / rate(d, P→S) × rate(d, P→T)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use `1` when `S = P` or `T = P`. If either required non-identity quote has no
|
|
124
|
+
returned fact whose `date` is exactly `d`, exclude that authorization. Never
|
|
125
|
+
fill weekends or missing dates with a prior-business-day, monthly average,
|
|
126
|
+
latest, or next-day rate.
|
|
127
|
+
|
|
128
|
+
Count exclusions by reason, date, and currency. Report:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
canonical cleaned attempts → exact-date converted attempts → FX exclusions
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Converted partition universe
|
|
135
|
+
|
|
136
|
+
For each frozen canonical descriptor partition `D`, define:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
P_FX(D) = authorization attempts in D successfully converted using exact-date
|
|
140
|
+
Frankfurter v2 facts under the validated cache contract
|
|
141
|
+
|P_FX| = the attempt count of P_FX(D) for the current descriptor partition
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Identity legs valued at `1` remain exact-date conversions; every non-identity
|
|
145
|
+
leg must have the matching returned date/base/quote/rate fact. Rows excluded
|
|
146
|
+
for mapping, quote, or conversion reasons are not in `P_FX`.
|
|
147
|
+
|
|
148
|
+
Compute FX candidate counts and both FX thresholds only from `P_FX` in the
|
|
149
|
+
current descriptor:
|
|
150
|
+
|
|
151
|
+
| Set | FX membership |
|
|
152
|
+
|---|---|
|
|
153
|
+
| High-count (seeds) | union of (a) FX pairs with count ≥ `max(30, 0.5% of \|P_FX\|)`, (b) the `P_FX` partition top 25 FX pairs by count, and (c) per target currency in `P_FX`, the top 10 FX pairs with count ≥30 |
|
|
154
|
+
| Rung pool | FX pairs with count ≥ `max(5, 0.1% of \|P_FX\|)` |
|
|
155
|
+
|
|
156
|
+
State the resulting thresholds per descriptor. FX-excluded rows never enter
|
|
157
|
+
these denominators, rankings, rung pools, families, baselines, or cluster
|
|
158
|
+
metrics. They remain only in exclusion coverage and reconciliation back to
|
|
159
|
+
canonical `U`:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
|U_D| = |P_FX(D)| + |FX exclusions in D|
|
|
163
|
+
|U| = sum_D |P_FX(D)| + all FX exclusions
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Rounding and keys
|
|
167
|
+
|
|
168
|
+
Round converted values once, half-up, to the target currency's ISO 4217 minor
|
|
169
|
+
unit in effect on the transaction date. Record the exponent table actually
|
|
170
|
+
used. Do not round rates before conversion.
|
|
171
|
+
|
|
172
|
+
Inside each frozen descriptor partition, define the supplemental key as:
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
(target BIN-origin currency, rounded base_amount)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Apply the fixed 2x, three-level, multi-anchor support, disjointness, and ≥100
|
|
179
|
+
exact-member gates as the canonical detector after applying the `P_FX`-only
|
|
180
|
+
high-count and rung-pool thresholds above. Final FX membership is exact
|
|
181
|
+
equality on descriptor, target currency, and rounded base amount; tolerance is
|
|
182
|
+
discovery-only.
|
|
183
|
+
|
|
184
|
+
Scale absolute residual caps in target-currency minor units. A defensible
|
|
185
|
+
starting sensitivity grid is 1, 2, and 5 minor units, retaining the smallest
|
|
186
|
+
cap under which material paths are stable. Report currency-specific cluster
|
|
187
|
+
counts and covered attempts; do not repeat the portfolio-wide total on every
|
|
188
|
+
currency row.
|
|
189
|
+
|
|
190
|
+
## Baseline, risk, and reporting
|
|
191
|
+
|
|
192
|
+
For each `FXC*` family, the comparison baseline uses only `P_FX` for the same
|
|
193
|
+
frozen canonical descriptor and removes that family's exact FX keys. FX-
|
|
194
|
+
excluded rows cannot enter the same-descriptor baseline. Show exposure against
|
|
195
|
+
both the converted universe and all canonical cleaned authorization attempts.
|
|
196
|
+
Reconcile identified FX attempts + converted baseline + FX exclusions to the
|
|
197
|
+
canonical cleaned universe.
|
|
198
|
+
|
|
199
|
+
BIN country causally determines target currency in this view. Therefore:
|
|
200
|
+
|
|
201
|
+
- show BIN-country composition and descriptor-country slice coverage;
|
|
202
|
+
- caption exact 100% single-country concentration as descriptive and
|
|
203
|
+
constructed by the target-currency mapping;
|
|
204
|
+
- do not count country concentration as independent risk evidence;
|
|
205
|
+
- do not raise the FX tier because of country concentration;
|
|
206
|
+
- assign High/Medium/Low using only structural and other independent
|
|
207
|
+
`P_FX` same-descriptor characteristics.
|
|
208
|
+
|
|
209
|
+
Use the consolidated per-cluster table in [report-template.md](report-template.md).
|
|
210
|
+
Keep the anchor table and BIN-country attribution table separate because they
|
|
211
|
+
are one-to-many detail, not duplicate cluster facts. Include the same four
|
|
212
|
+
charts as the canonical report and label converted anchors with their target
|
|
213
|
+
base currency.
|
|
214
|
+
|
|
215
|
+
## FX audit checklist
|
|
216
|
+
|
|
217
|
+
- [ ] Canonical cleaned universe and `C*` results were frozen first
|
|
218
|
+
- [ ] As-of-date country-to-legal-tender mapping and minor units are recorded
|
|
219
|
+
- [ ] Frankfurter `/v2/rates` uses explicit `from`, `to`, `base`, `quotes`, and `expand=providers`; authentication is not required
|
|
220
|
+
- [ ] Provider selection/default blend, complete request URL, UTC retrieval time, returned facts with contributing provider keys or documented peg-derived omission, cache path/SHA-256, and supported cache schema version are recorded
|
|
221
|
+
- [ ] Missing, mismatched, incomplete, or legacy cache facts fail closed before conversion
|
|
222
|
+
- [ ] Formula supports non-pivot source currencies
|
|
223
|
+
- [ ] Exact-date no-fill coverage and every exclusion are reconciled
|
|
224
|
+
- [ ] Per descriptor, `|P_FX|` is only exact-date-converted attempts; thresholds and same-descriptor baselines use only `P_FX`
|
|
225
|
+
- [ ] `FXC*` membership uses exact descriptor + target currency + rounded amount
|
|
226
|
+
- [ ] Caps and sensitivity are expressed per currency in minor units
|
|
227
|
+
- [ ] Country concentration is descriptive and excluded from FX risk evidence
|
|
228
|
+
- [ ] Converted and all-authorization exposure denominators are both shown
|
|
229
|
+
- [ ] `C*` and `FXC*` exposure are never added together
|
|
@@ -18,30 +18,31 @@ writing, grep banned strings in [terminology.md](terminology.md).
|
|
|
18
18
|
English Markdown headings on the left; Chinese HTML / `.zh.md` on the right.
|
|
19
19
|
|
|
20
20
|
```markdown
|
|
21
|
-
# Potential TLD currency-amount pairs — <window>
|
|
22
|
-
#
|
|
21
|
+
# Potential transaction laundering (TLD) currency-amount pairs — <window>
|
|
22
|
+
# 潜在交易清洗(TLD)币种金额对 — <window>
|
|
23
23
|
|
|
24
24
|
## TLD Risk Assessment # TLD 风险评估
|
|
25
25
|
[Separate methodology link] # [独立方法文档链接]
|
|
26
|
+
## Upstream Streams # 上游流
|
|
26
27
|
## High-Risk Clusters # 高风险 Cluster
|
|
27
28
|
## TLD Risk Exposure # TLD 风险敞口
|
|
28
29
|
### By risk tier and descriptor # 按风险等级及 Descriptor
|
|
29
30
|
### Overall exposure # 整体风险敞口
|
|
30
31
|
## All Identified Currency-Amount Pairs # 全部识别出的币种金额对
|
|
31
32
|
### Currency-Amount Pair C1 # 币种金额对 C1
|
|
32
|
-
####
|
|
33
|
-
#### Characteristics # 特征
|
|
33
|
+
#### Cluster Profile and Comparison # Cluster 概况与对比
|
|
34
34
|
## Verdict # 结论
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
The separate methodology document uses this structure:
|
|
38
38
|
|
|
39
39
|
```markdown
|
|
40
|
-
# TLD currency-amount-pair detection methodology — <scope/window>
|
|
41
|
-
# TLD
|
|
40
|
+
# Transaction laundering (TLD) currency-amount-pair detection methodology — <scope/window>
|
|
41
|
+
# 交易清洗(TLD)币种金额对识别方法 — <范围/时段>
|
|
42
42
|
|
|
43
43
|
## Universe and cleaning # 分析总体与清洗
|
|
44
44
|
## Descriptor partition logic # Descriptor 分区逻辑
|
|
45
|
+
## Stream inference logic # 上游流推断逻辑
|
|
45
46
|
## Currency-amount-pair logic # 币种金额对逻辑
|
|
46
47
|
## Baseline and audit details # 基线与审计明细
|
|
47
48
|
```
|
|
@@ -88,6 +89,21 @@ descriptor-local high-count rule, baseline definitions, below-threshold
|
|
|
88
89
|
families, risk-tier rules, all run-specific materiality thresholds, and files
|
|
89
90
|
used. No skill name in the HTML footer; filenames only.
|
|
90
91
|
|
|
92
|
+
### Upstream streams / 上游流
|
|
93
|
+
|
|
94
|
+
One table, one row per stream from `detect_streams.py`, per descriptor:
|
|
95
|
+
stream label (wallet currency, menu scheme, live or frozen; retail constant;
|
|
96
|
+
off-menu), attempts, share of descriptor, top countries, approval rate,
|
|
97
|
+
suspected-fraud share, repeat-card share, retry after decline, top-three-hour
|
|
98
|
+
share, address-verification presence. Under the table, at most three
|
|
99
|
+
sentences: how many upstreams the descriptor carries, which stream each
|
|
100
|
+
cluster below belongs to, and which streams (frozen, off-menu, retail) are
|
|
101
|
+
evidence in their own right. A frozen-rate stream is described as generated
|
|
102
|
+
traffic with the drift comparison against live groups on the same rungs and
|
|
103
|
+
days; it is not called laundering, fraud or a bot. Methodology holds the
|
|
104
|
+
parameters, rates cache, per-country wallet coverage and drift, and the
|
|
105
|
+
alternatives rejected.
|
|
106
|
+
|
|
91
107
|
### High-risk clusters / 高风险 Cluster
|
|
92
108
|
|
|
93
109
|
Include High-risk clusters only. For each, show descriptor, representative
|
|
@@ -107,30 +123,42 @@ or “supported TLD” impact figure.
|
|
|
107
123
|
|
|
108
124
|
### Currency-amount-pair analysis / 全部识别出的币种金额对
|
|
109
125
|
|
|
110
|
-
One subsection per identified currency-amount pair.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
One subsection per identified currency-amount pair. Show the risk tier and
|
|
127
|
+
concise basis immediately below the cluster heading, then use one consolidated
|
|
128
|
+
profile/comparison table. Do not emit separate Definition, Characteristics,
|
|
129
|
+
Specification, and Metrics tables because they repeat descriptor, attempts,
|
|
130
|
+
and cluster context.
|
|
131
|
+
|
|
132
|
+
The consolidated table has columns `Section`, `Item`, `Cluster / value`, and
|
|
133
|
+
`Same-descriptor baseline`. It contains each fact exactly once:
|
|
134
|
+
|
|
135
|
+
| Section | Items |
|
|
136
|
+
|---|---|
|
|
137
|
+
| Definition | Descriptor, seed, representative anchors |
|
|
138
|
+
| Population | Descriptor attempts, cluster attempts, cluster share of descriptor |
|
|
139
|
+
| Specification | MCC |
|
|
140
|
+
| Metrics | Authorization success rate, suspected fraud as share of attempts, Top 3 issuing banks, Top 3 BIN countries |
|
|
141
|
+
|
|
142
|
+
Use `—` in the baseline column for definition/specification values that have no
|
|
143
|
+
comparison. The baseline population is the same descriptor after removing the
|
|
144
|
+
cluster's exact keys. Express anchors as `USD 11.7 → USD 23.39* → USD 46.78`.
|
|
145
|
+
|
|
146
|
+
Follow the consolidated table with one anchor table whose columns are anchor,
|
|
147
|
+
attempts, authorization success rate, and suspected fraud as share of attempts.
|
|
148
|
+
Always compute both rates for every theoretical level from that level's exact
|
|
149
|
+
member attempts, including levels with fewer than the contrast min-n (default
|
|
150
|
+
30). Do not write `n/a` on this table because the rung is small. For a level
|
|
151
|
+
containing multiple nearby exact values, display its highest-count amount with
|
|
152
|
+
`*`; the table totals attempts and rates across that level and explains `*`.
|
|
127
153
|
|
|
128
154
|
For every Top 3 cluster BIN country, add a table with country, cluster attempts,
|
|
129
155
|
share of cluster, total attempts in the same descriptor-country slice, and
|
|
130
|
-
cluster coverage of that slice.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
because it is high.
|
|
156
|
+
cluster coverage of that slice. In canonical original-currency results only,
|
|
157
|
+
mark exact 100% single-country composition as a standout. Treat `Mixed` as
|
|
158
|
+
neutral. Do not assign a threshold or risk points to country-slice coverage,
|
|
159
|
+
and do not promote any composition below 100% merely because it is high. In FX
|
|
160
|
+
results, caption even 100% concentration as descriptive because BIN country
|
|
161
|
+
constructs target currency; it cannot raise the FX tier.
|
|
134
162
|
|
|
135
163
|
Follow the tables with transaction-date and transaction-time charts comparing
|
|
136
164
|
the cluster with the same-descriptor baseline. These are characteristics, not
|
|
@@ -140,6 +168,14 @@ When clustering finds no qualifying family, omit currency-amount-pair subsection
|
|
|
140
168
|
publish the ranking and below-threshold candidates in the methodology
|
|
141
169
|
document, show zero identified-cluster exposure, and still write Verdict.
|
|
142
170
|
|
|
171
|
+
For an FX sensitivity, keep the same consolidated layout, label anchors as
|
|
172
|
+
base-currency anchors, and link separate `<scope>_bin_base_currency_clusters*`
|
|
173
|
+
and methodology files. Define `|P_FX|` as the exact-date-converted attempts in
|
|
174
|
+
the current frozen canonical descriptor partition. Use only `P_FX` for FX
|
|
175
|
+
high-count/rung-pool thresholds and the same-descriptor baseline; keep excluded
|
|
176
|
+
rows only in exclusion coverage and reconciliation to canonical `U`. State both
|
|
177
|
+
converted-universe and all-authorization exposure denominators.
|
|
178
|
+
|
|
143
179
|
### Verdict / 结论
|
|
144
180
|
|
|
145
181
|
[decision.md](decision.md). **≤3 sentences** for the whole book, then an
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Stream inference
|
|
2
|
+
|
|
3
|
+
Run after cleaning and before clustering. A descriptor is a billing name, not a
|
|
4
|
+
merchant: one descriptor can carry several upstreams. Exact-amount ladders miss
|
|
5
|
+
most of them, because a cashier that converts a wallet-currency preset to the
|
|
6
|
+
processing currency at a live rate turns one €10 preset into a dozen exact
|
|
7
|
+
amounts across days. Stream inference works in the wallet currency instead.
|
|
8
|
+
|
|
9
|
+
## Script
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python3 {baseDir}/scripts/detect_streams.py cleaned-authorizations.csv \
|
|
13
|
+
--rates rates_cache.json --fetch-rates 2026-07-01 2026-08-31 \
|
|
14
|
+
--amount-column amount --currency-column currency --date-column timestamp \
|
|
15
|
+
--descriptor-column descriptor --bin-country-column bin_country \
|
|
16
|
+
--response-column response --approval-values A001 --suspected-fraud-values D102 \
|
|
17
|
+
--account-column account --card-type-column card_type --avs-column avs --auth-column auth \
|
|
18
|
+
--output streams.json --markdown streams.md
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`--rates` is a daily cache `{base, rates:{YYYY-MM-DD:{CCY:rate}}}` quoted as
|
|
22
|
+
units per one base unit; `--fetch-rates` fills it from Frankfurter v1 once.
|
|
23
|
+
Prior-day carry (default 7 days) covers weekends; carried days are counted in
|
|
24
|
+
the output. Pegged currencies (SAR, AED, QAR, KWD, BHD, OMR, HKD) are built in.
|
|
25
|
+
This cache is for discovery; the audit FX path in
|
|
26
|
+
[fx-conversion.md](fx-conversion.md) keeps its own contract.
|
|
27
|
+
|
|
28
|
+
## What it does, per descriptor and BIN-country group (default ≥80 attempts)
|
|
29
|
+
|
|
30
|
+
1. Retail list, strong test: one constant `k` in 1.00 to 1.15 under which at
|
|
31
|
+
least 30% of amounts are retail-shaped (`x.99`, `x.95`, `x.98`, `x.00`) with
|
|
32
|
+
at least 8 distinct base prices, against a control that jitters amounts by
|
|
33
|
+
0.4 to 2%. Rows on the list become a `retail x<k>` stream.
|
|
34
|
+
2. Wallet inference: for each candidate currency (legal tender of the card
|
|
35
|
+
country, EUR, USD, GBP, CHF), convert amounts at the day's rate and find
|
|
36
|
+
peaks within 1.2%; coverage is the share of rows on the top ten peaks, the
|
|
37
|
+
control re-measures jittered rows against the same centres, and day drift is
|
|
38
|
+
how much the daily median of each peak moves. Prefer the currency whose
|
|
39
|
+
peaks land on a known menu, then the lowest drift, then legal tender, EUR,
|
|
40
|
+
USD. A wallet is accepted at 45% coverage, or at 15% coverage when the peaks
|
|
41
|
+
sit on a menu (a minority stream inside a mixed group).
|
|
42
|
+
3. Menu scheme: peaks are matched (1.5% tolerance) against the 10-scheme
|
|
43
|
+
(10, 20, 25, 30, 40, 50, 100 and multiples, with a scale of 1, 5, 10, 25,
|
|
44
|
+
100, 400 or 1000 for small-unit currencies) and the 12-scheme (12, 24, 36,
|
|
45
|
+
48, 72, 96, 108, 144, 192, 288, 384). When a scheme covers at least half of
|
|
46
|
+
the peak attempts, only the on-menu rows form the stream; off-menu peaks stay
|
|
47
|
+
in the residual for the next round.
|
|
48
|
+
4. Rate consistency: for each rung with at least 3 rows on at least 4 days,
|
|
49
|
+
the stream is `frozen` when the processed amount is identical to the cent on
|
|
50
|
+
every day while the market moved at least 0.1%; otherwise `live`. Players on
|
|
51
|
+
a live cashier drift; a script that fetched one rate does not.
|
|
52
|
+
5. Weak retail test on what remains (20% share, 5 distinct bases), then
|
|
53
|
+
`off-menu` for the residual.
|
|
54
|
+
|
|
55
|
+
Up to three rounds per group. Every threshold is a CLI option and is echoed in
|
|
56
|
+
the JSON under `parameters`.
|
|
57
|
+
|
|
58
|
+
## Reading the output
|
|
59
|
+
|
|
60
|
+
| Label | Meaning | Treat as |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `EUR menu-10 live` | wallet presets converted live near the market rate | player deposits on a euro-priced cashier |
|
|
63
|
+
| `AUD menu-10 live`, `CZK menu-10 live` | local-currency menus per market | the same cashier's local settings |
|
|
64
|
+
| `EUR menu-12 frozen` | preset menu at one rate for days | generated traffic; contrast its behaviour |
|
|
65
|
+
| `retail x1.06` | list prices times one constant, many distinct bases | a shop checkout; the constant is a fake tax line or a stale rate, not a fee to interpret |
|
|
66
|
+
| `GBP menu-none live` | peaks without a known scheme | a menu not in the built-in schemes, or a mixture; inspect peaks |
|
|
67
|
+
| `off-menu` | no peak structure | typed amounts or another upstream; a whole country off-menu is a finding |
|
|
68
|
+
|
|
69
|
+
Behaviour per stream: attempts, cards, repeat-card share, retry within 30
|
|
70
|
+
minutes after a decline, top-three-hour share (UTC), burst gap regularity,
|
|
71
|
+
address verification present, 3-D Secure present, prepaid share, top-five BIN
|
|
72
|
+
share, approval, suspected fraud. On the reference book, retry after decline
|
|
73
|
+
separated players (31% casino, 45% shop) from a generated batch (0.4%) more
|
|
74
|
+
sharply than approval rate did.
|
|
75
|
+
|
|
76
|
+
## Rules
|
|
77
|
+
|
|
78
|
+
- Infer the wallet before interpreting any constant. Two apparent fees on the
|
|
79
|
+
reference book (5.75% and 12.1%) were AUD and EUR conversions.
|
|
80
|
+
- A shared numeric menu across several wallet currencies is a cashier
|
|
81
|
+
fingerprint; two menus in one descriptor are two cashiers.
|
|
82
|
+
- A country whose cards are charged in a currency that is not its legal
|
|
83
|
+
tender (Saudi cards in euro, US cards in euro) is reported as such; the
|
|
84
|
+
wallet currency is inferred, never assumed from BIN country.
|
|
85
|
+
- Streams feed step 2: run exact currency-amount-pair clustering inside a
|
|
86
|
+
stream and use the stream, not the whole descriptor, as the baseline
|
|
87
|
+
population when a descriptor carries several streams.
|
|
88
|
+
- Do not call a frozen-rate stream laundering or fraud in the report; call it
|
|
89
|
+
generated traffic and show the drift comparison against live groups on the
|
|
90
|
+
same rungs and days.
|
|
@@ -4,13 +4,14 @@ Use these terms in TLD reports. Read before writing. After writing, run
|
|
|
4
4
|
the proofread in `SKILL.md`.
|
|
5
5
|
|
|
6
6
|
TLD in this skill is **transaction laundering**. Spell it out on first
|
|
7
|
-
use
|
|
7
|
+
use as **transaction laundering (TLD)**. The English report title is the first
|
|
8
|
+
use and must carry that expansion. It is not a top-level domain.
|
|
8
9
|
|
|
9
10
|
## Canonical glossary
|
|
10
11
|
|
|
11
12
|
| English | Chinese | Do not write |
|
|
12
13
|
|---|---|---|
|
|
13
|
-
|
|
|
14
|
+
| transaction laundering (TLD) | 交易清洗(TLD) | 把 TLD 写成域名;洗钱结案 |
|
|
14
15
|
| potential TLD currency-amount pair | 潜在 TLD 币种金额对 | 已确认 TLD;TLD 商户 |
|
|
15
16
|
| cluster (report section) | 币种金额对 | 客户簇;客群;分群结果 |
|
|
16
17
|
| anchor detection | 锚点识别 | 客户聚类(对业务读者) |
|
|
@@ -31,13 +32,19 @@ use. It is not a top-level domain.
|
|
|
31
32
|
| BIN country / issuer country | 发卡行国家 / BIN 国家 | 客户国家;收货国;发卡国 |
|
|
32
33
|
| cluster composition by BIN country | BIN 国家占 Cluster | 客户国家占比 |
|
|
33
34
|
| descriptor-country slice coverage | Cluster 占 Descriptor 对应 BIN 国家 | BIN 国家风险率;国家命中率 |
|
|
34
|
-
| exact single-country standout | 100% 单一 BIN 国家 standout | 高国家占比;接近 100% |
|
|
35
|
+
| canonical exact single-country standout | canonical 原币 100% 单一 BIN 国家 standout | 高国家占比;接近 100% |
|
|
35
36
|
| BIN-country local hour | BIN 国家当地小时 | 客户所在地时间;统一美国东部时间 |
|
|
36
37
|
| this analysis window | 当前分析时段内 | 本窗口 |
|
|
37
38
|
| MCC vs descriptor | MCC 与 descriptor 合理性 | 纠正 MCC;改 MCC |
|
|
38
39
|
| verdict | 结论 | 长文综述(作结论部分时) |
|
|
39
40
|
| High / Medium / Low risk | 高风险 / 中风险 / 低风险 | Supported;Weak;Ladder only;已确认 TLD |
|
|
40
41
|
| risk exposure | 风险敞口 | 已确认 TLD 影响;定罪金额 |
|
|
42
|
+
| stream | 上游流 | 客群;渠道 |
|
|
43
|
+
| wallet currency (inferred) | 钱包币种(推断) | 发卡国货币(作为钱包币种时) |
|
|
44
|
+
| preset menu | 预设金额菜单 | 阶梯(指菜单时);手续费 |
|
|
45
|
+
| frozen-rate stream | 固定汇率流 | 机器人;已确认套现 |
|
|
46
|
+
| retail constant | 标价常数 | 税率;手续费率 |
|
|
47
|
+
| generated traffic | 生成流量 | 欺诈交易(作为结论时) |
|
|
41
48
|
|
|
42
49
|
Keep proper nouns: Visa, MasterCard, BIN, IIN, MCC, MID, Worldpay,
|
|
43
50
|
Pazien, Order ID, descriptor. Tables may show response codes; prose uses
|
|
@@ -45,9 +52,10 @@ Pazien, Order ID, descriptor. Tables may show response codes; prose uses
|
|
|
45
52
|
|
|
46
53
|
## Axis and UI
|
|
47
54
|
|
|
48
|
-
-
|
|
49
|
-
(`授权尝试金额 USD`, `授权尝试金额 EUR`).
|
|
50
|
-
|
|
55
|
+
- Canonical amount axes use and name the cluster's **original transaction
|
|
56
|
+
currency** (`授权尝试金额 USD`, `授权尝试金额 EUR`). FX amount axes use and
|
|
57
|
+
name the **target BIN-origin legal-tender currency**. Never mix currencies on
|
|
58
|
+
one axis in either view.
|
|
51
59
|
- Date charts use each series' share of attempts by UTC transaction date.
|
|
52
60
|
- Time charts use each BIN country's local hour, falling back to UTC when no
|
|
53
61
|
timezone is configured. Normalize Cluster and Baseline independently.
|
|
@@ -59,7 +67,7 @@ Chinese: `走廊`, `杠杆`, `转化率`, `赋能`, `抓手`, `闭环`, `打法`
|
|
|
59
67
|
`本窗口`, `已确认 TLD`, `已确认交易清洗`, `洗钱`, `发卡国`, `客群运营`,
|
|
60
68
|
`客户簇`, `金额币种对`
|
|
61
69
|
|
|
62
|
-
English: `confirmed laundering`, `confirmed TLD`, `money laundering`,
|
|
70
|
+
English: `deposit fee` or `surcharge` for a converted preset, `bot` as a finding, `confirmed laundering`, `confirmed TLD`, `money laundering`,
|
|
63
71
|
`SAR`, `file a SAR`, `top-level domain`, `customer country`,
|
|
64
72
|
`conversion rate` (when meaning auth rate), `corridor`, `price band`
|
|
65
73
|
(as the cluster name)
|
|
@@ -71,7 +71,7 @@ is frozen.
|
|
|
71
71
|
|
|
72
72
|
## Order that must not move
|
|
73
73
|
|
|
74
|
-
Clean → descriptor partition → currency-amount pairs → same-
|
|
74
|
+
Clean → descriptor partition → stream inference (wallet currency, menu, frozen, retail) → currency-amount pairs inside streams → same-stream baseline (same-descriptor when one stream) → decide → report.
|
|
75
75
|
|
|
76
76
|
Comparison features must not create a cluster that amount detection did not
|
|
77
77
|
find. Decision does not add evidence; it only writes the ≤3-sentence
|