Classifier: Fail Closed

How Clara should decide what a message is about, and when to stay quiet. For Gera and Fede, after the 10-27 call.

2026-10-27 · read-only audit of prod and code, plus a literature review · nothing changed yet

The collections bug is not the classifier. The check that decides whether Clara replies files "rent balance" under other, and other always sends. A bigger model would not stop it. The fix is to teach that check about money and make "unsure" mean "stay quiet".

Today vs. target

Today

  1. Tenant: "I can't pay rent this month"
  2. Reply check: 3 choices — renewal, maintenance, other
  3. "Rent balance" is listed as other, which no module owns → sends
  4. Timeout or error → sends
  5. Topic label lands ~10s later. Nothing reads it to decide a reply.

Target

  1. Tenant: "I can't pay rent this month"
  2. Tripwire first: money trouble, hardship, dispute, legal, "talk to a person" → silent + human
  3. Classifier: can pick more than one topic, each scored by 3–5 votes
  4. Any possible topic whose module is off, or not sure → hold
  5. Timeout or error → hold, never send

Next steps

  1. Stop the leak: a money choice in the reply check, block when a module is off, timeouts hold.
  2. Tripwire before every reply.
  3. An answer key of 300+ checked messages.
  4. Multiple topics with vote-based confidence.
  5. Ask counsel about debt-collection rules; rent replies stay drafts until then.

Details

How good the labels are today

248 real tenant messages, judged by hand against the conversation:

MeasureResult
Overall, weighted by volume87.7% strict · 94% counting defensible calls
Leasing · maintenance · billing100% · 90% · 87%
General resident questions75%
Collections-like messages labelled as something else~1 in 7

Small samples, ±10–15 points per topic. Collections isn't its own topic today (it sits inside billing). The test suite has 102 cases and none for late rent or eviction.

Is Opus 5.5 worth it?

Not as the main fix. Studies find a bigger model alone moves classification accuracy only a little, and the speed cost is real. Votes across several calls tell us more about confidence than model size does. Use the big model where it's cheap and it matters: the one reply-time check.

SetupPer month nowIf today's spike lastedSpeed
Today (Haiku 4.5)~$9~$78~13s
Opus 5.5 for every label~$38–62~$330–530~20–60s
Cheap first, Opus only when unsure (~35%)~$23–29~$195–245mixed
Opus on the reply-time check only~$0.004 per replyneeds a longer timeout

List prices, estimated from message sizes rather than measured. Opus speeds are estimates. "Classifying…" must clear in ~20s.

Keeping it right, and fixing it when it's wrong
Sources and code checked

Stated confidence vs. sampling · Guaranteed error rates (conformal) · Models rarely abstain · Small fine-tuned vs. frontier models · Multi-intent messages · Intercom's escalation classifier · CFPB on chatbots · Claude API: no token probabilities

Code checked: the reply check's choices and its "rent balance → other" line are in classify-reply-topic.ts; supervised-reply-rule.ts maps "other" to no module, so it sends.

PropFlow Docs