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.
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.
$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.
$ 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)
§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.
# 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)
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.
# 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 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.
$ 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.
Stripped to the two rules, the effect fits in the playground. Three files, one cap, three spellings of it.
# 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.
# 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.
# 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.
@@ 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.
$ 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
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.
# 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.
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.
$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
$ 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)
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.
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.