Preventing a voice agent from promising a call-back it cannot keep
A model will say *someone will ring you at four* whether or not anything was recorded. Connect refuses the record rather than trusting the sentence: record_event rejects a time that shares no time word with the caller's most recent time-bearing line, unless the caller agreed to a time the voice said aloud. The tool answers NOT recorded, so the model cannot report a booking it does not hold.
The failure this rule was written against#
One reviewed call looped three times: the voice offered a call-back, the caller accepted, the tool recorded something that did not match, the voice asked again. Underneath was a simpler problem than a loop. A model asked to book a time will produce a plausible time, and a tool that accepts whatever it is handed turns that plausibility into a row. The customer then waits for a call at a time nobody agreed.
A promise a business does not keep is more expensive than no promise. The design question is therefore not *how do we get the model to be careful* but *what does the tool refuse*.
The time must come from the caller's own words#
record_event compares the when the model supplies against the caller's latest time-bearing line. If the two share no time word, conversation.when_mismatch refuses the record. The comparison runs in English, Telugu and Hindi.
- The caller named a time — the words overlap, the follow-up is recorded.
- The caller agreed to a time the voice said aloud (
when_agreed) — recorded, because the words are on the wire even though the caller did not originate them. - Neither — refused, and the tool returns *NOT recorded* rather than a success the model can paraphrase into a promise.
- Neither, and the call is still live — a read-back is asked for, so the caller hears the time and can correct it. A read-back is only asked when it is needed, not on every booking.
Turning agreed words into a due time#
voice_engine.due_at resolves the promised phrase in the line's own timezone. *In 10 minutes*, *tomorrow 11am*, *Friday 3pm* and *today evening* all resolve; anything that resolves to nothing usable becomes 11:00 tomorrow rather than now or never. follow_up_channel then decides who owns it: a call-back goes on phone, and work only a person can do — *send the quotation*, *review the recording* — goes on the task channel, which no drain ever sends. *No follow-up needed* schedules nothing at all.
Every follow-up carries a reason, because a dated commitment with no reason is not useful to whoever inherits it. A second follow-up with the same *when* for the same person is treated as a duplicate rather than a second promise.
What the queue does with it#
| Condition | What happens |
|---|---|
| Due, line ready | runner.chase_phone places it — two per tick, through the same gates as any outbound call |
| Held by autonomy | Recorded as *awaiting approval*, and it appears in Needs You |
| Gate refuses | Recorded as refused: with the reason — budget, consent, a block, a suppression |
| Line not ready | Retried in an hour rather than burned |
| More than 24 hours late | Closed as missed: and never rung — a very late call-back is worse than none |
Measuring what is left, and what the rule cannot do#
The rule prevents the false record; it does not stop the sentence being spoken. promised_unbooked is the review finding for exactly that residue — a promise on the wire with nothing behind it — so the gap between what was said and what exists is counted rather than assumed to be zero.
- The overlap test is lexical. A caller who agrees in a way that shares no time word is refused, and the read-back is what recovers that case.
- Three languages are covered. Another language on the line falls back to asking rather than to guessing.
- A call with fewer than two caller lines and fewer than five caller words is never sent to the summariser at all: ten one-turn rows once carried an invented afternoon at a sales desk.
- No figure exists here for how often a promise is spoken and refused. That rate is UNKNOWN and would be worth measuring per line.
Questions#
Why not simply prompt the model to only book times the caller said?
That instruction was already present. A prompt shapes the average case; it does not make an error impossible, and the error here writes a row that a person later relies on. Putting the check in the tool means the worst case is a missing follow-up rather than a fictional one.
What happens if the caller genuinely named a time and the check refuses it?
The voice asks for a read-back, which is the same thing a person does when a line is unclear. The cost of the false refusal is one extra sentence; the cost of the false acceptance is a customer waiting for a call that was never scheduled.
Does this apply to follow-ups created outside a call?
The time-word rule is specific to what a live voice claims, because that is where the words and the record can diverge without anybody seeing it. A follow-up created from the follow-ups screen or by the Assistant is a deliberate act with a visible form, and carries the same reason and duplicate rules.