D2 — who answered
Traced at commit e53d0dc ·
libs/contracts/src/lib/{vector-resolution,extractor,canonicalization,contracts,outbox}.ts ·
libs/temporal/src/{workflows,activities}/call-attempt.ts ·
apps/worker/src/main.ts · live state read 10 September 2026
Summary
- D2 records who answered the call. It holds one value or nothing.
- Four values exist. The model can also answer
UNKNOWN, which the platform discards. - Only
WRONG_PERSONandREASSIGNEDchange the headline. Both givewrong_number. - Three sources can fill D2. They apply in a fixed order, and the first value stays.
- The voice provider runs first. It can set
WRONG_PERSONand no other value. - A phrase list runs second. A language model runs third.
- The phrase list and the model run on every answered call.
- On a real call, D2 can therefore hold any of the four values.
- The phrase list reads the agent’s words as well as the caller’s words.
- In a test, the agent question
Am I speaking with Sam?turned a wrong number intoDECISION_MAKER. - In a second test, a shop owner who said
retiredbecameREASSIGNEDandwrong_number.
Workflow 1 — where the value comes from
Step 1 — the voice provider
- The provider sends a call analysis after the call ends.
- The platform reads three fields from it:
call_successful,in_voicemailandcall_summary. - The platform sets
WRONG_PERSONonly when all three conditions below hold. call_successfulis the booleanfalse.in_voicemailis the booleanfalse. An absent field does not satisfy this condition.call_summarycontainswrong numberorwrong person, in any letter case.- In a test, an analysis without
in_voicemailgave no D2 value. The headline stayedneeds_review. - The platform stores the value with confidence 0.8 and the source
retell_analysis. - Step 1 cannot set
DECISION_MAKER,GATEKEEPERorREASSIGNED.
Before step 2
- The workflow asks for extraction only for an answered call.
- Each answered call makes a model request. The model is
gpt-4o-miniunlessOPENAI_MODELnames another. - Before the request, the extractor removes phone numbers, email addresses and card numbers from the transcript.
- It also removes social security numbers, street addresses and ZIP codes. It matches fixed patterns only.
- It does not remove names. It sends the provider’s call summary without any removal.
- In a test, a phone number with spaces and the whole call summary reached the model unchanged.
- The extractor sends the model request before it checks the phrase list. A phrase match then discards the paid answer.
- If the request fails, the extractor uses the phrase list alone.
- The cost Blueprint does not price this request.
- A rehearsal runs extraction against an impersonated model and spends no tokens.
- Extraction needs
OPENAI_API_KEY. Without it, steps 2 and 3 do not run.
Step 2 — the phrase list
- Step 2 outranks the model. A phrase match wins even when the model answered first.
- It joins every turn of the transcript into one string. It drops the speaker labels.
- It checks the values in a fixed order:
DECISION_MAKER,GATEKEEPER,WRONG_PERSON,REASSIGNED. - For each value, it looks for any phrase of that value as whole words.
- The first value with a match wins. Step 2 then stops.
- A match replaces the model answer, whatever the model said.
- The list holds 35 phrases. Their confidence runs from 0.85 to 0.95.
- Every phrase confidence is above the review threshold of 0.7. A match alone never sends a call to review.
Step 3 — the language model
- The model receives the transcript with
AGENTandUSERlabels, and the call summary. - The platform uses the model answer only when no phrase matched.
- The platform discards an
UNKNOWNanswer. That answer never fills D2. - An
UNKNOWNanswer for D2 can still send the call to review. See Outcome resolution.
What the record keeps
- The platform writes D2 into
disposition_vectorunder the keyd2Party. - The entry holds the value, the confidence, the evidence and a source label.
- Step 1 writes the source
retell_analysisand no evidence. - Step 2 and step 3 both write the source
llm. - When a phrase replaces the model answer, the record keeps no copy of the model answer.
- To find a phrase match, compare the single evidence entry with the phrase list below.
The source label hides the phrase list. A count of D2 values by source
puts every phrase match under llm. An accuracy figure for the model then
includes the phrase list.
Workflow 2 — what each value does
- D2 is step 4 of the headline waterfall. D5, D6 and D1 come first.
WRONG_PERSONandREASSIGNEDboth give the headlinewrong_number.DECISION_MAKERandGATEKEEPERgive no headline. The waterfall moves on to D4, then D3.- Steps 2 and 3 change the headline only when step 1 left it at
needs_review. - A later D2 value never replaces a headline that step 1 decided.
- Step 1 never sets D2 and D4 on the same call. D2 needs
call_successfulfalse, and D4 needs it true. - A gauge counts D2 values over seven days for calls that reached a person.
- The gauge shows an empty D2 as
UNRECORDED. - The gauge also counts phrase matches from the agent’s own words.
Workflow 3 — the agent’s own words
- The test ran the real extractor on the two turns in the diagram.
- A model stub answered
WRONG_PERSONwith confidence 0.9. - The extractor returned
DECISION_MAKERat 0.9, from the phrasespeaking. - The phrase came from the agent’s question, not from the caller.
- The same call without the agent’s turn returned
WRONG_PERSONat 0.95. - An August 2026 flow backup tells the agent to confirm that it is
speaking withthe merchant. - An agent line with
hold onsetsGATEKEEPERin the same way. - A real wrong-number call gets
DECISION_MAKERthis way, unless step 1 caught the wrong number.
A caller’s words out of context
- A second test used one caller line:
I'm retired from the navy, I run the shop now. Tell me more. - Step 2 matched
retiredand setREASSIGNED. - The merged result had the headline
wrong_number, confidence 0.9 and no review. - The caller owned the business and asked to hear more.
The phrase list
Step 2 checks the rows from top to bottom. The first row with a match wins. Dictionary version 1.0.
| Order | Value | Confidence | Phrases |
|---|---|---|---|
| 1 | DECISION_MAKER | 0.90 | yes this is · speaking · that's me · you got the right person · i am the owner · i make the decisions · i handle that · i'm in charge · yes i am · this is he · this is she |
| 2 | GATEKEEPER | 0.85 | let me transfer you · i'll get them · hold on · they're not available · can i take a message · i'm the assistant · i'm the secretary · who should i say is calling · i'll pass the message |
| 3 | WRONG_PERSON | 0.95 | wrong number · wrong person · i don't know them · never heard of them · they don't live here · no one by that name · you've got the wrong · nobody here by that name |
| 4 | REASSIGNED | 0.90 | no longer works here · left the company · moved on · doesn't work here anymore · retired · no longer with us · transferred to another |
Known defects
- Step 2 reads the agent. The phrase list receives agent turns and caller turns as one string. Fix: pass only the caller’s turns.
- Value order beats word order. A transcript with
yes this isandwrong numbergetsDECISION_MAKER. The position of each phrase does not matter. - Short phrases match ordinary speech.
speaking,hold on,moved onandretiredoccur in calls with the right person. - A match erases the disagreement. The record keeps no copy of the model answer that the phrase replaced.
- One label for two sources. Step 2 and step 3 both write
llm. - The model call happens first. Every answered real call pays for a model request, and any phrase match then discards the answer.
- Names reach the model. Redaction matches fixed patterns only, and the call summary goes to the model unchanged.
- Step 1 needs an explicit field. An analysis without
in_voicemailnever givesWRONG_PERSON.
What rehearsal data can show
- On 10 September 2026, prod held 2,157,695 rehearsal attempts and 4 real attempts.
- None of the 4 real attempts holds a D2 value.
- A sample of about 1% of stage attempts held 240 D2 values. The phrase list set none of them.
- The impersonated model set 213 of them. Step 1 set the other 27.
- Rehearsal values show that the code path runs. They do not measure how well D2 reads a real caller.
- The corrections in the review queue are the only real measurement. See Outcome resolution.
D3 — intent — the same three sources, and five values.
D4 — qualification — the step 1 rule that no live agent satisfies.
Outcome resolution — how D2 fits among the seven dimensions.