← Tidelines/Best practices

You are about to add one more rule

Nobody reviews the fourth line of a prompt as carefully as they reviewed the first forty. The launch checklist ran once, months ago. This is the checklist for every Tuesday after that.

by TypeGlish team9 min read#best-practices
One more line.

TL;DR Eight checks before a new rule lands in a live agent prompt, each with a command behind it, because the naive version of the rule passes all of your automatic gates: it compiles with 0 errors, survives check --strict, and scores A (93/100) while promising delivery dates the same file forbids and offering twice the goodwill the same file caps. A green check is the floor of the gate, not the gate.

Ops messages you on Monday. Late orders are generating complaints, so could the bot tell people their order ships within 24 hours and offer them ten percent off. It is one line. You have written a thousand lines like it. The prompt is in production, it has been fine for four months, and the whole change is about ninety seconds of typing.

That is the moment this list is for. Not the launch, which got a review, a sign-off, and a checklist of its own. Not the incident, which gets a triage order and everybody's attention. The steady state, where a prompt accumulates one reasonable line at a time until it is a different prompt than the one anybody approved.

§1The file, and the rule that passes everything

Here is the live prompt. It is genuinely good: every rule bounded or scoped, an annotation over each one, a test, and a clean bill of health at A (95/100) with nothing at all in the findings list.

orders.tg - live, before the edit✓ 0/0/0 · A (95/100)
# Role
@@ role: the addressee, so every rule below has a doer
You are an order-support agent for Acme, an online retailer.

# Facts
@@ order: the record every conversation is about
The %order number% IS a six-digit code.
@@ total: the number the goodwill cap is a percentage of
The %order total% IS the amount the customer paid at checkout.
@@ goodwill_cap: above this an agent needs a human to sign off
The %goodwill cap% IS at most 5 percent of the %order total%.

# Constraints
@@ brevity: three sentences keeps a chat reply scannable
- MUST keep every response to at most 3 sentences.
@@ ask_order: the order number unlocks account context
- ALWAYS ask for the %order number%.
@@ no_dates: a promised date the warehouse misses becomes a second complaint
- NEVER promise a delivery date.
@@ angry: a heated customer gets a human
- IF the customer is angry THEN escalate to a human agent.

$TEST late_order
  - input:: My order was supposed to arrive Tuesday and it is still not here.
  - expect::
    - at most 3 sentences
Two of these rules are about to matter: NEVER promise a delivery date, and a goodwill cap declared as a fact at 5 percent. Ops has just asked for a delivery promise and ten percent.

Now the ninety-second version of the change. One conditional, both halves of the ask, phrased the way the request was phrased.

the one-line edit✓ 0 error, 0 warning, 0 info · A (93/100)
@@ late_orders: ops asked for this on Monday
- IF an order is late THEN promise a delivery date within 24 hours and offer 10 percent off the %order total%.
This line contradicts two rules sitting eight lines above it, in a file that grades A. Read the next figure before you decide the checker is broken; it isn't, and understanding why is most of the value here.
tg check & tg score - after the naive edit
$ npx typeglish check naive.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score naive.tg
naive.tg — A (93/100)  proven errors: none  tiers: base+z3
  planes  runtime 91 (what the model reads) · hygiene 100 (source only)
Zero findings of any severity, and an A. Every automatic gate you own is green on a rule that instructs the agent to do the two things the file most explicitly forbids. This is not a bug: a prover reaches a conflict only when two rules land in the same slot, and a delivery date within 24 hours is not the same slot as a delivery date. The bound you added to be helpful is what hid the rule.
A green check means nothing was proven. It has never meant nothing is wrong.

§2Three checks on the words, before you save the file

1. Write the rule with the nouns and verbs the file already uses. This is the highest-value item on the list and it costs nothing. The prover compares rules that land in the same slot, so your phrasing decides whether it can see a collision at all. Watch the same policy pass and fail depending on three extra words.

the same policy, two phrasings
$ npx typeglish check bare.tg        # - MUST promise a delivery date.
bare.tg:19:1  error  logic/contradiction  Logical conflict — "promise a delivery
  date" is both required and forbidden. Keep one, or scope the two rules so they cannot
  both apply (IF <condition> THEN ...). (conflicts with line 12)

 1 file — 2 error, 0 warning, 0 info

$ npx typeglish check qualified.tg   # - MUST promise a delivery date within 24 hours.
✓ 1 file — 0 error, 0 warning, 0 info
Two errors, then none. Same intent, same policy, same file. If you want the compiler to referee your edit, write the rule in the file's own words first and add the qualifier second, having seen what the plain version collides with. The general mechanism is two rules, no words in common: a slot is the action plus the object, and anything that changes the object moves the rule out of range of the prover.

2. One action per rule. The ask arrived as a compound and the naive line kept it that way: promise a date and offer a discount. A compound rule cannot be individually scoped, tested, deleted or measured, and the checker will not complain, because a sentence with three verbs in it is legal English. This one is on you. Two policies means two lines.

3. Commit to a modal, and put the number in. The other thing that happens under time pressure is hedging your way out of a decision you did not want to make. Ops said a small discount, so you write should probably offer a small discount, which reads like caution and lands as permission to skip the rule entirely.

tg check - the hedged version
hedge.tg:23:3  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).

 1 file — 0 error, 0 warning, 1 info
An info line, so it does not block and it does not fail your build. Worth reading anyway: a suggestion the model may skip is a precise description of what you shipped, and hardness drops from 100 to 86 on the score card because a statement takes the minimum over its force words.

§3Four checks on the file, before you open the PR

4. Run check, then run check --strict. They give different answers and different exit codes, which means only one of them is your CI result. Strict escalates every IMPORTANT finding to a blocking error. The everyday case on an edit like this one is a tool you imported for the new rule and then never actually pointed at, because the rule ended up phrased as prose.

the same file, two exit codes
$ npx typeglish check refund.tg
refund.tg:2:3  warn   structure/unused-import  Imported tool "issue_refund" is never used.

 1 file — 0 error, 1 warning, 0 info        # exit 0

$ npx typeglish check refund.tg --strict
refund.tg:2:3  error  structure/unused-import  Imported tool "issue_refund" is never used.

 1 file — 1 error, 0 warning, 0 info        # exit 1
One finding, two severities, two exit codes. A dangling import is harmless right up until somebody deletes the tool it names, or trusts the prompt's declared surface when wiring the runtime.

5. Compare the score to the number this file scored yesterday, not to a grade floor. A grade floor is a launch gate. It cannot see a regression, because there are eleven points of room between an A and a B and a year of small edits fits in there comfortably. Put the previous number in the command.

the floor that catches it, and the one that doesn't
$ npx typeglish score hedge.tg --min B
hedge.tg — A (93/100)  proven errors: none  tiers: base+z3
  planes  runtime 91 (what the model reads) · hygiene 100 (source only)
# exit 0 - passes

$ npx typeglish score hedge.tg --min 95
typeglish score: 93 (A) is below the --min floor 95
# exit 1 - caught
--min takes a grade or a number. Use the number, keep it in the repo next to the prompt, and raise it when a change earns it. A ratchet is the only version of this gate that keeps working after month three.

6. Look for what your rule did to the rules already there. A new line does not only get judged; it changes the standing of its neighbours. Two failures show up constantly on edits. The first is restating something the file already says, at a weaker strength, because you did not know it was there.

tg check - the rule that kills an old one
$ npx typeglish check softer.tg      # - SHOULD ask for the %order number%.
softer.tg:23:3  error  logic/force-subsumption  Subsumed on the obligation axis:
  line 17 already guarantees this rule ("always" entails "should" for the same action), so
  it adds nothing but waver. Delete it, or give it a genuinely distinct scope.

 1 file — 1 error, 0 warning, 0 info
Adds nothing but waver is the phrase to remember. A softer restatement of an existing rule does not reinforce it, it gives the model a weaker version it is always allowed to satisfy instead.

The second is arithmetic. Your rule carries a number, the file carries numbers, and the two only have to be checked against each other once, which is now.

tg check - the number that can't hold
$ npx typeglish check longer.tg      # - MUST explain the delay in at least 5 sentences.
longer.tg:15:1  error  logic/numeric  Numeric conflict — "at most 3 sentences" and
  "at least 5 sentences" can't both hold. (conflicts with line 12)
longer.tg:23:1  error  logic/numeric  Numeric conflict — "at most 3 sentences" and
  "at least 5 sentences" can't both hold. (conflicts with line 8)
longer.tg:23:1  info   prompt/unintroduced-definite  "the delay" retrieves something this
  document never introduces - a model must guess which delay is meant.

 1 file — 2 error, 0 warning, 1 info
This is the class of collision the checker is genuinely reliable on, because a bound lowers into an interval and a solver settles it. Which is exactly why item 1 matters so much: get your rule into a slot the prover can reach and you inherit this quality of answer for free.

7. Cover the new rule with a $TEST, and read the coverage number rather than the tick. A passing suite that never exercises your rule tells you nothing about your rule. Coverage attribution follows the words, so an expectation phrased in the rule's own language attributes to it while a bare assert often attributes to nothing.

tg test --dry - coverage before and after
$ npx typeglish test orders.tg --dry          # one case, one bare assert
✓ orders.tg  coverage: 0/3 rules exercised
  · late_order — "My order was supposed to arrive Tuesday and it is still not " (not run)
      ✓ at most 3 sentences
✓ 1 prompt — 0 failed

$ npx typeglish test orders.tg --dry          # two cases, asserts + prose in the rules' words
✓ orders.tg  coverage: 2/4 rules exercised
  · late_order — "My order was supposed to arrive Tuesday and it is still not " (not run)
      ✓ at most 3 sentences
      ✓ rubric 0.00 — not run (--dry)
  · late_shipment — "My order still has not shipped and it is three days past the" (not run)
      ✓ contains "24 hours"
      ✓ rubric 0.00 — not run (--dry)
✓ 1 prompt — 0 failed
Both runs report a passing suite. Only the second one is testing the thing you just added. --dry is fully offline and needs no API key, so this belongs in the same CI step as check; the trade between deterministic asserts and judged prose is worked through in deterministic asserts vs. an LLM judge.

§4One check on the artifact, before you merge

8. Build it and diff what the model actually receives. You edited source. The model reads a build artifact, with the annotations stripped, the tests removed, the name literals unwrapped and the declarations folded. The diff of that file is the real changelog of your edit, and it is usually shorter than you expect.

the actual change, in the actual prompt
$ npx typeglish build orders.tg
✓ built .typeglish/dist/orders.txt ← orders.tg (1e612329ac3a, full)

$ diff orders-before.txt .typeglish/dist/orders.txt
11a12,13
> - IF an order has passed its promised ship date THEN tell the customer that it ships within 24 hours.
> - IF a goodwill offer is above the goodwill cap THEN escalate to a human agent.
Two lines added and nothing else moved. Two @@ notes and a whole $TEST block went into the source in the same edit and cost the artifact zero bytes, which is the answer to whether documenting a rule is worth the tokens. The hash goes in your change record: it is how you later prove production was running this build and not somebody's hand-edit.

§5The rule, rewritten

Run the list against Monday's ask and it stops being one line, because it was never one policy. It was a shipping communication and a goodwill authority, and the goodwill half is a decision above your pay grade: ops asked for ten percent in a file whose cap is five.

That is the item the checklist really earns. Not the typo, the escalation. Ten percent is either a new cap, which is one edit in one place with a sign-off attached, or it is an escalation path. It is not a number you quietly put in a second rule.

orders.tg - the two rules that shipped✓ 0/0/0 · A (92/100)
# Role
@@ role: the addressee, so every rule below has a doer
You are an order-support agent for Acme, an online retailer.

# Facts
@@ order: the record every conversation is about
The %order number% IS a six-digit code.
@@ total: the number the goodwill cap is a percentage of
The %order total% IS the amount the customer paid at checkout.
@@ goodwill_cap: above this an agent needs a human to sign off
The %goodwill cap% IS at most 5 percent of the %order total%.

# Constraints
@@ brevity: three sentences keeps a chat reply scannable
- MUST keep every response to at most 3 sentences.
@@ ask_order: the order number unlocks account context
- ALWAYS ask for the %order number%.
@@ no_dates: a promised date the warehouse misses becomes a second complaint
- NEVER promise a delivery date.
@@ angry: a heated customer gets a human
- IF the customer is angry THEN escalate to a human agent.
@@ ship_window: a ship date is a warehouse fact; a delivery date is a courier guess, and only one of them is ours to promise
- IF an order has passed its promised ship date THEN tell the customer that it ships within 24 hours.
@@ goodwill_route: ops asked for 10 percent, which is above the cap, so this is an escalation and not a discount
- IF a goodwill offer is above the %goodwill cap% THEN escalate to a human agent.

$TEST late_order
  - input:: My order was supposed to arrive Tuesday and it is still not here.
  - expect::
    - at most 3 sentences
    - asks for the order number

$TEST late_shipment
  - input:: My order still has not shipped and it is three days past the ship date.
  - expect::
    - contains "24 hours"
    - tells the customer that it ships within 24 hours
The ship_window annotation is the load-bearing part of this edit. It records that a ship date and a delivery date are different promises, which is the distinction that lets this rule coexist with NEVER promise a delivery date rather than sneak past it. Without the note, the next person reads two rules about dates and deletes the wrong one.
tg score - after the real edit
orders.tg — A (92/100)  proven errors: none  tiers: base+z3
  planes  runtime 89 (what the model reads) · hygiene 100 (source only)
  facets  enforceability 65 x.21 · hardness 100 x.12 · directness 90 x.08 · consistency 100 x.17
          · structure 100 x.12 (hygiene) · annotation 100 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  lever   enforceability 65/100 (up to +7 overall) — Write rules as MUST / NEVER <verb> … with
          concrete bounds ("at most 3 sentences"), not vague qualities.
A (95/100) to A (92/100). The correct edit lowered the score, because enforceability fell from 77 to 65: a conditional is inherently less enforceable than a bound, and this change added two of them. Item 5 did its job. It made you look, and then you accept it, because the alternative is an unconditional rule that would be wrong. A regression gate that you never override is set too loose to be useful.
Field note

Eight items sounds like a lot until you notice that six of them are one command each and two are decisions. The realistic version is a script in the repo running check --strict, score --min <last>, test --dry and a build diff, so the machine half is a single invocation and your attention goes where it belongs: on whether the rule is phrased in the file's own words, and on whether anybody actually has the authority to approve ten percent. Every figure on this page is re-run against the real compiler by CI, including the ones showing a defect slipping through, so if a future release starts catching the qualified delivery promise, this post fails the build and gets rewritten.

FAQCommon questions

How do I safely add a rule to a system prompt that is already in production?
Treat the edit as a change to a running system rather than a piece of writing. Phrase the new rule with the nouns and verbs the file already uses, keep it to one action, run check and then check --strict, compare the score against the number the file scored before your edit rather than against a grade floor, look for an existing rule your new one now subsumes or contradicts, cover the rule with a $TEST and read the coverage number, and diff the built artifact so you can see the two lines you actually changed in what the model receives. The launch checklist proves the prompt is fit to ship once. This is the one you run on every edit afterwards.
Does typeglish check catch every problem with a new rule?
No, and assuming it does is the trap. A one-line rule that promises a delivery date within 24 hours and offers 10 percent off compiles with 0 errors, 0 warnings and 0 info and scores A (93/100), in a file whose existing rules say NEVER promise a delivery date and cap goodwill at 5 percent. The prover only reaches a conflict when the two rules land in the same slot, so the qualifier within 24 hours is enough to make the prohibition invisible to it. A green check means nothing was proven, not that nothing is wrong.
What is the difference between typeglish check and check --strict?
Plain check blocks on provable defects and reports everything else as a warning or an info line, so it exits 0. The --strict flag escalates every IMPORTANT finding to a blocking error, which changes the exit code and therefore your CI result. Importing a tool your new rule never actually points at is the everyday case: check reports one warning and exits 0, check --strict reports it as an error and exits 1. Run both on an edit, because the warning you skim past today is the dead reference somebody deletes around next month.
Why did my prompt score go down after I added a correct rule?
Usually because a conditional rule is less enforceable than a bounded one, and enforceability carries the heaviest weight on the score card at x.21. Adding two correct IF/THEN rules to a clean prompt took our example from A (95/100) to A (92/100), with enforceability moving 77 to 65, and nothing was wrong with either new rule. That is what the regression check is for: it is a prompt to go and look at the reason, not an automatic veto. Accept the drop when the reason is sound, and write down why in an annotation so the next person does not re-litigate it.
∿ washed up Jul 31, 2026 ∿