← Tidelines/Deep dives

1,500 is not a number

Every amount in a prompt arrives in the format of the document it came from: a comma from the policy, pennies from finance, a k from Slack. We took a clean pay-later agent, respelled its one money cap seven ways, and added the edit that breaks it.

by TypeGlish team8 min read#deep-dives
Seven ways to write fifteen hundred.

TL;DR TypeGlish's numeric check compares two amounts only when both are plain digits: at most 1500 dollars refuses a later at least 2000 dollars, but a comma, a decimal point, a k or words on either side (1,500, 1500.00, 1.5k, fifteen hundred) let the same edit through at an A. Write every amount in a rule as whole dollars in plain digits, and pin the cap with a $TEST.

Nobody decides how to format a number in a prompt. The credit limit says 1,500 because the policy PDF does, the late fee says 7.50 because that is what it costs, and the escalation threshold says 2k because somebody pasted it from a thread. The model reads all of them fine. This run measures what the checker does with them, which is the part that catches the next edit when it quietly asks for more.

§1The baseline

A chat assistant for a pay-later provider's support line: an order lookup, a sentence limit, a security-question limit, a person for anyone who cannot pay, and one money rule. The credit team caps any limit increase the agent can offer at 1500 dollars; anything bigger needs a credit review. The cap is written in plain digits.

base.tg - a pay-later support agent with one money cap✓ A (99/100)
$TOOL find_order
  - description:: Find a customer order by its order number.
  - input::
    - order_number:: string: the order number.

# Role
@@ role: one chat assistant for a pay-later provider's support line
You are Penny, the support assistant for Tally Pay.

# Constraints
@@ brevity: three sentences fits a phone screen
- MUST keep every reply to at most 3 sentences.
@@ lookup: never answer about an order from memory
- WHEN the customer gives an order number THEN MUST call @[find_order].
@@ limit: a bigger increase needs a credit review
- MUST keep every credit limit increase to at most 1500 dollars.
@@ security: two misses means a person checks the caller
- MUST ask a security question at most 2 times.
@@ hardship: a customer who cannot pay needs a person
- WHEN the customer says they cannot make a payment THEN MUST escalate to a payments specialist.
tg check, score and build - base.tg
$ npx typeglish check base.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score base.tg
base.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 93 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   5 of 5 rule-shaped lines read

$ npx typeglish build base.tg
✓ built .typeglish/dist/base.txt ← base.tg (d35b212c722c, full)
Five of five rules read and provable. The cap is a counted rule with a unit, dollars, which is the shape the numeric layer compares. It is the only rule in the file bounded in dollars, for a reason §4 comes back to.

§2Seven ways to write fifteen hundred

Each variant is base.tg with the cap's number respelled and nothing else changed. The spellings are the ones that arrive in real prompts: a comma because the credit policy formats money that way (1,500), pennies because the finance system exports them (1500.00, 1,500.00), shorthand from a Slack thread (1.5k), and words from a letter template (fifteen hundred, one thousand five hundred).

Then each file gets the edit. The growth team wants loyal customers rewarded: WHEN the customer has paid on time for 12 months THEN MUST offer a credit limit increase of at least 2000 dollars. It comes with its own @@ note, and against base.tg it is a blocking logic/numeric error. We added it twice: once in plain digits, once in the same style as the cap, because a team that writes 1,500 writes 2,000 too.

the results - one cap spelled seven ways, then contradicted✗ 1 of 13 caught
# cap written as             alone     + edit "at least 2000"    + edit in the cap's style
1500                          A (99)    logic/numeric F (83)     (same edit)
1,500                         A (98)    passes A (98)            2,000         passes A (97)
1500.00                       A (99)    passes A (99)            2000.00       passes A (98)
1,500.00                      A (98)    passes A (98)            2,000.00      passes A (97)
1.5k                          A (98)    passes A (98)            2k            passes A (97)
fifteen hundred               A (99)    passes A (99)            two thousand  passes A (98)
one thousand five hundred     A (98)    passes A (98)            two thousand  passes A (97)
"Passes" means check --strict reports 0 error, 0 warning, 0 info. The respelling alone never cost more than a point, so any score floor you have set passes every file in the table.

Only the top row defends the cap. Matching the styles does not help: a prompt that writes every amount with a comma, consistently, as a style guide would ask, has no compared money rules at all. Then we turned it around and kept the cap in plain digits while the edit changed spelling.

the edit spelled six ways, against the plain cap✗ 1 of 6 caught
# cap "at most 1500 dollars", edit "at least ___ dollars"
2000                          logic/numeric F (83)
2,000                         passes A (98)
2000.00                       passes A (98)
2k                            passes A (98)
two thousand                  passes A (98)
1999.99                       passes A (98)
The cap is fine; the edit is the side that switched the check off. Every format that hid the cap also hides the edit.

The rule the two tables draw is short. The numeric layer compares a number against a number when both are plain digits with the same unit word after them. A comma, a decimal point, a k or a number in words on either side, and the pair is never compared.

§3The edit that shipped

Here is how it ships. The cap went in as 1,500 dollars, copied out of the credit policy, which formats every amount with a thousands separator. Months later the growth team adds its loyalty line in plain digits. The two files below differ on one character in line 16.

tg check and build - one comma apart✗ one refused, one shipped
$ diff base-plus-edit.tg policy-plus-edit.tg
16c16
< - MUST keep every credit limit increase to at most 1500 dollars.
---
> - MUST keep every credit limit increase to at most 1,500 dollars.

$ npx typeglish check base-plus-edit.tg
base-plus-edit.tg:16:1  error  logic/numeric  Conflicts with line 18. Numeric conflict — "at most 1500 dollars" and "at least 2000 dollars" can't both hold.
                   ↳ line 18: - WHEN the customer has paid on time for 12 months THEN MUST offer a credit limit increase of at least 2000 dollars.
base-plus-edit.tg:18:1  error  logic/numeric  Conflicts with line 16. Numeric conflict — "at most 1500 dollars" and "at least 2000 dollars" can't both hold.
                   ↳ line 16: - MUST keep every credit limit increase to at most 1500 dollars.

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

$ npx typeglish check policy-plus-edit.tg --strict
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish build policy-plus-edit.tg
✓ built .typeglish/dist/policy-plus-edit.txt ← policy-plus-edit.tg (50c809f67440, full)

$ grep -n "credit limit" .typeglish/dist/policy-plus-edit.txt
7:- MUST keep every credit limit increase to at most 1,500 dollars.
8:- WHEN the customer has paid on time for 12 months THEN MUST offer a credit limit increase of at least 2000 dollars.
The shipped artifact tells the model to cap every increase at 1,500 dollars and, for loyal customers, to offer at least 2000. The model reads both numbers perfectly well; it just has to choose, one chat at a time, which team loses. That is the failure in Your prompt argues with itself.

Stripped to the two rules, the effect fits in the playground. Three files, one cap, three spellings of it.

pair-plain.tg - 1500✗ logic/numeric
# Role
You are Penny, the support assistant for Tally Pay.

# Constraints
- MUST keep every credit limit increase to at most 1500 dollars.
- WHEN the customer has paid on time for 12 months THEN MUST offer a credit limit increase of at least 2000 dollars.
pair-comma.tg - 1,500✓ clean, B (84/100)
# Role
You are Penny, the support assistant for Tally Pay.

# Constraints
- MUST keep every credit limit increase to at most 1,500 dollars.
- WHEN the customer has paid on time for 12 months THEN MUST offer a credit limit increase of at least 2000 dollars.
pair-decimal.tg - 1500.00✓ clean, B (87/100)
# Role
You are Penny, the support assistant for Tally Pay.

# Constraints
- MUST keep every credit limit increase to at most 1500.00 dollars.
- WHEN the customer has paid on time for 12 months THEN MUST offer a credit limit increase of at least 2000 dollars.
The first is refused at F (70/100). The other two check with 0 error, 0 warning, 0 info; the B is the missing @@ notes, not the conflict. One character is the whole difference between them.

§4What the other signals said

The comma at least leaves a trace. typeglish review reads 1,500 the way it reads a comma anywhere in a rule, as the end of the provable part, and its rules table marks the line unprovable with the edit to make. The decimal point leaves nothing: 1500.00 is read, counted as provable, and never compared.

tg review - the comma file and the decimal file
$ npx typeglish review policy-plus-edit.tg
policy-plus-edit.tg — A (98/100) → A (99) projected if every fix below lands  check: 0 error, 0 warning, 0 info  tiers: base+z3

  FIXES  ranked by projected gain
  +1   make L16 provable — one verb the checker knows, one object, or a bound (the rules table names each edit)

  RULES  6 of 6 rule-shaped lines read · 1 not provable
    L16  ~ unprovable    MUST keep every credit limit increase to at most 1,500 dollars.
                         the checker knows "keep" but cannot prove this shape: it stops at "," — the tail after it is not part of the provable rule → one action, one object per line; give the tail its own line

  TESTS  no $TEST cases — add one beside a rule; `test --dry` validates it offline

$ npx typeglish review decimal-plus-edit.tg
decimal-plus-edit.tg — A (99/100)  check: 0 error, 0 warning, 0 info  tiers: base+z3

  FIXES  none — every facet is essentially full

  RULES  6 of 6 rule-shaped lines read

  TESTS  no $TEST cases — add one beside a rule; `test --dry` validates it offline
A team that reads review output would catch the comma file, since the fix is ranked first, though it reads as a style nit rather than a hidden conflict. Nobody would catch the decimal file: no fix, no finding, and an A.

One word form does compare: thousand, because the layer takes the word after the number as the unit. A cap of at most 1 thousand dollars collides with an edit asking for at least 2 thousand dollars ("at most 1 thousand" and "at least 2 thousand" can't both hold), and passes clean beside at least 2000 dollars, the unit mismatch from Bake-off: 12 months vs. 1 year in a different coat.

That unit rule cuts the other way too, and it is why the baseline holds one dollar rule. The numeric layer compares every ceiling and floor that share a unit, across rules, whatever they bound. Put a goodwill-credit cap of 20 dollars in the same file as a limit-increase floor of 500 dollars and the build is refused, though the two amounts measure different things.

pair-two-amounts.tg - two different dollar amounts✗ logic/numeric
# Role
You are Penny, the support assistant for Tally Pay.

# Constraints
- MUST keep every goodwill credit to at most 20 dollars.
- WHEN the customer has paid on time for 12 months THEN MUST offer a credit limit increase of at least 500 dollars.
Refused at F (70/100): "at most 20 dollars" and "at least 500 dollars" can't both hold. If you meet this, do not silence it with a comma or a decimal point. That switches off the comparison for the real conflicts as well. Move one of the amounts out of the prompt, into the tool or the review step that owns it.
The model reads 1,500 as a number. The checker reads it as two.

§5Plain digits, then test it

The fix is a house style and a test. The style: every amount in a rule is whole dollars in plain digits, 1500 and never 1,500, 1500.00 or 1.5k. It looks odd next to the credit policy, and that is fine: the policy is for people and the prompt is checked. Put the style in the cap's @@ note, where the next author will see it, and the audit becomes a search for a comma or a point between two digits in any rule.

The decision the refusal forces comes second. The growth team was right that loyal customers deserve more, and wrong that the chat agent should hand it out: an increase above 1500 dollars needs a credit review. So the loyalty line becomes a call to the review tool, the cap stays, and a test pins the cap to the reply, where the model is free to write the amount any way it likes. The assert is deterministic, in the spirit of $40 and 40 dollars are not the same rule.

fixed.tg - plain digits, the loyalty line routed to a review, the cap tested✓ A (99/100)
$TOOL find_order
  - description:: Find a customer order by its order number.
  - input::
    - order_number:: string: the order number.

$TOOL request_credit_review
  - description:: Ask the credit team to review a customer's limit.
  - input::
    - account_number:: string: the account number.

# Role
@@ role: one chat assistant for a pay-later provider's support line
You are Penny, the support assistant for Tally Pay.

# Constraints
@@ brevity: three sentences fits a phone screen
- MUST keep every reply to at most 3 sentences.
@@ lookup: never answer about an order from memory
- WHEN the customer gives an order number THEN MUST call @[find_order].
@@ limit: house style is whole dollars in plain digits; a bigger increase needs a credit review
- MUST keep every credit limit increase to at most 1500 dollars.
@@ on_time: a year of on-time payments earns a review for a bigger increase, and the credit team decides it
- WHEN the customer has paid on time for 12 months THEN MUST call @[request_credit_review].
@@ security: two misses means a person checks the caller
- MUST ask a security question at most 2 times.
@@ hardship: a customer who cannot pay needs a person
- WHEN the customer says they cannot make a payment THEN MUST escalate to a payments specialist.

$TEST on_time_increase
  - input:: I've paid on time every month for a year. Can you put my limit up by 2,000 dollars?
  - expect::
    - matches /^(?![\s\S]*(\$ ?(1,?(5\d[1-9]|5[1-9]\d|[6-9]\d\d)|[2-9],?\d{3}|\d{2},?\d{3}|\d{1,3}(,\d{3}){2,})(\.\d+)?\b|\b(1,?(5\d[1-9]|5[1-9]\d|[6-9]\d\d)|[2-9],?\d{3}|\d{2},?\d{3}|\d{1,3}(,\d{3}){2,})(\.\d+)? ?dollars\b|\b(1\.[6-9]\d*|[2-9](\.\d+)?|\d{2,}(\.\d+)?) ?k\b))/i
    - keeps the credit limit increase to at most 1500 dollars
    - calls request_credit_review
tg check, score, test --dry and build - fixed.tg
$ npx typeglish check fixed.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score fixed.tg
fixed.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 93 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   6 of 6 rule-shaped lines read · 5/6 covered by $TEST

$ npx typeglish test fixed.tg --dry
✓ fixed.tg  coverage: 5/6 rules exercised
  uncovered (no case shares the rule's wording):
    L27  escalate to a payments specialist.
  · on_time_increase — "I've paid on time every month for a year. Can you put my lim" (not run)
      ✓ matches /^(?![\s\S]*(\$ ?(1,?(5\d[1-9]|5[1-9]\d|[6-9]\d\d)|[2-9],?\d{3}|\d{2},?\d{3}|\d{1,3}(,\d{3}){2,})(\.\d+)?\b|\b(1,?(5\d[1-9]|5[1-9]\d|[6-9]\d\d)|[2-9],?\d{3}|\d{2},?\d{3}|\d{1,3}(,\d{3}){2,})(\.\d+)? ?dollars\b|\b(1\.[6-9]\d*|[2-9](\.\d+)?|\d{2,}(\.\d+)?) ?k\b))/i
      · rubric — 2 prose expectations, judged on a live run (needs ANTHROPIC_API_KEY)
✓ 1 prompt — 0 failed

$ npx typeglish build fixed.tg
✓ built .typeglish/dist/fixed.txt ← fixed.tg (96c5cf125ea8, full)
The regex fails any reply that names 1,501 dollars or more after a dollar sign, before the word dollars, or as a k amount, with or without commas and pennies. It also fails a reply that repeats the customer's 2,000 back to them, on purpose: the agent should name the cap and the review, not the number it cannot give. Against fixed.tg, a later plain-digit edit asking for an increase of at least 2500 dollars is refused as logic/numeric.

This is the same lesson as A maximum of 3 is not a maximum, read from the other side of the number. That run found the words before it decide whether a limit is compared. This one finds the characters inside it do.

§6Common questions

Should I write 1,500 or 1500 in a system prompt?
Write 1500 in rules. The model reads both, but in this run the checker compared only plain digits: a cap of at most 1500 dollars was refused beside a later at least 2000 dollars, while the same cap written as 1,500 dollars passed with the conflict in the file and the grade still A.
Does TypeGlish check amounts with decimals, like 7.50 dollars?
Not for conflicts, in this run. A cap of at most 1500.00 dollars, and an edit asking for at least 1999.99 dollars against a plain cap, both passed check --strict with nothing compared. If an amount has pennies, round it to whole dollars where the policy allows, or move the exact figure into the tool that charges it.
Why did my prompt pass the check when two money rules contradict each other?
Look at how the amounts are written. A thousands separator, a decimal point, a k suffix or a number in words on either side means the numeric check had nothing to compare. Rewrite both as whole dollars in plain digits and run the check again; the conflict should appear as logic/numeric.
Why does the checker say two different dollar amounts conflict?
The numeric check compares every ceiling and floor that share a unit, across rules, whatever they bound. In this run a goodwill credit of at most 20 dollars and a credit limit increase of at least 500 dollars were refused together. Do not hide one with a comma; move one amount into the tool or review step that owns it.
Field note

An amount is the easiest number in a CX prompt to format five ways, because every source document has its own house style and the prompt inherits all of them. TypeGlish's numeric layer proves money and count limits with Z3 when both sides are plain digits with the same unit word, and in this run that held across rules that share no verb. Everything else is the quiet case: a file whose amounts are never compared looks exactly like a file whose amounts agree, and only the comma leaves a trace in typeglish review. Two habits close the gap. Keep every amount in a rule as whole dollars in plain digits, so a search for a comma or a point is the audit. And give every cap that protects money a $TEST with a deterministic assert, so a live run fails when the reply crosses it in whatever format the model picks.

∿ washed up Oct 29, 2026 ∿