← Tidelines/Deep dives

Your agent is certain because you said so

Every prompt review reads the MUSTs and the NEVERs. Nobody reads the sentences in between, and those are the ones that tell the model what is true.

by TypeGlish team7 min read#deep-dives
The prompt asserted it.

TL;DR A system prompt carries two kinds of sentence, rules about what the agent does and claims about what is true, and only the first kind is a rule. TypeGlish proves a clash between two claims with logic/epistemic-conflict, but the certainty axis is one word wide on the negative side (cannot, not can not and not never), so a capability claim with nothing to contradict it compiles at 0 error, 0 warning, 0 info and ships to the model as a premise. Write the capability as a rule over something observable.

Open any agent prompt that has been alive for six months and count the lines that are not instructions. You have access to the customer's order history. Our standard delivery window is 3 working days. You cannot see anything the customer has not told you. None of those is a MUST or a NEVER. All of them are in the file, all of them reach the model, and every one of them is a statement about the world that somebody typed and nobody tested.

They get written for good reasons. An agent that refuses everything gets a capability line to loosen it up. An agent that invents things gets a knowledge line to tighten it up. Two sprints apart, two different people, and neither line is a rule anyone would think to put on the rule table.

§1The sentence that is not a rule

Here is a delivery-and-billing prompt for a mobile network. Five content lines, four of them the kind of thing a review would look at, one of them a leftover.

orders.tg - as inherited✗ 1 error
# Role
You are a support agent for Cobalt Mobile.

# Constraints
- MUST greet the customer by name.
- You definitely have access to the order history.
- MUST answer a delivery question in at most 3 sentences.
- NEVER invent a delivery date.
- You cannot have access to the order history.
Line 6 was added to stop the agent hedging on every order question. Line 9 was added, later, to stop it inventing tracking numbers. Both authors were right about the problem in front of them.
tg check orders.tg - output
orders.tg:9:7  error  logic/epistemic-conflict  Epistemic conflict with line 6:
  "definitely" and "cannot" sit on opposite sides of the certainty axis for the same
  claim, and both cannot hold. Keep one stance.

 1 file — 1 error, 0 warning, 0 info
One error, not two. Unlike logic/contradiction, which reports on both lines, this one lands on the later stance and names the earlier. Column 7 is the word cannot itself.

The phrase worth stopping on is the certainty axis. TypeGlish reads a directive for the action it commands, which is the deontic plane every other post here is about. It also reads a plain statement for how strongly it claims to be true, which is a different plane entirely, and the two do not mix. MUST and NEVER are about obligation. definitely and cannot are about possibility. A prompt full of the first kind can still be incoherent in the second.

tg score orders.tg - output
orders.tg — F (65/100)  proven errors — grade capped at F  tiers: base+z3
  ✖ L8 logic/epistemic-conflict: Epistemic conflict with line 6: "definitely" and
    "cannot" sit on opposite sides o
  planes  runtime 69 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 70 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 0 x.17
          structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  lever   consistency 0/100 (up to +17 overall) — Fix the ledger rows with logic/ or
          clarity/ codes; state each fact in one place.
enforceability 70, hardness 100, directness 100. Every facet that measures the rules is fine, because the rules are fine. consistency 0 is carrying the whole failure, and the lever spells out the job: state each fact in one place.

§2The axis is one word wide

Having found a real prover, the natural next move is to trust it, and that is the move to resist. We enumerated the words either side of the axis against the same pair of lines, one file per word, changing nothing else.

tg check prompts/ - four files, one word apart
prompts/might.tg:3:7  error  logic/epistemic-conflict  Epistemic conflict with line 2:
  "might" and "cannot" sit on opposite sides of the certainty axis for the same claim,
  and both cannot hold. Keep one stance.

 4 files — 1 error, 0 warning, 0 info
program: 4 independent files — no $IMPORT compositions, nothing co-checked
Four files: might.tg, may.tg, obviously.tg, can-not.tg. One error. The other three assert the same pair of things in the same order and pass.

The results, run word by word against cannot:

  • Proven conflicts: definitely, certainly, undoubtedly, probably, possibly, might. These are the certainty operators, from the certain corner down to the merely possible one, and each of them contradicts impossible.
  • Nothing at all: surely, clearly, obviously. These read as emphasis rather than as certainty, which is exactly how a human reads them too. Obviously you have access to the order history is a rhetorical shrug, not a claim, and it is treated as one.
  • A hedging advisory instead: maybe, perhaps. These come back as prompt/hedging at info level, on the grounds that the problem with them is softness rather than falsity.
  • And may: clean. You may have access to the order history is permission, not possibility, so it lives on the deontic plane beside MUST and MAY NOT and never meets cannot at all. Its near-twin might is a blocking error. One letter of difference, two different planes.

The negative corner is narrower still. cannot as one word proves the conflict. can not as two words does not. Neither does can never, could not, or a plain do not.

A green check on a claim means nobody wrote its opposite in a spelling the prover recognises.

That is a real boundary and it is worth stating plainly rather than shipping a post that implies the checker guards your facts. It does not. It guards one axis, in one vocabulary, between two statements that are both present in the file. Everything below is what to do about the rest.

§3Resolving it the green way is the dangerous way

An error is an instruction to pick one stance, and there are two ways to comply. Delete the capability claim and the prompt says the agent cannot see order history, which is honest if it is true. Delete the knowledge limit and the prompt says the agent can. Both make the file green. Only one of them is a statement about your actual system.

orders.tg - the cannot line deleted✓ B (80/100)
# Role
You are a support agent for Cobalt Mobile.

# Constraints
- MUST greet the customer by name.
- You definitely have access to the order history.
- MUST answer a delivery question in at most 3 sentences.
- NEVER invent a delivery date.
0 error, 0 warning, 1 info, and the one info is prompt/unintroduced-definite on the order history, which is a discourse finding about the noun and not a word about the claim. Consistency is back to 100.

Build it and read what the model receives, because that is the only text that matters.

tg build - the artifact
 built .typeglish/dist/orders-one-stance.txt ← orders-one-stance.tg (50917b83e95f, full)

# Role
You are a support agent for Cobalt Mobile.

# Constraints
- MUST greet the customer by name.
- You definitely have access to the order history.
- MUST answer a delivery question in at most 3 sentences.
- NEVER invent a delivery date.
The capability claim is in the artifact verbatim, one line above the rule that forbids inventing a delivery date. If no tool actually returns order history, those two lines together are an instruction to produce a delivery date from nowhere and not call it invention.

This is the part that generalises past certainty words. A claim in the context window is not a proposition the model weighs against evidence, it is a premise it reasons from, and there is no evidence in a system prompt. Ask the agent whether it can see the order history and it will say yes, because you told it, and it has nothing to check that against.

Now the same failure with the certainty words removed entirely. Here is a file with a declared policy fact sitting beside the rule that is supposed to prevent the agent using it.

orders-facts.tg - a fact and a prohibition✓ 0/0/0
<$CONFIG>
  $IMPORT tool get_order
</$CONFIG>

# Role
You are a support agent for Cobalt Mobile.

# Constraints
- The %standard delivery window% IS 3 working days.
- You MUST call @[get_order] before you state a delivery date.
- You NEVER state a delivery date that @[get_order] did not return.
0 error, 0 warning, 0 info at C (78/100). Two rules say the delivery date must come from the tool. The line above them hands the model a delivery date. Nothing in the checker reconciles a stated fact with a rule about stating facts, and the artifact carries all three lines in that order.

That is the same shape as a prompt that quotes a 30-day returns window at a company with a 14-day one, which is where the policy doc somebody pasted into your agent ends up. The difference is that a pasted policy at least looks like content. A single declared fact tucked between two rules looks like part of the rule table.

§4Write the capability as behaviour

The rule of thumb that falls out of all of this: never tell the agent what it knows, tell it what to do and where to get it. A capability claim has no observable referent, so it cannot be checked, tested, or scoped. A rule about calling a tool has all three.

orders-final.tg - the same intent, as behaviour✓ A (91/100)
<$CONFIG>
  $REQUIRE variable order_id: one of present, missing
  $IMPORT tool get_order
</$CONFIG>

# Role
@@ role: order-status handling for a mobile network
You are a support agent for Cobalt Mobile.

# Constraints
@@ greeting: the name is on the contact record, so using it costs nothing
- You MUST greet the customer by name.
@@ lookup_first: a delivery date lives in one system, and it is not this file
- You MUST call @[get_order] before you state a delivery date.
@@ no_memory: the tool result is the whole permitted source for a date
- You NEVER state a delivery date that @[get_order] did not return.
@@ brevity: a delivery answer is one fact, and three sentences is generous
- You MUST answer a delivery question in at most 3 sentences.

$SWITCH ON @{order_id}
  - present:: You MUST quote the delivery date from @[get_order].
  - missing:: You MUST ask the customer for an order number.

$TEST missing_order
  - input:: When will my phone arrive?
  - expect::
    - contains "order number"
0 error, 0 warning, 0 info at A (91/100), runtime 91 and hygiene 92. Not one line says what the agent knows.

Three moves, and each one turns a claim into something with a referent.

  • The capability became a tool and a rule. You have access to the order history is unfalsifiable prose. $IMPORT tool get_order plus MUST call @[get_order] before you state a delivery date is a declared name the compiler resolves and a rule the prover can hold. Delete the import and the file stops compiling.
  • The knowledge limit became a typed input. Whether an order number is in hand is not something the model can introspect, it is something the host knows before the first turn. $REQUIRE variable order_id: one of present, missing makes it a value, and a $SWITCH ON over a typed domain has to cover every member or it is a compile error.
  • The refusal got written down. The missing arm says what to do when the lookup cannot run, so the fallback is a rule rather than whatever the model improvises. The $TEST pins it: typeglish test --dry validates the assert offline at coverage 0/4, no API key involved.

F (65/100) to A (91/100), and no policy changed on the way. The prompt makes exactly the same promises about delivery dates it always did. What it stopped doing is asserting things.

Worth naming the cousin of this defect, because they get confused. Conditioning a rule on the model's own confidence, as in IF you are unsure THEN ask, is a different failure with a different fix, and we ran that one as a bake-off against naming the missing fact. Here the prompt is not asking the model to assess its certainty. It is telling it what its certainty is.

§5Common questions

Why does my AI agent make up order details or policy facts?
Often because the prompt told it those facts are available. A line like You have access to the order history is not a permission and it is not a rule, it is an assertion about the world, and an assertion in the context window is a premise the model reasons from rather than a claim it evaluates. If the data is not actually there at runtime, you have instructed the agent to behave as though it is, and the most cooperative way to do that is to produce something plausible. Delete capability claims and write the behaviour instead: MUST call the lookup tool before stating a delivery date.
Does TypeGlish catch contradictions about what the agent knows?
Yes, on one axis and within a narrow vocabulary. logic/epistemic-conflict is a blocking error when two statements make the same claim at opposite ends of the certainty axis: definitely, certainly, undoubtedly, probably, possibly and might all conflict with cannot. The negative corner is exactly the one word. Writing can not as two words, or can never, or never, produces 0 error, 0 warning, 0 info on the identical pair, and so does may, because may is permission rather than possibility.
Should facts live in the system prompt or come from a tool?
A fact belongs in the prompt when it is policy that changes on a review cycle, and in a tool when it changes per conversation. The trap is holding both: a prompt that declares a standard delivery window of 3 working days and also says NEVER state a delivery date that the lookup did not return checks at 0 error, 0 warning, 0 info, because nothing reconciles a stated fact with a rule about stating facts. Both lines reach the model, and the nearest number wins.
How do I stop my agent claiming it can do something it cannot?
Replace every sentence of the form you can, you have access to, or you know with a rule over something observable. Declare the tool with $IMPORT tool and require it in the rule (MUST call the tool before you state a delivery date), declare what the host actually knows as a typed input, and give the missing case its own arm in a $SWITCH so the refusal is written down rather than improvised. A rewrite of that shape took a mobile-network prompt from F (65/100) to A (91/100) with no policy changed.
Field note

Every post here that ends in a false green ends there for the same reason: the checker was handed less than you thought you gave it. A hard wrap hides a bound by breaking the statement it belonged to. A certainty claim hides in plain sight by not being a statement the prover has an opposite for. The practical version is a grep, not a command: search your prompt for you can, you have access, you know, you are able, and read each hit as though it were true, because the model will.

∿ washed up Aug 5, 2026 ∿