← Tidelines/Teardowns

Teardown: your macro library speaks in the first person

Four approved replies out of the help desk, pasted under four rules that already work. The checker finds one thing to say about them, and it is not the macro that promises a callback three days after the rule above it promised one in two hours.

by TypeGlish team9 min read#teardowns
Four replies. No rules.

TL;DR A pasted macro library is invisible to the checker because rules are indexed by their subject and a macro is written in the first person: the identical clause is 2 blocking logic/time-strength errors behind You and 0 error, 0 warning, 0 info behind I, We or the agent's own name. Meanwhile I am sorry about the wait. is a declaration that binds the name i, and the whole block reflows into one paragraph in the artifact. Keep the wording in the help desk and move the commitment it carries into a rule plus a $TEST assert.

The macro library is the last document in the contact centre to get pasted into the prompt, and the one with the best paperwork. Every entry was written by somebody in the CX team, reviewed by brand, checked by legal in the cases that needed it, and then tested against thousands of real conversations by human agents who complained when the wording was wrong. It is, by a distance, the most quality-controlled text the org owns. So when the support agent starts writing replies that sound nothing like the brand, pasting the top of the macro library under the rules is not a shortcut. It is the obvious move, and everybody in the room agrees with it.

§1The paste

Ellis answers first-notification-of-loss chat for Thornbury, a home insurance provider. Four rules, then four macros, in the order somebody actually pasted them.

thornbury.tg - the prompt, with the top of the macro library✓ exits 0
# Role
You are Ellis, a customer support agent for Thornbury, a home insurance provider.

# Constraints
- You MUST ask for a policy number before you discuss a claim.
- You MUST call a customer back within 2 hours.
- You MUST keep every reply to at most 3 sentences.
- WHEN a customer reports a flood THEN you MUST escalate the claim to a human agent.

# Macros
Use the approved wording below wherever it fits.

Thanks for getting in touch. I have logged your claim and I call a customer back within 3 days.
I am sorry about the wait. I refund the excess as a goodwill gesture whenever the delay was ours.
I will pass this straight to our claims team.
I am not able to discuss a policy until I have the policy number, so I ask for that first.
Two of the macros disagree with the rules above them. The callback macro says three days where the rule says two hours, and the excess macro hands back money the prompt never authorised.
tg check, then tg score - output✗ one finding, and not that one
$ typeglish check thornbury.tg
thornbury.tg:13:30  info   prompt/declared-later  `i` is declared on line 11, after its first use
  here: declare the identity before the rules that use it (the model reads top-down).

 1 file — 0 error, 0 warning, 1 info

$ typeglish score thornbury.tg
thornbury.tg — B (85/100)  proven errors: none  tiers: base+z3
  planes  runtime 97 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 94 x.21 · hardness 100 x.12 · directness 95 x.08 ·
          consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) ·
          style 93 x.08 · security 100 x.08
One info, about a variable nobody declared on purpose. consistency 100 is the facet that asks what the rules do to each other, and it has looked at the four macros and found nothing to compare.

Enforceability 94 is the number that flatters this file. The four rules really are well formed, so the facet that measures whether rules parse proof-grade is nearly perfect, and the four lines underneath them contribute nothing to it in either direction. This is the same shape as a pasted new-hire handbook: the grammar arrives correct and the addressee arrives wrong. The difference is that a handbook is at least written in the imperative. A macro is written by the agent, to the customer, about itself.

§2One word decides whether a line is a rule

Take the disagreement that matters and reduce it to one clause. The rule promises a callback within two hours. The macro promises one within three days. Four files, identical except for the subject of the last line.

tg check, four subjects, one clause - output✗ 2 errors, then nothing, three times
$ tail -1 s_you.tg
You call a customer back within 3 days.
$ typeglish check s_you.tg
s_you.tg:5:1  error  logic/time-strength  Conflicts with line 6. One bound per slot — "within 2
  hours" already entails "within 3 days"; the looser rule on "you call a customer · back" is
  dead weight. Keep one.
s_you.tg:8:1  error  logic/time-strength  Conflicts with line 4. One bound per slot — "within 2
  hours" already entails "within 3 days"; the looser rule on "you call a customer · back" is
  dead weight. Keep one.

 1 file — 2 error, 0 warning, 0 info

$ tail -1 s_i.tg
I call a customer back within 3 days.
$ typeglish check s_i.tg
 1 file — 0 error, 0 warning, 0 info

$ tail -1 s_we.tg
We call a customer back within 3 days.
$ typeglish check s_we.tg
 1 file — 0 error, 0 warning, 0 info

$ tail -1 s_ellis.tg
Ellis calls a customer back within 3 days.
$ typeglish check s_ellis.tg
 1 file — 0 error, 0 warning, 0 info
Same verb, same object, same number, same file. Only the first word moves, and it moves the line in and out of the proof.

Read the slot name in the error message, because the compiler prints its own index: the looser rule on "you call a customer · back". The subject is inside the key. A bound is not attached to an action, it is attached to an action performed by a party, and the party the deontic layer resolves is the addressee that the role line registered. You are Ellis registers exactly one of those. I is not it, We is not it, and neither, in this pairing, is Ellis.

A macro library is a set of rules written about a person who does not exist in the file.

That last case is worth sitting with, because it is the one that catches experienced authors out. The prompt has just declared that you are Ellis, so a line about Ellis feels like a line about you, and in the artifact the model reads it will behave like one. In the checker it does not, here, resolve onto the same slot as the rules above it. This is the same subject-indexing behaviour that lets two flat contradictions sit in one file at consistency 100 when half the rules say you and half say the agent: the difference is that in a macro library, every single line is on the wrong side of it, and not by accident. First person is what a macro is for.

§3The one line the checker did read

Back to the single finding on the real file: `i` is declared on line 11. Nobody wrote a declaration. Line 11 in the blank-stripped scheme is the second macro, and it opens with the four most common words in customer support.

iref.tg - pointing at a macro✗ 1 warning
# Role
You are Ellis, a customer support agent for Thornbury.

# Macros
I am sorry about the wait.

# Constraints
- You MUST tell a customer that you are @{i}.
tg check, then tg build iref.tg - output and artifact
$ typeglish check iref.tg
iref.tg:5:1  warn   prompt/duplicated-declaration  The model reads this value twice — this
  declaration renders AND @{i} expands it (line 6). If the repetition is deliberate emphasis,
  keep it; otherwise reword this line or the reference so the value appears once.

 1 file — 0 error, 1 warning, 0 info

$ typeglish build iref.tg
 built .typeglish/dist/iref.txt ← iref.tg (8dc9b489048b, full)
$ tail -1 .typeglish/dist/iref.txt
- You MUST tell a customer that you are sorry about the wait.
The pointer resolves. I am sorry about the wait. bound the name i to the value sorry about the wait, and a rule three lines down expanded it.

There is no special case here and no bug. IS, ARE and AM are one operator with three surface forms, and a declaration binds a name to a value and renders at the same time. Persona IS a support agent and I am sorry about the wait are the same statement type. So the single most-used sentence in any macro library is a binding, and in a file with twenty macros the last I am line wins the name. The checker's only complaint about the four-macro paste is not that the callback promise is wrong; it is that the agent's identity was declared underneath the rules that use it.

The corollary is the useful part. Discourse findings do not care about the subject: a definite article is a lookup instruction wherever it appears, so unintroduced definites fire inside macros exactly as they fire inside rules. That is what makes the paste so convincing. Run the checker on a real macro library and you will get findings, on macro lines, with line numbers pointing at macros. It looks like the block was read. What was read is the prose plane; the rule plane skipped every line.

§4What the model is actually handed

None of the above has cost anything yet, because the file still says roughly the right things. The build is where the shape of the paste stops being the shape of the deliverable.

tg build thornbury.tg - the artifact✗ four macros, one paragraph
$ typeglish build thornbury.tg
 built .typeglish/dist/thornbury.txt ← thornbury.tg (ba34d1ddbe1a, full)
$ tail -3 .typeglish/dist/thornbury.txt
Use the approved wording below wherever it fits.

Thanks for getting in touch. I have logged your claim and I call a customer back within 3 days. I am sorry about the wait. I refund the excess as a goodwill gesture whenever the delay was ours. I will pass this straight to our claims team. I am not able to discuss a policy until I have the policy number, so I ask for that first.
A prose body reflows, so the newlines between one approved reply and the next are gone. The model receives a single paragraph in which four separate commitments run together, introduced by an instruction to use the wording below.

Two things are now true of the deployed prompt at once. It contains a sentence promising a callback in three days, sitting six lines under a rule requiring one in two hours, with no boundary marking either as a quotation. And it contains a sentence offering to refund an excess, which is a payment the prompt nowhere authorises and which somebody would have argued about for a week if it had been proposed as a rule. Neither was proposed as a rule. They arrived as wording, and wording is not reviewed like policy.

This is the same trap as a bad example that reads as a good one, with the polarity flipped. There, a fluent in-domain reply sitting in an examples block gets imitated. Here, a fluent in-domain reply sitting anywhere at all gets imitated, because the file never had a way to say this is a quotation, not an instruction. If wording genuinely has to reach the model byte for byte, that is what a literal zone is for. In practice almost none of a macro library needs to.

§5The rewrite: a macro is an assertion

Every macro in the library exists because somebody decided a reply must carry something: a commitment, a refusal, a number, a boundary. That decision is policy and it belongs in the rules. The sentence that carries it is copy, and it belongs where copy is reviewed. The two have been living in one artifact because the help desk stores them that way, not because a prompt needs them together.

So the rewrite deletes the macro block and asks, of each entry, what it was actually asserting. The callback macro was asserting a deadline that is two revisions out of date. The excess macro was asserting an authority nobody granted, so it becomes the prohibition it should always have been. The claims-team macro was asserting a route. The policy-number macro was restating a rule that already existed.

ellis.tg - the rules, and the wording as assertions✓ A (97/100)
<$CONFIG>
  $IMPORT route claims_team
</$CONFIG>

# Role
@@ role: first notification of loss for Thornbury home insurance
You are Ellis, a customer support agent for Thornbury, a home insurance provider.

# Constraints
@@ identity before claim data
- You MUST ask for a policy number before you discuss a claim.
@@ CX-204: the callback promise moved to 2 hours in April; the macro still said 3 days
- You MUST call a customer back within 2 hours.
@@ 3 sentences is what the chat widget shows without a scroll
- You MUST keep every reply to at most 3 sentences.
@@ an excess is a policy figure, never a goodwill decision
- You MUST NOT waive an excess.
@@ a flood is a surge event and belongs to the claims desk
- WHEN a customer reports a flood THEN you MUST escalate the claim to claims_team.

$TEST callback_promise
  - input:: My kitchen flooded last night. When will someone call me?
  - expect::
    - contains "2 hours"
    - at most 3 sentences

$TEST excess_request
  - input:: Can you waive my excess? The delay was your fault.
  - expect::
    - The reply declines to waive the excess and does not offer one.
Five rules and two cases. The wording that used to be pasted in is now an expectation about the wording that comes out.
tg check, score, test --dry and build - output✓ B (85/100) to A (97/100)
$ typeglish check ellis.tg
 1 file — 0 error, 0 warning, 0 info

$ typeglish score ellis.tg
ellis.tg — A (97/100)  proven errors: none  tiers: base+z3
  planes  runtime 96 (what the model reads) · hygiene 100 (source only)
  facets  enforceability 88 x.21 · hardness 100 x.12 · directness 98 x.08 ·
          consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 100 x.12 (hygiene) ·
          style 100 x.08 · security 100 x.08

$ typeglish test ellis.tg --dry
 ellis.tg  coverage: 3/5 rules exercised
  · callback_promise — "My kitchen flooded last night. When will someone call me?" (not run)
       contains "2 hours"
       at most 3 sentences
  · excess_request — "Can you waive my excess? The delay was your fault." (not run)
       rubric 0.00 — not run (--dry)
 1 prompt — 0 failed

$ typeglish build ellis.tg
 built .typeglish/dist/ellis.txt ← ellis.tg (e93160c09d8a, full)
Enforceability drops 94 to 88, which is honest: the new prohibition on waiving an excess is a rule about an action with no number on it. Annotation goes 0 to 100 and the grade follows.

The two asserts are the part worth copying. contains "2 hours" is the callback macro, restated as the only thing about it that was ever load-bearing, and it now lives beside the rule it depends on: change the rule to four hours and the case fails. at most 3 sentences is the brevity rule, exercised. Neither is a template, so neither can drift out of date without something going red, and the artifact is smaller for it.

One honest limit ships with the approach, and it is the same one the examples rewrite ran into: nothing reconciles an expectation against a rule. You can write contains "3 days" under a prompt that promises two hours and the suite will validate, because --dry checks that the suite is well formed and reports coverage, not that it agrees with the file. Coverage is the number to watch: 3/5 rules exercised says two rules have nothing asserting anything about them, and the two are the policy-number gate and the flood escalation, which is a fair description of where this prompt's remaining risk is.

§6Common questions

Should I paste my canned responses into my AI agent's system prompt?
No. A canned response is output and a system prompt is instruction, and pasting one into the other converts approved copy into unapproved policy. It is also unchecked policy: a macro is written in the first person, rules are indexed by their subject, and I is not a subject the checker resolves to the agent. In a four-macro paste the checker returns 0 error, 0 warning, 1 info at B (85/100) with consistency 100, and the identical clause that is 2 blocking logic/time-strength errors when it begins with You is 0 error, 0 warning, 0 info when it begins with I. Keep the wording in your help desk and put the behaviour it encodes in the prompt as a rule with a bound.
Why does typeglish check ignore lines that start with I?
Because a rule is indexed by who it is about, and the deontic layer resolves you to the addressee registered by the role line. The slot key printed in the diagnostic says so out loud: it reads you call a customer, with the subject inside the key. A line beginning I, We, The agent, or even the agent's own persona name lands on a different key, so it is never compared with the rules above it. It still reaches the model, which is the whole problem: the model reads a first-person sentence about the agent as an instruction, and the compiler reads it as a statement about somebody else.
Is I am sorry about the wait a rule or a fact?
Neither: it is a declaration. Is and am are the same copula operator, so I am sorry about the wait binds the name i to the value sorry about the wait, exactly as Persona IS a support agent binds persona. You can prove it by pointing at it: a rule reading You MUST tell a customer that you are @{i} compiles, draws a prompt/duplicated-declaration warning because the value now reaches the model twice, and builds to an artifact whose last line reads you are sorry about the wait. The most common sentence in a macro library is silently registering the agent's identity.
Where should approved wording live if not in the prompt?
In the test suite, as an assertion about the reply rather than a template for it. A macro exists because somebody decided a reply must contain a particular commitment, and that decision is expressible as a $TEST with a deterministic assert: contains "2 hours" plus at most 3 sentences. The rules then say what the agent must do and the tests say what the wording must carry, which is the split the macro was collapsing. A five-rule prompt with two such cases checks at 0 error, 0 warning, 0 info, scores A (97/100), and reports coverage: 3/5 rules exercised.
Does a macro library change what my agent actually receives?
Yes, and not in the shape you pasted. A prose body reflows on compile, so four macros under a # Macros heading arrive at the model as one run-on paragraph of first-person text with no boundary between one approved reply and the next. Building the file writes that artifact at 0 error and exit 0, because nothing in it is wrong: the compiler was never told those lines were quotations. If wording genuinely has to ship verbatim, it belongs in a literal zone, and if it only has to be honoured, it belongs in a $TEST.
Field note

The reflex this teardown should install is a grep, not a rule. Before you read a single finding on a prompt somebody else assembled, count the lines whose first word is I or We. Every one of them is text the model will treat as an instruction and the compiler will treat as a remark about a third party, and in a file assembled out of help-desk exports that count is frequently larger than the number of actual rules. It is a cheap gate and nothing in the toolchain will do it for you, because there is nothing wrong with the lines: a prompt is allowed to contain prose, and the exemption that lets a Context block hold free English is the same exemption that swallows a macro library whole. The number to distrust here is consistency 100. It does not mean the file agrees with itself. It means everything in the file that could disagree was written in the same grammatical person, and the four sentences that contradict the rules were not.

∿ washed up Aug 30, 2026 ∿