The real Coach, asked realistic questions on real bank data, graded blind — every failure traced to where it started, fixed there, and proven before it shipped. One cycle, following Hamel Husain and Shreya Shankar’s method.
Asked the live Coach realistic questions on the owner’s real bank data. 100 scenarios built from a grid — 10 jobs a person hires the Coach for × 7 steps of a good answer, all 10 red lines covered. Every answer is traced end to end: each model call, each tool call and exactly what it returned.
Test the thing you ship, on the data it really sees. Sample the space systematically so coverage is measured, not assumed.
The previous scripted suite faked every tool result, so it could never see a tool returning the wrong thing — which is where most failures turned out to be.
services/tracing/coach_tracing.ts services/tracing/trace_masking.ts PRs #314, #316evals/scenarios/round-1.json PR #317services/ai/coach_turn.ts PR #318commands/coach_scenarios.ts evals/runs/<run_id>.json PR #319| Frozen | Example |
|---|---|
| The question | “Can I afford a $90 dinner tonight?” |
| The moment | The original run’s date and time |
| Every tool call and its exact output | Safe to Spend → balance $1,250.00, today $84.00, card payments due later in the month |
| Not frozen (what is under test) | The system prompt, the tool descriptions, the Coach code, the model’s answer |
services/evals/scenario_runner.ts PR #330The owner graded the first answers (Pass/Fail plus the first thing that went wrong). An AI judge was tuned on one blind set, frozen, and tested once on another before grading the rest. The 53 failures were grouped by first upstream cause into 14 types.
The owner’s judgement is the standard; a judge may stand in only after agreeing on examples it wasn’t tuned on. Then fix specification failures first.
Round 1 passed 47 of 100. And 45 of 53 failures weren’t the model’s reasoning — a prompt that never said, a tool that returned the wrong thing, an engine edge case, missing data.
| Judge alignment | Agreement |
|---|---|
| Judge v1, blind set 1 | 7 / 10 |
| Tuned on set 1, then frozen | 10 / 10 |
| Frozen judge, held-out set 2 | 7 / 9 |
| v2.2 on all 20 owner-graded answers* | 20 / 20 |
| # | Failure type (round 1) | Count | Fix lives in |
|---|---|---|---|
| 1 | Payday Safe to Spend treated as real money | 6 | engine |
| 2 | Card payments falsely said to be 'already factored in' | 5 | prompt + tool description |
| 3 | Pending + posted treated as a double charge or fraud | 6 | tools + prompt |
| 4 | Negative 'uncategorized' bucket read as refunds or savings | 6 | data |
| 5 | Ambiguous question answered without asking which meaning | 6 | prompt |
| 6 | Distress handled badly: dismissive, no crisis resources | 3 | prompt |
| 7 | Tool logic wrong: anomaly flags habitual merchants, expenses include transfers, recurring misses repeated transfers | 3 | tools |
| 8 | Bills/subscriptions miss card payments and card-billed subscriptions | 3 | tool |
| 9 | Assumes facts it doesn't have, or doesn't check the user's premise | 5 | prompt |
| 10 | Arithmetic or direction errors | 3 | code check (phase 6) |
| 11 | Income detection wrong: transfers called pay, several deposits stated as one certain paycheck | 2 | engine |
| 12 | Stale or partial data not disclosed | 3 | prompt + tool output |
| 13 | Product stance: not a budgeting app; the recommendation must lead | 2 | prompt |
| Step | Agreement with the owner |
|---|---|
| Judge v1 on blind set 1 | 7 / 10 |
| Tuned (v2.1) on set 1 | 10 / 10 |
| Frozen v2.1 on held-out set 2 | 7 / 9 |
| v2.2 re-grading all 20 owner-graded answers (partly in-sample) | 20 / 20 |
evals/labels/round-1.json PR #329evals/labels/round-1-failure-types.json PR #330Fixed each failure type in the layer it lives in — prompt, tool, engine or data — and proved every fix before it shipped, graded blind by the same frozen judge on both sides.
A prompt can’t out-argue a confidently wrong tool, so facts are fixed in tools and data, behaviour in the prompt. Prompt changes are proven on frozen replays; tool and engine changes on live re-runs.
Prompt iterations on frozen replays took round 1 from 47 to 52 → 56, and the tool fixes took their scenarios from 1/14 to 8/14. Reading every regression — not just the total — caught four new prompt rules overreaching.
| Layer | What was fixed | How it was proven |
|---|---|---|
| Prompt | Dead features removed; card-payment truth; ask when ambiguous; person first in distress; not a budgeting app | Frozen replays, 3 iterations |
| Engine | Payday + month end collapsed the look-ahead to one day; a paycheck counted twice | Unit tests on the failing day; unchanged on ordinary days |
| Tools | Each charge once; card payments in “bills coming up”; smarter anomalies; full pay schedule | Live re-runs of the affected scenarios |
| Data | New bank category codes left paychecks uncategorized | Backfill + coverage test |
A live run records each question, the moment it was asked, and every tool output — e.g. “Can I afford a $90 dinner tonight?” with Safe to Spend returning $84.00 for today. To test a prompt change, the same question is asked again with the clock set back and every tool answered from the recording. Only the change differs, so a different verdict is the change’s doing — not a paycheck landing. A replay can’t show tool or engine fixes (it serves the old outputs), so those are proven live.
| Iteration | Scope | Pass |
|---|---|---|
| Round 1 (before) | 100 | 47 |
| Iteration 1 | 100 | 52 |
| Iteration 2 | 100 | 56 |
| Iteration 3 (targeted) | 21 distress + safety | 18 → 19 |
| Final | 100 | 57 |
services/ai/system_prompt.ts evals/labels/round-1-5a-replays.json PR #331services/safe_to_spend_service.ts (v5.6) PR #332services/ai/coach_insights.ts evals/labels/round-1-5c-live.json PR #333A fresh live run of all 100 scenarios after every fix, graded blind, each failure filed under an existing type or marked new. Twelve free code checks now run over every recorded run and in CI.
Keep coding fresh traces until no new failure types appear — each fix changes the system the next round sees. Automate what’s objective; certify judges for the rest.
Round 2 passed 54 of 100 (round 1: 47). Failure types fixed at the source fell from 18 to 0. Two new types appeared, and grading surfaced a real product bug — a paycheck counted twice by Safe to Spend — fixed the same day.
| Round 1 | Round 2 | |
|---|---|---|
| Pass | 47 | 54 |
| Failures in types fixed at the source | 18 | 0 |
| Arithmetic slips (now a code check) | 3 | 14 |
| New failure types | — | 2 |
| Round 1 | Round 2 | |
|---|---|---|
| Pass | 47 | 54 |
| Payday-engine, pending/posted, data, dead-feature failures | 18 | 0 |
| Arithmetic slips (now a code check) | 3 | 14 |
| New failure types | — | 2 |
evals/judges/scenario_checks.ts npm run scenario:checkThe cycle is closed; these are on the product backlog.