Promises made on a call
A promise on a call is checked against what the caller actually said. record_event refuses a when that shares no time word with the caller's latest time-bearing line, unless the caller just agreed to a time the voice proposed. The refusal comes back as "NOT recorded", and a promise left with nothing behind it becomes the promised_unbooked finding on the review.
The failure this was written for#
A language model asked to book a call-back will produce a time. It is very good at producing a plausible one, and nothing in the shape of the conversation tells it that the time has to have come from the caller. Left alone it invents Thursday, tells the caller Thursday is booked, and the caller arranges their week around a commitment that exists in one sentence of a transcript and nowhere else.
One recorded call looped three times over exactly this before the rule existed: promise, refusal to persist, promise again. The rule is what broke the loop, and the wording of the refusal is what stopped the model narrating around it.
The mismatch rule#
record_event compares the when the model wants to book against the caller's most recent line that carries a time at all. If they share no time word, the booking is refused as conversation.when_mismatch. The comparison runs across English, Telugu and Hindi, because a caller who says the day in one language and the rest of the sentence in another is ordinary, not exotic.
| What the caller said | What the model tried to book | Result |
|---|---|---|
| "call me tomorrow morning" | tomorrow 10:00 | Booked — *tomorrow* is shared |
| "call me when you can" | Thursday 15:00 | when_mismatch — nothing in the caller's line names Thursday |
| Nothing about time at all | any time | when_mismatch — there is no time-bearing line to match against |
| "yes, that works" after the voice said "Friday at three?" | Friday 15:00 | Booked — when_agreed |
Agreeing counts as naming#
The exception matters as much as the rule. A caller who says "yes, that suits me" has named a time just as clearly as one who said the words — the words came from the voice a moment earlier. when_agreed covers that case: a time the voice proposed aloud and the caller accepted is a time the caller gave.
Without the exception the rule would have punished the most natural shape a booking takes, which is the voice offering a slot and the person accepting it. A rule that fires on correct behaviour is worse than no rule, because the fix people reach for is to switch it off.
Read-backs, and when one is asked#
A read-back is asked in exactly one situation: the caller neither named a time nor agreed to one, and a commitment is nonetheless being made. The voice says the time back and waits to be corrected. It is not asked on every booking — a caller who has just said "tomorrow morning" and is then asked "so, tomorrow morning?" is being made to do the system's work.
Values get their own read-back, on a different trigger. A phone number, an email address or a code heard in the caller's own words produces an entity_heard note naming what was heard, and the following two lines are watched for confirmation, recorded as entity_readback {confirmed}. Digits are where speech recognition fails most expensively, and the read-back is what turns a misheard digit into a corrected one instead of a wrong number on a contact record.
The finding for a promise with nothing behind it#
promised_unbooked is what the quality review reports when the call contains a commitment and the workspace contains no follow-up for it. It is the check on the check: the mismatch rule stops a false booking, and this finding stops a refused booking from quietly becoming a forgotten one.
- It appears on the call review alongside the deterministic findings, with the promise it refers to.
- It is a signal about the escalation and closing wording on the profile as much as about the call — a voice that promises call-backs it cannot date is being asked to promise too casually.
- It does not create a follow-up by itself. Somebody decides what the commitment actually was, which is the correct place for that decision.
What the language work did and did not touch#
Reconciled on 2026-09-10 against f5ea518. That commit reworked language selection and regional style in backend/app/voice_region.plan; it did not touch the time-word matcher or the read-back. The matcher still recognises English, Telugu and Hindi time words — in their own scripts, romanised, and in the hybrid spellings a model produces when it writes an English word in Devanagari — and the mismatch refusal is unchanged.
The one thing to carry across from that work is that a caller can arrive at a language part-way through a call: where their city or state made one likely, two or three words in it move every later reply into it. The refusal does not care which language the promise was made in, only that a time word in the caller's own latest time-bearing line matches the one being recorded.
Questions#
The voice refused to book a time the caller clearly wanted. What now?
It asks. A refusal is not the end of the exchange — the voice reads a time back and the caller confirms it, which converts the guess into an agreement the rule accepts. If the call ended without that happening, the promised_unbooked finding is on the review.
Does this apply to promises other than call-backs?
The mismatch rule is about the when of a commitment, so it applies to anything dated — a call-back, a quotation to send, a document to review. What varies is the channel the follow-up lands on, not whether the time has to be real.
Why check the caller's words rather than trusting the model's own summary?
Because the summary is produced by the same model that produced the promise. Checking a claim against the source it is supposed to come from is the only version of this that catches an invention; asking the model whether it invented something does not.