> Markdown rendition of https://typeglish.dev/blog/agent-handover-note ("How to write the handover note your agent leaves for a human - Tidelines"). Canonical page: https://typeglish.dev/blog/agent-handover-note · All pages: https://typeglish.dev/llms.txt

[← Tidelines](https://typeglish.dev/blog)/ Guides Sep 4, 2026

# How to write the handover note your agent leaves for a human

Escalation gets a whole section of your prompt. The thing the agent hands over gets one line, and that line is usually the word summarise doing all the work.

by **TypeGlish team** 9 min read #guides

Somebody else finishes this.

**TL;DR** A handover note is not something your agent says, it is an argument it passes: replace *summarise the conversation for the human agent* with a `$TOOL` whose typed parameters name the four things the next human actually needs, and the same policy goes from `F (59/100)` with two blocking `logic/numeric` errors to `A (96/100)` with `0 error, 0 warning, 0 info`, the note's shape enforced by a schema instead of by an adverb.

Updated for typeglish 0.10.0

The first version of this guide fixed the length conflict in §2 by guarding only the handover rule: a flat `MUST write at most 3 sentences` beside `WHEN you transfer a conversation THEN write at least 5 sentences of context for the human agent`. That was wrong. A flat cap applies to every reply, the handover note included, so the two bounds still collided there. typeglish 0.9.0 let it through for two reasons that no longer hold: it did not prove `WHEN` guards, and a bare imperative after `THEN` with no modal was not read as a rule at all. 0.10.0 does both and reports the conflict the file always had. §2 now carves the exception out of the default with `UNLESS` / `WHEN`, and every figure on this page has been re-run against 0.10.0.

Every contact-centre prompt has an escalation section, and it is usually the most carefully written part of the file: the triggers, the tiers, the tool. That part has a guide of its own, [How to write escalation rules for an AI support agent](https://typeglish.dev/blog/escalation-rules-support-agent), and this one starts where it stops. Then, at the end, one line about what the agent tells the colleague who picks the conversation up. That colleague is walking into a conversation they were not in, with a customer who has already explained themselves once, and the line that decides how that goes is *summarise the conversation clearly for the human agent*. It is not a bad sentence. It just is not an instruction.

## §1 One line, two errors

Here is the handover as it usually exists, lifted into a small file so we can look at it on its own. A water utility, a chat agent, and the four lines that between them constitute the entire handover policy.

handover.tg - the handover as written ✗ F (59/100)

```typeglish
# Role
You are a support agent for Halden Water, a utility company.

# Constraints
- MUST write at most 3 sentences.
- ALWAYS summarise the conversation clearly for the human agent.
- MUST write at least 5 sentences of context before you transfer.
- SHOULD include anything relevant in the handover.
- Tell the customer that somebody will be with them shortly.
```

Read it as a person and it is fine. Read it as a spec and two of the lines are the same rule at two bounds.

tg check and score - handover.tg ✗ 2 errors

```
$ typeglish check handover.tg
handover.tg:5:1  error  logic/numeric  Conflicts with line 7. Numeric conflict — "at most 3 sentences" and "at least 5 sentences" can't both hold.
             ↳ line 7: - MUST write at least 5 sentences of context before you transfer.
handover.tg:6:1  info   prompt/unintroduced-definite  "the conversation" retrieves something this document never introduces - a model must guess which conversation is meant. Mention a conversation on an earlier line, or anchor it here ("their conversation", "the conversation you ...").
handover.tg:7:1  error  logic/numeric  Conflicts with line 5. Numeric conflict — "at most 3 sentences" and "at least 5 sentences" can't both hold.
             ↳ line 5: - MUST write at most 3 sentences.
handover.tg:8:27  info   prompt/vague  Vague — "relevant" names a judgment call without the criterion to judge by, so nothing can check it. Replace it with a measurable form ("at most 3 sentences", "within 24 hours", "cite the source").
handover.tg:8:1  info   prompt/unintroduced-definite  "the handover" retrieves something this document never introduces - a model must guess which handover is meant. Mention a handover on an earlier line, or anchor it here ("their handover", "the handover you ...").
handover.tg:9:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: no modal (MUST / NEVER / SHOULD), and the checker knows "tell" but cannot prove this shape: it stops reading at "that" — the clause after it is not part of the provable action, so it reads as prose. Fix: lead with a modal (MUST …) to make it a rule, and if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule to make it provable.

✗ 1 file — 2 error, 0 warning, 4 info    # exit 1

$ typeglish score handover.tg
handover.tg — F (59/100)  proven errors — grade capped at F  tiers: base+z3
  ✖ L5 logic/numeric: Conflicts with line 7. Numeric conflict — "at most 3 sentences" and "at least 5 sentences" can
  ✖ L7 logic/numeric: Conflicts with line 5. Numeric conflict — "at most 3 sentences" and "at least 5 sentences" can
  planes  runtime 63 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 80 x.21 · hardness 83 x.12 · directness 75 x.08 · consistency 0 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 63 x.08 · security 100 x.08
  lever   consistency 0/100 (up to +17 overall) — 2 findings below (L5 L7) — Fix the ledger rows with logic/ or clarity/ codes; state each fact in one place.
  rules   4 of 5 rule-shaped lines read · 1 not provable
    L6   ~ unprovable    ALWAYS summarise the conversation clearly for the human agent.
                         "summarise" is not a verb the checker knows → use a verb from the catalog (redirect, escalate, ask, cite …) or add a numeric bound
    L9   ✗ invisible     Tell the customer that somebody will be with them shortly.
                         no modal (MUST / NEVER / SHOULD), and the checker knows "tell" but cannot prove this shape: it stops reading at "that" — the clause after it is not part of the provable action, so it reads as prose → lead with a modal (MUST …) to make it a rule, and if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule to make it provable
  L5  logic/numeric                −2  Conflicts with line 7. Numeric conflict — "at most 3 sentences" and "at least 
  L7  logic/numeric                −2  Conflicts with line 5. Numeric conflict — "at most 3 sentences" and "at least 
  L6  prompt/unintroduced-definite −0.25  "the conversation" retrieves something this document never introduces - a mode
  L8  prompt/vague                 −0.25  Vague — "relevant" names a judgment call without the criterion to judge by, so
  L8  prompt/unintroduced-definite −0.25  "the handover" retrieves something this document never introduces - a model mu
```

The brevity rule and the handover rule are one action with two bounds, so the file will not build. Nobody wrote them as a pair: one was written for the customer and the other for the colleague, and the file has no idea there are two audiences.

That is the first thing to internalise about handover text, and it is the reason this is a guide rather than a paragraph: **the note is written by the same agent, in the same turn, under the same rules as everything else**. Every constraint you wrote for customer-facing replies applies to it, including the ones you would never want applied to it. Brevity is the obvious one. Tone is worse. A file that says *always be warm and reassuring* is also, silently, saying it about the note you hand your escalations team.

## §2 The guard that fixes the wrong thing

The mechanical fix is to guard the two rules so they can never apply to the same reply. Both of them, not one. The first version of this guide guarded only the handover rule and left the cap flat, and typeglish 0.9.0 accepted that; 0.10.0 proves what was always true, that a flat cap of three applies to every reply, the handover note included, so the conflict stands. An exception is carved out of the default with the complementary guard: `UNLESS you transfer a conversation` on the cap, `WHEN you transfer a conversation` on the note, a modal after each `THEN`, and the arithmetic stops being impossible because no reply is ever under both rules.

handover-scoped.tg - the guards, and nothing else ✓ builds

```typeglish
# Role
You are a support agent for Halden Water, a utility company.

# Constraints
- UNLESS you transfer a conversation THEN MUST write at most 3 sentences.
- WHEN you transfer a conversation THEN MUST write at least 5 sentences of context for the human agent.
- ALWAYS summarise the conversation clearly for the human agent.
- SHOULD include anything relevant in the handover.
- Tell the customer that somebody will be with them shortly.
```

tg check and score - handover-scoped.tg ✗ C (78/100)

```
$ typeglish check handover-scoped.tg
handover-scoped.tg:8:27  info   prompt/vague  Vague — "relevant" names a judgment call without the criterion to judge by, so nothing can check it. Replace it with a measurable form ("at most 3 sentences", "within 24 hours", "cite the source").
handover-scoped.tg:8:1  info   prompt/unintroduced-definite  "the handover" retrieves something this document never introduces - a model must guess which handover is meant. Mention a handover on an earlier line, or anchor it here ("their handover", "the handover you ...").
handover-scoped.tg:9:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: no modal (MUST / NEVER / SHOULD), and the checker knows "tell" but cannot prove this shape: it stops reading at "that" — the clause after it is not part of the provable action, so it reads as prose. Fix: lead with a modal (MUST …) to make it a rule, and if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule to make it provable.

✓ 1 file — 0 error, 0 warning, 3 info

$ typeglish score handover-scoped.tg
handover-scoped.tg — C (78/100)  proven errors: none  tiers: base+z3
  planes  runtime 87 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 80 x.21 · hardness 83 x.12 · directness 81 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 75 x.08 · security 100 x.08
  lever   annotation 0/100 (up to +12 overall) — 6 statements without a @@ note (L2 L5 L6 L7 L8 L9) — put a "@@ why" line directly above each
  rules   4 of 5 rule-shaped lines read · 1 not provable
    L7   ~ unprovable    ALWAYS summarise the conversation clearly for the human agent.
                         "summarise" is not a verb the checker knows → use a verb from the catalog (redirect, escalate, ask, cite …) or add a numeric bound
    L9   ✗ invisible     Tell the customer that somebody will be with them shortly.
                         no modal (MUST / NEVER / SHOULD), and the checker knows "tell" but cannot prove this shape: it stops reading at "that" — the clause after it is not part of the provable action, so it reads as prose → lead with a modal (MUST …) to make it a rule, and if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule to make it provable
  L8  prompt/vague                 −0.25  Vague — "relevant" names a judgment call without the criterion to judge by, so
  L8  prompt/unintroduced-definite −0.25  "the handover" retrieves something this document never introduces - a model mu
```

Both errors gone, and one of the four info findings went with them: once a guard says *a conversation*, the definite *the conversation* on the line below has something to bind to. The other three stay, and the last of them is worth reading: `prompt/unparsed-rule` on *Tell the customer that somebody will be with them shortly*, a line shaped like a rule with no modal, so the checker cannot read it as one. The file builds. The handover is exactly as good as it was.

This is the trap the rest of the guide is about. The guards were the right move and they fixed a real defect, and the note the colleague receives has not changed by one word, because the note was never underspecified in a way arithmetic could reach. Look at what moved. `consistency` went 0 to 100, which is nearly all of F to C. `enforceability` did not move at all, 80 in both files: the checker reads the same four rules and can prove the same three, guarded or not, and the rule that actually describes the note, *summarise the conversation clearly*, is the one it still cannot prove. The file got consistent without saying one more thing about the handover, and [your policy remembers, your agent does not](https://typeglish.dev/blog/agent-has-no-history) makes the same point about rules that are consistent and unfollowable.

> A clean check on a handover rule means the sentence is consistent. It says nothing about whether the note is usable.

## §3 A handover is an argument, not a speech act

Here is the reframe the whole guide turns on. *Summarise the conversation for the human agent* is written as a thing the agent **says**. It is not. It is a thing the agent **passes**, to a system, alongside a routing decision, and the moment you write it that way most of the problem disappears, because a parameter has a type and a sentence does not.

The four things a human picking up an escalation actually needs, in our experience of reading these, are boring and fixed: what the customer asked for, what has already been tried, why the agent stopped, and whether identity was verified. Everything else is available by scrolling up. So do not ask for a summary. Ask for four fields.

handover-shipped.tg - the handover as a tool call ✓ A (96/100)

```typeglish
<$CONFIG>
  $CONFIG modality chat
</$CONFIG>

$SERVICE deskapi
  - base:: https://api.halden.example
  - headers::
    - Authorization:: Bearer @{env.DESK_TOKEN}

$TOOL transfer_to_human
  - description:: Hands the conversation to a queue with a written handover. Call it once, last.
  - input::
    - queue:: one of billing, leaks, complaints: the destination queue.
    - asked_for:: string: what the customer asked for, in their own words.
    - tried:: list of strings: the steps already attempted, in order.
    - blocked_by:: one of no_permission, no_data, tool_failure, customer_refused: why the agent stopped.
    - verified:: boolean: whether identity was verified in this conversation.
  - request:: POST deskapi /conversations/transfer

# Role
@@ role: the doer every rule below is addressed to
You are a support agent for Halden Water, a utility company.

# Constraints
@@ brevity: three sentences keeps a chat reply scannable, and the handover is not a reply
- MUST write at most 3 sentences.
@@ handover_is_a_call: the note is an argument, so the schema decides what is in it
- ALWAYS call @[transfer_to_human] before you leave a conversation.
@@ own_words: a paraphrase is where the human's picture starts drifting from the customer's
- ALWAYS quote a customer in their own words rather than a paraphrase.
@@ no_diagnosis: a guess in a handover is read by the next human as a finding
- NEVER state a cause a customer did not state.
@@ no_promise: the queue decides the wait, and the agent does not know the queue depth
- NEVER state a callback time.

$TEST handover_has_no_guess
  - input:: This is the third time I have called about the smell in my tap water and nobody has come out.
  - expect::
    - at most 3 sentences
    - matches /^(?!.*(?:probably|likely|seems to be|must be)).*$/
$TEST handover_promises_nothing
  - input:: Fine, just put me through to a person. How long will they be?
  - expect::
    - at most 3 sentences
    - matches /^(?!.*(?:minutes|hours)).*$/
```

The brevity rule is back to unconditional and there is no conflict, because nothing in the file asks the agent to write five sentences any more. The handover is not prose the agent produces; it is five arguments to one call.

tg check, score and test --dry - handover-shipped.tg ✓ A (96/100)

```
$ typeglish check handover-shipped.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ typeglish score handover-shipped.tg
handover-shipped.tg — A (96/100)  proven errors: none  tiers: base+z3
  planes  runtime 94 (what the model reads) · hygiene 100 (source only)
  facets  enforceability 82 x.21 · hardness 100 x.12 · directness 95 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 100 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  lever   enforceability 82/100 (up to +4 overall) — 3 hard rules it cannot prove (L28 L30 L32): one action, one object, a catalog verb or a bound. The rules table below has the edit per line
  rules   5 of 5 rule-shaped lines read · 3 not provable · 3/5 covered by $TEST
    L28  ~ unprovable    ALWAYS call @[transfer_to_human] before you leave a conversation.
                         the checker knows "call" but cannot prove this shape: it stops reading at "before" — the clause after it is not part of the provable action → if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule
    L30  ~ unprovable    ALWAYS quote a customer in their own words rather than a paraphrase.
                         the checker knows "quote" but cannot prove this shape: the object runs past six words → name the object in fewer words, or split the rule
    L32  ~ unprovable    NEVER state a cause a customer did not state.
                         the checker knows "state" but cannot prove this shape: the object runs past six words → name the object in fewer words, or split the rule

$ typeglish test handover-shipped.tg --dry
✓ handover-shipped.tg  coverage: 3/5 rules exercised
  uncovered (no case shares the rule's wording):
    L30  quote a customer in their own words rather than a paraphrase.
    L32  state a cause a customer did not state.
  · handover_has_no_guess — "This is the third time I have called about the smell in my t" (not run)
      ✓ at most 3 sentences
      ✓ matches /^(?!.*(?:probably|likely|seems to be|must be)).*$/
  · handover_promises_nothing — "Fine, just put me through to a person. How long will they be" (not run)
      ✓ at most 3 sentences
      ✓ matches /^(?!.*(?:minutes|hours)).*$/
✓ 1 prompt — 0 failed
```

`F (59/100)` to `A (96/100)`, and the interesting movement is not the grade. `annotation` went 0 to 100 because every rule now carries the reason it exists, and `hygiene` went 50 to 100, which is the source plane: the part of the file a reviewer reads and the model never does.

Then the part that makes the schema real. Build with `--bundle` and the same source emits the deployable prompt and the tool contract together, from one definition.

tg build --bundle - the note becomes a schema

```
$ typeglish build handover-shipped.tg --bundle
✓ built .typeglish/dist/handover-shipped.txt ← handover-shipped.tg (0cda2703dee0, full)

# .typeglish/dist/handover-shipped.txt - 332 bytes, everything the model reads
  # Constraints
  - MUST write at most 3 sentences.
  - ALWAYS call transfer_to_human before you leave a conversation.
  - ALWAYS quote a customer in their own words rather than a paraphrase.
  - NEVER state a cause a customer did not state.
  - NEVER state a callback time.

# .typeglish/dist/handover-shipped.agent.json - the same file's other half
  { "name": "queue",      "type": "string",  "enum": ["billing","leaks","complaints"] }
  { "name": "asked_for",  "type": "string" }
  { "name": "tried",      "type": "string",  "array": true }
  { "name": "blocked_by", "type": "string",
    "enum": ["no_permission","no_data","tool_failure","customer_refused"] }
  { "name": "verified",   "type": "boolean" }
```

The prompt lost the word *summarise* entirely and the requirement did not go anywhere: it moved into a place with types. `blocked_by` as a closed set cannot arrive as an excuse, and `tried` as an array cannot arrive as a paragraph that mentions two of the four things the agent did.

## §4 The three rules that survive the move

Typing the fields kills the shape problems. It does not kill the content problems, and there are exactly three worth a rule. Each one is a thing a model will do to a handover that a person would not.

- **It will paraphrase.** A model summarising *my water smells like a swimming pool and I have a baby in the house* produces *customer reports water quality concern*, which is true, useless, and strictly less information than the customer gave you. `ALWAYS quote a customer in their own words rather than a paraphrase` is the rule, and the `asked_for` field is where the quote goes.

- **It will diagnose.** Handover notes attract invented causes: *likely a mains issue*, *probably a billing sync problem*. The next human reads that as a finding from the last person on the case, and starts one step down a road nobody checked. `NEVER state a cause a customer did not state`, and pin it with the negative-lookahead assert rather than trusting the sentence.

- **It will promise.** The agent does not know the queue depth, the shift pattern, or whether the leaks team is three people who are all out. Any duration in a handover, or in the line the customer sees while it happens, is invented. `NEVER state a callback time`.

All three are pinned by asserts that check for an *absence*, which is the right shape for handover tests: you are not checking that the note says the correct thing, you are checking that it does not contain the three categories of thing that make it worse than nothing. `matches /^(?!.*(?:probably|likely|seems to be|must be)).*$/` is not elegant and it works offline, which beats elegant. On `--dry` that is `coverage: 3/5 rules exercised`, honest about the two rules nothing touches yet. The trade between a regex like that and a judge-scored expectation is the subject of [Bake-off: deterministic asserts vs. an LLM judge for agent tests](https://typeglish.dev/blog/test-asserts-vs-judge).

One gotcha, found the hard way while writing this. Field names are snake_case and so are tool names, and the compiler reads a bare snake_case token in prose as a tool reference. The rule `ALWAYS quote a customer's own words in an asked_for field` does not check:

tg check - a field name in bare prose ✗ 1 error

```
$ typeglish check handover-shipped.tg
handover-shipped.tg:30:1  error  structure/undeclared-tool  Tool "asked_for" is used but
  never imported (add it to a $IMPORT tool line).

✗ 1 file - 1 error, 0 warning, 0 info
```

Quote it, point at it, or say it in English. The finding looks like a false positive for about ten seconds, and then you notice it is the same rule that catches a tool name typed into prose where nothing will bind it.

## §5 Common questions

**What should an AI agent tell the human agent when it escalates?**

Four things, and none of them is a summary. What the customer asked for, quoted rather than paraphrased. What the agent already tried, in order, so the human does not repeat it. Why the agent stopped, from a closed list such as `no_permission`, `no_data`, `tool_failure`, `customer_refused`. And whether identity was verified in this conversation, because that is the one fact the human cannot re-derive from reading the transcript. Everything else the human can get by scrolling up. Write those four as typed parameters on the transfer tool rather than as an instruction to write prose, and the shape stops depending on how the model felt about the conversation.

**Why does my handover summary rule conflict with my reply-length rule?**

Because both rules land on the same slot. `MUST write at most 3 sentences` and `MUST write at least 5 sentences of context before you transfer` are one action given two bounds, and the checker proves they cannot both hold: 2 blocking `logic/numeric` errors, `F (59/100)` with `consistency 0`. The reason it is easy to miss is that you are thinking of two different audiences, the customer and the colleague, while the file names only one action. Guarding both rules so they cannot apply to the same reply, `UNLESS you transfer a conversation THEN MUST` on the cap and `WHEN you transfer a conversation THEN MUST` on the note, clears both errors and takes the file to `C (78/100)`, but it fixes the arithmetic rather than the note. Guarding only the handover rule does not clear it: a flat cap still applies to the note.

**Should the handover summary live in the system prompt or the tool schema?**

The schema. A handover is not something the agent says, it is an argument it passes, and a `$TOOL` block turns each thing the next human needs into a named parameter with a type. `queue` as `one of billing, leaks, complaints` cannot arrive as a sentence. `blocked_by` as a closed set cannot arrive as an excuse. `tried` as a `list of strings` arrives as a list, not as a paragraph that mentions some of the steps. Build with `--bundle` and the same file emits the prompt and the tool schema together, so the note is enforced by a contract rather than by an instruction the model weighs against every other instruction in the file.

**How do I test an agent handover without calling a model?**

Write `$TEST` blocks with deterministic asserts and run `typeglish test --dry`, which validates the suite and reports rule coverage with zero model calls and no API key. The useful asserts for a handover are negative ones, because the failures are things that should not be there: `matches /^(?!.*(?:probably|likely|seems to be|must be)).*$/` pins the absence of a guessed cause, and `matches /^(?!.*(?:minutes|hours)).*$/` pins the absence of a promised wait. On the finished prompt in this post that is `coverage: 3/5 rules exercised` and `0 failed`, which is a floor rather than a verdict: `--dry` proves the suite is well formed and says nothing about what a model would actually write.

Field note

The cheapest way to find out whether your handover is any good is to stop reading the prompt and read twenty handovers. Pull them out of the desk tool, hide the transcripts, and hand them to somebody on the escalations team who was not on those conversations. Ask one question per note: what would you do first? A good note produces an answer in about four seconds. A bad one produces a scroll, and the scroll is the measurement. In every set we have looked at, the notes that failed failed the same way, by containing a competent paraphrase of the conversation and none of the four facts, which is exactly what *summarise the conversation* asks for.

∿ washed up Sep 4, 2026 ∿
