# Making a correction actually change behaviour

A correction changes behaviour only if it lands on the field the decision actually reads. The clearest case: a block written into the body of a memory row is stored, displayed and ignored, because `directives()` reads only the tag list. The row exists, the screen shows it, somebody believes the problem is handled — and the next message goes out anyway.

- **Status:** Available
- **Audience:** both
- **In the app:** #/maya-rules, #/relationships
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/research/memory-correction-that-sticks/

## The failure is indistinguishable from success

Nothing about a correction that was never read looks wrong. The person typed it, the system accepted it, the record shows it. There is no error, no warning, and no second prompt. The only evidence is behaviour that has not changed, which is discovered days later by whoever is on the receiving end of it — usually the customer the correction was about.

That makes this a design problem rather than a user-education problem. If a field can be written to and never read, somebody will write to it, and the interface will not tell them.

## The canonical case: a directive in the wrong field

Blocking a contact on a channel is a `connect_memory` row tagged `block:<channel>` in `tags` — deliberately not a column, and deliberately not prose in the body. `directives()` reads only the tag list, so a sentence in the body saying *do not contact this person on WhatsApp* is a note about a block rather than a block.

The upside of the same design is why it is worth keeping: because the block is a memory row against the contact rather than a flag on a thread, it holds across channels and across future conversations, including ones that start months later on a channel nobody was thinking about at the time.

## The rule, generalised

| The correction | Where it must go | If it lands elsewhere |
|---|---|---|
| Stop contacting this person here | `tags` as `block:<channel>` | Stored and displayed; never read by `directives()` |
| This conversation matters more | `threads.priority`, which the engine reads when it picks work | A re-sorted list and unchanged behaviour |
| This reply was wrong | Guidance on the thread, or a memory row | The rejection is recorded and the same reply is not immediately re-drafted — but nothing is learned |
| Our warranty is 18 months | Knowledge, so answers can be grounded in it | A handling note that no answer is grounded in |
| Speak more slowly on this line | The voice profile at the right level in the resolution order | A setting overridden by a narrower one that still says otherwise |

Rejecting a drafted reply is worth spelling out, because it feels like teaching. The rejection is recorded against the thread and the engine does not immediately produce the same reply again — but a rejection on its own is a decision about one message, not a lesson. If the reason should apply next time, it has to be said as guidance or written as a memory.

## Resolution order is the other way corrections vanish

Where several levels can set the same thing, the narrowest wins, and a correction made at the wrong level is overridden without complaint. Voice settings resolve **workspace → default profile → profile by purpose → profile by line number → the profile on the contact → the Lab's draft on a test call**. Memory and autonomy resolve narrowest-first across their four tiers. A change at workspace level that a contact-level row contradicts is a change that did nothing.

The remedy is to make the resolved result inspectable rather than to expect people to hold the order in their heads. The settings a call actually ran with are snapshotted on `call.quality`, so the review judges what ran rather than what the screen says now; the memory viewer shows which tier a direction came from.

## A correction that cannot be delivered must say so

The most instructive case is a correction that is read, accepted, and still does not arrive. On Gemini 3.1 Flash Live the SDK marks the session **immutable**, so mid-call notes and supervisor guidance cannot reach the model at all. Rather than assuming they applied, the worker records what happened: a stalled reply nudged in silence (`nudge {via: none}`), notes marked undelivered, guidance marked `guidance_undeliverable`.

That is the standard the rest of this page is arguing for. An undelivered correction recorded as undelivered is a manageable situation — the colleague steering the call knows to speak to the customer another way. An undelivered correction recorded as delivered is the failure at the top of this page, wearing a different hat.

## Checking that one took

- Look at the field the decision reads, not the screen the correction was typed into.
- Use the audit trail: it records what was decided, by what, **under which rule**, and what happened — so the rule named in the next decision is the fastest test of whether the correction is the rule now.
- Check the tier. Right field, wrong level is the most common near-miss, and it is silent.
- No adoption rate for corrections is measured here: UNKNOWN. What is checkable per case is whether the next decision cites the corrected rule.

## Questions

### Why is a block a tagged memory row rather than a column on the contact?

Because a column belongs to one table and one channel's code path, while the behaviour needs to hold everywhere and for conversations that do not exist yet. The cost is that the tag is the operative part and the body is not, which is exactly the trap this page describes — worth stating plainly wherever the row can be edited.

### If I reject the same draft three times, does Connect learn?

Not from the rejections alone. Each one is a decision about one message. Standing behaviour comes from guidance on the thread or a memory row, and the reason that is not automatic is that a rejection has no stated reason — inferring one and acting on it is how an agent acquires rules nobody agreed to.

### How do I know which tier a direction is on?

The memory viewer shows every tier and what is set at each, mounted from the mailbox row, the channel screen and the contact panel. An empty tier is shown as empty rather than hidden, which is what makes *nothing is set here* a usable answer when a correction seems to have had no effect.

## Related

- [Memory in Connect](https://connectbyjbrh.com/docs/memory/)
- [Relationship memory versus chat history](https://connectbyjbrh.com/research/relationship-memory/)
- [Structured business memory instead of a longer prompt](https://connectbyjbrh.com/research/structured-business-memory/)
- [What Connect may do](https://connectbyjbrh.com/docs/autonomy/)
- [Making a next-best-action defensible](https://connectbyjbrh.com/research/evidence-for-next-action/)

## What this page is based on

- `docs-source/sources/GENERAL.md` §3 and §6 — the block directive, `directives()` and the memory tiers
- `docs-source/sources/CHANNELS.md` §1 — triage fields the engine reads
- `docs-source/sources/PHONE.md` §6 and §7 — the resolution order, `call.quality` and undeliverable guidance
- `docs-source/sources/GENERAL.md` §5 — the audit trail and its rule attribution
