Teardown: the system prompt an AI wrote for you
Ask a model for an agent system prompt and it gives you something fluent, tidy, and instantly recognisable. Run it through a checker and you get twelve warnings, none of which is the bug. The bug is underneath, and the typography is what was hiding it.
TL;DR A machine-drafted agent prompt draws its first twelve findings on its own typography (em-dash, emoji, decorative bullets, a math symbol) and those warnings are not cosmetic: they keep the checker from reaching the logic. Sweep the character plane, commit the hedges to real modals, and the actual defect surfaces as logic/numeric - two length rules that cannot both hold, present since the first draft. Rebuilt properly, with the exception carved out of the cap, it goes from D (50/100) to A (92/100).
The first version of this post rebuilt the file with a flat cap, MUST keep every reply to at most 3 sentences, beside WHEN you explain the return policy THEN give at least 5 sentences of detail, and called the pair scoped. That was wrong. A flat rule applies to every reply, the policy explanation included, so the two bounds still met there. typeglish 0.9.0 let it through because a bare imperative after THEN, with no modal, was not read as a rule at all; 0.10.0 reads it, proves WHEN and UNLESS guards, and reports the conflict the file always had. The rebuild in §5 now carves the exception out of the cap with UNLESS, and every figure on this page has been re-run against 0.10.0. One more change shows in those figures: 0.10.0 files the character-plane codes as warnings, not errors, so the twelve errors of the first version are twelve warnings here, and the grades have moved with them.
Generated prompts have a house style. Once you have seen a few you can spot one from across the room: the em-dash, the "highly capable and empathetic" preamble, the bullet list with a star emoji, the hedge in every third clause. The interesting thing is not that the style is recognisable. It is that the style is load-bearing in the worst way: every one of those tics costs the checker a tier of analysis, so the fluent draft is also the one whose real defects you cannot see. Below is a returns-agent prompt in that house style. If you have ever asked a model to draft one, you will recognise every line.
§1The prompt, as it arrives
# Role You are a highly capable, empathetic AI assistant for Acme — a leading provider of home coffee equipment. Your goal is to delight every customer! # Guidelines • Always strive to be as helpful as possible 🌟 • Keep responses ≤ 3 sentences where possible • When explaining the return policy, provide at least 5 sentences of detail so the customer feels fully informed • If the customer’s request seems complex, you should probably escalate to a human
Before reading the diagnostics, try the exercise a reviewer would do: find the behavioural defect. It is in there, in plain English, and almost nobody finds it on the first pass. We will come back to that.
§2Twelve warnings, and not one of them is the bug
returns.tg:2:59 warn structure/em-dash A typographic dash hides its break from the ASCII parsers, and models mirror the tic (the canonical AI-tell). Its plain form is the spaced hyphen: " - ". returns.tg:5:46 warn structure/special-character Emoji is outside the language: the instruction plane is checkable English, and models mirror prompt decoration into output. Remove it, or demonstrate it inside <examples>. returns.tg:5:1 warn structure/special-character Not a list bullet the parser reads: use "- ". returns.tg:5:1 warn structure/missing-period Unterminated statement — end it with a period (or ! ?; a lead-in may end with ":"). Statement boundaries are a compile contract. returns.tg:5:10 info prompt/hedging Hedging — "strive to" turns this instruction into a suggestion the model may skip. Delete the hedge, or commit to a modal (MUST / NEVER / SHOULD). returns.tg:6:18 warn structure/special-character The numeric tier parses bounds written in English: '≤ 3' lowers with no bound while 'at most 3' is Z3-provable. Write it out. returns.tg:7:1 info prompt/unintroduced-definite "the return policy" retrieves something this document never introduces - a model must guess which policy is meant. Mention a policy on an earlier line, or anchor it here ("their policy", "the policy you ..."). returns.tg:8:18 warn structure/curly-quote A curly apostrophe is a lookalike: the parsers read ASCII ', so possession and contractions written with it are invisible to the consistency checker. Straighten it. ✓ 1 file — 0 error, 12 warning, 4 info
Not a list bullet and Unterminated statement warning, which is how eight lines produce twelve. Every single warning is about characters and line shape. Not one is about what the agent will do.Now the part that should bother you. Run the score and look at the consistency facet.
returns.tg — D (50/100) proven errors: none tiers: base+z3 planes runtime 73 (what the model reads) · hygiene 0 (source only) facets hardness 32 x.16 · directness 85 x.11 · consistency 100 x.21 · structure 0 x.16 (hygiene) · annotation 0 x.16 (hygiene) · style 40 x.11 · security 100 x.11 lever structure 0/100 (up to +16 overall) — 12 findings below (L2 L5 L6 L7 L8) — Fix the ledger rows with structure/ codes; most carry a one-click fix.
consistency 100. The checker is telling you, honestly, that it found no conflict between any two rules in this file. It is also wrong, and it is wrong because of the structure score sitting at zero next to it. Notice what else is missing from the facets line: no enforceability facet, and no rules table below it, because not one line in this file is a rule the checker could read.This is the mechanism worth taking away from the whole teardown. The checker works in tiers, and the early ones gate the later ones. A line the rule reader cannot resolve into a rule never reaches the solver, so a conflict inside that line is not missed, it is unreachable. None of the four bullets here is a rule to the checker: • is not a list marker its parser reads, so the rule reader never looks at them, and the one bound in the file is a glyph the numeric tier cannot lower. There is nothing for the prover to prove.
The typography is not the defect. It is the reason you cannot see the defect.
§3Pass one: sweep the character plane
So we do the boring pass first, and only the boring pass: plain hyphen for the dash, - for the bullets, straight apostrophe, the emoji gone, and ≤ 3 written out as at most 3. Not one word of policy changes.
# Role You are a highly capable, empathetic AI assistant for Acme, a leading provider of home coffee equipment. Your goal is to delight every customer! # Guidelines - Always strive to be as helpful as possible. - Keep responses at most 3 sentences where possible. - When explaining the return policy, provide at least 5 sentences of detail so the customer feels fully informed. - If the customer's request seems complex, you should probably escalate to a human.
returns.tg:5:10 info prompt/hedging Hedging — "strive to" turns this instruction into a suggestion the model may skip. Delete the hedge, or commit to a modal (MUST / NEVER / SHOULD). returns.tg:6:38 info prompt/hedging Hedging — "where possible" turns this instruction into a suggestion the model may skip. Delete the hedge, or commit to a modal (MUST / NEVER / SHOULD). returns.tg:6: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 "keep" but cannot prove this shape: the object runs past six words, so it reads as prose. Fix: lead with a modal (MUST …) to make it a rule, and name the object in fewer words, or split the rule to make it provable. returns.tg:7:1 info typeglish/if-then WHEN needs a THEN — write WHEN <condition> THEN <action>. returns.tg:7: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: the condition has no THEN branch, so nothing is required when it holds. Fix: add THEN MUST / NEVER / SHOULD <verb> …. returns.tg:7:1 info prompt/unintroduced-definite "the return policy" retrieves something this document never introduces - a model must guess which policy is meant. Mention a policy on an earlier line, or anchor it here ("their policy", "the policy you ..."). returns.tg:8:48 info prompt/hedging Hedging — "should probably" turns this instruction into a suggestion the model may skip. Delete the hedge, or commit to a modal (MUST / NEVER / SHOULD). returns.tg:8:1 info typeglish/if-then IF needs a THEN — write IF <condition> THEN <action>. returns.tg:8: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: the condition has no THEN branch, so nothing is required when it holds. Fix: add THEN MUST / NEVER / SHOULD <verb> …. ✓ 1 file — 0 error, 0 warning, 9 info
prompt/unparsed-rule saying the same thing from the other side: three of the four lines read like rules and parse as prose. Consistency is still reported at 100, because these lines still are not rules.§4Pass two: make them rules, and meet the bug
The remaining notes all ask for the same thing: commit. A hedged line is not an instruction, and When explaining the return policy, provide... is a condition with no consequent. So we do what the diagnostics say. Every rule gets a modal, and the sentences say plainly what the draft was gesturing at.
# Role You are a support agent for Acme, a home coffee equipment retailer. # Guidelines - MUST keep every reply to at most 3 sentences. - MUST give at least 5 sentences of detail when you explain the return policy. - WHEN the request needs an exception to policy THEN escalate to a human agent.
returns.tg:5:1 error logic/numeric Conflicts with line 6. Numeric conflict — "at most 3 sentences" and "at least 5 sentences" can't both hold. ↳ line 6: - MUST give at least 5 sentences of detail when you explain the return policy. returns.tg:6: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 keep every reply to at most 3 sentences. ✗ 1 file — 2 error, 0 warning, 0 info
Note what it took to surface a two-line contradiction: a character sweep and a modal pass. Nobody was hiding it. The solver simply could not reach it, because ≤ 3 lowers with no bound and the five-sentence line opened a condition with no THEN, so it was not yet a rule the prover reads. The numbers in a prompt are constraints a prover can hold, which is the argument in the arithmetic in your prompt, but only once they are written in a form it can read.
§5The rebuild
The conflict has two honest resolutions, and choosing between them is the decision the generated draft quietly declined to make. Either reconcile the band (pick one length for every reply), or admit that the policy explanation is a genuine exception and carve it out, so each bound only applies where it belongs. A trailing "when you explain the return policy" does not scope anything, it is prose hanging off an unconditional MUST. A real WHEN ... THEN is a rule the prover reads, and it is only half the job. The cap has to carry the complementary guard, UNLESS you explain the return policy, or it still applies inside the exception and the two bounds still meet there. The first version of this post left the cap flat and called the pair scoped; 0.10.0 proves it was not.
# Role @@ role: name the domain, so "helpful" has an edge You are a support agent for Acme, a home coffee equipment retailer. # Constraints @@ brevity: three sentences is the length a chat reply actually gets read at; the UNLESS carves the one exception out of the cap - UNLESS you explain the return policy THEN MUST keep every reply to at most 3 sentences. @@ policy_detail: the one place length is worth spending; its guard is the complement of the cap's, so the two can never both apply - WHEN you explain the return policy THEN MUST give at least 5 sentences of detail. @@ order_number: the order number is what makes a return reconcilable - MUST ask for the order number before starting a return. @@ window: stating the window prevents the second contact - MUST state the 30-day return window when a return is requested. @@ escalation: an exception costs less as a handoff than as a wrong answer - WHEN the request needs an exception to policy THEN escalate to a human agent. @@ no_dismissal: dismissal is the complaint that reaches the review site - NEVER dismiss a customer's complaint. $TEST policy_length - input:: What is your return policy on a grinder I opened last week? - expect:: - states the 30-day return window - at least 5 sentences
UNLESS x is IF NOT x, so no reply satisfies both guards, and the prover can see that. The $TEST pins the expensive case: tg test --dry reports coverage: 3/6 rules exercised, so you also know exactly which rules are still riding on hope.✓ returns.tg coverage: 3/6 rules exercised uncovered (no case shares the rule's wording): L11 ask for the order number before starting a return. L15 escalate to a human agent. L17 dismiss a customer's complaint. · policy_length — "What is your return policy on a grinder I opened last week?" (not run) ✓ at least 5 sentences · rubric — 1 prose expectation, judged on a live run (needs ANTHROPIC_API_KEY) ✓ 1 prompt — 0 failed
states the 30-day return window, is a rubric: --dry parses it and holds it for a live run, it does not judge it.Compare the two files as artifacts and the trade is plain. The generated draft has a persona paragraph, four friendly bullets, and a star. The rebuild has six rules, a note on each saying why it exists, and one test. It is not more beautiful. It is checkable, and the version that ships to customers should be the checkable one.
The character plane is not a style guide with delusions of grandeur: structure/em-dash, structure/curly-quote, and structure/special-character exist because each of those marks hides meaning from a tier that parses ASCII English, and every figure above is re-run against the real checker in CI on each commit. The same "clean it before you can read it" order shows up in the voice-agent teardown, where the decorative marks were being read aloud to customers. And the hedges in §3 are the first item in say what you mean, for the same reason: a rule the model may skip is not a rule.
FAQCommon questions
- Is an AI-generated system prompt good enough to ship?
- As a first draft, yes; as a spec, not without a pass. A generated prompt is fluent, which is the problem: it reads as finished. The draft in this teardown drew twelve warnings before anyone looked at its logic, and the real defect (two length rules that cannot both hold) only became provable after the typography was swept and the hedges were committed to modals. Treat the generated text as raw material and run it through a checker before it reaches a customer.
- Why do em-dashes and emoji matter in a system prompt?
- Two reasons, and only one of them is taste. The model mirrors prompt decoration into its output, so an em-dash in the prompt buys you em-dashes in customer replies. More importantly, the marks hide meaning from the tiers that parse English: a curly apostrophe means a possession is invisible to the consistency checker, and a written-out bound like at most 3 is provable by the solver while the math symbol version lowers with no bound at all. The typography is not the bug, it is what stops you seeing the bug.
- How do I clean up a system prompt a model wrote for me?
- Four passes, in this order. Sweep the character plane first, so the checker can read the file: plain hyphens, straight quotes, ASCII bullets, bounds written in English. Then commit every hedge to a modal, because try to and should probably are not rules. Then finish every conditional, so a line that starts with when or if actually says what to do. Only then are the remaining diagnostics about logic, and that is where the real conflicts surface. When one rule is an exception to another, carve it out: the default carries the complementary guard, so the two can never both apply.
- Why did new errors appear after I fixed the ones the checker reported?
- Because the checker works in tiers and the early ones gate the later ones. A line the rule reader cannot parse as a rule is never handed to the solver, so a numeric or logical conflict inside it is not missed, it is unreachable. Clean the structure, commit the modals, and those lines become rules, the rules reach the solver, and conflicts that were always in the file finally get proven. A rising error count after a cleanup pass is progress: the file is now legible enough to be wrong out loud.