Superseded as the living doc → the Eval & Testing Roadmap (this page stays as the frozen record, 2026-08-20).

COMPLETE — every number independently re-run

Catching Clara's Mistakes

The three-layer testing proof — built, measured, stress-tested

2026-08-20 · Four competing prototypes vs. five real Camellia conversations that actually failed. Companion to the Quality System Plan.

Verdict: compose, don't adopt. Our own TypeScript checkers + promptfoo as the runner + one shared before/after diff + the AI judge only for wording-level faults. All four lanes caught 5/5 real failures, 0 false alarms, 2/2 seeded regressions — and all of it reproduced when independently re-run. The audit's real finding: structure-based checks generalize; phrase-based checks memorized the answer key.

5/5 ×4real failures caught, correct layer + verdict, every lane — independently reproduced
0 ×4false alarms on clean real conversations
2/2 ×4seeded regressions killed — the "did a change make it worse" primitive works
$0metered spend across all builds and re-runs
6.7%how much of the best framework lane's code the framework actually contributed
B B C Boverfit grades — the reason perfect scores aren't the headline

How a turn gets checked

Every Clara turn produces three records
Context
what Clara was told
Truth
what was actually true — serialized text, no live database
Trace
what tools ran & returned
Draft
what she's about to send
↓
Three gates, cheapest first
L1 · Input
context vs truth diff · thread-ownership rule
→
L2 · Loop
draft claims vs actual tool results
→
L3 · Outbound
numbers vs truth · promises backed · narration leak
↓
pass
send as-is
repair
fix the exact span, cite the truth row
hold
human takes the next turn
block
kill it, page engineering
Verdicts always cite their evidence — a field diff, a tool result, a span. A judge reading one transcript cold is banned (measured ~15–25% accuracy on "which layer broke").

The five real failures it had to catch

#LayerWhat really happened (sanitized, verbatim otherwise)Verdict
1INPUTCurrent tenant told "you moved out" — stale residency record fed the contextblock
2INPUTAuto-replied on a thread a human teammate already ownedhold
3LOOPTool failed; Clara presented the failure as policy ("that's just how the process works")repair
4OUTBOUNDInternal system narration sent to a prospect — the "kill it, page engineering" caseblock
5OUTBOUND"The team will follow up" with nothing behind it — the class prod now catches, so it doubles as a known-fixed-bug testrepair

Plus two clean real turns as false-alarm bait, and a regression set: fixed versions of the 5 failures + deliberately worsened versions of the 2 clean cases.

The four lanes, verified

LaneWall timeCodeOverfitKeep?One-line reason
Hand-rolled TypeScript~1.3s1,313 lines, zero depsBKEEP — the checkersBest evidence-cited verdicts; nothing to depend on or be abandoned
promptfoo-extended~42s~850 lines glueCKEEP — the runnerChecks drop into the format of our 56 existing suites; login just works; CI reporting already wired
Guardrails AI~0.05sframework wrote 6.7%BPASSProved the serialized-truth thesis, then contributed scaffolding + one silent bug (chained validators drop all but the last, no warning — reproduced)
inspect-ai (Python)~1.0s~1,026 linesBPASSIts one differentiator (model-graded scoring) went untested; a Python sidecar isn't earned

All four reproduced on independent re-run. Discrepancies found and disclosed: one stale line-count (3% under), one wall-time variance band, one run-to-run drift in the unscored judge leg.

The finding that matters

Structural checks — wording-robust, schema-brittle
truth-vs-context diffs · dollar figures vs ledger · tool succeeded · thread ownership. Zero wording dependence — survived every paraphrase and 100% of 560 perturbed cases. But schema drift (renamed/nested fields, numbers-as-strings) broke 17/20 attacks and crashed 2 — production-hardening + unit tests required before the grade-A label is earned.
Phrase checks — confirmed memorizers
narration leak · invented policy · unbacked promise. Keyword lists copied from the answer key in all four lanes — and the bigger run measured it: 1/80 reworded attacks caught (1.3%), 17% recall on real over-promises, 7% on real hallucinations. These classes belong to the judge.
↓
Consequence — the wording-level third of the problem belongs to the AI judge, not more regexes. Where the judge did run, it recovered exactly what rules couldn't (hold vs block on a bad reply). Its full quality is the one unmeasured number — being measured right now (below).

How CI tells a PR it made Clara worse

PR opens
→
Old code
replies to the corpus
→
Checkers
verdict per case
→
Diff
which cases flipped
New code
replies to the same corpus
→
Checkers
same rules, same judge
↗
↓
Per-case flips, evidence quoted
"case 12: pass → block — draft now contradicts the lease status"
→
Sticky PR comment
the same loop the review bot already reads — the builder fixes it before merge
Proven in the POC: every lane reported all 7 before/after flips correctly. Deterministic layer costs seconds per hundreds of cases; judge calls are serialized (concurrent use rate-limits the subscription — measured today).

How it connects to the golden set & the grading UI

Quality Desk
the annotations UI — founder & team grade real conversations
→
Golden set
385 graded rows and growing — the labeled corpus
→
Quality engine
replays the corpus with checkers + judge on every change
↓
Disagreements
machine vs human verdicts land back in the Desk as review items — annotating them recalibrates the judge
→
New paired cases
every prod incident + every overturned verdict becomes a permanent before/after test
→
CI corpus grows
the bug that happened once is checked forever
Same loop Sierra and Intercom run: humans annotate, the harness replays, disagreement drives the next annotation. The Desk already exists; the golden set already exports; the engine closes the circle.

The bigger run — measured

1.3%of 80 reworded attacks caught by the phrase detectors — memorization confirmed with a number
17% / 7%recall on the golden set's real over-promises / hallucinations (answer-key-blind, never-seen rows)
0false alarms on every testable founder-graded good reply
560 in 8mscases through the checkers at scale — 100% verdict accuracy, ~0.014ms per case
105 in 5mspaired before/after diffs — 30/30 regressions killed, 75/75 fixes validated
3/20structural rules surviving schema-drift attacks — the new hardening work-list
ProofWhat ranResultWhat it means
Generalization
measured
Shipped detectors vs. all 385 founder-graded golden rows they had never seenOver-promise recall 4/23 (17%) · hallucination 4/60 (7%) · good replies 0 false alarms · hand-audit: several "catches" matched the analyst's incident notes, not Clara's own wordsPhrase rules don't generalize — as predicted. Two data findings: golden rows mix Clara text with analyst notes (checkers must only ever see Clara's words), and 24% of rows carry no reply text at all. The dominant real hallucination shape (fabricated facts, IDs, "already done" claims) is structural — it needs the truth ledger, exactly what the architecture builds.
Adversarial
measured
130 synthetic attacks: 80 reworded faults, 20 near-miss clean baits, 20 schema-drift attacks, 10 quoted-customer confusionsParaphrases: 1/80 caught (narration 0/20, policy 1/20, promise 0/20, residency 0/20) · bait: 2/20 false alarms, both auto-block · schema drift: 3/20 survived, 2 crashes · quoting confusion: 7/10Three concrete bugs for the engine: nesting one field deeper silently defeats the human-ownership hold; the narration rule blocks on ordinary phrases like "on my end"; no detector separates Clara's voice from quoted customer text. The 130-item set is now a permanent fixture.
Scale
measured
560 perturbed cases + 105 before/after pairs through the unmodified checkers, reproduced 5×7.9ms total (0.014ms/case, p95 0.018ms) · 560/560 correct verdicts · pairs: 5.4ms, 100% kill + fix-validationThe deterministic layer is free at any corpus size — running it on every PR, on the full corpus, costs nothing. CI cost lives entirely in the judge leg.
Judge vs. founder
measured
147 golden rows judged label-blind by claude-sonnet-5 (the planned judge model), exact rubric, one verdict per rowAgreement 69.9% (72/103 rows with text) · miss rate 14.4% (judge said clean on 14/97 founder-flagged rows) · false alarms 2 of 6 testable good replies · top confusions: over-promise read as wrong-fact (6), hallucination read as clean (6)This is the floor: a context-free judge reading excerpts alone. It already beats the phrase rules by ~4–10× on recall, but 2-of-6 false alarms (tiny sample, but directionally over the 2% trust budget) says an excerpt-only judge must never block on its own — it needs the receipts + truth the checkpoint feeds it, and clean-reply calibration on a much bigger sample. Method note: judged in agent batches (the direct API path was device-throttled — account rotation disproved quota as the cause); disclosed in RESULTS.json. Adjudication of all 31 disagreements against the founder's verbatim verdicts: only 1 was a mislabeled row (the machine-inferred-label worry proved small) · 6 confirmed judge errors · 11 genuine taxonomy overlaps (an unbacked "done!" claim is both an over-promise and a false statement — the classes need a precedence rule) · 13 were unfair tests (the excerpt never contained the evidence — the context-free ceiling, not judge failure). Adjusted agreement 70.9%. Deeper data finding: 27 of the 31 rows carry no raw founder quote at all, only a reconstructed summary — golden-set provenance needs rebuilding, not just relabeling.

What gets us to grade A

Honest edges that remain

Method: 4 independent builder teams, one sealed fixture pack (sanitized real conversations — zero real names, proven by programmatic scan), one scorecard frozen before any build, hard rules (subscription auth only, offline replay only, nothing merged). Then 4 adversarial verifiers re-ran every harness from scratch and audited every checker for answer-key overfitting. Full per-lane write-ups, code, and the fixture pack move into the quality-engine repo once it's named.

PropFlow Docs