# Fintech sample pack — payments/settlement reconciliation

Built against `experiments/cpa_reconcile_demo/apostol-reconcile-demo.zip`
(sha256 `8b42561427242567266e31f887a9180506406a678a70ecd268c32d0725f73d3a`).
Every number in `RESULTS.md` was produced by running the bundle's own binaries
and scripts against the files in this pack — nothing here computes a
reconciliation, an anomaly flag, or a teach decision itself.

## Domain

A payment processor settlement export reconciled against a merchant's
internal ledger — the fintech-native shape of the reconciler's bank/ledger
pair. `Description` carries `merchant_id`-style tokens (`Merchant <Name>
invoice <ref>`), which is also what the AI Node's supplier-extraction in
`csv_to_ledger.py` keys off of.

## Input shapes (exactly what the bundle reads)

Both files are the same 3-column CSV shape the bundle's `GUIDE.md` documents:
a date column (`YYYY-MM-DD`), an amount column (`4520.00`, `-3415.10`, or
`(3415.10)` for a negative), and a free-text description/memo column.

- `samples/settlement_export.csv` — the **statement** role (processor
  settlement export). Header: `Date,Amount,Description`.
- `samples/internal_ledger.csv` — the **ledger** role (internal books).
  Header: `Date,Amount,Memo`.

## Honest scope

- The reconciler's matcher (`apostol-zig/0.22/substrate/reconcile_v2.zig`)
  is a **bank-side greedy matcher**: for each statement row it picks the
  single best-scoring ledger row (amount within 50 cents, date within 3
  days, description trigram-Jaccard ≥ 30%) — it does **not** consume/mark
  ledger rows as used. Two statement rows can both independently match the
  *same* ledger row. This is exactly what happens to the two planted
  duplicate settlements below (see `RESULTS.md`) — it is not a bug in this
  pack, it is a real structural property of the two-file reconciler that is
  the reason the separate duplicate-detection pass (`run_duplicates.sh`)
  exists as an independent check, not a redundant one.
- `run_duplicates.sh`'s TASK 2 ("Reconciliation" self-check) is the
  module's own internal research fixture check, calibrated to a 200-row
  UK-government dataset with hardcoded drop/perturb indices up to 180. On a
  26-row file those indices are out of range, so TASK 2 reports 0 bank rows
  and its bars (M3) FAIL / the script exits 1. This is a known limitation of
  reusing that module's self-test on a small pack-sized file, not a defect
  in the pack's duplicate-detection result (TASK 1, the part this pack
  actually exercises, is unaffected and exact).
- The Benford first-digit test abstains on this pack (`n=26 <
  param:BENFORD_MIN_N=100`) — correctly, by the module's own stated bar.
  Only the just-under-threshold test is exercised here (it carries no
  minimum-n gate).
- Amounts and merchants are synthetic. Nothing here is a real settlement or
  a real merchant.

## Files

| File | Role |
|---|---|
| `samples/settlement_export.csv` | statement — 26 rows |
| `samples/internal_ledger.csv` | ledger — 26 rows |
| `plants.json` | every planted defect, logged with its exact row, its type, and the detector expected to find it |
| `teach/chargeback_window_adopt.json` | teach family designed to ADOPT a `days_to_chargeback` review-window rule |
| `teach/chargeback_window_refuse.json` | teach family designed to REFUSE (underdetermined) with a discriminating probe |
| `RESULTS.md` | verbatim outcomes of every run, plants-vs-found reconciliation, iteration count |

## Run it yourself

From a copy of the extracted bundle with this pack's two CSVs dropped into
`samples/`:

```bash
bash run_reconcile.sh samples/settlement_export.csv samples/internal_ledger.csv --html samples/fintech_workpaper.html
bash ai_node/run_duplicates.sh samples/settlement_export.csv
bash ai_node/run_anomaly.sh samples/internal_ledger.csv
bash ai_node/teach_skill.sh <path-to>/teach/chargeback_window_adopt.json
bash ai_node/teach_skill.sh <path-to>/teach/chargeback_window_refuse.json
```
