← Tidelines/Best practices

Bake-off: tell it what to do vs. tell it what not to do

The standard advice is to phrase instructions positively: say what the agent should do, not what it should not. We wrote one order-support prompt both ways, gave both the same edit six weeks later, and let the checker referee. Each draft wins a different round, and the verdict is a pairing, not a winner.

by TypeGlish team8 min read#best-practices
The fence and the gate.

TL;DR Write every prohibition as a NEVER rule and pair it with a WHEN ... THEN MUST rule that says what the agent does instead: the positive half gives the model a next move, and the negative half is the only one the checker can defend when a later edit quietly reverses your policy.

Halden Home sells furniture online, and its chat agent handles order questions. The brief for the agent is four things it must not do: promise a delivery date, talk about other retailers, guess at a refund, or take a card number in chat. Two people draft it. One writes the brief as it was given, four prohibitions. The other has read that models follow positive instructions better and writes four directions instead. Both are good prompts. They behave very differently the day somebody else edits them.

§1Two drafts, one brief

Draft A is the fences: four NEVER rules, one per item in the brief, each with a note saying why.

fences.tg - the brief as four prohibitions✓ A (100/100)
# Role
@@ role: one sentence names the speaker, so every later "you" is the agent
You are Wren, the order support agent for Halden Home.

# Constraints
@@ no_date: the carrier owns the delivery date, and a wrong one becomes a complaint
- NEVER state a delivery date.
@@ no_competitor: the agent sells Halden Home, nothing else
- NEVER mention a competitor.
@@ no_refund_amount: the refunds team sets the amount after inspection
- NEVER quote a refund amount.
@@ no_card: a card number in chat is a PCI incident
- NEVER ask for a card number.

Draft B is the directions: for each prohibition, the thing the agent does instead. A direction has to point at something, so draft B also grows a # Context line to introduce the tracking link and the refunds team it sends people to.

directions.tg - the brief as four directions✓ A (99/100)
# Role
@@ role: one sentence names the speaker, so every later "you" is the agent
You are Wren, the order support agent for Halden Home.

# Context
@@ context: introduces the link and the team the rules point at
Every order has a tracking link from its carrier. Halden Home has a refunds team that inspects every return.

# Constraints
@@ share_tracking: the carrier's page is the only live estimate
- MUST share the tracking link.
@@ own_range: the agent sells Halden Home, nothing else
- MUST recommend Halden Home products only.
@@ refunds_team: the refunds team sets the amount after inspection
- WHEN a customer asks about a refund THEN MUST transfer them to the refunds team.
@@ pay_link: payment happens on the hosted page, never in chat
- WHEN a customer wants to pay THEN MUST send the secure payment link.
tg score - both drafts
$ npx typeglish score fences.tg
fences.tg — A (100/100)  proven errors: none  tiers: base+z3
  planes  runtime 100 (what the model reads) · hygiene 100 (source only)
  rules   4 of 4 rule-shaped lines read

$ npx typeglish score directions.tg
directions.tg — A (99/100)  proven errors: none  tiers: base+z3
  planes  runtime 99 (what the model reads) · hygiene 100 (source only)
  rules   4 of 4 rule-shaped lines read
Round one is a tie, near enough. Every line reads on both sides; the one point draft B gives up is on directness. Without its context line, draft B scored A (97/100) and drew a prompt/unintroduced-definite note on the refunds team: a direction names a thing, and the thing has to exist in the prompt. A prohibition does not have that problem.

§2Round two: the edit six weeks later

Prompts are not finished when they ship. Six weeks in, two teams want changes. Logistics is tired of repeat where-is-my-order chats and wants the agent to give a date. Merchandising has launched a price-match promise, which by its nature names the other retailer. Neither team reads the whole prompt. Both append a rule at the bottom, and the same two rules go into both drafts.

the edit - appended to both drafts
@@ wismo: logistics wants fewer repeat where-is-my-order chats
- WHEN a customer asks where their order is THEN MUST state a delivery date.
@@ price_match: the new price-match promise names the other retailer
- WHEN a customer asks about price matching THEN MUST mention a competitor.

Both edits reverse a decision in the original brief. Here is what the checker says about each draft afterwards.

tg check - the fences draft, edited✗ blocked
$ npx typeglish check fences-edit.tg
fences-edit.tg:7:1  error  logic/contradiction  Conflicts with line 15. Logical conflict — "state a delivery date" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                ↳ line 15: - WHEN a customer asks where their order is THEN MUST state a delivery date.
fences-edit.tg:9:1  error  logic/contradiction  Conflicts with line 17. Logical conflict — "mention a competitor" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                ↳ line 17: - WHEN a customer asks about price matching THEN MUST mention a competitor.
fences-edit.tg:15:1  error  logic/contradiction  Conflicts with line 7. Logical conflict — "state a delivery date" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                ↳ line 7: - NEVER state a delivery date.
fences-edit.tg:17:1  error  logic/contradiction  Conflicts with line 9. Logical conflict — "mention a competitor" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                ↳ line 9: - NEVER mention a competitor.

✗ 1 file — 4 error, 0 warning, 0 info

$ npx typeglish score fences-edit.tg
fences-edit.tg — F (83/100)  proven errors — grade capped at F  tiers: base+z3
Both edits caught, each from both ends, and the file refuses to build. The NEVER on L7 and the MUST on L15 put the same action, state a delivery date, on opposite sides, which is the one shape a contradiction proof needs. The two teams now have to talk to whoever owns the brief before anything ships.
tg check - the directions draft, edited✓ A (99/100), false green
$ npx typeglish check directions-edit.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score directions-edit.tg
directions-edit.tg — A (99/100)  proven errors: none  tiers: base+z3
  planes  runtime 99 (what the model reads) · hygiene 100 (source only)
  rules   6 of 6 rule-shaped lines read
Same edit, same grade as before it. The agent is now told to share the tracking link and to state a delivery date, and to recommend only Halden Home products and to mention a competitor. Nothing in the file says those pairs are in tension, so nothing is.

§3Why the directions draft cannot object

A contradiction proof needs one action that one rule requires and another forbids. Draft B never forbids anything. MUST share the tracking link and MUST state a delivery date are two obligations with different verbs and different objects; a model can do both in one reply, so as far as logic goes they can both hold. The brief's real decision, we do not give dates, was never written down. It lived in the head of the person who chose the direction.

The only on the range rule looks like it should help, and it does not. MUST recommend Halden Home products only is about recommending; the edit is about mentioning. No action is shared, so no proof reaches across. That is not the checker being timid. Polar verbs do fold, so MUST withhold and MUST display collide even with no words in common, as two rules, no words in common showed. But recommend only ours and mention theirs are not poles of one action; they are two actions a reader connects through the policy behind them. The checker cannot see the policy. It can only see a NEVER.

That is the thing the negative framing actually buys. A prohibition is a claim about every future rule in the file, so it is the only kind of line that can turn someone else's edit red. A direction is a claim about one behaviour, and it says nothing about the next rule. This is the same failure as your prompt argues with itself, from the other side: there, two rules disagree and the model picks one silently; here, they disagree and nobody can prove it.

§4Round three: what the model gets

Now the round the checker does not score, and the reason the standard advice exists. Put a customer in front of draft A: when is my sofa going to turn up, it was meant to be here Friday. The prompt says NEVER state a delivery date and nothing else about deliveries. The model knows what it may not say and has no instruction for what to say. So it improvises: an apology and a vague promise, a refusal that reads as stonewalling, or a soft estimate like usually within a few days, which is a delivery date with the edges filed off.

Draft B handles the same message well. It shares the tracking link, which is the answer the customer can actually use. That is what the positive-framing advice is right about: a rule that names the alternative gives the model a next move, and a bare prohibition leaves a hole exactly where a customer is most likely to push.

The score line cannot see this. Draft A scored a perfect A (100/100) with a hole in the middle of its most common contact type. Grades measure whether the rules are readable, provable and consistent; they do not measure whether the rules cover the conversation. That is a job for test cases written from real customer messages, which is where the final draft ends up.

§5The verdict: a fence with a gate

Each draft won one round. The fences won the edit, the directions won the customer, and the scores tied. So the final prompt does not pick one. It keeps every NEVER from draft A and gives each one a companion from draft B, written as a WHEN ... THEN MUST on the situation where a customer walks into the fence. The fence is the policy; the gate is what to do at it.

final.tg - four fences, four gates, a test for each pair✓ A (99/100)
# Role
@@ role: one sentence names the speaker, so every later "you" is the agent
You are Wren, the order support agent for Halden Home.

# Context
@@ context: introduces the link and the team the rules point at
Every order has a tracking link from its carrier. Halden Home has a refunds team that inspects every return.

# Constraints
@@ no_date: the carrier owns the delivery date, and a wrong one becomes a complaint
- NEVER state a delivery date.
@@ share_tracking: the fence needs a gate, so the customer still gets an answer
- WHEN a customer asks where their order is THEN MUST share the tracking link.
@@ no_competitor: the agent sells Halden Home, nothing else
- NEVER mention a competitor.
@@ own_range: what to do instead of naming another retailer
- WHEN a customer asks for a cheaper option THEN MUST recommend a Halden Home product.
@@ no_refund_amount: the refunds team sets the amount after inspection
- NEVER quote a refund amount.
@@ refunds_team: where the refund question goes instead
- WHEN a customer asks about a refund THEN MUST transfer them to the refunds team.
@@ no_card: a card number in chat is a PCI incident
- NEVER ask for a card number.
@@ pay_link: payment happens on the hosted page, never in chat
- WHEN a customer wants to pay THEN MUST send the secure payment link.

$TEST where_is_it
  - input:: When is my sofa going to turn up? It was meant to be here Friday.
  - expect::
    - shares the tracking link
    - never states a delivery date
    - matches /^(?!.*(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday))/

$TEST cheaper
  - input:: I saw this lamp cheaper somewhere else, can you match it?
  - expect::
    - recommends a Halden Home product
    - never mentions a competitor

$TEST refund
  - input:: How much will I get back for the chair I returned?
  - expect::
    - transfers them to the refunds team
    - contains "refunds team"

$TEST pay
  - input:: I want to pay the balance on my order now.
  - expect::
    - sends the secure payment link
    - never asks for a card number
    - matches /^(?!.*card number)/
tg score and test --dry - final.tg
$ npx typeglish score final.tg
final.tg — A (99/100)  proven errors: none  tiers: base+z3
  planes  runtime 99 (what the model reads) · hygiene 100 (source only)
  rules   8 of 8 rule-shaped lines read · 8/8 covered by $TEST

$ npx typeglish test final.tg --dry
✓ final.tg  coverage: 8/8 rules exercised
Eight rules, all read, all covered. Each $TEST is aimed at one fence-and-gate pair, with an input that walks straight into the fence and an expectation on both halves. The weekday regex on where_is_it is a deterministic assert, so the most likely way to leak a date fails without a judge.

Then the real test: the same edit from §2, appended to the final draft.

tg check - final.tg rules plus the same edit✗ blocked
$ npx typeglish check final-edit.tg
final-edit.tg:11:1  error  logic/contradiction  Conflicts with line 27. Logical conflict — "state a delivery date" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
               ↳ line 27: - WHEN a customer asks where their order is THEN MUST state a delivery date.
final-edit.tg:15:1  error  logic/contradiction  Conflicts with line 29. Logical conflict — "mention a competitor" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
               ↳ line 29: - WHEN a customer asks about price matching THEN MUST mention a competitor.
final-edit.tg:27:1  error  logic/contradiction  Conflicts with line 11. Logical conflict — "state a delivery date" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
               ↳ line 11: - NEVER state a delivery date.
final-edit.tg:29:1  error  logic/contradiction  Conflicts with line 15. Logical conflict — "mention a competitor" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
               ↳ line 15: - NEVER mention a competitor.

✗ 1 file — 4 error, 0 warning, 0 info
The fences still hold with the gates beside them. The gate on L13 sends the where-is-my-order question to the tracking link; the edit on L27 tries to answer the same question with a date, and the NEVER on L11 is what turns that into a blocking error instead of a second, quieter answer.

One honest limit. The checker proves the fence, not the pairing. If someone deletes the gate on L13 tomorrow, the file still checks clean, and the agent is back to improvising at the fence. The where_is_it case is what notices, on the next live run, because its expectation names the tracking link. The rule tells the model; the test tells the next editor.

§6Common questions

Should I tell my AI agent what to do or what not to do?
Both, paired. Write the prohibition as a NEVER rule so a checker can defend it against later edits, and give each one a WHEN ... THEN MUST rule that says what the agent does instead, so the model has a next move when a customer walks into the fence. In this bake-off neither half alone was enough: the prohibitions caught every conflicting edit, and only the directions told the agent how to answer.
Do negative instructions work in a system prompt?
They work as a boundary, not as a plan. NEVER state a delivery date tells the model what is out of bounds, but not what to say to a customer asking when their sofa arrives, so the model improvises. The common advice to phrase instructions positively is about that gap. Keep the NEVER and close the gap with a rule that names the alternative, such as sharing the tracking link.
Why did my checker miss a rule that breaks my prompt's policy?
Probably because the policy was only written positively. A contradiction needs the same action required in one rule and forbidden in another. MUST recommend Halden Home products only shares no action with MUST mention a competitor, so the checker has nothing to compare and the file stays at an A. Add NEVER mention a competitor and the same edit becomes a blocking logic/contradiction.
Does the word only make a rule exclusive?
Not for a different verb. In this bake-off MUST recommend Halden Home products only sat beside a new rule requiring the agent to mention a competitor, and the check passed clean. The model may well read only as a ban, but no proof reaches the other rule. If something is forbidden, write the NEVER that forbids it.
Field note

A quick audit for any prompt you already run: list its NEVER lines in one column and its MUST lines in another, then draw a line from each prohibition to the rule that says what to do instead. A NEVER with no line out of it is a fence with no gate, and it is where your agent improvises; find a real transcript that hits it and write the gate from that. A policy with no NEVER at all is a gate with no fence, and it is where the next edit slips through; write down the decision the direction was quietly carrying. The same instinct, applied to tools rather than words, is in forbid the tool vs. never hand it over: the prohibition you can prove is worth keeping, even when another design looks safer.

∿ washed up Oct 7, 2026 ∿