# Memory from a phone call

When a call ends, `voice.end_call` produces four things in one place: a summary, a lead where there is one, memory, and any follow-up that was promised. What survives is what was said clearly enough to be worth keeping — a thin call is never summarised at all, and a name that might be a form of address is refused rather than stored.

- **Status:** Available
- **Audience:** both
- **Channels:** phone
- **In the app:** #/calls, #/relationships
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/memory/memory-from-call/

## What runs at the end

Both engines end the same way. On the realtime path the worker posts to `/engine/end`, which runs the same `voice.end_call` the turn-based carrier path uses, so a workspace that switches engines does not get a different record. Model usage is booked into `costs` at the same moment.

That ending used to wait on the summary and the review — two model calls — inside one twenty-second request, and logged "end not recorded" for an ending the engine had in fact recorded. Now `finish(defer=True)` closes the row, books the cost, marks the digest as pending and answers; a background task runs the digest once. The practical consequence for you is that the record appears immediately and the summary and any memory from it land a moment later. A blank summary thirty seconds after hang-up is normal; a blank one an hour later is not.

## Two filters before anything is believed

| Condition | What happens | Why |
|---|---|---|
| Fewer than two spoken lines and fewer than five words from the other party | Never sent to the summariser | Ten one-turn rows once carried an invented afternoon at the sales desk |
| `no_answer` — it rang and nobody picked up | No lead, no facts | Nothing was said, so there is nothing to learn |
| `not_reached` — it never rang at all | No lead, no facts | The dial, the dispatch or the worker failed; this is an operational fault, not an outcome |
| A greeting that could not be spoken | Recorded as a silent failure, outcome `not_reached` | Five inbound calls once sat in silence and were logged as somebody hanging up before speaking |

A conversation that never happened has its own word in the code, `NOT_A_CONVERSATION`, and the distinction between *nobody answered* and *it never rang* is kept rather than flattened. That matters for memory because an agent that writes "unreachable, not interested" from a line that never rang has manufactured a fact about a person out of its own outage.

## Names, and why one is refused

Speech makes names fragile. A transcript turns a form of address into a proper noun, a summariser writes "the caller" where a name should be, and once either is stored it is read back aloud on the next contact. `identity.PROTECTED_MARKERS`, with `name_rejected`, `clean_name` and `scrub_names`, sits on every door a name passes through — summariser output, lead capture, person upsert and `connect_memory.remember` — and every door it is read back through.

- A form of address is never stored as a name.
- `fact_rejected` refuses a name-keyed fact whose subject is a suffix, a pronoun or "the caller".
- A person created by lead capture carries `phone_key` from the start, so the number is linked rather than orphaned.
- A number that matches two person records resolves to the oldest, so a repeat contact does not quietly build a second, emptier history.

The visible behaviour is that Connect would rather hold no name than a wrong one, and will ask again next time. That reads as forgetfulness for about a second, until you consider the alternative — a business confidently addressing somebody by a word its transcriber invented.

## Values that were spoken aloud

A phone number, an email address or a reference code said out loud is the highest-value and least reliable thing on the line. The conversation layer notes what was heard as an `entity_heard` event naming the value, then checks the next two lines for a read-back, recording `entity_readback {confirmed}` when the other party agrees with it. A value that was never confirmed is exactly the sort of thing you should not let into a durable record without looking at it.

Promises get the same treatment. `record_event` refuses a follow-up time that shares no time word with the other party's latest time-bearing line, unless they just agreed to a time the voice said aloud. The tool answers "**NOT recorded**" so the model cannot claim a booking it did not make, and `promised_unbooked` is the finding for a promise with nothing behind it. Follow-up integrity is documented under [follow-ups](/docs/follow-ups/); what matters here is that a fabricated appointment never becomes a remembered one.

## Questions

### Why is there no summary for a very short call?

Because it was below the floor: fewer than two spoken lines and fewer than five words from the other party is not enough for a summariser to work from, and asking it anyway produced invented detail. The record still exists with its times, cost and outcome — there is simply no narrative on top of it.

### A fact from a call is wrong. What do I do?

Correct or forget the row in the memory viewer, at the contact tier where it was written. That changes the next piece of work on every channel, not just the phone. If the fact was a name, check the person record too — a wrong name is usually a wrong identity resolution rather than a wrong memory.

### Does the transcript itself become memory?

No. The transcript and the summary are part of the record and stay readable from the conversation; memory is the much smaller set of durable claims written at the end. Keeping them separate is what stops a person's tier growing by a page every time they ring.

## Related

- [Person memory](https://connectbyjbrh.com/docs/memory/person-memory/)
- [How a memory is created](https://connectbyjbrh.com/docs/memory/creating-memory/)
- [A fact learned on a call, end to end](https://connectbyjbrh.com/docs/workflows/memory-from-call-workflow/)
- [Where a memory came from](https://connectbyjbrh.com/docs/memory/provenance/)
- [Phone and voice in Connect](https://connectbyjbrh.com/docs/phone/)
- [Preventing a voice agent from promising a call-back it cannot keep](https://connectbyjbrh.com/research/callback-promises/)

## What this page is based on

- docs-source/sources/PHONE.md §3 — /engine/end and voice.end_call
- docs-source/sources/PHONE.md §9 — NOT_A_CONVERSATION, silent failure, finish(defer=True)
- docs-source/sources/PHONE.md §10 — name guards, entity read-backs, when_mismatch
- docs-source/sources/PHONE.md §7 — entity_heard and the conversation layer
