← Tidelines/Deep dives

Nothing in your prompt ends the conversation

Every agent prompt has a beginning: a Role, a greeting, a first move. Almost none of them has an ending. The closing section is four rules about how to be polite on the way out, and not one of them says when the way out is.

by TypeGlish team7 min read#deep-dives
Four closing rules. No ending.

TL;DR An ending is a rule with an observable trigger, and most closing sections do not contain one: MUST ask whether there is anything else you can help with is 0 error, 0 warning, 0 info at B (81/100), the highest-scoring closing line we checked and the one that guarantees another turn. Enumerate the endings as a closed set, give each member a trigger you could verify from a transcript, and the section goes to A (94/100) with the one real conflict gone.

Harbour Bank runs a chat agent over its help centre. The prompt is careful about the beginning: who the agent is, how it greets, what it may answer, when it escalates. Then there is a section at the bottom called Closing the conversation, written after the first month of transcripts showed the agent trailing off, and it contains four rules. Three of them are about tone. The fourth contradicts one of the others. None of them is an ending.

§1The section that closes nothing

closing.tg - the section as it ships✗ C (72/100)
# Role
You are a chat support agent for Harbour Bank, a retail bank.

# Answering
- MUST answer a question about a current account.
- MUST escalate a complaint to a human agent.

# Closing the conversation
- MUST ask whether there is anything else you can help with.
- SHOULD close the conversation when it feels finished.
- MUST close the conversation after 3 turns without a reply.
- MUST NOT close the conversation until the customer confirms that the issue is resolved.
Read it as a machine would. Line 9 always fires. Line 10 fires on a feeling. Line 11 fires on a count the agent is not keeping. Line 12 forbids the thing line 11 requires. The conversation this describes has no last turn in it.
tg check, --strict and score - closing.tg✗ consistency 57
$ typeglish check closing.tg
closing.tg:11:1  warn  logic/only-gate  Gated here, mandated there - the gate allows
  this action ONLY under "the customer confirms that the issue is resolved.", but this
  line orders it unconditionally, dropping the gate. Add the gate's condition here, or
  loosen the gate.
closing.tg:12:1  info  prompt/unintroduced-definite  "the issue" retrieves something this
  document never introduces - a model must guess which issue is meant.
closing.tg:12:1  info  prompt/agentless-passive  The doer is deleted (agentless passive) -
  who performs this? Name the actor or use the imperative.

 1 file - 0 error, 1 warning, 2 info

$ typeglish check closing.tg --strict
 1 file - 1 error, 0 warning, 2 info

$ typeglish score closing.tg
closing.tg - C (72/100)  proven errors: none  tiers: base+z3+spell
  planes  runtime 80 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 82 x.21 · hardness 92 x.12 · directness 83 x.08
          consistency 57 x.17 · structure 100 x.12 · annotation 0 x.12
          style 79 x.08 · security 100 x.08
  lever   annotation 0/100 (up to +12 overall)
One finding, and it is the right one: logic/only-gate is the timeout walking straight through the confirmation gate. It is a warning by default and a blocking error under --strict, so a team with a strict gate in CI cannot merge this section. A team without one ships it and never learns.

§2Seven ways to say it is over

One policy, seven spellings, one rule per file. This is how the same intention comes out of seven different sprint tickets, and what the checker makes of each.

tg check and score - one closing rule per file✗ the loop scores highest
  the rule                                                    check               score
  MUST end the conversation politely.                         0 error, 1 info      C (74/100)
  SHOULD close the conversation when it feels finished.       0 error, 1 info      C (74/100)
  MUST wrap up when the customer is satisfied.                0 error, 1 info      C (71/100)
  MUST close a conversation with a summary of what            0 error, 1 info      C (71/100)
    was agreed.
  MUST close the conversation after 3 turns without           0 error, 1 info      C (74/100)
    a reply.
  MUST NOT close the conversation until the customer          0 error, 1 info      C (74/100)
    confirms.
  MUST ask whether there is anything else you can             0 error, 0 info     B (81/100)
    help with.

  # and the only pair that draws a finding:
  MUST close the conversation after 3 turns without a reply.  1 warning           C (65/100)
  MUST NOT close the conversation until the customer confirms.
The six info lines are prompt/unintroduced-definite on the conversation and prompt/agentless-passive on what was agreed, which is to say: grammar. The one line with no findings at all, scoring seven points above everything else at B (81/100), is the line that asks a question. A rule that ends with a question mark is a rule that requires a reply.
Politeness at the exit is not an exit. Somewhere a rule has to say: this was the last turn.

§3What the checker can and cannot see

Be precise about the boundary here, because it matters more than the finding. TypeGlish proves conflicts between rules. It does not simulate a conversation, so it has no way to notice that a section describes a process with no terminating state. MUST ask whether there is anything else you can help with, alone in a file, is 0 error, 0 warning, 0 info, and it will stay that way no matter how long you stare at it.

What it does see is the pair. The gate and the mandate are two claims about one action, and when both are in scope the prover can show one of them is being dropped:

tg --explain logic/only-gate✓ the one provable defect
$ typeglish --explain logic/only-gate
logic/only-gate (warn)
  An action is gated (`X only when C` / `never X until C` - C is necessary for X) yet
  mandated elsewhere without the condition: the gate is provably dropped where the two
  co-serve (THE PROCESS SCOPE). Add the condition to the mandate, or loosen the gate.
Add the condition to the mandate, or loosen the gate. Applied to a closing section that is the whole design question: either the timeout is an exception to the confirmation rule, in which case say so, or confirmation was never required, in which case stop writing that it is.

So the division of labour is: the loop is yours to notice, the inconsistency is the file's to report. That is the same split as everywhere else in this language, and the reason the reviewing habit matters as much as the tool. It is the maintenance property described in your prompt argues with itself, arriving one closing rule at a time.

§4Enumerate the endings

The fix is not a better adjective. It is to stop describing the feeling of an ending and start listing them. A conversation with this agent ends in exactly one of three states, and that sentence is a thing you can write down:

closing.tg - the closed set, and what it proves✗ logic/redeclaration
# Role
You are a chat support agent for Harbour Bank, a retail bank.
The %closing state% IS one of resolved, handed off, abandoned.
The %closing state% IS resolved.
The %closing state% IS abandoned.
The last two lines are there to show the set is load-bearing, not decorative.
tg check - two endings at once✗ 2 error, build refused
$ typeglish check closing.tg
closing.tg:4:1  error  logic/redeclaration  Conflicts with line 5. Mutually-exclusive
  values - this line asserts "resolved" while line 5 asserts "abandoned", and both
  belong to one closed "one of" set (an object holds exactly ONE member). Keep one
  assertion, or widen the declared set.
closing.tg:5:1  error  logic/redeclaration  Conflicts with line 4. Mutually-exclusive
  values - this line asserts "abandoned" while line 4 asserts "resolved", and both
  belong to one closed "one of" set (an object holds exactly ONE member).

 1 file - 2 error, 0 warning, 2 info
A closed one of set is a real constraint, not a comment. Once the three endings are declared, a fourth one added next quarter by somebody who did not read the file is a compile error rather than a fourth opinion. The quantifier machinery that makes that work is the same one in gold and silver conflict, priority and standard do not.

Then one rule per member, and the test each trigger has to pass: could a person reading the transcript say whether it fired? The customer answers no, yes. You escalate a complaint, yes. Ten minutes pass without a reply, yes. It feels finished, no, and that is the whole reason it was never an ending.

§5What ships

closing.tg - three endings, three triggers✓ A (94/100)
<$CONFIG>
  $IMPORT tool close_conversation
</$CONFIG>

# Role
@@ role: the desk this file speaks for
You are a chat support agent for Harbour Bank, a retail bank.
@@ states: the three ways this conversation is allowed to end - a fourth one is a compile error
The %closing state% IS one of resolved, handed off, abandoned.

# Closing the conversation
@@ offer: the offer of more help fires once, so it cannot be the thing that keeps the turn alive
- MUST ask once whether there is anything else you can help with.
@@ resolved: the customer's no is the trigger, not a feeling about the conversation
- MUST close a conversation with @[close_conversation] when the customer answers no.
@@ handed_off: an escalation ends this conversation too - the human owns the next turn
- MUST close a conversation with @[close_conversation] when you escalate a complaint.
@@ abandoned: a measurable silence, so the timeout is a rule and not a runtime accident
- MUST close a conversation with @[close_conversation] after 10 minutes without a reply.

$TEST closes_on_no
  - input:: No, that is everything, thanks.
  - expect::
    - at most 2 sentences
tg check, score, test --dry and build - closing.tg after✓ consistency 100
$ typeglish check closing.tg
 1 file - 0 error, 0 warning, 0 info

$ typeglish score closing.tg
closing.tg - A (94/100)  proven errors: none  tiers: base+z3+spell
  planes  runtime 92 (what the model reads) · hygiene 100 (source only)
  facets  enforceability 70 x.21 · hardness 100 x.12 · directness 100 x.08
          consistency 100 x.17 · structure 100 x.12 · annotation 100 x.12
          style 100 x.08 · security 100 x.08
  lever   enforceability 70/100 (up to +6 overall)

$ typeglish test closing.tg --dry
 closing.tg  coverage: 0/4 rules exercised
  · closes_on_no - "No, that is everything, thanks." (not run)
       at most 2 sentences
 1 prompt - 0 failed

$ typeglish build closing.tg
 built .typeglish/dist/closing.txt ← closing.tg (f6e4deac6ccc, full)
Consistency moves from 57 to 100 because the gate is gone: there is no rule that forbids closing, only three rules that require it under three named conditions. coverage: 0/4 rules exercised is the remaining debt and it is honest. One test, and the assertion it makes is about length, not about whether the conversation actually ended.

Four lines in, four lines out, and the difference is entirely in what the triggers are made of. The artifact says so too:

.typeglish/dist/closing.txt - what the model reads✓ every rule has a trigger
# Role
You are a chat support agent for Harbour Bank, a retail bank. The closing state IS one
of resolved, handed off, abandoned.

# Closing the conversation
- MUST ask once whether there is anything else you can help with.
- MUST close a conversation with close_conversation when the customer answers no.
- MUST close a conversation with close_conversation when you escalate a complaint.
- MUST close a conversation with close_conversation after 10 minutes without a reply.
The annotations and the config are gone, the name literal has lost its sigils, the tool pointer is a bare tool name, and every remaining line names a condition a transcript could settle. That is the whole artifact: 4 rules and one sentence of world.

§6Common questions

Why does my AI support agent keep the conversation going?
Because the only unconditional rule in most closing sections is the one that produces another turn. MUST ask whether there is anything else you can help with is 0 error, 0 warning, 0 info at B (81/100), the highest score of any closing line we checked, and it has no terminating condition attached to it. Every other rule in the section is scoped to something the model has to judge. Add once to the offer, and give each way out its own rule with a trigger you could check from a transcript.
How do I tell an AI agent when to end a chat?
Enumerate the endings instead of describing the feeling of one. Declare the closed set with The %closing state% IS one of resolved, handed off, abandoned, then write one MUST rule per member whose trigger is observable: the customer answers no, you escalate a complaint, ten minutes pass without a reply. The set being closed is load-bearing, because asserting two members is a blocking logic/redeclaration error reading an object holds exactly ONE member.
Is a timeout rule enough to close a conversation?
Not beside a confirmation gate, and that pair is the one defect in this section a checker will actually find. MUST close the conversation after 3 turns without a reply beside MUST NOT close the conversation until the customer confirms is a logic/only-gate warning reading the gate allows this action ONLY under that condition, but this line orders it unconditionally, dropping the gate. It escalates to a blocking error under --strict. Either the timeout carries the gate's condition, or the gate was never a gate.
Does TypeGlish catch a prompt that never terminates?
No, and it is worth being precise about that. The checker proves conflicts between rules, not properties of a conversation over time, so an offer of more help with no stopping condition draws nothing at all. What it catches is the pair that contradicts: the gate dropped by the timeout is 1 warning, and the closed set of endings makes a fourth ending a blocking error. The loop is something you have to notice. The inconsistency is something the file can tell you about.
Field note

The reason this section is the last one written and the worst one written is that nobody owns the end of a conversation. Support ops owns containment, and containment is measured on the conversations that never reached a human, which is a statement about the middle. QA owns tone, and tone is easiest to grade at the greeting. Product owns the CSAT prompt, which fires after the end and therefore assumes one. So the ending arrives as an afterthought, in adjectives, written by whoever noticed the transcripts trailing off. It is worth asking, once, who on your team could answer the question what are the three ways this conversation is allowed to finish. If the answer is nobody, that is not a prompt problem yet. It becomes one the moment somebody writes the section anyway.

∿ washed up Sep 15, 2026 ∿