Files
PIG-Demo/envs/alert_triage/alert_triage/generator.py
T
karti-ai 2dfa96939e
ci / web (push) Successful in 2m43s
ci / python (push) Successful in 2m36s
Alert Triage: environment #2, built end to end by the pipeline
The first environment shipped through .claude/workflows/new-environment.js:
specification, three adversarial reviews (all 'fixable', none fatal), the
Python environment, the TypeScript port, captured rollouts, and the demo page.
Eleven agents, no errors.

The proof that the platform scales is one line long. Alert Triage has a
completely different shape from Word Five — JSON actions, priced lookups, an
analyst screen instead of a grid — and the only change under
src/components/demo/ is a comment edit, because the isolation lint refused the
word "wordle" there. Zero shell code changed. 415 contract checks now pass
against two demos, up from 206 against one.

The environment is honest by construction. Every alert is synthetic, generated
from the seed, and the banner saying so sits inside the board surface. Two of
the eleven scenario templates are hidden-suspicious: generated by the same code
as their benign twin with the signal overlaid only in lookup data, so the free
screen is identically distributed and a screen-only policy STRUCTURALLY cannot
tell them apart. The probe ladder measures it: `fast` catches 0.0 of hidden
seeds. That is the counterweight made real rather than asserted.

Twelve policies, thirteen ladder assertions, a genuine three-way trade:

  fast      0.846   wins hours (0.85), misses every hidden case
  targeted  0.894   wins the shipped total
  thorough  0.820   wins evidence (1.00), spends 2.9 hours

None dominates. 92 Python tests, 35 TypeScript tests, 65 fixtures replaying at
delta 0, and conformance gated on world + scorer + protocol so the browser shows
the same alert for ?seed= that Python generated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
2026-08-28 19:48:36 -07:00

882 lines
40 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""The world for a seed: one alert on one synthetic customer.
Everything here is synthetic. No customer, account, transaction, counterparty,
analyst or institution exists behind any of it — see `names/PROVENANCE.md`.
Portability is the whole design. `src/demos/alert-triage/` carries a port of
this file, and CI hashes the canonical JSON of every world for seeds 04095 on
both sides. So:
* every random draw goes through `XorShift32` seeded by FNV-1a over the
decimal seed, in the order written here — the order IS the contract;
* every amount is an integer in minor units (cents), never a float;
* every date is a day ordinal turned into YYYY-MM-DD by our own civil-date
function, never a platform date type;
* canonical JSON is sorted keys, no whitespace, `null` for absent.
The hidden tier — a suspicious alert whose free screen is drawn from a benign
template — is produced by running the benign builder to completion, freezing
the screen, and only then overlaying the suspicious signal onto data that a
lookup returns. `screen_of(world)` is byte-identical with the overlay on or
off, and `tests/test_screen_leak.py` asserts it for every seed.
"""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from typing import Any
from .rng import (
XorShift32,
add_months,
days_from_civil,
days_in_month,
fnv1a32,
iso_date,
month_key,
)
# ------------------------------------------------------------ constants --
MAX_TURNS = 8
WINDOW_MONTHS = 12
SCREEN_COUNTERPARTIES = 2 # top-N by volume shown on the free screen
# Mix by seed. Deliberately not the real false-positive base rate — see
# README.md, "what this is easier than".
BENIGN_PCT = 55
VISIBLE_PCT = 30
# hidden = the remaining 15
FAMILIES = ("STR", "VEL", "CASH", "WIRE", "DORM")
# Rule ids per slice. The held-out slice renames them so a policy that learned
# "9,xxx and R-STR-01" rather than the pattern is caught.
RULE_IDS = {
"main": {"STR": "R-STR-01", "VEL": "R-VEL-02", "CASH": "R-CASH-03", "WIRE": "R-WIRE-04", "DORM": "R-DORM-05"},
"held_out": {"STR": "TM-A1", "VEL": "TM-A2", "CASH": "TM-A3", "WIRE": "TM-A4", "DORM": "TM-A5"},
}
RULE_TEXT = {
"STR": "Three or more cash deposits just under the reporting threshold within ten days",
"VEL": "Funds received and moved out again within 48 hours",
"CASH": "Monthly cash deposits materially above the trailing six-month average",
"WIRE": "Outbound wires to a jurisdiction on the institution's monitored list",
"DORM": "Large credit to an account with no material activity for nine months",
}
# The typology the rule nominally points at — what a defensive filer would
# write. Diagnostic only; `typology` is never scored.
NOMINAL_TYPOLOGY = {"STR": "STRUCTURING", "VEL": "RAPID_MOVEMENT", "CASH": "STRUCTURING", "WIRE": "UNKNOWN", "DORM": "UNKNOWN"}
CURRENCIES = {
# threshold in minor units; scale multiplies every generated amount
"main": {"currency": "USD", "threshold": 1_000_000, "scale": 1},
"held_out": {"currency": "KRN", "threshold": 2_000_000, "scale": 2},
}
BENIGN_TEMPLATES = ("B1", "B2", "B3", "B4", "B5", "B6")
VISIBLE_TEMPLATES = ("S1", "S3", "S6")
OVERLAYS = ("funnel", "serial_closer", "doc_mismatch")
DOC_TEMPLATES = ("B2", "B3", "B6") # where a document is the explaining fact
TEMPLATE_FAMILY = {"B1": "CASH", "B2": "VEL", "B3": "VEL", "B4": "STR", "B5": "WIRE", "B6": "DORM", "S1": "STR", "S3": "VEL", "S6": "CASH"}
INBOUND = ("cash_in", "wire_in", "ach_in", "p2p_in")
_NAMES = Path(__file__).parent.parent / "names"
def _load(name: str):
return json.loads((_NAMES / name).read_text())
FIRST_NAMES: list[str] = _load("first_names.json")
SURNAMES: list[str] = _load("surnames.json")
WORDS: dict[str, list[str]] = _load("words.json")
JURISDICTIONS: dict[str, Any] = _load("jurisdictions.json")
BOILERPLATE = "Reviewed. Activity consistent with customer profile. No further action."
BENIGN_RATIONALE = {
"STR": "Closed: deposits are documented daily takings; amounts vary with trading days; within kyc.expected_monthly_cash.",
"VEL": "Closed: pass-through matches the settlement terms in the agreement on file; counterparties verified.",
"CASH": "Closed: seasonal peak consistent with kyc.expected_activity; volume within stated expectation.",
"WIRE": "Closed: destination and amount match the remittance declared at onboarding; within stated income.",
"DORM": "Closed: reactivation explained by a documented source of funds; document reviewed and retained.",
}
# ----------------------------------------------------------- utilities --
def canonical_json(value: Any) -> str:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
def _round_to(cents: int, step: int) -> int:
return (cents // step) * step
class _Ledger:
"""Transactions are collected unordered, then ids are assigned by date.
A stable sort on the day ordinal, with insertion order breaking ties, is
what makes T-ids identical across the two implementations.
"""
def __init__(self) -> None:
self.rows: list[dict[str, Any]] = []
def add(self, day: int, channel: str, amount: int, branch: str | None = None, cp: str | None = None) -> int:
self.rows.append({"day": day, "seq": len(self.rows), "channel": channel, "amount": amount, "branch": branch, "cp": cp})
return len(self.rows) - 1
def finish(self) -> tuple[list[dict[str, Any]], dict[int, str]]:
ordered = sorted(self.rows, key=lambda r: (r["day"], r["seq"]))
id_of: dict[int, str] = {}
out = []
for i, row in enumerate(ordered):
tid = f"T-{i + 1}"
id_of[row["seq"]] = tid
out.append({
"id": tid,
"date": iso_date(row["day"]),
"month": iso_date(row["day"])[:7],
"channel": row["channel"],
"amount": row["amount"],
"branch": row["branch"],
"cp": row["cp"],
})
return out, id_of
class _Ctx:
"""Everything a template builder needs, plus the accumulating world."""
def __init__(self, seed: int, rng: XorShift32, slice_name: str) -> None:
self.seed = seed
self.rng = rng
self.slice = slice_name
cur = CURRENCIES[slice_name]
self.currency = cur["currency"]
self.threshold = cur["threshold"]
self.scale = cur["scale"]
self.rule_ids = RULE_IDS[slice_name]
self.home = JURISDICTIONS["home"][self.currency]
self.ledger = _Ledger()
self.cps: list[dict[str, Any]] = []
self.customer: dict[str, Any] = {}
self.months: list[tuple[int, int]] = []
self.fire_day = 0
self.fire_y = 0
self.fire_m = 0
self.documents: list[dict[str, Any]] = []
self.doc_party_cp: str | None = None # the CP a party-bearing document names
self.branches = rng.sample(WORDS["branches"], 2)
# money: `usd(n)` is n whole units of the slice currency, in minor units.
def usd(self, n: int) -> int:
return n * 100 * self.scale
def money(self, lo: int, hi: int, step: int = 10) -> int:
"""A random whole-unit amount in [lo, hi], rounded down to `step` units."""
return _round_to(self.usd(self.rng.between(lo, hi)), self.usd(step))
def set_fire(self, month_choices: list[int], day_lo: int = 12, day_hi: int = 27) -> None:
self.fire_y = 2026
self.fire_m = self.rng.pick(month_choices)
self.fire_day = days_from_civil(self.fire_y, self.fire_m, self.rng.between(day_lo, day_hi))
self.months = [add_months(self.fire_y, self.fire_m, -(WINDOW_MONTHS - 1 - i)) for i in range(WINDOW_MONTHS)]
def fire_dom(self) -> int:
return self.fire_day - days_from_civil(self.fire_y, self.fire_m, 1) + 1
def day_in(self, i: int, lo: int = 1, hi: int | None = None) -> int:
"""A day ordinal inside window month i. Nothing is ever dated on or after the fire date."""
y, m = self.months[i]
last = days_in_month(y, m) if hi is None else hi
if i == WINDOW_MONTHS - 1:
last = min(last, self.fire_dom() - 1)
lo = min(lo, last)
return days_from_civil(y, m, self.rng.between(lo, last))
def days_in(self, i: int, k: int, lo: int = 1, hi: int | None = None) -> list[int]:
"""k distinct days in month i, ascending."""
y, m = self.months[i]
last = days_in_month(y, m) if hi is None else hi
if i == WINDOW_MONTHS - 1:
last = min(last, self.fire_dom() - 1)
lo = min(lo, last)
first = days_from_civil(y, m, 1)
picks = self.rng.sample(list(range(lo, last + 1)), k)
return sorted(first + d - 1 for d in picks)
def person(self) -> str:
return f"{self.rng.pick(FIRST_NAMES)} {self.rng.pick(SURNAMES)}"
def business(self, noun: str | None = None) -> str:
adj = self.rng.pick(WORDS["adjectives"])
noun = noun or self.rng.pick(WORDS["nouns"])
return f"{adj} {noun} {self.rng.pick(WORDS['suffixes'])}"
def cp(self, name: str, kind: str, jurisdiction: str | None = None) -> str:
cid = f"CP-{len(self.cps) + 1}"
self.cps.append({"id": cid, "name": name, "type": kind, "jurisdiction": jurisdiction or self.home})
return cid
def doc(self, kind: str, description: str, party: str | None = None) -> str:
while True:
did = f"doc.D-{self.rng.hex4()}"
if all(d["id"] != did for d in self.documents):
break
self.documents.append({"id": did, "kind": kind, "description": description, "party": party})
return did
def add(self, day: int, channel: str, amount: int, branch: str | None = None, cp: str | None = None) -> int:
return self.ledger.add(day, channel, amount, branch, cp)
def branch(self) -> str:
return self.rng.pick(self.branches)
def max_month_cash(self) -> int:
"""The largest month of cash actually deposited so far — what the KYC expectation must cover."""
totals: dict[int, int] = {}
for row in self.ledger.rows:
if row["channel"] == "cash_in":
key = iso_date(row["day"])[:7]
idx = int(key[:4]) * 12 + int(key[5:])
totals[idx] = totals.get(idx, 0) + row["amount"]
return max(totals.values()) if totals else 0
def _kyc_common(ctx: _Ctx, customer_type: str) -> None:
"""The KYC draws every template makes, in the same order.
Prior-alert and prior-SAR counts are drawn HERE, before any template or
label-specific code runs, from one distribution. The serial-closer overlay
rewrites what the prior alerts say; it can never change how many there are,
which is the number the screen shows.
"""
rng = ctx.rng
ctx.customer = {
"id": f"C-{rng.between(1000, 9999)}",
"name": "",
"customer_type": customer_type,
"country": ctx.home,
"pep": rng.chance(3),
"prior_alerts": 0,
"prior_sars": 0,
"remittance_declared": "none declared",
}
roll = rng.below(100)
ctx.customer["prior_alerts"] = 0 if roll < 35 else 1 if roll < 55 else 2 if roll < 70 else 3 if roll < 88 else 4
ctx.customer["prior_sars"] = 1 if ctx.customer["prior_alerts"] >= 1 and rng.chance(8) else 0
opened_years = rng.between(2, 15)
ctx.customer["account_opened"] = iso_date(ctx.fire_day - opened_years * 365 - rng.between(0, 300))
ctx.customer["kyc_refreshed"] = iso_date(ctx.fire_day - rng.between(40, 500))
def _salary(ctx: _Ctx, monthly: int, employer: str) -> None:
for i in range(WINDOW_MONTHS):
amount = monthly + ctx.usd(ctx.rng.between(-60, 60))
ctx.add(ctx.day_in(i, 1, 5), "ach_in", amount, cp=employer)
def _small_cash_out(ctx: _Ctx, lo: int, hi: int, per_month_lo: int, per_month_hi: int) -> None:
for i in range(WINDOW_MONTHS):
n = ctx.rng.between(per_month_lo, per_month_hi)
for day in ctx.days_in(i, n):
ctx.add(day, "cash_out", ctx.money(lo, hi), branch=ctx.branch())
def _cash_deposits(ctx: _Ctx, i: int, total: int, n: int, lo_cap: int, hi_cap: int, days: list[int] | None = None) -> list[int]:
"""n cash deposits in month i summing roughly to `total`, each clamped."""
days = days if days is not None else ctx.days_in(i, n)
out = []
for day in days:
amount = total // n * ctx.rng.between(75, 125) // 100
amount = max(lo_cap, min(hi_cap, _round_to(amount, ctx.usd(10))))
out.append(ctx.add(day, "cash_in", amount, branch=ctx.branch()))
return out
# --------------------------------------------------------------- benign --
def _build_B1(ctx: _Ctx) -> dict[str, Any]:
"""Seasonal cash business. CASH fires on the first peak month; the KYC file said it would."""
rng = ctx.rng
kind = rng.pick([
("landscaping services", "Landscaping", [4, 5, 6, 7, 8, 9], [4, 5], "April to September"),
("tax preparation", "Tax Services", [2, 3, 4], [3], "February to April"),
("pool maintenance", "Pool Care", [5, 6, 7, 8], [5, 6], "May to August"),
])
business_type, noun, peak, fire_choices, peak_text = kind
ctx.set_fire(fire_choices)
_kyc_common(ctx, "business")
ctx.customer["name"] = ctx.business(noun)
ctx.customer["business_type"] = business_type
off = ctx.money(3000, 7000, 100)
mult = rng.between(30, 40)
supplier = ctx.cp(ctx.business("Supply"), "business")
equipment = ctx.cp(ctx.business("Equipment"), "business")
for i, (y, m) in enumerate(ctx.months):
in_peak = m in peak
total = off * mult // 10 if in_peak else off
total = total * rng.between(85, 115) // 100
n = rng.between(8, 12) if in_peak else rng.between(3, 5)
_cash_deposits(ctx, i, total, n, ctx.usd(200), ctx.threshold * 8 // 10 - ctx.usd(100))
ctx.add(ctx.day_in(i, 20, 28), "ach_out", total * rng.between(20, 35) // 100, cp=supplier)
if m % 3 == 0:
ctx.add(ctx.day_in(i, 6, 12), "wire_out", ctx.money(2500, 9000, 100), cp=equipment)
expected = _round_to(ctx.max_month_cash() * rng.between(110, 130) // 100, ctx.usd(1000)) + ctx.usd(1000)
ctx.customer["expected_monthly_cash"] = expected
ctx.customer["expected_activity"] = f"Seasonal business; cash receipts peak {peak_text}."
ctx.customer["stated_income"] = _round_to(expected * 7, ctx.usd(1000))
ctx.doc("registration", f"Business registration — {ctx.customer['name']}")
ctx.doc("licence", f"Trade licence, {business_type}, renewed annually")
return {
"family": "CASH",
"triggering": "fire_month_cash",
"planted": [["kyc.expected_activity", "kyc.expected_monthly_cash"], ["kyc.expected_monthly_cash"]],
}
def _build_B4(ctx: _Ctx) -> dict[str, Any]:
"""Cash restaurant. Three of the month's deposits land in the band; the file expected more cash than this."""
rng = ctx.rng
business_type, noun = rng.pick([("restaurant", "Bistro"), ("diner", "Diner"), ("pizzeria", "Kitchen"), ("cafe", "Grill")])
ctx.set_fire([3, 4, 5, 6], 14, 27)
_kyc_common(ctx, "business")
ctx.customer["name"] = ctx.business(noun)
ctx.customer["business_type"] = business_type
base = ctx.money(40000, 75000, 1000)
produce = ctx.cp(ctx.business("Produce"), "business")
foods = ctx.cp(ctx.business("Foods"), "business")
band_lo = ctx.threshold * 8 // 10
for i in range(WINDOW_MONTHS):
total = base * rng.between(90, 110) // 100
n = rng.between(11, 14)
if i == WINDOW_MONTHS - 1:
# Three window deposits in the band, on distinct days; the rest ordinary.
fire_dom = ctx.fire_dom()
window_days = ctx.days_in(i, 3, max(1, fire_dom - 10), fire_dom - 1)
for day in window_days:
ctx.add(day, "cash_in", ctx.money(band_lo // ctx.usd(1) + 100, ctx.threshold // ctx.usd(1) - 100), branch=ctx.branch())
n -= 3
_cash_deposits(ctx, i, total, n, ctx.usd(1500), band_lo - ctx.usd(100))
for day in ctx.days_in(i, 4, 1, 28):
ctx.add(day, "ach_out", total * rng.between(4, 7) // 100, cp=produce)
ctx.add(ctx.day_in(i, 1, 10), "ach_out", total * rng.between(10, 16) // 100, cp=foods)
expected = _round_to(ctx.max_month_cash() * rng.between(110, 130) // 100, ctx.usd(1000)) + ctx.usd(1000)
ctx.customer["expected_monthly_cash"] = expected
ctx.customer["expected_activity"] = "Cash-intensive food service; daily takings deposited most trading days."
ctx.customer["stated_income"] = _round_to(base * 12, ctx.usd(1000))
ctx.doc("registration", f"Business registration — {ctx.customer['name']}")
ctx.doc("licence", "Food service licence, renewed annually")
return {
"family": "STR",
"triggering": "band_window",
"planted": [["kyc.expected_monthly_cash"], ["kyc.expected_monthly_cash", "kyc.business_type"]],
}
def _build_B2(ctx: _Ctx) -> dict[str, Any]:
"""Processor settlement. Wires in from a processor, contractors paid within two days; the agreement is on file."""
rng = ctx.rng
business_type, noun = rng.pick([("staffing agency", "Staffing"), ("freight brokerage", "Freight"), ("event production", "Events"), ("general contractor", "Contracting")])
ctx.set_fire([3, 4, 5, 6], 14, 27)
_kyc_common(ctx, "business")
ctx.customer["name"] = ctx.business(noun)
ctx.customer["business_type"] = business_type
processor_name = f"{rng.pick(WORDS['adjectives'])} Payments LLC"
processor = ctx.cp(processor_name, "processor")
ctx.doc_party_cp = processor
contractors = [ctx.cp(ctx.business(), "business") for _ in range(2)]
revenue = 0
window: list[int] = []
for i in range(WINDOW_MONTHS):
k = rng.between(3, 4)
days = ctx.days_in(i, k, 1, 24)
if i == WINDOW_MONTHS - 1:
# Guarantee one settlement inside the 14-day window with its payouts before the fire date.
fire_dom = ctx.fire_dom()
days = ctx.days_in(i, k - 1, 1, max(1, fire_dom - 8)) + [ctx.fire_day - rng.between(3, 6)]
for day in days:
amount = ctx.money(15000, 60000, 100)
revenue += amount
t_in = ctx.add(day, "wire_in", amount, cp=processor)
share = rng.between(70, 90)
first = amount * share // 100 * rng.between(50, 70) // 100
second = amount * share // 100 - first
t_a = ctx.add(day + 1, "wire_out", _round_to(first, ctx.usd(10)), cp=contractors[0])
t_b = ctx.add(day + rng.between(1, 2), "ach_out", _round_to(second, ctx.usd(10)), cp=contractors[1])
if i == WINDOW_MONTHS - 1 and day >= ctx.fire_day - 14:
window += [t_in, t_a, t_b]
ctx.customer["expected_monthly_cash"] = ctx.usd(0)
ctx.customer["expected_activity"] = "Card settlements from a payment processor; subcontractor payouts within days of settlement."
ctx.customer["stated_income"] = _round_to(revenue, ctx.usd(10000))
ctx.doc("registration", f"Business registration — {ctx.customer['name']}")
ctx.doc("processing_agreement", f"Merchant processing agreement between {ctx.customer['name']} and {processor_name}; settlement to this account on T+1.", party=processor_name)
return {"family": "VEL", "triggering": window, "planted": [["DOC"], ["DOC", "kyc.business_type"]]}
def _build_B3(ctx: _Ctx) -> dict[str, Any]:
"""Property sale. One title-company wire in, moved to a brokerage; closing statement on file."""
rng = ctx.rng
ctx.set_fire([3, 4, 5, 6], 14, 27)
_kyc_common(ctx, "individual")
ctx.customer["name"] = ctx.person()
ctx.customer["occupation"] = rng.pick(["teacher", "software engineer", "pharmacist", "electrician", "accountant"])
salary = ctx.money(4000, 9000, 100)
employer = ctx.cp(ctx.business(), "business")
_salary(ctx, salary, employer)
_small_cash_out(ctx, 100, 400, 2, 4)
title_name = f"{rng.pick(SURNAMES)} Title & Escrow"
title = ctx.cp(title_name, "title_company")
ctx.doc_party_cp = title
brokerage = ctx.cp(f"{rng.pick(WORDS['adjectives'])} Securities", "brokerage")
proceeds = ctx.money(180000, 650000, 1000)
day_in = ctx.fire_day - rng.between(3, 6)
t_in = ctx.add(day_in, "wire_in", proceeds, cp=title)
t_out = ctx.add(day_in + rng.between(1, 2), "wire_out", _round_to(proceeds * rng.between(85, 100) // 100, ctx.usd(100)), cp=brokerage)
ctx.customer["expected_monthly_cash"] = ctx.usd(0)
ctx.customer["expected_activity"] = "Salaried individual; payroll deposits and routine spending."
ctx.customer["stated_income"] = _round_to(salary * 12, ctx.usd(1000))
ctx.doc("identity", "Government-issued identity document, verified at onboarding")
address = f"{rng.between(12, 980)} {rng.pick(WORDS['streets'])}"
ctx.doc("closing_statement", f"Closing statement — sale of {address}; net proceeds to seller disbursed by {title_name}.", party=title_name)
return {"family": "VEL", "triggering": [t_in, t_out], "planted": [["DOC"], ["DOC", "kyc.occupation"]]}
def _build_B5(ctx: _Ctx) -> dict[str, Any]:
"""Family remittance. A monthly wire to one relative in a monitored jurisdiction, declared at onboarding."""
rng = ctx.rng
ctx.set_fire([3, 4, 5, 6])
_kyc_common(ctx, "individual")
surname = rng.pick(SURNAMES)
ctx.customer["name"] = f"{rng.pick(FIRST_NAMES)} {surname}"
ctx.customer["occupation"] = rng.pick(["registered nurse", "civil engineer", "teacher", "pharmacist"])
salary = ctx.money(4500, 8500, 100)
employer = ctx.cp(ctx.business(), "business")
_salary(ctx, salary, employer)
_small_cash_out(ctx, 100, 400, 2, 4)
jurisdiction = rng.pick(JURISDICTIONS["monitored"])
relation = rng.pick(["mother", "father", "brother", "sister"])
relative = ctx.cp(f"{rng.pick(FIRST_NAMES)} {surname}", "individual", jurisdiction)
monthly = ctx.money(800, 2500, 50)
wires = []
for i in range(WINDOW_MONTHS):
wires.append(ctx.add(ctx.day_in(i, 1, 7), "wire_out", monthly + ctx.usd(rng.between(-50, 50)), cp=relative))
ctx.customer["expected_monthly_cash"] = ctx.usd(0)
ctx.customer["expected_activity"] = "Salaried individual; payroll deposits and routine spending."
ctx.customer["remittance_declared"] = f"Monthly support to {relation} in {jurisdiction}, about {monthly // 100 // ctx.scale * ctx.scale:,} {ctx.currency} per month."
ctx.customer["stated_income"] = _round_to(salary * 12, ctx.usd(1000))
ctx.doc("identity", "Government-issued identity document, verified at onboarding")
ctx.doc("address", "Proof of address, utility statement")
return {"family": "WIRE", "triggering": wires[-3:], "planted": [["kyc.remittance_declared"], ["kyc.remittance_declared", "kyc.stated_income"]]}
def _build_B6(ctx: _Ctx) -> dict[str, Any]:
"""Inheritance. A dormant account receives an estate distribution; the executor's letter is on file."""
rng = ctx.rng
ctx.set_fire([3, 4, 5, 6], 14, 27)
_kyc_common(ctx, "individual")
ctx.customer["name"] = ctx.person()
ctx.customer["occupation"] = "retired"
ctx.customer["account_opened"] = iso_date(ctx.fire_day - rng.between(8, 20) * 365 - rng.between(0, 300))
for i in range(WINDOW_MONTHS - 1):
if rng.chance(35):
ctx.add(ctx.day_in(i), "cash_out", ctx.money(40, 200), branch=ctx.branch())
firm = f"{rng.pick(SURNAMES)} & {rng.pick(SURNAMES)} LLP, client trust account"
trust = ctx.cp(firm, "law_firm_trust")
ctx.doc_party_cp = trust
brokerage = ctx.cp(f"{rng.pick(WORDS['adjectives'])} Securities", "brokerage")
amount = ctx.money(60000, 400000, 1000)
day_in = ctx.fire_day - rng.between(4, 7)
t_in = ctx.add(day_in, "wire_in", amount, cp=trust)
t_out = ctx.add(day_in + rng.between(1, 3), "wire_out", _round_to(amount * rng.between(50, 90) // 100, ctx.usd(100)), cp=brokerage)
ctx.customer["expected_monthly_cash"] = ctx.usd(0)
ctx.customer["expected_activity"] = "Retired; low activity expected."
ctx.customer["stated_income"] = ctx.money(20000, 40000, 1000)
ctx.doc("identity", "Government-issued identity document, verified at onboarding")
executor, decedent = ctx.person(), ctx.person()
ctx.doc("executor_letter", f"Letter from {executor}, executor, via {firm}: distribution from the estate of {decedent} to the named beneficiary.", party=firm)
return {"family": "DORM", "triggering": [t_in, t_out], "planted": [["DOC"]]}
# -------------------------------------------------------------- visible --
def _build_S1(ctx: _Ctx) -> dict[str, Any]:
"""Structuring. Eight deposits just under the threshold in twelve days across three branches, on a modest income."""
rng = ctx.rng
ctx.set_fire([3, 4, 5, 6], 14, 27)
_kyc_common(ctx, "individual")
ctx.customer["name"] = ctx.person()
ctx.customer["occupation"] = rng.pick(["warehouse associate", "delivery driver", "retail supervisor", "line cook"])
ctx.branches = rng.sample(WORDS["branches"], 3)
salary = ctx.money(2600, 3600, 100)
employer = ctx.cp(ctx.business(), "business")
_salary(ctx, salary, employer)
_small_cash_out(ctx, 60, 300, 2, 4)
fire_dom = ctx.fire_dom()
days = ctx.days_in(WINDOW_MONTHS - 1, 8, max(1, fire_dom - 12), fire_dom - 2)
lo = ctx.threshold // ctx.usd(1) * 92 // 100
hi = ctx.threshold // ctx.usd(1) * 99 // 100
deposits = []
total = 0
for j, day in enumerate(days):
amount = ctx.money(lo, hi)
total += amount
deposits.append(ctx.add(day, "cash_in", amount, branch=ctx.branches[j % 3]))
holdings = ctx.cp(ctx.business("Holdings"), "business")
ctx.add(ctx.fire_day - 1, "wire_out", _round_to(total * rng.between(85, 95) // 100, ctx.usd(100)), cp=holdings)
ctx.customer["expected_monthly_cash"] = ctx.money(0, 500, 100)
ctx.customer["expected_activity"] = "Salaried individual; payroll deposits and routine spending."
ctx.customer["stated_income"] = _round_to(salary * 12 + ctx.usd(rng.between(0, 6000)), ctx.usd(1000))
ctx.doc("identity", "Government-issued identity document, verified at onboarding")
return {"family": "STR", "triggering": "band_window", "planted": [deposits], "typology": "STRUCTURING"}
def _build_S3(ctx: _Ctx) -> dict[str, Any]:
"""Money mule. Four P2P credits from unrelated individuals, each wired to an exchange within a day."""
rng = ctx.rng
ctx.set_fire([3, 4, 5, 6], 16, 27)
_kyc_common(ctx, "individual")
ctx.customer["name"] = ctx.person()
ctx.customer["occupation"] = rng.pick(["student", "retired", "part-time cashier"])
stipend = ctx.money(900, 1800, 50)
source = ctx.cp(ctx.business(), "business")
_salary(ctx, stipend, source)
_small_cash_out(ctx, 40, 200, 2, 4)
exchange = ctx.cp(f"{rng.pick(WORDS['adjectives'])} Digital Assets", "exchange")
fire_dom = ctx.fire_dom()
days = ctx.days_in(WINDOW_MONTHS - 1, 4, max(1, fire_dom - 14), fire_dom - 2)
pairs = []
for day in days:
sender = ctx.cp(ctx.person(), "individual")
amount = ctx.money(2000, 4900, 10)
t_in = ctx.add(day, "p2p_in", amount, cp=sender)
t_out = ctx.add(day + rng.between(0, 1), "wire_out", _round_to(amount * rng.between(90, 97) // 100, ctx.usd(10)), cp=exchange)
pairs += [t_in, t_out]
ctx.customer["expected_monthly_cash"] = ctx.money(0, 300, 100)
ctx.customer["expected_activity"] = "Low-income individual; small regular credits and routine spending."
ctx.customer["stated_income"] = _round_to(stipend * 12, ctx.usd(1000))
ctx.doc("identity", "Government-issued identity document, verified at onboarding")
return {"family": "VEL", "triggering": pairs, "planted": [pairs], "typology": "MONEY_MULE"}
def _build_S6(ctx: _Ctx) -> dict[str, Any]:
"""Smurfing over time. Monthly cash triples across the year and no single deposit ever reaches the threshold."""
rng = ctx.rng
business_type, noun = rng.pick([("vending route", "Vending"), ("car wash", "Car Wash"), ("laundromat", "Laundry"), ("convenience store", "Market")])
ctx.set_fire([3, 4, 5, 6], 14, 27)
_kyc_common(ctx, "business")
ctx.customer["name"] = ctx.business(noun)
ctx.customer["business_type"] = business_type
c0 = ctx.money(9000, 14000, 500)
supplier = ctx.cp(ctx.business("Supply"), "business")
cap = ctx.threshold - ctx.usd(100)
for i in range(WINDOW_MONTHS):
total = c0 * (11 + 2 * i) // 11 * rng.between(97, 103) // 100
n = rng.between(6, 9)
_cash_deposits(ctx, i, total, n, ctx.usd(2000), cap)
ctx.add(ctx.day_in(i, 20, 28), "ach_out", total * rng.between(15, 25) // 100, cp=supplier)
ctx.customer["expected_monthly_cash"] = _round_to(c0 * 12 // 10, ctx.usd(500))
ctx.customer["expected_activity"] = "Cash-intensive retail; steady volume expected."
ctx.customer["stated_income"] = _round_to(c0 * 14, ctx.usd(1000))
ctx.doc("registration", f"Business registration — {ctx.customer['name']}")
return {"family": "CASH", "triggering": "fire_month_cash", "planted": "last_two_months", "typology": "STRUCTURING"}
BUILDERS = {"B1": _build_B1, "B2": _build_B2, "B3": _build_B3, "B4": _build_B4, "B5": _build_B5, "B6": _build_B6, "S1": _build_S1, "S3": _build_S3, "S6": _build_S6}
# ------------------------------------------------------------- assembly --
def _prior_alerts(ctx: _Ctx, family: str) -> list[dict[str, Any]]:
"""Benign prior alerts: mixed rules, specific rationales, a few days each."""
rng = ctx.rng
out: list[dict[str, Any]] = []
used: set[str] = set()
for j in range(ctx.customer["prior_alerts"]):
while True:
aid = f"A-{rng.between(1000, 4999)}"
if aid not in used:
used.add(aid)
break
fam = rng.pick(list(FAMILIES))
fired = ctx.fire_day - rng.between(30, 720)
escalated = j == 0 and ctx.customer["prior_sars"] >= 1
out.append({
"id": aid,
"rule": ctx.rule_ids[fam],
"fired": iso_date(fired),
"disposition": "escalated — report filed" if escalated else "closed",
"analyst": ctx.person(),
"rationale": "Escalated: pattern not explained by the file at the time." if escalated else BENIGN_RATIONALE[fam],
"elapsed_days": rng.between(2, 9),
})
out.sort(key=lambda a: a["fired"])
return out
def _assemble(ctx: _Ctx, template: str, spec: dict[str, Any]) -> dict[str, Any]:
transactions, id_of = ctx.ledger.finish()
family = spec["family"]
# Triggering set, from the rule where the rule is mechanical.
if spec["triggering"] == "band_window":
lo, hi = ctx.threshold * 8 // 10, ctx.threshold - 1
triggering = [t["id"] for t in transactions
if t["channel"] == "cash_in" and lo <= t["amount"] <= hi
and ctx.fire_day - 10 <= days_from_civil(*map(int, t["date"].split("-"))) <= ctx.fire_day - 1]
elif spec["triggering"] == "fire_month_cash":
fire_month = month_key(ctx.fire_y, ctx.fire_m)
triggering = [t["id"] for t in transactions if t["channel"] == "cash_in" and t["month"] == fire_month]
else:
triggering = sorted((id_of[s] for s in spec["triggering"]), key=lambda x: int(x[2:]))
# Counterparties: transactions and first-seen from the ledger, direction from the channel.
by_cp: dict[str, list[dict[str, Any]]] = {c["id"]: [] for c in ctx.cps}
for t in transactions:
if t["cp"]:
by_cp[t["cp"]].append(t)
counterparties = []
for c in ctx.cps:
rows = by_cp[c["id"]]
counterparties.append({
**c,
"first_seen": rows[0]["date"] if rows else iso_date(ctx.fire_day),
"other_alerted_accounts": 1 if ctx.rng.chance(15) else 0,
"transactions": [t["id"] for t in rows],
})
ranked = sorted(counterparties, key=lambda c: (-sum(t["amount"] for t in by_cp[c["id"]]), int(c["id"][3:])))
screen_cps = []
for c in ranked[:SCREEN_COUNTERPARTIES]:
rows = by_cp[c["id"]]
screen_cps.append({
"id": c["id"],
"direction": "in" if rows and rows[0]["channel"] in INBOUND else "out",
"count": len(rows),
"total": sum(t["amount"] for t in rows),
})
summary = []
for (y, m) in ctx.months:
key = month_key(y, m)
row = {"month": key, "cash_in": 0, "cash_out": 0, "wires_in": 0, "wires_out": 0, "ach_in": 0, "ach_out": 0, "count": 0}
for t in transactions:
if t["month"] != key:
continue
row["count"] += 1
ch = t["channel"]
col = {"cash_in": "cash_in", "cash_out": "cash_out", "wire_in": "wires_in", "wire_out": "wires_out",
"ach_in": "ach_in", "ach_out": "ach_out", "p2p_in": "wires_in", "p2p_out": "wires_out"}[ch]
row[col] += t["amount"]
summary.append(row)
# Planted citation sets. "DOC" is the template's explaining document.
explaining = next((d["id"] for d in ctx.documents if d["party"]), None)
if spec["planted"] == "last_two_months":
last_two = {month_key(*ctx.months[-1]), month_key(*ctx.months[-2])}
planted = [[t["id"] for t in transactions if t["channel"] == "cash_in" and t["month"] in last_two]]
else:
planted = []
for alt in spec["planted"]:
planted.append([
(explaining if x == "DOC" else id_of[x] if isinstance(x, int) else x) for x in alt
])
world = {
"seed": ctx.seed,
"held_out": ctx.slice == "held_out",
"currency": ctx.currency,
"threshold": ctx.threshold,
"screen_order": "kyc_first" if ctx.slice == "held_out" else "alert_first",
"customer": ctx.customer,
"months": [month_key(y, m) for (y, m) in ctx.months],
"transactions": transactions,
"counterparties": counterparties,
"screen_counterparties": screen_cps,
"alert": {
"id": f"A-{ctx.rng.between(5000, 9999)}",
"rule": ctx.rule_ids[family],
"family": family,
"rule_text": RULE_TEXT[family],
"fired": iso_date(ctx.fire_day),
"triggering": triggering,
},
"prior_alerts": _prior_alerts(ctx, family),
"documents": ctx.documents,
"summary": summary,
"template": template,
"overlay": None,
"label": "suspicious" if template.startswith("S") else "benign",
"typology": spec.get("typology"),
"planted": planted,
"doc_party_cp": ctx.doc_party_cp,
}
return world
# -------------------------------------------------------------- overlay --
def compatible_overlays(world: dict[str, Any]) -> list[str]:
"""Which hidden signals this benign world can carry. Depends on the screen, never on the label."""
kinds = ["funnel"]
if world["customer"]["prior_alerts"] >= 3 and world["customer"]["prior_sars"] == 0:
kinds.append("serial_closer")
if world["template"] in DOC_TEMPLATES:
kinds.append("doc_mismatch")
return kinds
def _overlay_urn(kinds: list[str]) -> list[str]:
"""funnel is always available, so the other two are drawn twice as often where they can be."""
urn: list[str] = []
for kind in kinds:
urn += [kind] * (1 if kind == "funnel" else 2)
return urn
def _outbound_target(world: dict[str, Any]) -> str:
"""The highest-volume outbound counterparty on the screen. Every benign template has one."""
for c in world["screen_counterparties"]:
if c["direction"] == "out":
return c["id"]
raise AssertionError(f"template {world['template']} has no outbound counterparty on screen")
def apply_overlay(world: dict[str, Any], kind: str, rng: XorShift32) -> None:
"""Overlay a suspicious signal onto lookup data only. Mutates `world`.
The screen — alert, triggering set, KYC block, summary, screen
counterparties — is not touched, and `tests/test_screen_leak.py` proves it.
"""
family = world["alert"]["family"]
triggering = world["alert"]["triggering"]
world["overlay"] = kind
world["label"] = "suspicious"
if kind == "funnel":
target = _outbound_target(world)
cp = next(c for c in world["counterparties"] if c["id"] == target)
cp["other_alerted_accounts"] = rng.between(5, 14)
world["typology"] = "FUNNEL_ACCOUNT"
world["planted"] = [[target] + cp["transactions"], list(cp["transactions"])]
elif kind == "serial_closer":
priors = world["prior_alerts"]
analysts = []
while len(analysts) < len(priors):
name = f"{rng.pick(FIRST_NAMES)} {rng.pick(SURNAMES)}"
if name not in analysts:
analysts.append(name)
fire_day = days_from_civil(*map(int, world["alert"]["fired"].split("-")))
days = sorted(rng.sample(list(range(fire_day - 330, fire_day - 30)), len(priors)))
for j, prior in enumerate(priors):
prior["rule"] = world["alert"]["rule"]
prior["fired"] = iso_date(days[j])
prior["disposition"] = "closed"
prior["analyst"] = analysts[j]
prior["rationale"] = BOILERPLATE
prior["elapsed_days"] = rng.between(0, 1)
world["typology"] = NOMINAL_TYPOLOGY[family]
ids = [p["id"] for p in priors]
world["planted"] = [ids + triggering, ids]
elif kind == "doc_mismatch":
target = world["doc_party_cp"]
cp = next(c for c in world["counterparties"] if c["id"] == target)
cp["name"] = f"{rng.pick(WORDS['adjectives'])} {rng.pick(['Holdings', 'Ventures', 'Trading'])} {rng.pick(WORDS['suffixes'])}"
doc = next(d["id"] for d in world["documents"] if d["party"])
inbound = [t["id"] for t in world["transactions"] if t["id"] in triggering and t["cp"] == target]
world["typology"] = NOMINAL_TYPOLOGY[family]
world["planted"] = [[doc, target] + inbound, [doc, target]]
else:
raise ValueError(kind)
# -------------------------------------------------------------- public --
def is_held_out(seed: int) -> bool:
return fnv1a32(str(seed)) % 8 == 7
def generate(seed: int, overlay: str | None = "auto") -> dict[str, Any]:
"""The world before reference hours. `overlay` is for tests:
"auto" follows the seed; None forces the benign twin; a kind forces it on."""
h = fnv1a32(str(seed))
slice_name = "held_out" if h % 8 == 7 else "main"
rng = XorShift32(h)
ctx = _Ctx(seed, rng, slice_name)
roll = rng.below(100)
tier = "benign" if roll < BENIGN_PCT else "visible" if roll < BENIGN_PCT + VISIBLE_PCT else "hidden"
if tier == "visible":
template = VISIBLE_TEMPLATES[rng.below(len(VISIBLE_TEMPLATES))]
else:
template = BENIGN_TEMPLATES[rng.below(len(BENIGN_TEMPLATES))]
spec = BUILDERS[template](ctx)
world = _assemble(ctx, template, spec)
world["tier"] = tier
if tier != "visible":
kinds = compatible_overlays(world)
if overlay == "auto":
if tier == "hidden":
apply_overlay(world, rng.pick(_overlay_urn(kinds)), rng)
elif overlay is not None:
apply_overlay(world, overlay, rng)
del world["doc_party_cp"]
return world
def world_for_seed(seed: int) -> dict[str, Any]:
"""The world with `reference_minutes` attached — part of the digest.
The reference is the cheapest shipped policy that reached the correct
disposition on this seed, found by running them through the engine. The
policies read only the engine's view, never this dict, so attaching their
result here is not circular; it is the same move as wordle's
`reference_depth`, made part of the world so the browser recomputes it
instead of trusting the recorded number.
"""
from .policies import reference_for # local import: policies -> engine -> generator
world = generate(seed)
minutes, policy = reference_for(world)
world["reference_minutes"] = minutes
world["reference_policy"] = policy
return world
SCREEN_KEYS = ("seed", "held_out", "currency", "threshold", "screen_order", "customer", "months",
"summary", "screen_counterparties", "alert")
def screen_of(world: dict[str, Any]) -> dict[str, Any]:
"""Exactly what turn 0 shows, including the triggering transactions in full."""
triggering = set(world["alert"]["triggering"])
screen = {k: world[k] for k in SCREEN_KEYS}
screen["triggering_transactions"] = [t for t in world["transactions"] if t["id"] in triggering]
return screen
def world_digest(seeds=range(4096)) -> str:
"""Digest (a): SHA-256 over the canonical world for each seed, newline-joined."""
digest = hashlib.sha256()
for seed in seeds:
digest.update(canonical_json(world_for_seed(seed)).encode())
digest.update(b"\n")
return digest.hexdigest()