← Tidelines/Deep dives

The arithmetic in your prompt

Prompts are full of numbers: at most three sentences, at least two options, call one tool. Writers treat them as emphasis. The checker treats them as constraints it can actually solve, which means it can catch the ones that add up to nothing.

by TypeGlish team6 min read#deep-dives
Every number is a constraint.

TL;DR Every number in a prompt is a constraint, not a suggestion. When two of them can't both be true, like at most 3 sentences and at least 5 sentences, the model quietly satisfies one per reply and the other looks ignored. TypeGlish reads the numbers as real inequalities and proves the conflict at compile time with logic/numeric, so you fix it before it ships.

Ask a builder why their agent's replies keep blowing past the length limit and they will usually blame the model. Sometimes that is fair. More often the prompt contains two numbers that cannot both be honored, and the model is doing the only thing it can: picking one, differently, every time. A number in a prompt is not decoration and it is not emphasis. It is a claim about the shape of a valid answer, and like any claim it can be checked, and like any pair of claims it can be inconsistent. Most prompt tools cannot see that far, because to them "at most 3" is just three words. To a checker with a solver behind it, it is an inequality.

§1A number is a promise the checker can hold

There are two ways to write a magnitude, and only one of them is checkable. "Keep replies short" names a feeling; nothing can hold the model to it and nothing can hold you to it either, which is why it drifts. "Reply in at most 3 sentences" names a bound. The difference is not style. A bound is a value a checker can compare, count against, and reason about; a vibe is invisible to every tool you own. The moment you commit to a number, you have handed the compiler something it can actually work with, including the ability to tell you when you have committed to two numbers that fight.

A vague magnitude constrains nothing. A precise one can be proven inconsistent. Only one of those is a spec.

§2Two limits, one reply

Here is how it happens in real prompts, and it is never on purpose. Someone adds a brevity rule for scannability. Months later, someone else adds a thoroughness rule for the returns flow. Both are sensible. Together they demand a reply that is at most three sentences and at least five, on every message, which is no reply at all.

support.tg✕ won't compile
# Role
You are a support agent for an online electronics retailer.

# Constraints
@@ brevity: replies over three sentences get skimmed
- MUST keep every reply to at most 3 sentences.
@@ thorough: a returns explanation has to cover the conditions
- MUST answer in at least 5 sentences.
Each rule reads perfectly on its own. The defect is only in the pair, and only if you do the arithmetic: there is no whole number of sentences that is both at most 3 and at least 5.

A prose linter cannot catch this, because there is nothing wrong with either sentence. You need something that reads the numbers as numbers. TypeGlish hands both bounds to Z3, its solver, asks whether any value satisfies them at once, and gets back "no." That "no" is a proof, not a heuristic, so it is a blocking error rather than a warning.

tg check - output
support.tg:6:1  error  logic/numeric  Numeric conflict - "at most 3 sentences" and
  "at least 5 sentences" can't both hold.
support.tg:8:1  error  logic/numeric  Numeric conflict - "at most 3 sentences" and
  "at least 5 sentences" can't both hold.

 1 file - 2 error, 0 warning, 0 info
The checker names both numbers and both lines. It is not guessing that these might conflict; it has proven that no answer satisfies both, which is exactly the thing a human review of two far-apart rules almost never does.

§3The fix is a decision, not a bigger number

The wrong instinct is to nudge a number until the error goes away. The right one is to notice the checker has surfaced a decision you never actually made: which rule governs which reply. There are two honest answers. Reconcile the bounds into one satisfiable band, or scope them so they never apply to the same message. Scoping keeps both intentions, brevity for routine replies and depth for the returns explanation, without asking a single reply to be two lengths at once.

support.tg✓ compiles
# Role
You are a support agent for an online electronics retailer.

# Constraints
@@ brevity: a routine reply over three sentences gets skimmed
- MUST keep a routine reply to at most 3 sentences.
@@ thorough: a returns explanation has to cover the conditions
- WHEN explaining the return policy THEN answer in at least 5 sentences.
Now the two bounds live in different worlds: the cap governs the routine reply, the floor governs the returns explanation. No single message has to satisfy both, so the solver is content.
tg check - output
 1 file - 0 error, 0 warning, 0 info
The arithmetic works out. And because the scope is written into the rule, a future edit that widens the routine cap can't quietly reintroduce the conflict without the checker noticing.

§4The rest of the arithmetic

Length bounds are the most common case, but the same solver reasons about every quantity you put in a prompt. It is worth knowing what else it can prove, because these are the numbers that break agents quietly:

  • Action counts (logic/action-count) - one action given two different counts, like "call five tools" and "call six tools" for the same step. The agent cannot do both.
  • Set cardinality (logic/cardinality) - two counts for one set that cannot both hold, like "there is only one escalation path" and "there are many escalation paths."
  • Insufficient sets (logic/insufficient-set) - a directive that needs more items than the set actually holds, like "offer three alternatives" when only two are defined.

They share a shape with the length case: each is a claim about a quantity, each is invisible to a reader skimming for tone, and each is decidable the instant you treat the numbers as numbers. That is the whole move. Vague magnitudes ask the model to guess and let it guess differently every time. Precise ones let a solver check your work, and occasionally tell you that the work does not add up, which is the most useful thing a spec can do before it ships.

Field note

The logic/numeric proof is Z3-backed and shipped in TypeGlish 0.1.0. A numeric conflict is the arithmetic cousin of the modal conflicts in your prompt argues with itself: same idea, different operator, both proven rather than guessed. And the reason vague magnitudes never get caught is the reason to write measurable rules in the first place, which is the before-and-after in the rewrite of a bloated support prompt.

FAQCommon questions

Why does my agent ignore the length limit in my prompt?
The usual cause is a second rule that contradicts the first. If one rule says at most 3 sentences and another says at least 5, no output satisfies both, so the model picks one per reply and the limit looks ignored. A vague limit like "keep it short" has the opposite problem: there is no number to hold it to. Give every length rule a concrete bound and make sure two bounds cannot both fire on the same reply.
Can I set both a minimum and a maximum length in a system prompt?
Yes, as long as the minimum is not greater than the maximum and the two apply to the same situation. At least 2 and at most 5 sentences is a satisfiable band. At least 5 and at most 3 is a numeric impossibility, and TypeGlish rejects it at compile time with logic/numeric. If you want a short default but a longer answer for one case, scope the longer bound with a WHEN so the two never apply to the same reply.
How do I stop two rules in my prompt from contradicting each other on length?
Decide which rule wins instead of shipping both and hoping. Either reconcile the numbers into one satisfiable band, or scope them so they never fire together, for example at most 3 sentences for a routine reply and WHEN explaining the return policy THEN at least 5 sentences. Running npx typeglish check proves whether any two numeric rules can both be true; if they cannot, it fails the build with logic/numeric and names the two numbers.
What is the difference between at most and at least in TypeGlish?
They are inclusive numeric bounds. "At most 3" means 3 or fewer; "at least 5" means 5 or more. The checker reads them as real inequalities and solves them, so it can prove when a set of bounds has no solution (at most 3 and at least 5 cannot both hold) rather than treating the words as decoration. That is why a numeric conflict is a blocking error, not a style warning.
∿ washed up Jul 19, 2026 ∿