Classifier Rebuild Plan

How Clara decides whether to reply, rebuilt so she stays quiet when unsure. For Gera, Fede and the team.

2026-10-29 · v3, the build plan · follows Classifier Plan v2 · reviewed by Fable · design only: nothing here is built yet beyond the PRs named as shipped

Clara replies only when she's sure the reply is allowed, helps, and is true. The page keeps its topic labels. The reply decision adds who's writing, the thread's state, the kind of message and risk, then an Opus check reads the draft itself. Every check must pass, and we count what each one blocks.

Today (on main)

  • Reply check on the small model; a timeout still lets most replies go.
  • Doesn't look at a teammate mid-thread, risk, or whether the draft helps or is true.

After

  • Our records first: who's writing, thread state, money keywords. Any can stop Clara before she drafts.
  • An Opus check on every draft. A timeout or any doubt means quiet, and the question goes in the digest.

Headline decisions

  1. The page label never decides a reply. Both read one shared topic list.
  2. A teammate in the thread, or a person on the call, means silence, from records with no model (the study: 84% and 44 of 45).
  3. The draft check asks three things: allowed, helps, true. Live module sends, Supervised goes to a person, Off or no module stays quiet.
  4. A failed draft is never put in front of a person. It's dropped and the question goes in Fede's digest.
  5. Measured first: about 1,350 real moments (450 about money) settled by Gera and Fede, then two weeks silent through the Nov 1 rent cycle. 17 small PRs; the first, this week, makes a timed-out check hold.

The plan in full

1 · Where we are today (checked on main, Oct 29)

Shipped and live: #22879 (money owed holds when Collections is Off or Supervised), #22880 (its eval), #22972 (a general reply follows who is writing), #22995 (an unknown sender's reply waits under the first module that isn't Off), #23220, #23237, #23258 (the hold says "We don't know who this person is"), #22935 (Resident Services is a real module), #23380 (Resident Services is its own reply topic and sends only while Live), #23428 (silent hand-off, slice 1).

In flight: #23500 and #23508 (silent hand-off 2a and 2b: a reply that is only a hand-off or a hedge sends nothing), and the neighbor-complaint hold (being built, no PR yet).

Still true on main:

Measured on 639 real moments (Sep 27 to Oct 27)Today
Page topic agrees with the reviewers75.7%
Rent trouble labelled "billing" instead of collections74%
Old 3-label reply check: right action81.6%
Old reply check: "shouldn't reply" moments it called "other" (sends)75%
Email "shouldn't reply" moments Clara actually replied to24% (18/75)
Text "shouldn't reply" moments Clara actually replied to4/119
Today's 5-label reply check on the same setnot measured yet: part A1a measures it

344 of the 639 moments are texts and emails (179 + 165); the other 295 are voice, which this plan leaves alone. Every replay gate below uses the 344. A second, separate measure of page labels: the 10-27 audit hand-judged 248 messages at 84.7% strict and 92.3% counting defensible calls. This plan's page target uses the turn-study measure: 75.7% today, 90% target.

Reviewer agreement (Fleiss κ): who's writing 0.85, kind 0.70, topic 0.68 (0.82 when all three named one), action 0.67, any risk 0.39.

2 · The target, step by step on Fede's engine, and who owns each step

Fede's engine: gather context → shouldSpeak → writer → draft check → record → proof rail. Tenant comms is Gera's swarm. The core doors (shouldSpeak, the notice engine, the send door, the writer) are Fede's.

StepWhat it doesModelOwner
1 · Gather contextWho's writing, from identity records. Thread state, from the record: a teammate active, a person on the call, an open escalation, Clara asked last. The keyword risk floor. Each module's state at the building. Only what existed at that moment.NoneInterface: Fede. The parts that fill it in: swarm
2 · shouldSpeakRules only. Silent when a teammate is active, a person took the call, the thread is escalated with an open matter, or the sender is automated. Silent plus a tracked ask when the keyword floor finds money trouble, legal or hardship. The emergency chain is unchanged. An unknown sender's reply is held under the first module that isn't Off (as today). Otherwise Clara drafts.NoneFede
3 · WriterOpus. Writes a draft, or [[NO_REPLY]]. It also returns its own read: kind, topics, risk flags, the value it adds, the facts it used (tool results).OpusFede (prompt), swarm (the shape of its read)
4 · Draft check (new)A separate Opus call with a different job. It doesn't see the writer's reasoning. It answers three questions: allowed, adds value, true. It also gives its own kind, topics and risks.OpusSwarm. Where it plugs into the send path is a change to Fede's contract, which Fede approves.
5 · Combine and recordEvery check must pass, decided by rules and not by a model. Passed everything and the module is Live: send. Passed everything and the module is Supervised: a person approves it. Anything failed, unsure, timed out or unreadable: silent, flagged for the team, and it goes into Fede's daily open-conversation digest. Engineering failures also post to #alerts and never reach the customer. One decision record per turn names every check that blocked.NoneSwarm (record), Fede (notice engine)
6 · Proof railAnswer key, arms, judge, nightly CI gate, shadow diff.Opus judgeSwarm

Value rule (Gera): reply only with real value. A greeting gets a greeting. Never "I'll let the team know". A hand-off is silent. This covers texts and email; voice and the tour offer keep their wording. The draft check's "adds value" question generalizes the silent hand-off work (#23428, #23500, #23508): those stay as the fixed rule underneath it, and the check catches the hedges they miss.

No topic: the reply follows who's writing. Prospect → Leasing, resident → Resident Services, vendor → Maintenance, unknown → held under the first module that isn't Off.

The whitelist: every topic in the message and in the draft has to resolve through one map. Live → can send. Supervised → a person approves. Off → silent. general or none → use who's writing (above). Truly unmapped → silent. Nothing sends by being left out.

3 · The data: five dimensions on one record per turn

One TurnAssessment per inbound text or email. Records are only ever added, never edited. The message's page topic and subtopic stay where they are.

DimensionValuesComes from
Who's writing (actor)resident · prospect · vendor · staff · owner · unknown · automated, plus the identity record it matchedRecords, no model
Thread statestaffActive: the latest staff message is newer than Clara's latest, and under 24 hours old (with a backstop of the last 10 rows). humanOnCall: a transfer happened. escalated: an open matter exists. awaitingOurAnswer: Clara asked last. The exact constants are set by replaying the 344 rows.Records, no model
Kindgreeting · thanks/bye · ok · real ask · answer to our question · FYI · automated · unclearWriter and draft check
Topic + subtopicThe page's labels, one list, more than one allowed (9% of messages have two). Changes: collections is split out of billing; resident inquiry becomes Resident Services; a staff lookup is a kind of sender, not a topic.Page labeller, writer, draft check
Riskmoney trouble · hardship · legal/dispute · safety · privacy/identity · fair housing · staff decision pending · distress · confusion · wants a person · neighbor complaint, plus the keyword floorKeyword floor, writer, draft check. A flag from any source counts. Never a vote.

Each record also keeps what the labels were when the decision was made next to the current best guess. If a later relabel would have changed a past decision, it goes to review. It also stores the model, prompt, policy, keyword-list and taxonomy versions.

4 · The Opus draft check: inputs, outputs, time
5 · The answer key
SliceRows
Money-adjacent (hardship, promises to pay, disputes, late fees, eviction, the post-transfer half of a balance call): 150 for development, 300 locked for the final challenge450
Random real traffic (to measure how common each case is)150
Leasing, maintenance, renewal, Resident Services (100 each)400
Thread state: teammate active, person on the call, escalated100
Soft speech: hi, thanks, ok75
Unknown, vendor, automated senders75
Safety, fair housing, privacy50
Adversarial: instructions hidden in a message, wrong identity50
Total≈1,350
6 · How we measure it

Primary: the unsafe-send rate among sends: sends whose right answer was "don't reply", or that fail allowed or true, divided by all sends. We report it with a one-sided 95% upper bound, overall and per module.

Always shown beside it, so "hold everything" can't win: missed holds on the 300 locked money rows (0 of 300 means under about 1%), and coverage, meaning the share of right-answer-is-reply rows that actually send.

Secondary: blocks per check; value violations (a hand-off or hedge that went out); page topic agreement (75.7% → 90%); Resident Services vs Collections mix-ups; time p50 and p90 and the timeout rate; how often a label flips across 5 repeats of the hard rows; cost per 1,000 turns; holds per customer per day; digest size.

Release barOn the keyIn shadow
Money0 misses on the 300 locked rowsn/a
Unsafe sends95% upper bound ≤ 2% on sealed sends0 confirmed unsafe would-sends in ≥ 400 graded (bound ≈ 0.75%)
Coverage (reported, not a gate)target ≥ 75% of right-answer-is-reply rows send; feeds the silence-budget callsilent and digest volume, reported against the silence budget (open call 2)
Timewithin the budget in section 4timeout rate < 2%

Honest sizing: the key is for design and catching regressions. It cannot show a 0.1% error rate; that needs about 3,000 graded sends and builds up after launch. It is never a launch gate.

7 · Rollout and rollback
  1. Shadow. The whole new pipeline runs beside today's: the new shouldSpeak, writer, draft check and combine step. It makes a would-have decision on every text and email turn, including turns today's path holds or silences. That second half needs the Preview "would-have" records, a hard dependency owned by the maintenance session. Every disagreement between old and new gets graded in both directions, plus 10% of the agreements. At least two weeks, through Nov 1, with at least one of those weeks on the writer version that will be live when we switch.
  2. One customer. A founder's call, Fede or Gera. It uses Fede's release-template company setting, defaulting to shadow; Fable treats this as an extension of the template's "new writer" carve-out, to be approved alongside D1. Blocks are reviewed daily for a week on the block-count page.
  3. Everyone. Then the setting and the old reply check are deleted (D4).

Rollback: flip the setting back. The check can only block, so rolling back makes Clara louder and never opens a new way to send. A timeout never goes back to meaning "send".

8 · Merging the two classifiers into one topic list

One taxonomy module (the topic list, definitions, the topic + subtopic → module map, the risk flags) is read by both the page labeller and the draft check. The old reply-topic classifier is retired into the draft check. The page labeller stays separate and never decides a reply. When the draft check and the page disagree on the same message, that is logged as free test data.

Page today (8)Reply check today (5)One listModule
leasingotherleasingLeasing
renewalrenewalrenewalRenewals
maintenancemaintenancemaintenanceMaintenance
turnoverotherturnoverproposed: Renewals (notice to move out) · Maintenance (turnover work)
billing · balance, due date, autopay, portal, depositresident_servicesresident_servicesResident Services
billing · delinquency, late fee, payment plan, owedcollectionscollectionsCollections
resident_inquiryresident_servicesresident_servicesResident Services
vendorothervendorMaintenance
noneothergeneral / nonewho's writing decides

Page chips: Haiku labels within the 20-second bar, then an Opus 5.5 pass relabels inside the existing stamp workflow through the one canonical writer (never a new classification caller, and a relabel never reverts to "Classifying"), on untagged and reconsidered rows only; the record keeps both. Fable's pick: the only option that keeps Opus as the standard and the 20-second bar.

9 · The PR plan: 17 small slices in order

Each one is ≤ 300 lines, branched from main, and works on its own. Order corrected by Fable: thread state is recorded before anything reads it, the combine step gets its own slice, and the block-count page lands before any customer is switched on.

#SliceOwnerNeedsDone when
A1aEvery failure path of today's reply check (timeout, error, no verdict) holds. Time limit 6 s → 20 s. (An interim on today's check; in the new pipeline a timeout means silent + digest.)Swarm drafts, Fede merges (send door)none, nowA test that forces a timeout holds; the 344 rows replayed on main's prompt = the before-number
A1bReply check moves to Opus 5.5 lowSwarmA1aReplay again = the after-number; timeout rate < 2%
A2Keyword risk floor writes flags; the door reads them (silent + tracked ask)Swarm (flags) + Fede (door), same weeknone0 misses on the money development rows; false alarms on 150 random rows reported
C1Who's writing and thread state recorded on every turn, no behavior changeSwarmnoneReplay identical to today's
A3shouldSpeak goes silent on thread stateFedeC1Constants chosen from the 344 replay; identical elsewhere
B1The taxonomy module; the reply check reads itSwarmnoneEvals green
B1bThe whitelist in the reply check, with who's writing as the fallbackSwarm + Fede (door)B1Block rate on the 344 replay reported before it enforces
B2Answer-key harness: the format, loader, upper bounds, counts per check, CI gate on the synthetic rowsSwarmnoneCI runs it nightly
B3Labelling: 100-row pilot, then the full roundSwarm + Gera and FedeB2κ bars met
C2The writer returns its own read, recorded onlyFedenoneReplay identical to today's
D0The combine step and the decision record, recorded onlySwarmC1Every turn has one record
D1Draft check in shadow, whole-pipeline shadowSwarm; Fede approves where it plugs inD0, C2, Preview recordsWould-have decision on every text and email turn
D2Bench the arms on the key: today; today + A1 to A3 + B1b; + draft checkSwarmB3, D1The release bars in section 6
E1Block counts per check on a page for Gera; digest feedSwarmD0Live before D3
D3Enforce at one customerSwarm; a founder switches it onD2, E1, shadow barsA week of clean daily block reviews
D4Enforce everywhere; delete the old reply check and the rollout settingSwarmD3Two clean weeks
E2Page chips on the one list; collections split visible; kind and sender behind the (i); Opus relabel pass inside the stamp workflowSwarmB1Page agreement measured on the key
10 · Risks
11 · Open decisions (product calls only)
  1. Gera: the edge between Resident Services and Collections. Proposed: plain balance, due date, autopay and portal questions are Resident Services. Owing, late fees, a promise to pay ("I'll pay Friday"), "am I behind?" and "can you waive the late fee?" are Collections.
  2. Gera and Fede: the silence budget. How many quiet turns per customer per day, and how big a digest, is acceptable? Needed before D2 can be judged.
  3. Gera and Fede: money trouble that goes quiet ("I can't pay until the 14th"). Is tomorrow's digest fast enough, or does the owner get a same-day notice?
  4. Gera and Fede: a tenant who follows a quiet turn with "hello?? anyone there?". Does that get a greeting back, or a second, escalating tracked ask?
  5. Gera: Friday night. Is one "the office will reply Monday" ever allowed, or is it always silence?
  6. A founder (Fede or Gera): which customer enforces first.

Settled here, not open: an Off module stays silent and goes to the digest (Gera's value rule already decides it: "we don't handle that here" adds nothing). Collections is never Live until counsel answers; the action is sending counsel the per-customer question now. The seam for the draft check is an engineering sign-off for Fede on D1, not a product call.

12 · What we will not do
What Fable changed

Sources

Earlier pages: Classifier Plan v2 · Classifier: Fail Closed (audit and literature) · Clara Reply Scenarios (the 40 cases to vote on) · Fede's engine: Clara, the new hire. Evidence: the 10-27 code and production audit; Fable's and Astra's critiques of v1; the turn study (639 scrubbed moments, three blind labellers); the literature review (selective risk, conformal bounds, why models don't abstain, multi-intent). Code checked on main on Oct 29: classify-reply-topic.ts, supervised-reply-rule.ts, should-speak.ts, model-paths.ts.

PropFlow Docs