> Markdown rendition of https://typeglish.dev/blog/never-send-links ("Never send links includes the tracking link - Tidelines"). Canonical page: https://typeglish.dev/blog/never-send-links · All pages: https://typeglish.dev/llms.txt

[← Tidelines](https://typeglish.dev/blog)/ Deep dives Oct 21, 2026

# Never send links includes the tracking link

A blanket ban is the easiest rule in the world to add: one line, no conditions, written in a hurry after something went wrong. It also owns every narrower case of its noun, including the ones another team wrote a MUST for last spring.

by **TypeGlish team** 7 min read #deep-dives

A ban owns its whole noun.

**TL;DR* NEVER send links* forbids the tracking link too, so a later rule that says *MUST send a tracking link* is not an exception but a contradiction the model settles per chat; write each ban as narrow as the risk it guards against, and let a tool supply the one link the agent is meant to send.

Larch Outdoor sells tents and boots online, and its chat assistant, Wren, answers the question every retailer gets most: where is my order. For a year Wren replied with a tracking link. Then a wave of fake delivery texts went round, customers forwarded a few to support, and the security team added one line to the bottom of the prompt. Nobody touched the order-status rule. A month later, some customers asking where their parcel is get the link. Others get a polite note that, for their security, Wren cannot send links.

## §1 The prompt that checks clean

wren-before.tg - the order rule, plus security's line ✓ 0 error

```typeglish
# Role
You are Wren, the support assistant for Larch Outdoor.

# Constraints
- WHEN the customer asks where their order is THEN MUST send a tracking link.
- MUST keep every reply to at most 3 sentences.
- NEVER ask the customer for a card number.
- NEVER send links.
```

Line 5 is a year old and came from the operations team. Line 8 is a month old and came from security. Each one is a reasonable thing to want.

tg check, score and statements (excerpt) - wren-before.tg ✓ B (87/100)

```
$ typeglish check wren-before.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ typeglish score wren-before.tg
wren-before.tg — B (87/100)  proven errors: none  tiers: base+z3
  planes  runtime 99 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 100 x.21 · hardness 100 x.12 · directness 94 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  lever   annotation 0/100 (up to +12 overall) — 5 statements without a @@ note (L2 L5 L6 L7 L8) — put a "@@ why" line directly above each
  rules   4 of 4 rule-shaped lines read

$ typeglish statements wren-before.tg
  - WHEN the customer asks where their order is THEN MUST send a tracking link.
  When the customer asks where their order is: Requires Wren to send a tracking link.
  partial coverage; ~20 tokens; 0 diagnostics; 0 relations

  - NEVER send links.
  Requires Wren not to send links.
  partial coverage; ~5 tokens; 0 diagnostics; 0 relations
```

Every rule reads, consistency is 100, and the score’s only complaint is the missing `@@` notes. The statements report is the giveaway: the two lines about sending links have no relation to each other, or to anything else.

To a person, line 8 obviously covers line 5. A tracking link is a link. Security did not mean to switch off order tracking, but they wrote a rule whose object is every link there is, and an agent that follows both lines exactly is impossible. So the model does what it always does with two instructions it cannot both obey: it weighs them fresh in each conversation, the way [Your prompt argues with itself](https://typeglish.dev/blog/prompt-contradictions) describes. The customer sees that as a coin flip.

## §2 A ban owns everything under its noun

An obligation and a prohibition on the same action are a contradiction when the prohibition covers everything the obligation asks for. *NEVER send links* covers secure links, short links, tracking links and links to your own help centre. So any *MUST send* with a narrower kind of link on the end is inside the ban, not beside it. Adding detail to the obligation does not carve it out. It just makes it a smaller target that is still hit.

TypeGlish has a code for exactly this shape. Strip the condition off line 5 and change only the noun phrase, so the obligation is the same noun with an adjective in front, and the checker proves the overlap:

wren-secure.tg - the obligation as an adjective on the banned noun ✗ logic/modifier-contradiction

```typeglish
# Role
You are Wren, the support assistant for Larch Outdoor.

# Constraints
- MUST send secure links.
- MUST keep every reply to at most 3 sentences.
- NEVER ask the customer for a card number.
- NEVER send links.
```

tg check and build - wren-secure.tg ✗ build refused

```
$ typeglish check wren-secure.tg
wren-secure.tg:8:3  error  logic/modifier-contradiction  Conflicts with line 5. Contradiction through a modifier: line 5's "must" rule and this "never" rule share one action (send links), and the prohibition covers everything the obligation demands - both cannot hold. Drop one, or narrow the prohibition's scope.
                ↳ line 5: - MUST send secure links.

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

$ typeglish build wren-secure.tg
typeglish build: wren-secure.tg refused — nothing written
```

Read the fix in the message: *narrow the prohibition’s scope*. Not “add an exception to the obligation”. The broad rule is the one that has to move.

> A narrower MUST is not an exception to a broad NEVER. It is a smaller part of what the NEVER already forbids.

## §3 Where the proof stops

The real line 5 does not say *secure links*. It says *a tracking link*, and that pair checked clean. So we kept security’s *NEVER send links* fixed and swapped the obligation through the shapes people actually write, one two-rule file at a time, on typeglish 0.11.0:

the results - each MUST line against NEVER send links

```
# the MUST line                                  typeglish check
MUST send a link.                                logic/contradiction
MUST send the link.                              logic/contradiction
MUST send secure links.                          logic/modifier-contradiction
MUST send a secure link.                         0 error
MUST send tracking links.                        0 error
MUST send a tracking link.                       0 error
MUST send links to the tracking page.            0 error
WHEN ... THEN MUST send secure links.            0 error
```

Each row is a file with the Role line, *NEVER send links.* and the MUST line. The guarded row is the *secure links* line from §2 with line 5’s condition put back. The same split showed up on other nouns: *NEVER offer discounts* against *MUST offer small discounts* fails, against *MUST offer a 10 percent discount* passes; *NEVER offer refunds* against *MUST offer partial refunds* fails, against *MUST offer a partial refund* passes.

Three things decide whether the overlap is proven. The article folds: *a link* and *the link* against *links* are plain contradictions. An adjective in front of the same noun, in the same number, is caught as a modifier contradiction. Past that, the pair goes quiet. A singular against a plural once an adjective is in play, a compound noun like *tracking link*, a quantity like *a 10 percent discount*, or the link moved into a phrase like *links to the tracking page*: none of them are matched to the ban. And a condition on the obligation hides it from the ban entirely, even though an unconditional NEVER applies inside every condition there is.

This is the same narrowness [Rewrite: read me the code we just texted you](https://typeglish.dev/blog/otp-code-rewrite) ran into with *a code* and *the code we texted them*. It is worth knowing where the line is, but the lesson is not to phrase your rules so the checker can see them. The lesson is that the shape itself is the bug: an unconditional ban, written broad, with something inside it that the agent is supposed to do.

## §4 Ban the risk, not the format

Security did not care about links. They cared about a customer clicking something in a chat that looks like Larch Outdoor and landing on a site that is not. Operations did not care about links either: they cared about the customer seeing where their parcel is. Write both of those down, and the two rules stop overlapping in meaning, not just in the checker’s eyes.

- **Narrow the ban to the risk.* NEVER send a link to another website.* The tracking page is Larch Outdoor’s own, so it is outside the ban by what the ban says.

- **Let a tool supply the link.** The agent should not be composing tracking URLs at all. A tool that returns the real link for one order means the only link Wren ever has comes from your system.

- **Test both sides.** One case asks where the order is and expects the tracking page. One case asks for the courier’s website and expects no outside link at all.

wren.tg - the ban narrowed, the link from a tool ✓ A (99/100)

```typeglish
$TOOL get_tracking_link
  - description:: Return the Larch Outdoor tracking page link for one order.
  - input::
    - order_number:: string: the customer's order number.

# Role
@@ role: one sentence names the speaker, so every later "you" is Wren
You are Wren, the support assistant for Larch Outdoor.

# Constraints
@@ tracking: the tracking page is the answer to where is my order, and only the tool knows the real link
- WHEN the customer asks where their order is THEN MUST call @[get_tracking_link].
@@ phishing: security's real worry is a link to somebody else's site in a chat that looks like ours
- NEVER send a link to another website.
@@ brevity: three sentences keeps a chat reply readable on a phone
- MUST keep every reply to at most 3 sentences.
@@ card_numbers: card numbers never pass through the chat
- NEVER ask the customer for a card number.

$TEST where_is_order
  - input:: Where is my order? The number is LO-48213.
  - expect::
    - calls get_tracking_link
    - contains "larchoutdoor.com/track"
    - at most 3 sentences

$TEST courier_site
  - input:: Can you just send me the courier's website so I can chase them myself?
  - expect::
    - does not send a link to another website
    - matches /^(?![\s\S]*https?:\/\/(?!larchoutdoor\.com))/
```

The `@@` notes record why each rule exists, so the next person who wants to add *NEVER send links* can read that security’s concern is already handled. The regex assert fails on any `http` link whose host is not Larch Outdoor’s.

tg check, score, test --dry and build - wren.tg ✓ A (99/100)

```
$ typeglish check wren.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ typeglish score wren.tg
wren.tg — A (99/100)  proven errors: none  tiers: base+z3
  planes  runtime 99 (what the model reads) · hygiene 100 (source only)
  facets  enforceability 100 x.21 · hardness 100 x.12 · directness 94 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 100 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  rules   4 of 4 rule-shaped lines read · 3/4 covered by $TEST

$ typeglish test wren.tg --dry
✓ wren.tg  coverage: 3/4 rules exercised
  uncovered (no case shares the rule's wording):
    L18  ask the customer for a card number.
  · where_is_order — "Where is my order? The number is LO-48213." (not run)
      ✓ contains "larchoutdoor.com/track"
      ✓ at most 3 sentences
      · rubric — 1 prose expectation, judged on a live run (needs ANTHROPIC_API_KEY)
  · courier_site — "Can you just send me the courier's website so I can chase th" (not run)
      ✓ matches /^(?![\s\S]*https?:\/\/(?!larchoutdoor\.com))/
      · rubric — 1 prose expectation, judged on a live run (needs ANTHROPIC_API_KEY)
✓ 1 prompt — 0 failed

$ typeglish build wren.tg
✓ built .typeglish/dist/wren.txt ← wren.tg (2f4866cb02d1, full)

$ cat .typeglish/dist/wren.txt
# Role
You are Wren, the support assistant for Larch Outdoor.

# Constraints
- WHEN the customer asks where their order is THEN MUST call get_tracking_link.
- NEVER send a link to another website.
- MUST keep every reply to at most 3 sentences.
- NEVER ask the customer for a card number.
```

The card-number rule is the one uncovered line; it was not part of this change, and a case for it belongs with the payment rules. The tool schema and the test cases stay out of the artifact: the model reads four rules and nothing that argues with them.

A clean check on this file means less than it looks like, for the reasons in §3: the original pair checked clean too. What makes this version right is that the ban now says what security meant, and the two tests pin both halves of the behaviour. If someone later puts the blanket line back, `where_is_order` is the case written to catch it on a live run.

## §5 The blanket-ban audit

Every prompt that has been through an incident has a few of these. They take ten minutes to find.

- **List every NEVER with no condition.** Unconditional bans are the ones that apply inside every other rule’s scope. *NEVER send links*, *NEVER offer discounts*, *NEVER mention competitors*, *NEVER discuss account details*.

- **For each one, search the prompt for its noun.** Every other line that asks the agent to do something with a link, a discount or an account detail is inside that ban. Read each pair as a question: which one should win when both apply?

- **Ask what incident the ban was written after.** The answer is the real rule. A ban written after a phishing scare is about outside links. A ban written after a discount was given away is about discounts the agent invents, not the ones a tool approves.

- **Rewrite the ban to the risk, or scope it.** Narrow the object, or put a condition on the ban that the legitimate case falls outside. [Your prompt is a default and its exceptions](https://typeglish.dev/blog/default-and-exceptions) covers how to tell which rule is the default.

- **Pin the legitimate case with a test.** The ban is the line most likely to come back in its broad form, because it is short and feels safe. The test is what notices.

The habit underneath is small. When you write a NEVER, say the object out loud and ask whether anything in the rest of the prompt is a kind of it. **A ban is a claim about every member of its noun.** Write it as small as the thing you are actually afraid of.

## §6 Common questions

**Why does my support agent refuse to send the tracking link?**

Look for a blanket ban elsewhere in the prompt, usually NEVER send links or NEVER include URLs, added after a phishing scare. A tracking link is a link, so the ban covers it, and the model has to choose between the ban and the order-status rule in every chat. Narrow the ban to the risk it was written for, such as links to other websites, and the two rules stop overlapping.

**Can a NEVER rule have exceptions in a system prompt?**

Only if the exception is written into the ban. A later rule that says MUST for a narrower case does not carve anything out; it contradicts the ban whenever both apply. Either scope the ban with a condition the exception falls outside, or write the ban so its object does not include the thing you want the agent to do.

**Does TypeGlish catch a NEVER rule that conflicts with a more specific MUST rule?**

Sometimes. When the specific rule is the same noun with an adjective in front, like NEVER send links against MUST send secure links, typeglish 0.11.0 reports logic/modifier-contradiction and refuses to build. When the narrower thing is a compound noun such as a tracking link, a singular against a plural, or a phrase like links to the tracking page, the pair checks clean. Treat a clean check as the floor and audit blanket bans by hand.

**How do I stop my AI agent sending phishing links without blocking order tracking?**

Write the ban against the risk, not the format: NEVER send a link to another website. Then let a tool supply the one link the agent is meant to send, so the tracking URL comes from your system rather than the model, and add a test that asserts no outside link appears when a customer asks for one.

Field note

`logic/modifier-contradiction` is a blocking error: when it fires, `typeglish build` writes nothing, so the broad ban and the narrow obligation cannot ship together. Its message tells you to narrow the prohibition, which is the right repair even in the cases the prover cannot see. Pair it with `typeglish statements`: a NEVER with *0 relations* beside a MUST on the same noun is worth a second look.

∿ washed up Oct 21, 2026 ∿
