Resume a scale run from a checkpoint#
Goal. Work through a bounded, reproducible example and inspect the evidence before connecting an external service.
Prerequisites. Base FraudTwin install. Optional extras and Docker commands are clearly marked.
Produces. Tables, fingerprints, manifests, and verification output.
Source size. The default cells generate approximately 1,000 logical payments; increase duration and population together for a 10,000-payment run.
Offline path. All marked offline cells run without Docker or network services. Service cells are optional and explicitly marked in notebook metadata.
Cleanup. Outputs are written under a temporary directory; remove any local run directory if you changed the output location.
Set up a deterministic source run
from pathlib import Path
import polars as pl
from fraudtwin.config import load_config
from fraudtwin.generation import generate
root = next(
(p for p in (Path.cwd(), *Path.cwd().parents) if (p / "configs" / "minimal.yaml").exists()),
Path.cwd(),
)
base = load_config(root / "configs" / "minimal.yaml")
# Scale the population so the bounded example produces about 1,000 payments.
population = base.population.model_copy(
update={
"customers": 200,
"accounts": 300,
"cards": 240,
"devices": 240,
"pix_keys": 160,
"merchants": 60,
}
)
simulation = base.simulation.model_copy(update={"duration_days": 10})
fraud = base.fraud.model_copy(update={"enabled": True, "target_rate": 0.05})
config = base.model_copy(
update={"population": population, "simulation": simulation, "fraud": fraud}
)
data = generate(config, write=False)
run_id = data.run_id
payments = pl.DataFrame([item.model_dump(mode="json") for item in data.behavior.payments])
print("Generated source run")
print(f" run id: {run_id}")
print(f" payments: {len(payments):,}")
print(f" events: {len(data.behavior.payment_events):,}")
Generated source run
run id: RUN-2a3ad02ee370aeb8
payments: 1,092
events: 4,699
Inspect the resolved scale plan
from fraudtwin.config import load_config
from fraudtwin.scale import require_scale_plan, shard_descriptors
scale_config = load_config(root / "configs" / "scale-dev.yaml")
plan = require_scale_plan(scale_config)
print("Scale plan")
print(f" profile: {plan.profile}")
print(f" target payments: {plan.target_payments:,}")
print(f" shards: {plan.shard_count} | workers: {plan.worker_count}")
print(f" chunk size: {plan.chunk_size:,} payments")
print(f" checkpoint: every {plan.checkpoint_frequency_chunks} chunk(s)")
print(f" partitioning: {plan.partition_mapping}")
print(f" seed: {plan.seed}")
print(f" configuration: {plan.configuration_hash[:12]}...")
Scale plan
profile: dev
target payments: 1,000
shards: 2 | workers: 2
chunk size: 100 payments
checkpoint: every 1 chunk(s)
partitioning: stable_hash_v1
seed: 42
configuration: 74d79c06b059...
Review the deterministic shard layout
shards = shard_descriptors(plan)
display(pl.DataFrame([shard.model_dump(mode="json") for shard in shards]))
| shard_index | shard_id | mapping |
|---|---|---|
| i64 | str | str |
| 0 | "SHARD-000000" | "stable_hash_v1" |
| 1 | "SHARD-000001" | "stable_hash_v1" |
Measure and interpret the result
from fraudtwin.scale import fingerprint_rows
rows = [{"logical_id": f"payment-{i:04d}", "amount": float(i + 1)} for i in range(20)]
full = fingerprint_rows(rows)
partial = fingerprint_rows(rows[:10])
print("Checkpoint fingerprints")
print(f" partial checkpoint (10 rows): {partial[:12]}...")
print(f" complete run (20 rows): {full[:12]}...")
Checkpoint fingerprints
partial checkpoint (10 rows): a88a36d486d7...
complete run (20 rows): 566268e96f11...
Exercise a parameter or failure mode
resumed = rows[:10] + rows[10:]
assert fingerprint_rows(resumed) == full
print("Resume check")
print(" status: deterministic")
print(f" reconciled rows: {len({r["logical_id"] for r in resumed}):,}")
Resume check
status: deterministic
reconciled rows: 20
Write a compact artifact and fingerprint
Checkpoint files should live in a temporary run directory and be retained only for recovery.
Verify invariants and clean up
# A compact inspection is more useful than printing an entire run.
sample_columns = [
c for c in ("payment_id", "amount", "initiated_at", "payer_account_id") if c in payments.columns
]
sample_rows = payments.select(sample_columns).head(8).to_dicts()
print(f"Sample payments ({len(sample_rows)} of {payments.height} rows):")
for row in sample_rows:
print(
f" - {row.get('payment_id')}: amount={row.get('amount')}, "
f"initiated_at={row.get('initiated_at')}, payer={row.get('payer_account_id')}"
)
nulls = {name: count for name, count in payments.null_count().to_dicts()[0].items() if count}
print("\nData quality summary:")
print(f" rows: {payments.height}")
print(f" columns: {payments.width}")
if not nulls:
print(" nulls: none")
else:
print(" columns with nulls:")
for name, count in sorted(nulls.items()):
print(f" - {name}: {count}")
Sample payments (8 of 1092 rows):
- PAY-00000001: amount=70.7, initiated_at=2026-01-03T16:25:00Z, payer=ACC-000123
- PAY-00000002: amount=25.52, initiated_at=2026-01-05T11:37:00Z, payer=ACC-000174
- PAY-00000003: amount=18.37, initiated_at=2026-01-05T11:21:00Z, payer=ACC-000174
- PAY-00000004: amount=5.54, initiated_at=2026-01-02T22:30:00Z, payer=ACC-000003
- PAY-00000005: amount=13.71, initiated_at=2026-01-02T09:41:00Z, payer=ACC-000029
- PAY-00000006: amount=70.06, initiated_at=2026-01-04T10:40:00Z, payer=ACC-000179
- PAY-00000007: amount=42.73, initiated_at=2026-01-02T18:04:00Z, payer=ACC-000247
- PAY-00000008: amount=36.42, initiated_at=2026-01-05T09:27:00Z, payer=ACC-000255
Data quality summary:
rows: 1092
columns: 15
columns with nulls:
- card_id: 565
- merchant_id: 565
- payee_account_id: 86
- payee_institution_id: 86
- payee_pix_key_id: 857
- payer_institution_id: 86
- payer_pix_key_id: 857
Optional service integration
summary = {
"run_id": run_id,
"payments": len(data.behavior.payments),
"payment_events": len(data.behavior.payment_events),
"fraud_records": len(data.behavior.fraud_records),
}
assert summary["payments"] == len(payments)
assert summary["payments"] > 0
print("Generated dataset")
print(f" run id: {summary['run_id']}")
print(f" payments: {summary['payments']:,}")
print(f" payment events: {summary['payment_events']:,}")
print(f" fraud records: {summary['fraud_records']:,}")
Generated dataset
run id: RUN-2a3ad02ee370aeb8
payments: 1,092
payment events: 4,699
fraud records: 51
Record the generated shape and tutorial contract.#
summary = {
"payments": len(data.behavior.payments),
"events": len(data.behavior.payment_events),
}
assert summary["payments"] >= 0
print("Tutorial contract")
print(f" payments: {summary['payments']:,}")
print(f" events: {summary['events']:,}")
Tutorial contract
payments: 1,092
events: 4,699
Record the generated shape and tutorial contract.#
summary = {
"payments": len(data.behavior.payments),
"events": len(data.behavior.payment_events),
}
assert summary["payments"] >= 0
print("Tutorial contract")
print(f" payments: {summary['payments']:,}")
print(f" events: {summary['events']:,}")
Tutorial contract
payments: 1,092
events: 4,699
assert summary["events"] >= summary["payments"]
print("Checkpoint output contract passed")