GEORGE ANDRADE-MUÑOZSAN FRANCISCO --:--:-- · fog · 56°

/projects/m-clone — n°009FINTECH · ON-DEVICE AIACTIVE BUILD

asleep65 device runs · 3 on-device eval sittings · 412 edits building · 58 checkson the ops board →report filed 01 aug · 06:45

M-Clone

A full retail-banking app rebuilt as a lab — accounts, cards, budgets, goals, Plaid-linked data — with a money assistant that answers entirely on-device. No backend, no server database, and by hard rule no remote LLM. The assistant's judgment is a shipped eval product: a scored corpus, a sealed holdout, and gates that run on every commit.

Role

Solo — iOS app, on-device assistant, eval product

Stack

Swift · SwiftUI · SwiftData · Apple Foundation Models (on-device) · Plaid (sandbox) · Next.js relay · Python eval tooling

Timeline

Feb — Jul 2026 · ongoing

Status

ACTIVE BUILD

86.5%

routing accuracy, on-device

0/35

out-of-scope leaks, live corpus

81

sealed holdout cases

604

Swift test functions, model-free

00

Brief

#

Every bank now ships an assistant, and every one of them phones home. This project rebuilds the whole surface — accounts, credit cards, budgets, goals, Plaid-linked real data — and then answers the interesting question: how good can a money assistant be if it never leaves the phone? Chat history, financial ledgers, budgets, and Plaid tokens live on-device in SwiftData and the Keychain. There is no backend and no server-side database; the only hosted piece is a thin Plaid OAuth/webhook relay.

The assistant is a hybrid: a deterministic router with typed financial tools and calculators does the load-bearing work, and Apple's on-device foundation model assists where language helps — with the model's authority capped by construction. On the live corpus, the current arm routes 86.5% of prompts to the right capability family and has leaked zero out-of-scope requests in 35 attempts.

The deeper deliverable is the discipline around that number. The router's judgment is governed by a real eval product — versioned corpus packs, preregistered experiments, a failure taxonomy, a cryptographically sealed holdout — and by commit gates that keep every test model-free. The claim isn't "the assistant feels smart." It's "here is the corpus, here is the score, here is the evidence."

In July the suite itself went under review, scored against an outside eight-part standard by a reader with no session history — and the verdict shipped before any of it was repaired: two parts met, five partial, one missing.

01

The system

#

Two host shells share one Swift package; everything that matters lives in the package. The assistant's spine is deliberately boring: the model proposes an intent, deterministic routing decides what runs, and typed tools compute answers from the real ledgers — the model never free-generates a balance.

02

The eval product

#

The assistant's quality is not a vibe; it's a shipped artifact with its own versioning, its own guards, and its own report. Corpus packs cover development, confusables, workflows, journeys, and over-refusal probes. Experiments are preregistered before they run. Failures are classified in a written taxonomy. And an 81-case holdout sits sealed behind a hashing workflow so no tuning session can ever quietly train on the exam.

Every commit pays a fast toll; the full routing gate runs the expensive machinery:

what a change to the assistant has to survivesession
git commit → .claude/gate.sh (<120s, every commit)pytest scripts/tests · corpus validation · no-remote-LLM guard · model-free guard · anti-cheatthe full assistant-routing gateXCTest on an iPhone simulator · capability-pack native scoring · det-harness smokenightly 06:17 — both run again across the fleet; the digest lands before I wake

The same discipline covers the insights engine — the feed that turns ledgers into observations. Thirty-five engineered fixtures pin its rules; live narration runs are scored per-row, on device, and adjudicated by actually reading every output rather than trusting the metric. That habit exists because a metric lied once, and the story of how is below.

Grading the grader#

A suite this invested in measurement eventually has to measure itself. A reader with no session history scored it against an outside eight-part standard for eval work and filed the verdicts with the failing lines quoted — then the repair was deliberately left for separate, reviewed work. Nothing in the corpus, the gates, the thresholds or the grading code moved. This entry says audited, not fixed, and the eight verdicts are in the deep dive above.

The load-bearing finding is the one worth carrying here: the nightly check asserts that 61 cases were scored, not that they were answered correctly, so quality could fall through the floor and the run would still print a pass. And the matcher behind the headline routing numbers is documented in the repo as too generous — it accepts fields the answer key never asked for. Every score on this page, sealed ones included, is an upper bound until that ruler tightens.

BENCHJIMthe same eval-first spine, pointed at cited research memos
03

What broke

#
EVAL-01

The metric that read 1.000 while the answer lost its number

What happened

An any-of citation metric scored narration as perfect while the model dropped the single dollar figure the feature existed to surface. Token-level score: flawless. Output: wrong.

The fix

The metric went red-first before any feature fix: score predicates are now byte-identical to what production enforces, the harness's prompts are parity-fenced against the app's, and shipping-gate suites earn a periodic every-row read.

The lesson

Measurement drift is a first-class failure class. The cases you grade are only as honest as the ruler.

OS-01

Available, but not actually available

What happened

The on-device model reported .available while every generation failed. The typed error chain ran through the OS safety sanitizer: its guardrail asset had never downloaded, and availability doesn't reflect guardrail readiness (filed upstream as FB19844387).

The fix

The harness captures typed error chains instead of stringified errors, prewarms once, and documents the provisioning fix; the eval report superseded its own earlier 'no evidence' state with root cause attached.

The lesson

On-device AI has OS-level failure modes cloud APIs never taught you. Type the errors or you'll never see them.

DATA-01

Cards with no limit inflated everyone's utilization

What happened

Credit cards reporting a nil limit were silently inflating utilization in two separate services — financial health and credit score — the kind of confidently-wrong arithmetic a money app can't afford.

The fix

Both call sites fixed against a 30-source fixture corpus mirroring the real aggregator surface, with a direct test pinning the behavior. The bug became a case before it became a fix.

The lesson

Real-data edge cases are the product. The fixture corpus is where they go to become permanent.

04

Decisions & tradeoffs

#
Decision01

No remote LLM, as a guard — not a preference

Chose
A source-level check that fails any commit introducing a cloud-model call. The privacy claim is enforced, not aspirational.
Over
A cloud model behind a privacy policy, or 'local-first' with quiet exceptions.
Cost
The assistant is capped by what an on-device model can do; hard problems need routing craft instead of a bigger model.
Payoff
The core promise — your money data never leaves the phone — survives every future contributor, including me on a lazy day.
Decision02

Deterministic floor, model assist

Chose
A deterministic router and typed tools as the load-bearing path, with the on-device model assisting inside a bounded lane.
Over
LLM-first routing with the deterministic path as fallback.
Cost
Two systems to keep coherent, and an eval corpus to keep them honest.
Payoff
86.5% family accuracy with zero out-of-scope leaks — and every number in an answer computed, never generated.
Decision03

The holdout is sealed, cryptographically

Chose
A sealed 81-case holdout behind a hashing workflow; tuning happens on development packs, and the seal is checked, not promised.
Over
One big eval set everyone iterates against.
Cost
Slower feedback: the honest number arrives only at sealed reads.
Payoff
When tuning showed 55 and the holdout said ~23, the gap was visible instead of shipped. Overfitting became a measurement, not a surprise.
Decision04

Tests never touch the model

Chose
Every test runs against injected doubles with an availability override; a guard fails commits that reach for the live model.
Over
Integration tests against real on-device inference.
Cost
Live-model behavior needs separate, deliberate device sittings to observe.
Payoff
604 Swift test functions, plus 168 in the Python eval tooling, run every commit and every night at zero model cost and with no flake from the OS's model-asset state.
Decision05

Publish the audit before fixing any of it

Chose
Commit the eight-part verdict — two met, five partial, one missing — with the failing lines quoted, and leave the numbers standing until the repair is its own reviewed work.
Over
Quietly fixing the gate first and reporting the tidy version, or grading the suite in the same session that patches it.
Cost
The headline routing scores ship labeled as upper bounds, and the open finding stands with no repair attached to it.
Payoff
The verdict can't be edited by the diff that closes it. A suite that has publicly named its own blind spot is worth more than one that has never been asked.
05

What's next

#
  1. NX-01Teach the nightly to fail on wrong answers, not just missing ones — and to report a broken machine differently from a broken answer.
  2. NX-02Tighten the matcher, put the sealed holdout on a read budget, and let no agent grade a change it wrote. Scores are expected to move down; that's the ruler getting honest, not a regression.
  3. NX-03Cover the safety class that isn't covered: personal data arriving in the question, with benign near-misses alongside it so the fix can't just be a more nervous assistant.
  4. NX-04Template-first rendering on hardware tiers where the on-device context window stays small.
  5. NX-05Deeper insight narration under the same per-row adjudication bar that caught the 1.000 lie.
  6. NX-06Grow the capability corpus from real sittings — every confused routing becomes a case before its fix.
  7. NX-07Keep the relay boring: OAuth return, AASA, webhook-to-push — nothing else grows server-side.
POSTEvals Are the Productthe measurement-drift lesson, argued in fullPOSTYou Appear Exactly Twicethe working loop that built this — five human turns steering 561 agent events

The reflection — read the thinking behind this build

You Appear Exactly Twice