← Tidelines/Best practices

Bake-off: the reason in the rule vs. the reason in a note

Everybody writes the why into the rule, because a rule with a reason feels like a rule somebody thought about. Three files with the same three rules, and the checker has an opinion about where the word because should live.

by TypeGlish team8 min read#best-practices
Because is not free.

TL;DR A because clause welded to a rule is parsed like part of the rule and proved like nothing: it drags hardness from 100 to 50 when it contains a word like most, drags enforceability from 100 to 70 when it adds a claim with no observable action, and hides a contradiction that the same sentence on its own line reports as 2 blocking errors. Put reasons the model needs in a # Context prose block and reasons the reviewer needs in a @@ note, and the same policy goes from B (81/100) to A (96/100) with an artifact 167 bytes shorter.

This is a small habit with a large blast radius. Nobody debates whether to give a model reasons, and the honest answer is that reasons often help, which is exactly why the question of where the reason goes never gets asked. Three files, one policy, and a checker that treats each placement completely differently.

§1One policy, three files

Three rules from a chat desk at an electricity retailer: a length bound, a pricing rail, and a tool-first rule for callbacks. Every version says the same three things and records the same three reasons. Route A welds each reason to its rule.

rules-a.tg - the reason in the rule✗ B (81/100)
<$CONFIG>
  $IMPORT tool open_case
</$CONFIG>

# Role
You are a chat support agent for Northvale Energy, an electricity retailer.

# Constraints
- You MUST keep every reply to at most 3 sentences because most customers read on a phone.
- You MUST NOT state a tariff price, because published rates change every quarter and a quoted rate binds us.
- You MUST call @[open_case] before you promise a callback, so that the callback lands in the queue a human works.
This is what a well-intentioned prompt looks like. Every rule is justified in place, a reviewer can read it top to bottom, and nothing here is anybody's idea of a mistake.

Route B moves each reason to a @@ note, which documents the statement directly below it and is stripped at compile.

rules-b.tg - the reason in a note✓ A (95/100)
<$CONFIG>
  $IMPORT tool open_case
</$CONFIG>

# Role
You are a chat support agent for Northvale Energy, an electricity retailer.

# Constraints
@@ brevity: most customers read on a phone
- You MUST keep every reply to at most 3 sentences.
@@ no_price: published rates change every quarter and a quoted rate binds us
- You MUST NOT state a tariff price.
@@ callback_case: the callback lands in the queue a human works
- You MUST call @[open_case] before you promise a callback.
Same rules, same reasons, same file, different plane. The reasons are now source-only, which means the reviewer keeps them and the model never sees them.

Route C keeps the notes and also gives the model the background, in a # Context prose block, which is one of the section kinds exempt from rule analysis.

rules-c.tg - the reason in a prose block✓ A (91/100)
<$CONFIG>
  $IMPORT tool open_case
</$CONFIG>

# Role
You are a chat support agent for Northvale Energy, an electricity retailer.

# Context
Most customers reach us from a phone, on a mobile connection, mid-task.
Published rates change every quarter, and a rate quoted in a chat binds us to it.
A callback that is not in the queue is a callback nobody makes.

# Constraints
@@ brevity: most customers read on a phone
- You MUST keep every reply to at most 3 sentences.
@@ no_price: published rates change every quarter and a quoted rate binds us
- You MUST NOT state a tariff price.
@@ callback_case: the callback lands in the queue a human works
- You MUST call @[open_case] before you promise a callback.
Route B plus three sentences of background. Note that most customers is back, and in here it costs nothing at all, for the reason §3 gets to.

§2The check cannot referee this

tg check on all three - output
$ npx typeglish check rules-a.tg rules-b.tg rules-c.tg
 3 files — 0 error, 0 warning, 0 info

$ npx typeglish score rules-a.tg
rules-a.tg — B (81/100)  proven errors: none  tiers: base+z3
  planes  runtime 91 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 80 x.21 · hardness 83 x.12 · directness 92 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08

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

$ npx typeglish score rules-c.tg
rules-c.tg — A (91/100)  proven errors: none  tiers: base+z3
  planes  runtime 97 (what the model reads) · hygiene 72 (source only)
  facets  enforceability 90 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 43 x.12 (hygiene) · style 100 x.08 · security 100 x.08
Three files, zero findings between them, fourteen points of spread. The interesting number is not annotation, which obviously moves when you add notes. It is runtime: 91 for route A against 97 for both others. The reason clause is not just untidy bookkeeping, it is measurably changing the document the model reads.

Route C sits four points behind route B purely on annotation 43, and that is bookkeeping rather than substance: the three context lines are eligible statements with no notes on them. Put a @@ note above each context line and the same file reads A (96/100) with annotation 86, which is the best score in this bake-off. Nothing about the model-facing text changed.

§3What a because actually costs

Six single-rule files, to attribute the loss. Each pair is the same rule with and without its reason.

tg score, one rule at a time - output
$ npx typeglish score bound.tg
bound.tg — B (87/100)  proven errors: none  tiers: base+z3
  facets  enforceability 100 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08

$ npx typeglish score bound-why.tg
bound-why.tg — B (81/100)  proven errors: none  tiers: base+z3
  facets  enforceability 100 x.21 · hardness 50 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08

$ npx typeglish score ban.tg
ban.tg — B (87/100)  proven errors: none  tiers: base+z3
  facets  enforceability 100 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08

$ npx typeglish score ban-why.tg
ban-why.tg — B (81/100)  proven errors: none  tiers: base+z3
  facets  enforceability 70 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08
bound.tg is You MUST keep every reply to at most 3 sentences. and bound-why.tg adds because most customers read on a phone. ban.tg is You MUST NOT state a tariff price. and ban-why.tg adds because published rates change every quarter and a quoted rate binds us. Same six-point drop, two different facets.

The hardness half is the more surprising one, and the doctrine explains it exactly.

tg --explain hardness - output
$ npx typeglish --explain hardness
hardness (score facet — runtime plane, weight 0.15)
  THE HARDNESS DOCTRINE (SPEC §15): how BINDING the prompt's language is. Every force word sits on a spectrum (must not ↔ must, never ↔ always, no ↔ all); hardness is its distance from the waver center (THE POLE LAW — both poles bind, the middle wavers), and a statement scores the MIN over its force words (one hedge softens the whole rule). An unhedged command is 1.0; 1.0 renders PURE BOLD in the editor.
  raise it: Commit to pole words — MUST / NEVER / ALWAYS / ALL / exact bounds ("at most 3") — and delete the soft middle: should, sometimes, about, try to, as needed each cap their statement below 1.0.
The MIN over its force words. Most is a quantifier and sits in the middle of the square, so once it is inside the statement it is the statement's strength. The rule did not get softer in anyone's reading of it; it got softer because the reason brought a soft word into the room.

Which predicts the fix, and the prediction holds. Three variants of one rule:

tg score, three reasons for one bound - output
$ npx typeglish score plain-why.tg
plain-why.tg — B (87/100)  proven errors: none  tiers: base+z3
  facets  enforceability 100 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08

$ npx typeglish score most-why.tg
most-why.tg — B (81/100)  proven errors: none  tiers: base+z3
  facets  enforceability 100 x.21 · hardness 50 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08

$ npx typeglish score usually-why.tg
usually-why.tg — B (81/100)  proven errors: none  tiers: base+z3
  facets  enforceability 100 x.21 · hardness 50 x.12 · directness 100 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08
The three reasons are because customers read on a phone, because most customers read on a phone, and because a customer usually reads on a phone. A reason with no force word in it is free. Swap in most or usually and the obligation above it is worth half as much. That is not a rule you want to be following by accident, and it is not a rule anybody can review for by reading.
The clause is not commentary. It is part of the sentence, and the sentence is the rule.

§4The blind spot

If the reason is parsed, the fair expectation is that it is also checked. It is not, and the gap is easy to show with one claim in two positions.

slot.tg and slot-why.tg - the same claim, two homes✗ 2 error
slot.tg
  # Constraints
  - You MUST respond within 2 hours.
  - You respond within 30 minutes.

slot-why.tg
  # Constraints
  - You MUST respond within 2 hours.
  - You MUST set an expectation because you respond within 30 minutes.
Both files carry the claim you respond within 30 minutes alongside a two-hour obligation. In the first it is a line. In the second it is a reason.
tg check slot.tg slot-why.tg - output
$ npx typeglish check slot.tg slot-why.tg
slot.tg:2:1  error  logic/time-strength  Conflicts with line 3. One bound per slot — "within 30 minutes" already entails "within 2 hours"; the looser rule on "you · respond" is dead weight. Keep one.
slot.tg:3:1  error  logic/time-strength  Conflicts with line 2. One bound per slot — "within 30 minutes" already entails "within 2 hours"; the looser rule on "you · respond" is dead weight. Keep one.

 2 files — 2 error, 0 warning, 0 info
Two files went in and only one of them has a finding. The time layer read the bare line, matched it to the slot you · respond, and refused to compile the file. It did not read into the because clause, so the second file is clean. The clause is in the worst position available: shipped to the model, counted against the rule by the score, and out of reach of the provers that would catch it being wrong.

That combination is what makes this a placement question rather than a style question. A @@ note is also unproved, but nobody expects a comment to be proved and it costs nothing downstream. A because clause looks like it is inside the spec, is treated as inside the spec by the scorer, and is outside it everywhere it would matter. The same asymmetry shows up wherever unreviewed prose sits next to reviewed rules, which is the argument the Context section rewrite makes about the part of a prompt nobody reads.

§5The verdict, and where each reason goes

tg build, all three - output
$ npx typeglish build rules-a.tg rules-b.tg rules-c.tg --out-dir dist
 built dist/rules-a.txt ← rules-a.tg (a12e582f251f, full)
 built dist/rules-b.txt ← rules-b.tg (61f33dd55152, full)
 built dist/rules-c.txt ← rules-c.tg (b4a40dfc2af4, full)
 built 3/3

$ wc -c dist/*.txt
 411 dist/rules-a.txt
 244 dist/rules-b.txt
 473 dist/rules-c.txt

$ cat dist/rules-b.txt
# Role
You are a chat support agent for Northvale Energy, an electricity retailer.

# Constraints
- You MUST keep every reply to at most 3 sentences.
- You MUST NOT state a tariff price.
- You MUST call open_case before you promise a callback.
Route B is 244 bytes: three rules, no reasons, and the reasons still in the file where a reviewer will find them. Route A spends 167 bytes, 41 percent of its artifact, on three clauses the model was never asked to act on. Route C spends more than either and gets the most out of it, because its extra bytes are three sentences of genuine background rather than three subordinate clauses bolted to obligations.

So the verdict is not never explain. It is that a prompt has two audiences with different needs, and one construct cannot serve both:

  • The reason the model needs goes in a # Context prose block. It reaches the model, it is exempt from rule analysis, and no word in it can weaken an obligation somewhere else in the file.
  • The reason the reviewer needs goes in a @@ note above its rule. It is stripped at compile, it costs nothing in the artifact, and it is the thing you point at in six months when somebody asks why the bound is three sentences and not five.
  • The reason welded to the rule is the one to stop writing. It is the only placement that pays for the reason twice, once in tokens and once in strength.

Most reasons turn out to belong in the note. The test is simple and worth applying rule by rule: if the model would behave differently without this sentence, it is context; if only a person would, it is an annotation. Teams tend to discover that four rules in five are the second kind, which is also the finding that makes the annotation facet worth chasing rather than gaming, as the post on letting a coding agent edit your prompt gets into.

§6Common questions

Should I explain why in a system prompt, or just give the rule?
Both, in two different places. A reason the model needs in order to apply the rule sensibly belongs in a # Context prose block, where it reaches the model and is exempt from rule analysis. A reason only a reviewer needs belongs in a @@ note, which is stripped at compile and never costs a token. What does not work is welding the reason onto the rule with because: the same three rules score B (81/100) with the reasons inline and A (95/100) with the reasons in notes, and the inline artifact is 411 bytes against 244.
Why does adding because to a rule lower the TypeGlish score?
Because the clause is parsed, not skipped. The hardness facet scores the minimum over a statement's force words, so a quantifier or a frequency word inside the reason becomes the strength of the whole rule: because most customers read on a phone takes hardness from 100 to 50, and because a customer usually reads on a phone does the same, while because customers read on a phone costs nothing. The enforceability drop has a different cause, that the clause adds rule-shaped text with no observable action in it, which is what takes a prohibition from 100 to 70.
Does TypeGlish check the facts inside a because clause?
No, and that is the sharp edge. The line You respond within 30 minutes sitting under You MUST respond within 2 hours is 2 blocking logic/time-strength errors. Move the identical claim into a reason clause on the line above and the file is 0 error, 0 warning, 0 info. The provers do not read into the clause, but the score facets do, so a reason welded to a rule is in the worst available position: shipped to the model, counted against the rule, and invisible to the checks that would catch it being wrong.
Do @@ notes reach the model?
Never. A @@ note documents the statement directly below it and is stripped at compile, so it costs nothing in the artifact and lives entirely on the hygiene plane, where the annotation facet scores it. That is what makes it the right home for a rationale a reviewer or an auditor needs: three rules with their reasons in notes build to 244 bytes, the same 244 bytes they would build to with no reasons recorded at all.
Field note

There is a version of this that is not about scores at all. Ask why a rule exists and most prompts cannot answer, because the reason was either never written down or was written into a sentence that got edited three times by people fixing the wording of the rule. A @@ note survives that, since nobody rewrites a comment while tightening an obligation, and it is the artifact an auditor actually wants: not the rule, which they can read, but the decision behind it. The rules in this post are ordinary enough that the reasons look obvious. The ones in your file are not, and the day somebody proposes raising three sentences to five is the day you find out whether the reason was ever recorded anywhere that outlived the person who knew it.

∿ washed up Sep 2, 2026 ∿