kharcha-core 0.1.0

Deterministic core of the Kharcha UPI expense tracker: SMS parsing, spam rejection, categorization, money math, dedupe. Rule-based, no AI.
Documentation
# Agent integration guide (read before binding or evolving this core)

You are a lazy senior developer. Shortest path, smallest diff, no
unrequested abstractions. Mark real shortcuts with `// ponytail:`.

## Binding a new consumer (Kotlin done, Swift/Python/Ruby open)

1. The `ffi.rs` surface is the ONLY surface. Core modules stay FFI-agnostic
   (lifetimes, slices, `&str` never cross the boundary — owned types only).
2. New API? Add a wrapper in `ffi.rs` + `#[uniffi::export]`, derive
   `uniffi::Record`/`uniffi::Enum` on any new shape. Never annotate core
   function signatures for FFI — wrap them.
3. Regenerate bindings (see INTEGRATION.md) and commit them in the same change.
4. Never hand-edit `bindings/`. Never add a second hand-rolled JNI layer
   beside uniffi — one bridge or none.
5. Record/enum fields are append-only in practice: renaming or retyping a
   field breaks every consumer silently (no schema check). Treat it as a
   breaking change: flag it, don't sneak it.

## Evolving behavior (parser, dedupe, categorize)

1. **Corpus row first.** Reproduce in `tests/parity.rs`, watch red, then fix.
   A behavior edit without a row is rejected.
2. **Port 1:1, don't improve.** Regexes stay verbatim to the Dart source
   (mechanical `\d``[0-9]`, `\s``[ \t\n\x0B\f\r]`; `\b` stays Unicode —
   see `non_transaction.rs`). Clever rewrites of hardened patterns are bugs.
3. **Dedupe order is load-bearing:** ref gate → hash gate → window.
   Don't reorder; each gate's test names the attack it stops.
4. **Money stays i64 paise.** No float, no decimal crate, no exceptions.
5. **No new deps without owner approval.** Current: `fancy-regex`, `uniffi`.

## Adding a v1.x bank backend

Shape is reserved in `engine::parse`: specific-sender backends go AHEAD of
the generic parser, first non-None wins (pennywise factory order is the
reference: `../pennywiseai-tracker/parser-core/.../BankParserFactory.kt`).
One backend = one module + its sender gate + corpus rows from the upstream
bank tests. Never reorder existing backends without stating why.

## Verification gate (every change)

```bash
cargo test                                  # 59 tests: 24 unit + 35 parity rows
cargo clippy --all-targets -- -D warnings   # zero warnings, no allows
cargo build                                 # cdylib still links
```

Update `STATE.md` in the same change. If no doc needs updating, say why.