← Tidelines/Best practices

Why your agent follows half a rule

Somebody reflowed the prompt file to 80 columns, or pasted it out of a doc that already was. Every rule still reads correctly to a human. Three of them are now six statements, and the halves that carry the actual limits are floating free.

by TypeGlish team7 min read#best-practices
One rule. Two statements.

TL;DR A newline inside a rule is a statement boundary, not whitespace, so a hard-wrapped rule becomes a truncated instruction plus a stray fragment, and both reach the model. TypeGlish flags the second line as structure/wrapped-fragment, a warning that does not block, which means a numeric bound stranded on line two never enters the proof and a real contradiction reports consistency 100. Run typeglish fmt --check in CI before typeglish check.

This one does not announce itself. The prompt reads well, the review passes, the checker is green, and the agent is subtly wrong in a way nobody can reproduce on demand. It comes from the most innocuous act in the repository: a line got wrapped. Maybe an editor was set to a ruler, maybe the prompt came out of a Google Doc, maybe a YAML block folded it, maybe someone tidied a diff. No word changed. That is exactly why nobody suspects it.

§1A newline is a statement boundary

TypeGlish classifies every content line as exactly one statement type. That is not a stylistic position, it is how the language works: one statement per line, and the line break is where a statement ends. So a rule split across two lines is not one rule with a line break in the middle. It is a rule that ends mid-clause, followed by a second thing.

billing.tg - reflowed to 80 columns✓ compiles
# Role
You are a billing support agent for Cobalt Mobile.

# Constraints
- MUST confirm a date of birth before you discuss a
  bill.
- NEVER promise a refund date when a customer asks
  for one.
- MUST keep every chat reply to at most 3
  sentences.
- MUST write at least 5 sentences in every chat reply.
Read it as prose and it is four sensible rules for a mobile-carrier billing agent. Read it as lines and it is seven statements, three of which are noun phrases.
tg check billing.tg - output
billing.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.
billing.tg:6:1   warn  structure/bad-indent  Indentation mirrors section nesting: expected
  column 0 (0 levels deep), found 2. The indent unit is 2 spaces.
billing.tg:6:3   warn  structure/wrapped-fragment  Reads as a hard-wrapped continuation of
  line 4 - it starts lowercase, classifies as plain prose, and line 4 has no terminator. If they
  are ONE statement, join them (typeglish fmt); if this line stands alone, capitalize its first
  word (and terminate line 4).

 1 file - 0 error, 9 warning, 0 info
Trimmed to the first rule; the other two wraps produce the same trio. Note the summary and the exit code: 0 errors, and typeglish check exits 0. This file passes.

Three warnings per wrap, and each one is a different symptom of the same cause. structure/missing-period says the first line never ended. structure/bad-indent says the second line is indented as though it were nested inside something. structure/wrapped-fragment does the actual diagnosis, and it is worth reading in full, because it names both readings and tells you which command settles it: if they are ONE statement, join them (typeglish fmt); if this line stands alone, capitalize its first word.

§2What the model actually receives

Warnings do not block, so this file builds. Building it is the fastest way to stop arguing about whether the wrap matters.

tg build billing.tg - the artifact
 built .typeglish/dist/billing.txt ← billing.tg (e975f214df75, full)

# Role
You are a billing support agent for Cobalt Mobile.

# Constraints
- MUST confirm a date of birth before you discuss a
bill.
- NEVER promise a refund date when a customer asks
for one.
- MUST keep every chat reply to at most 3
sentences.
- MUST write at least 5 sentences in every chat reply.
The wrap survives into the artifact, and the continuation has lost even its indentation. The model reads MUST confirm a date of birth before you discuss a, then a separate line reading bill.

Now read those seven lines the way a model reads them, in order, with no memory of your editor. The first bullet is an obligation with a dangling preposition. The line under it is a single word with a full stop, which parses as prose and carries no force at all. A model will usually recover, because the preceding line is a strong cue and language is redundant. Usually is the problem. This is not a rule that is followed or not followed, it is a rule whose second half is a suggestion, and the failure surfaces on the hard cases: long conversations, adversarial phrasing, a smaller model on the cheap tier, the third turn after a tool call.

Your editor wraps for you. The file does not, and the file is what ships.

Worth separating two things that look identical on screen. Soft wrapping, where the editor folds a long line for display, puts no newline in the file and costs nothing. Hard wrapping, where a real newline goes in, is a permanent edit to the prompt. The distinction is invisible in most editors and total in the artifact.

§3The contradiction a line break hid

So far this is a quality problem. The third rule in that file makes it a correctness problem, because a wrap can hide a defect the prover would otherwise have proved.

Look at the last two rules again. At most 3 sentences and at least 5 sentences, both about a chat reply. Those cannot both hold. TypeGlish proves numeric conflicts with Z3, so it should be two blocking errors. Instead the file is green, because at most 3 ended one line and sentences began the next, so the bound never assembled into anything the prover could see. Ask for the score and the scorecard says so out loud.

tg score billing.tg - the wrapped file
billing.tg - C (72/100)  proven errors: none  tiers: base+z3
  planes  runtime 96 (what the model reads) · hygiene 0 (source only)
  facets  enforceability 85 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 100 x.17
          structure 0 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  lever   structure 0/100 (up to +12 overall) - Fix the ledger rows with structure/ codes; most
          carry a one-click fix.
consistency 100, in a file holding a flat contradiction about reply length. The prover is not wrong. From where it stands the two rules never meet, because one of them is not a bound any more.

Now run the formatter. typeglish fmt is mechanical and shape-only: it joins hard-wrapped fragments and normalizes whitespace, and it does not touch a word. It shipped as a subcommand in TypeGlish 0.7.0, which is also the release that made structure/wrapped-fragment name a command that actually existed.

tg fmt, then tg check again - output
$ typeglish fmt billing.tg
 formatted billing.tg (3 changes)
 1 formatted, 0 unchanged

$ typeglish check billing.tg
billing.tg:7:1  error  logic/numeric  Numeric conflict - "at most 3 sentences" and
  "at least 5 sentences" can't both hold. (conflicts with line 7)
billing.tg:8:1  error  logic/numeric  Numeric conflict - "at most 3 sentences" and
  "at least 5 sentences" can't both hold. (conflicts with line 6)

 1 file - 2 error, 0 warning, 0 info
Three joins, and the same file goes from exit 0 to exit 1. Nobody wrote a rule, deleted a rule, or changed a word. The contradiction was there the whole time, one newline away from being provable.

This is the part to take away, because it generalises past line breaks. A green check is a statement about what the checker could see, and anything that breaks a statement in half shrinks what it can see. The same shape shows up whenever a bound gets separated from its rule: the arithmetic in your prompt is only provable while the numbers are still attached to the actions they bound.

§4Put the formatter before the checker

The fix at the file level is a one-liner. The fix at the team level is an ordering.

tg fmt --check - the CI gate
$ typeglish fmt prompts/ --check
billing.tg: 3 changes needed
 1/1 file need formatting - run typeglish fmt
exit 1
--check writes nothing and exits 1 when a file would change: the same contract as any formatter check in any language, and the same place in the pipeline.

Run it as the step before typeglish check, not after and not instead. The ordering is the whole point. A checker running on an unformatted file is checking statements nobody wrote, and it will report clean on them, honestly and uselessly. Formatting first is what makes the check a check.

Three habits keep the wraps out in the first place:

  • Turn off hard wrap for .tg files. Soft wrap all you like. A ruler that inserts newlines at column 80 is a tool for source code with a line-continuation rule, and prompts do not have one.
  • Treat a paste as an import, not a paste. Text that arrives from a doc, a wiki, a ticket or a bot builder arrives with somebody else's line breaks in it. Run typeglish fmt on it before you read it, so you review the file the compiler will read.
  • Read the warnings you are used to. Nine warnings and zero errors is the exact profile people learn to scroll past. Every one of those nine was pointing at the same three lines.

Here is the same prompt with the wraps joined, the contradiction resolved, and a reason above each rule.

billing-final.tg - one statement per line✓ A (93/100)
# Role
You are a billing support agent for Cobalt Mobile.

# Constraints
@@ verify: a bill is account data, so it never moves before identity does
- MUST confirm a date of birth before you discuss a bill.
@@ no_dates: refund timing belongs to the payment provider, not to us
- NEVER promise a refund date when a customer asks for one.
@@ brevity: chat replies over 3 sentences stop being read
- MUST keep every chat reply to at most 3 sentences.
@@ dispute_detail: a disputed charge needs the line item, not a summary
- MUST name the disputed line item when a customer disputes a charge.
0 error, 0 warning, 0 info, and typeglish fmt --check reports it already formatted. The fourth rule is what the at least 5 sentences line was reaching for: not a length floor, a requirement to name the thing being disputed.

C (72/100) to A (93/100), and almost none of that came from writing better rules. Structure went 0 to 100 because the wraps are gone, annotation went 0 to 80 because the reasons are written down, and the contradiction that was hiding behind a newline had to be resolved on the way through.

§5Common questions

Why does my AI agent only follow part of an instruction?
Check whether that instruction is on one line. In a system prompt a newline is a statement boundary, so a rule you hard-wrapped across two lines is not one rule with a line break in it, it is a rule ending mid-clause followed by a fragment. The model receives both, with the second one stripped of its indentation and standing on its own, and the qualifier you thought was part of the rule is now a sentence of its own. TypeGlish calls the second line structure/wrapped-fragment and typeglish fmt joins it back.
Does line wrapping in a system prompt actually matter?
Soft wrapping in your editor does not matter, because it puts no newline in the file. Hard wrapping does, because it puts a real newline in the file and every downstream reader treats that as the end of a statement. The cheap way to see the difference is to build the prompt and read the artifact: a hard wrap is still there in the text the model receives, and the continuation has lost even its indentation.
Why does my prompt pass the checker but still behave wrong?
One common cause is that the checker never saw the rule you think it checked. A wrapped rule lowers as a truncated statement, so a bound that ended up on the second line is not in the proof at all: at most 3 sentences wrapped away from its rule sits beside at least 5 sentences and the prover reports consistency 100, because from where it stands the two rules never meet. Run typeglish fmt first and check second, and the same file comes back with 2 blocking logic/numeric errors.
How do I stop hard-wrapped rules getting into my prompt files?
Gate it in CI, and put the gate before the checker. typeglish fmt --check writes nothing and exits 1 when a file would change, so it fails the build on a wrap the way a formatter check fails it on code. The ordering matters more than it looks: fmt then check is the sequence that finds contradictions, because check on an unformatted file is checking statements you did not write.
Field note

False greens all have the same shape, and it is worth collecting them. Two rules, no words in common is a contradiction the checker can see and you cannot; this one is the mirror image, a contradiction you can see and the checker cannot, because the file lied to it about where a statement ended. The habit that catches both is the same one: never read a green check as this prompt is correct, read it as nothing was proven wrong in what I handed over, and then go and look at what you handed over.

∿ washed up Aug 3, 2026 ∿