Proposed — pending review · 2026-08-13 · Fede via Claude Code · sources: five deep audits of the repo, git history, live ElevenLabs configs, 301 recorded calls, and industry research (21 cited sources)

One Clara: a cross-channel architecture

How we stop building every behavior twice, and make Clara provably the same person on the phone, over text, and in email.

The goal

Build each behavior once, in one shared core. Every channel adds only a thin delivery layer — voice speaks briefly, SMS keeps it to a few sentences, email writes a fuller reply. Feature parity is part of the goal: a capability that exists on one channel exists on all of them unless we deliberately decided otherwise. Consistency is enforced by construction and proven by a harness built from our archive of real conversations.

Strategy in one paragraph: first build the measurement (a replay-first, cross-channel harness from past interactions — valuable even if nothing else ships), then stop the bleeding (shared modules for the worst-duplicated voice rules), then unify domain-by-domain using the pattern that already works in this codebase (renewals runs one shared rule body rendered per channel today). No big-bang rewrite; every phase is independently valuable and gated by the harness.

Why now

A single bad prospect call on Aug 13 surfaced four defects; tracing them exposed the architecture. Every one of them was a consistency failure: a rule or fix that existed in one place and not another.

And this doc has a sibling being built in parallel tonight: the escalation journey rethink, whose own audit independently named "channel as an architectural boundary" a first failure of framing — its channel-fork audit found ten landmines where knowledge or behavior exists on one lane and not another — most never decided, just forgotten — and callers experience the gaps as Clara lying: utilities answered on a call Aug 5 but "not on file" on Jul 17; parking answered correctly by email, transferred on voice. That doc's escalation redesign (the decision ladder with a deterministic rail "identical on every channel," and the teaching loop that turns each escalation into a knowledge entry) assumes the shared core this doc proposes — the two plans are halves of the same move: one behavior store, one knowledge store, every lane a renderer.

11 + 1
hand-maintained voice prompts + one text/email rulebook, zero shared prose between any two — ever
~90k
tokens of duplicated voice-prompt English (≈360k chars, measured)
~7 of 8
prompt commits since the May fork touched only one side — each is a drift event
51%
of recent Camellia agent-to-agent transfers open with a redundant second acknowledgment

Where we are (measured, not vibes)

Text + email — already the right shape

Clara personality (shared)
One specialist body, chosen by identity (leasing / maintenance / turnover / renewal)
Per-property fact sheet (shared with voice)
Delivery layer: SMS ≤3 sentences · email fuller
Outbound safety gate (fair-housing) before every send

Voice — 11 independent forks

Triage · Leasing · Maintenance ×2 · Lease & Billing · Renewal ×2 · Turnover · Unknown Caller · Emergency · Vendor
Each: own persona, own transfer etiquette (3 divergent generations), own closing rules
Per-property fact sheet (the one thing shared)
Settings split: 5 surfaces synced from git, the rest dashboard-only
No safety gate on anything Clara says aloud

The fork, in numbers

Full evidence lives in the five audit reports (architecture, git archaeology, prompt quality, eval quality, transfer seams) — session 2026-08-13; operational bugs already filed as Trello cards.

What the industry does (research, 21 sources)

Target architecture

One shared behavior core (source of truth)

Clara personality + judgment rules (fair housing, safety, honesty, escalation, no-repeat, contact policy)
Domain bodies: leasing · maintenance · renewals · turnover · billing (the renewals pattern, generalized)
Capability registry: every feature × channel, gaps are explicit decisions
Per-property knowledge plane (already shared — extended, e.g. guarantor/application process)

Thin channel renderers (build-time)

Voice: brief + spoken style layer, subtracts text-only sections, per-specialist delta; output = the synced ElevenLabs prompts
SMS: ≤3 sentences, one action
Email: fuller prose, no brochure
All settings config-as-code with drift fences (turn timing, voices, transfer flags — no dashboard-only dials)

Design principles (each traces to a scar or a measurement):

  1. Identity routes before the conversation starts. We know the caller before Clara speaks; a known tenant lands on the maintenance specialist directly instead of an audible mid-call transfer. The front door shrinks to unknown callers and intent changes. (Text already works this way.)
  2. Transfers are invisible when they do happen. One shared hand-off module — silent seam between agents, one warm word to a human — imported everywhere, with the receiving agent's first utterance graded by evals (never tested today; fix card filed).
  3. Composition is server-side, order-preserving, cache-friendly. ElevenLabs template variables can't express conditionals (shipped a prod bug); prompt position is load-bearing (a rail that works at the end demonstrably failed mid-prompt); shared core goes first for prompt-cache stability.
  4. Not all redundancy is redundancy. A worked example that pins a specific past failure is kept even when an abstraction seems to cover it — cutting three "redundant" examples re-opened a live incident within days in July.
  5. Budgets are fenced, not remembered. Every generated voice prompt carries a token budget; growing past it without an explicit bump fails CI. (Both diets regressed invisibly because no fence existed.)

Phase 0 — the harness (the gold mine)

Built primarily from real past interactions: the full ElevenLabs call archive, every SMS/email thread in the database, and the curated corpora (434 graded triage calls, 41 labeled turnover calls, the renewals bug corpus, incident calls including Aug 13's). ElevenLabs' test framework natively generates regression tests from past conversations and supports starting a simulation mid-conversation — a real call becomes a replayable test without hand-writing it.

Phases 1–3 (each independently shippable)

PhaseWhat shipsEffort (estimate)Proof
0 · HarnessReplay-first cross-channel grid + baseline scoreboard + parity matrix; flip the voice suite's soft gate~1–2 weeks of fleet workBaseline report published as a PropFlow doc
1 · Shared voice modulesExtract the worst-duplicated blocks (hand-off etiquette ×7, language ×7, AI disclosure ×8, closing rules ×5) into shared modules composed at build time into the same synced strings. Kills the on-call contradictions. No new engine, no sync changes.~1 weekHarness diff vs baseline; transfer-seam rate drops from 51%
2 · Domain unificationOne domain at a time onto the renewals pattern (shared body + channel renderers). Suggested order: renewals (3 sources of truth today → 1), then leasing, then maintenance. Tool schemas reviewed with prompts (they're one rulebook).~2–4 weeks per domainConsistency score per domain; latency + token budgets hold
3 · Full compiler + hard gatesAll prompts (11 voice + text + email) are build artifacts of the core; eval workflows become required checks; prompt-size ratchets; nightly consistency report over live traffic~2–3 weeks>95% cross-channel consistency in CI, sustained

Efforts are estimates, not measurements. The Aug 13 fix fleet (name-only tours, email persistence, 7-second silence, repetition fix) ships independently and first — its scenarios seed the harness.

Decisions

Pick one per question — answers save on this page and are shared with everyone who opens it.

D1 — How far do we take the architecture?

D2 — Do we consolidate the 11 voice specialists?

D3 — When do evals start blocking merges?

D4 — Does voice get a safety check like text has?

Every SMS/email passes a fair-housing gate before sending. Nothing Clara says aloud is checked by anything.

D5 — Gate/buzz codes on the phone

Text never shares codes (access info goes out ~1h before a tour). Voice currently instructs reading the code to callers — any caller.

Risks & constraints (the scars this plan respects)

Related work in flight

PropFlow Docs