Skip to content

RichardG-PU/mpc-hacks

Repository files navigation

Baxpace

A review queue that surfaces the ~7% of credit-card transactions worth a human's attention, explains why each one is suspicious, and lets a non-technical reviewer clear the queue in minutes with the keyboard.

Live demo: baxpace.netlify.app

Built for the MCP Hacks "Fraud Hunter" challenge (sponsored by Valsoft). One month of activity, 50 cards, 1,000 transactions, four hidden fraud patterns.

Quickstart

One command from a clean clone:

npm install && npm run dev

Then open http://localhost:3000. The app starts on an upload screen: drop a CSV (or click use the sample dataset to load the bundled transactions.csv) to begin. Once the queue loads, press A to confirm fraud, D to dismiss, E to escalate, U to undo, / to search. No instructions needed.

Run the tests:

npm test

What you get

  • A reviewer queue, not a table. One flagged transaction at a time, in the dataset's own (CSV) order, with the amount, merchant, card, route, device, a risk score, and a ranked list of plain-English reasons.
  • Keyboard-driven triage. Approve / dismiss / escalate / undo without touching the mouse. The next transaction loads instantly.
  • Explained flags. Every flag carries at least one human-readable reason, for example "amount $1,511 is 24.3x this card's median" or "6 different cards charged QuickPay Online within 120 min".
  • Per-transaction review with side context columns. A four-column workspace: the transaction history (far left), a read-only detail column beside it, the transaction under review (center, the focus), and settings (far right). You review one transaction at a time and decide each on its own. With grouping on, the queue orders related flags consecutively (a card's charges, then a merchant burst) and the history shows two newest-first lists: every transaction on the current card (top) and at the current merchant (bottom), flagged and not, with a "flagged of total" count. Click a row, or press up/down (left/right switches panes), to preview its detail in the detail column. The transaction under review never changes and is the only one you can act on, so the context is always there without losing your place. The layout is responsive: on a wide screen the detail gets its own column; on a laptop it folds into a dismissible card above the review so nothing feels cramped. Grouping is off by default for a focused two-column review; toggle it on to add the history and context columns.
  • Light and dark mode. Toggle in the header; the choice persists and there is no flash on reload.
  • Cost-aware sensitivity. A single slider trades the cost of a false positive against a missed fraud; its midpoint is the default and it re-scores the dataset live (about 74 to 116 flags across the range).
  • Advanced options. For power users, a collapsible panel exposes the actual detection thresholds (the amount ratio, minimum amounts, burst sizes, time windows) to fine-tune beyond the slider, with a one-click reset to defaults.
  • AI recommendation (optional). A "Suggest a recommendation" line under the actions calls a Google AI Studio model server-side and returns one normalized call, "confirm / dismiss / escalate because of ", from the transaction and its context. The default model is gemma-4-26b-a4b-it. The API key is read from API_KEY on the server only (copy .env.example to .env); it is never sent to the client, and the feature fails safe with a fallback message if the key is missing or the response is malformed.
  • Learns from your reviews. A session feedback loop tracks how often you confirm vs dismiss each of the four detectors. Once a detector's flags are repeatedly and one-sidedly dismissed (strong, skewed evidence past a deadband), its thresholds nudge up so similar future flags stop appearing, and the current card shows a "likely false positive" hint. The change is visible in the detection-settings sliders too: an amber marker and "→ value" slide to the nudged threshold while your base setting stays put. A "Learning from your reviews" panel shows the per-detector tallies; it is on by default, toggleable, and fully reversible with undo. It never reorders the queue.
  • Guided help, once. A "?" sits next to every important control with a plain-English tooltip. The first time you open the queue, the dots that explain a button pulse one at a time, walking you through the key controls; each pulses at most once, and anything you have already opened is remembered in localStorage so it never nags again.
  • An audit trail and export. Every decision is logged; one click exports the marked-up CSV.
  • Bring your own data. Upload any CSV with the same columns and the whole tool re-scores it live, no restart.

Detection strategy

Four behavioral detectors, all built on per-card baselines (a card's own median spend) so flags fire on deviation, not on absolute values, plus one cross-card signal. Full derivation, evidence, and rejected hypotheses are in analysis/HYPOTHESIS_LOG.md.

Pattern What it catches Rule
Card testing Stolen card validated with rapid small online charges >= 4 online charges by one card within 60 min
Amount anomaly Stolen card drained far above the card's norm amount >= 8x the card's median and >= $200
Risky category Gift-card and electronics cashouts (a higher-confidence flavor of the above) category in {gift_card, electronics} and amount >= $150
Merchant burst A compromised merchant hit across many cards at once >= 4 distinct cards on one merchant within 2 h

Each detector emits a weight graded by severity (how far over its threshold the flag is: the amount ratio, the burst size, the cashout amount, the number of cards in a merchant burst), and the weights combine with noisy-OR (1 - prod(1 - w)), so multiple independent signals raise confidence. A $1,753 charge at 55x the card's median scores 0.99; an 8x charge sits lower. The score is shown on every card and drives the AI context and the feedback loop; the queue itself is presented in the dataset's own (CSV) order so a reviewer can work through it the way the data arrived. The union flags 76 of 1,000 transactions (7.6%), matching the brief's ~7%. The cross-card detector earns its keep: two QuickPay Online bursts include charges below the per-card amount threshold that no single-card rule would catch.

What we deliberately did not build, after investigating and rejecting it: device/IP-reuse graphs (no signal in the data), country-mismatch flags (dominated by legitimate cross-border merchants like AliExpress and Spotify), and any ML model (transparent rules explain better and stay debuggable in 24 hours). See the PRD for the full non-goals.

Architecture: one backend, many front ends

The detection logic lives in core/, a framework-agnostic TypeScript library with zero next, react, or fs imports. It takes parsed rows plus a config and returns scored, explained transactions. Everything else is a consumer of the same detect() entry point:

core/  (pure detection backend)
  detect(txns, config) -> ScoredTransaction[]
        |
   +----+------------------------+----------------------+
   |                             |                      |
 web app (app/)            cli/ (terminal)         agents (call the CLI
  keyboard queue            detect / queue          or its JSON output)
  cost slider, undo         show / export

Swapping or adding a medium (an HTTP service, an MCP server, a notebook) means writing a new consumer, never touching the engine.

CLI and agent surface

The same engine runs headless. Useful for batch scoring, scripting, or letting an agent review on its own:

npm run cli -- detect transactions.csv              # human summary + top flags
npm run cli -- queue  transactions.csv --json       # ranked flags as JSON (for agents)
npm run cli -- show   transactions.csv tx_000987    # one transaction with full reasons
npm run cli -- export transactions.csv -o out.csv   # write the marked CSV

Flags: --aggressiveness 0..1 (precision vs recall), --threshold 0..1, --json.

Deliverables

Item Where
Working tool this app (npm run dev)
Marked-up CSV transactions_flagged.csv (1,000 rows, 76 flagged, with is_fraud, fraud_score, flag_reasons)
PRD docs/PRD.md
Implementation plan docs/IMPLEMENTATION_PLAN.md
Hypothesis log (bonus) analysis/HYPOTHESIS_LOG.md
Tests core/detect/detect.test.ts, core/feedback.test.ts

Project layout

core/              detection backend (pure TS)
  config.ts        thresholds + weights, single source of truth
  detect/          baselines, the four detectors, scoring, tests
  feedback.ts      pure feedback math: trust, threshold nudge, hint (+ tests)
  io/csv.ts        CSV parse + marked-CSV serialize
cli/index.ts       terminal + agent consumer
app/               Next.js reviewer UI
  api/             transactions (parsed sample) + recommend (server-only AI route)
  components/      queue card, history pane, advanced options, feedback panel, audit log, help dots, theme toggle, export
  store.ts         Zustand: config, aggressiveness, grouping, decisions, undo, previewId, feedback, audit, search
analysis/          the data-exploration that found the patterns
docs/              PRD + implementation plan
transactions.csv   the dataset

Tests

Twenty-one tests across core/detect/detect.test.ts and core/feedback.test.ts cover the engine and the feedback math. Known-fraud cases fire the right detector (gift-card cashout, card-testing burst, cross-card merchant burst, electronics cashout); known-legit cases stay clean (a normal grocery run, a single foreign online purchase); severity grading ranks an extreme anomaly above a mild one, and flags fire on per-card deviation, not absolute amount. An integration test confirms the engine reproduces the analysis exactly (76 flags) on the real dataset. The feedback tests assert trust is monotonic and bounded, the threshold nudge is a no-op until evidence is strong and skewed, and the "likely false positive" hint fires only after repeated dismissals.

If we had another week

  • Persist and statistically fit thresholds. The session feedback loop already nudges a detector's thresholds when you keep one-sidedly dismissing it. With more time we would persist decisions across sessions and fit per-pattern thresholds statistically, rather than by a fixed nudge, so the tool tightens precision where reviewers keep dismissing and loosens recall where they keep confirming.
  • Per-card and per-merchant allow-lists. A reviewer who confirms that a card legitimately buys electronics monthly should be able to teach the system, killing that recurring false positive (today the feedback loop eases a whole detector, not one card).
  • Richer cross-card signals. Merchant-velocity baselines (this merchant normally sees N cards/hour) would catch slower compromises that a fixed 2-hour window misses.
  • Reviewer throughput metrics. Track time-per-decision and agreement rate to find where the UI slows people down.
  • Persistence and multi-user. Move decisions to a real store with an append-only audit log, so a team can split the queue and hand off mid-shift.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors