TypeGlish 0.10.0: one run, ranked by gain
The checker has always been able to tell you a prompt is clean and mediocre at the same time. What it could not tell you was which line was costing you the points. 0.10.0 adds a command that prints both grades, the fixes in order of what they are worth, and the name of every rule it could not read.
TL;DR 0.10.0 is a workflow release, and the headline is typeglish review: check + score + test --dry collapsed into one report that leads with the grade you have and the grade you would have if every fix below it landed, then ranks the fixes by points. The gift underneath it is the rules table, which finally names the lines the checker cannot read as rules, quotes them, and gives the edit. check --fix applies the mechanical repairs in place and refuses to touch a word. Two upgrade notes: a $IF arm missing its colon is now caught at check time instead of panicking build, and WHEN guards now reach the contradiction prover where only IF did before, so a default-and-exception pair that was clean on 0.9.0 can block. Read §5 before you point CI at it. npm i -D typeglish@latest.
Here is the loop that 0.10.0 is about. You write a prompt. check says it is clean. score says it is a C. You stare at a facet called enforceability sitting at 60, you guess at a line, you re-run both commands, and the number moves or it does not. We have written that loop into a lot of posts on this blog, including the one where seven clean lines about card numbers score D (59/100), and every time the interesting part was the same: the score knew exactly which lines were the problem and had no way to say so. That is what got fixed.
§1One report, in the order you act on it
Take an ordinary constraints section. Four rules, nothing exotic, the kind of thing that lands in a repo on a Tuesday.
# Role You are Ada, the support agent for Northwind. # Constraints - MUST keep every reply to at most 3 sentences. - USUALLY offer a replacement before a refund. - IF the customer is angry THEN escalate to a human agent. - NEVER share internal notes or another customer's information.
This is the whole 0.9.0 diagnostic surface for that file. Two commands, two reports, and between them not one line number pointing at the problem:
✓ 1 file — 0 error, 0 warning, 0 info
support.tg — C (77/100) proven errors: none tiers: base+z3
planes runtime 87 (what the model reads) · hygiene 50 (source only)
facets enforceability 60 x.21 · hardness 88 x.12 · directness 97 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) — Put a "@@ why" note directly above each statement ("@@ name: why" also names it).
enforceability 60 and then try to act on it. Forty points of the largest facet on the card are missing and the report does not say from where. The only actionable line is the annotation lever, which is generic advice about the whole file.Now the same file, one command, on 0.10.0:
support.tg — B (81/100) → A (98) projected if every fix below lands check: 0 error, 0 warning, 1 info tiers: base+z3
FIXES ranked by projected gain
+12 put a "@@ why" note above L2 L5 L6 L7 L8 — annotation 0 → 100
+3 lead with a modal (MUST / NEVER / SHOULD) on L6 — 1 rule becomes visible to the checker
+2 make L8 provable — one verb the checker knows, one object, or a bound (the rules table names each edit)
RULES 3 of 4 rule-shaped lines read · 1 not provable
L6 ✗ invisible USUALLY offer a replacement before a refund.
USUALLY is a frequency word, not a modal — the logic plane weighs it on the occasion square, but the rule reader does not count it as a rule → lead with a modal (MUST / NEVER / SHOULD); a soft rule is SHOULD
L8 ~ unprovable NEVER share internal notes or another customer's information.
the checker knows "share" but cannot prove this shape: two objects or actions joined by "or" — it proves one at a time → one object per line: split into two rules
FINDINGS
support.tg:6:1 info prompt/unparsed-rule Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: USUALLY is a frequency word, not a modal — the logic plane weighs it on the occasion square, but the rule reader does not count it as a rule. Fix: lead with a modal (MUST / NEVER / SHOULD); a soft rule is SHOULD.
TESTS no $TEST cases — add one beside a rule; `test --dry` validates it offline
B (81/100) → A (98) projected if every fix below lands. You are told what the file is worth, what it could be worth, and then handed the work in the order that pays. Three fixes, seventeen points, every one of them attached to a line.The ordering is not cosmetic. check sorts by severity because a blocking error is a blocking error; review sorts by what your next ten minutes buys. On this file the top item is worth +12 and is not a defect at all, it is five missing @@ notes. The second is worth +3 and is one word. A human reading two separate reports would have started with the word.
Apply all three and the file lands where the header promised:
# Role @@ identity: one sentence names the speaker, so every later "you" is Ada You are Ada, the support agent for Northwind. # Constraints @@ brevity: three sentences keeps a chat reply scannable - MUST keep every reply to at most 3 sentences. @@ replacement_first: a replacement costs less than a refund and often ends the ticket - SHOULD offer a replacement before a refund. @@ anger_rail: an angry customer is a human call, not a policy call - IF the customer is angry THEN escalate to a human agent. @@ notes_private: internal notes are written for staff and read badly out of context - NEVER share internal notes. @@ other_customers: one customer's data must never surface in another's chat - NEVER share another customer's information.
0 error, 0 warning, 0 info. review on it reports A (99/100), FIXES none — every facet is essentially full and RULES 5 of 5 rule-shaped lines read. Two of the five rules are the one or rule split in half, which is the whole content of the +2.One practical note for CI: review takes --min and exits like check plus score together. review support.tg --min A exits 1 with typeglish review: 81 (B) is below the --min floor A; --min B exits 0. That replaces the two-command gate a lot of pipelines are running today. review is also served over MCP, so an agent holding the TypeGlish tools gets the ranked report rather than two unordered ones.
§2The rules table is the release
If you only read one part of this post, read the RULES block. Enforceability is the largest facet on the card, and it has always been the most honest number TypeGlish prints and the least useful: it measures how much of your document the checker can read as rules, which is the one thing you cannot inspect by reading your own document. You wrote rules. They look like rules. The share that are rules, to the machine, was a percentage.
The table splits every rule-shaped line three ways. Read, which earns its credit. Invisible, which the rule reader had to treat as prose. And unprovable, which counts as a rule but whose shape defeats the prover. Our file has one of each of the last two, and both are mistakes you would never spot unaided:
L6 ✗ invisible USUALLY offer a replacement before a refund.
L8 ~ unprovable NEVER share internal notes or another customer's information.
USUALLY sits where a modal belongs, so the line is weighed on the occasion square by the logic plane and counted as nothing by the rule reader. Line 8 is worse in a way, because it reads: the checker knows share, accepts the rule, and then cannot prove it, because two objects joined by or is two claims wearing one line.Both of those are patterns this blog has written about from the outside. The frequency-word problem is the rules that say usually; the one-verb-one-object discipline is why a prohibition covers only the verb it names. The difference in 0.10.0 is that you no longer have to know the pattern to find it in your own file. The checker points at the line.
The table is not confined to review, either. score now prints it, and --explain grew a second argument so you can ask a facet where it applies in a specific file:
in support.tg: enforceability 83/100 (x.21 of the grade) 1 rule-shaped line the checker reads as prose (L6): lead with a modal (MUST / NEVER / SHOULD). The rules table below has the edit per line invisible L6 rules 2 of 3 rule-shaped lines read
--explain <code> <file> works the same way for a diagnostic code, and on 0.9.0 the second argument did not exist.The score card also learned to cite lines. Where 0.9.0 printed annotation 0/100 (up to +12 overall) — Put a "@@ why" note directly above each statement, 0.10.0 prints annotation 0/100 (up to +12 overall) — 5 statements without a @@ note (L2 L5 L6 L7 L8). Same number, and now it is a worklist.
§3check --fix, and the fixes it refuses to make
check takes --fix now. It applies the declared-safe repairs in place and then checks the result, so the mechanical half of a cleanup stops being your problem. Here is a file with four of them:
$ typeglish check fixme.tg fixme.tg:5:1 warn structure/missing-period Unterminated statement — end it with a period fixme.tg:6:8 warn structure/phantom-column Phantom column: a space run doing alignment's job fixme.tg:6:1 warn structure/missing-period Unterminated statement — end it with a period fixme.tg:7:1 warn structure/missing-period Unterminated statement — end it with a period ✓ 1 file — 0 error, 4 warning, 0 info $ typeglish check --fix fixme.tg ✎ fixme.tg: applied 4 fixes — structure/missing-period ×3, structure/phantom-column ✓ 1 file — 0 error, 0 warning, 0 info
The more interesting behavior is what it will not do. Point --fix at support.tg from §1, whose only real defect is the word USUALLY, and the file comes back byte-identical with this on stderr:
1 more fix changes wording and stays yours to apply — each rides --json as { fix: { range, replacement } }
--json, that fix is fully specified and explicitly opted out of automation: {"title":"soft rule: SHOULD","replacement":"SHOULD","range":{"line":6,"column":3,"endColumn":10},"autofix":false}. An editor or an agent can offer it as a one-keystroke edit. --fix will not take the keystroke for you.That line is the whole posture, and it is the same one import --fix-citations took in 0.7.0: the flag is the consent. Whitespace is not an opinion. Changing USUALLY to SHOULD is a policy decision about how hard a rule binds, and a compiler that makes those quietly is a compiler you stop reading.
§4The colon that was not a gate
Now the fix set, which the changelog files as the honest quickstart. The piece with teeth is a missing colon.
$REQUIRE variable plan: one of standard, plus
# Role
You are Ada, the support agent for Northwind.
# Constraints
- MUST keep every reply to at most 3 sentences.
$IF @{plan} is equal to plus
- MAY waive the restocking fee.
plus. This block is deliberately unmarked and not run by this post's guard, for the reason in the field note: its verdict differs between the two builds, which is the point of the section.On 0.9.0 that file passes. Not "passes with a warning about the conditional": passes, exit 0, a single unrelated indent warning, and the arm is gone. resolve prints the $IF line back at you verbatim, which is the tell. Then you run build:
$ typeglish check tier.tg tier.tg:9:1 warn structure/bad-indent Indentation mirrors section nesting: expected column 0 ✓ 1 file — 0 error, 1 warning, 0 info $ typeglish build tier.tg --vars '{"plan":"plus"}' typeglish build: tier.tg refused — nothing written compiler invariant violated: control-plane scaffolding leaked into the artifact ("$IF {plan} is equal to plus") — this is a typeglish bug, nothing was written; please report it with this file
That guard on the artifact is doing its job and it is the last line of defence, not the first. 0.10.0 puts the check where it belongs, on the line, at check time:
tier.tg:8:1 error structure/bad-switch malformed $IF arm — a colon ends the condition and opens the body: "$IF @{x} is equal to <value>: <body>" (the body may also sit on the lines indented beneath). Without it this line is neither an arm nor prose, so nothing selects it and nothing ships it.
Give it the colon and an $ELSE, because exhaustiveness is still enforced, and the file is clean on both builds:
$REQUIRE variable plan: one of standard, plus # Role You are Ada, the support agent for Northwind. # Constraints - MUST keep every reply to at most 3 sentences. $IF @{plan} is equal to plus: - MAY waive the restocking fee. $ELSE: - MUST charge the restocking fee.
$ELSE and 0.10.0 gives you structure/non-exhaustive-switch: @{plan} can be standard, but no arm handles it. A switch with a hole is a prompt with a customer tier nobody wrote rules for.The same release also puts a Start here section at the top of typeglish reference: a copyable file skeleton, the loop (review after every edit, build when it is clean), and a plain-English taxonomy of what counts as a rule, what does not, and what reads but cannot be proven. If you have ever handed an agent the reference and watched it write USUALLY, that section is aimed squarely at you.
§5Read this before you point CI at it
One change in 0.10.0 has a wider blast radius than anything above, and it is not in the changelog. We found it the way you would want us to: by running the new build over every verified example on this blog. Four of them went red.
The default-and-exception pattern is the most common shape in a real support prompt. State the rule, then state the case where it does not apply. Here it is in the form we have published for months:
<role> You are a care agent for a broadband provider. </role> <constraints> @@ credit: goodwill is a supervisor decision except on a long outage - You MUST NOT offer a credit. - WHEN an outage lasted more than 24 hours THEN you MUST offer a credit. </constraints>
On 0.9.0 that is 0 error, 0 warning, 0 info. On 0.10.0 it is two blocking errors:
credit-pair.tg:7:3 error logic/contradiction Conflicts with line 8. Logical conflict — "offer a credit" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 8: - WHEN an outage lasted more than 24 hours THEN you MUST offer a credit. credit-pair.tg:8:3 error logic/contradiction Conflicts with line 7. Logical conflict — "offer a credit" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 7: - You MUST NOT offer a credit. ✗ 1 file — 2 error, 0 warning, 0 info
↳ lines, which are new and good: a conflict message now quotes the line it collided with instead of making you scroll. Note also the advice, which tells you to scope the two rules with a condition, when line 8 already has one.Before you file this as a regression, here is the part that settles it. Swap the single word WHEN for IF in that file and it is 2 blocking errors on 0.9.0 as well. It was never the exception pattern that was legal; it was one spelling of it. 0.9.0 proved IF guards and was blind to the rest, so the identical policy passed or failed depending on which conditional word you happened to reach for. We measured the four:
guard 0.9.0 0.10.0 IF 2 error (already proven) 2 error WHEN 0 error (silent) 2 error WHENEVER 0 error (silent) 2 error WHILE 0 error (silent) 2 error
WHEN, WHENEVER and WHILE now reach the contradiction prover the way IF always did. The language never had a specificity-wins rule for the prover to override.That makes this a good change with an awkward arrival. Good, because a prompt whose verdict depended on the synonym you picked was lying to you in one of the two cases, and you had no way to know which. Awkward, because WHEN is the word most people write, so the finding lands on files that have been green for months, and nothing in the changelog warns you.
The repair is mechanical and it is the same one the default-and-exceptions post already recommends for a different reason: put a scope on the general rule too, so neither rule claims the whole world.
<role> You are a care agent for a broadband provider. </role> <constraints> @@ credit: goodwill is a supervisor decision except on a long outage - UNLESS an outage lasted more than 24 hours THEN you MUST NOT offer a credit. - WHEN an outage lasted more than 24 hours THEN you MUST offer a credit. </constraints>
So: upgrade, and run check across everything before you trust an exit code.
npm i -D typeglish@latest # 0.10.0 npx typeglish@latest check prompts/ # do this before you trust exit codes in CI npx typeglish@latest review prompts/my-agent.tg npx typeglish@latest --explain enforceability prompts/my-agent.tg
Energy check: one new command, one new flag and a fix set, so this is a strong workflow release and not a language landmark. There is no new axis of proof here; the prover grew sideways rather than upward, picking up three conditional words it should always have read (§5). What actually changed is how fast you can act on what it proves, which for a daily user is worth more than a new axis. Method: we installed the published 0.9.0 and 0.10.0 tarballs and ran the real CLI over the same files, so every before and after here is a diff between two shipped builds. Every grade, count and message is pasted from those runs, clipped only where noted. Three honest notes. First, two of the source blocks in this post are deliberately unmarked and not re-run by this site's example guard, because the guard pins the checker to the version in this repo's package.json and those two files have different verdicts on the two builds; marking them would make CI depend on which build ran. This change leaves that pin on 0.9.0 and does nothing else, because the four posts §5 turned red need their figures and their arguments re-measured against 0.10.0, which is a bigger job than a release note and is in flight as its own change. Everything marked here carries the verdict both builds agree on. Second, prompt/unparsed-rule is the code behind the §1 info and it is absent from the 157-row code table that typeglish reference prints on both builds, so --explain is currently its only documentation. Third, the changelog names seven things in two lines: the rule ledger, one numbering, THE BRANCH IMPERATIVE, THE QUICKSTART LOCK, THE RULE SHAPES, THE COLON GATE and THE SCAFFOLD FENCE. We match the colon gate and the scaffold fence to §4 and the rule ledger to the rules table with confidence, and THE RULE SHAPES to the taxonomy the reference gained. We can measure one more: a bare catalog imperative after THEN now earns enforceability credit, so IF the customer is angry THEN escalate to a human agent. alone in a file scores C (75) on 0.9.0 and B (86) on 0.10.0. The rest we are leaving as quotes rather than guessing. The full changelog lives on GitHub.
FAQQuestions about 0.10.0
- What does
typeglish reviewdo thatcheckandscoredid not? - It merges
check,scoreandtest --dryinto one report, ordered by what to do next rather than by which command produced it. The header carries two grades: the one you have and the one you would have if every fix listed below it landed. Then the fixes, each with the points it is worth and the lines it touches; then the rules table; then the findings; then test coverage. It exits likecheckplusscore, soreview --min Bis a single CI gate. It is also served over MCP alongsidecheck,build,score,explainandreference. - What is the rules table telling me?
- Which of your rule-shaped lines the checker can actually read, and which it can prove. Enforceability is the largest facet on the score card, and before 0.10.0 it was a number with no line numbers attached: you could see
enforceability 60and have no way to learn which lines were costing you. The table marks each line invisible or unprovable, quotes it, explains why, and gives the edit. A frequency word likeUSUALLYin the modal slot is invisible. Two objects joined byoris readable but unprovable. - What does
check --fixchange, and what will it not touch? - It applies the declared-safe repairs in place: whitespace, the character set, statement terminators. It never rewrites a word. Run it on a file whose only real problem is a frequency word and it prints that
1 more fix changes wording and stays yours to apply, then leaves the line exactly as you wrote it. That fix still ridescheck --jsonas arangeplus areplacementwithautofixset tofalse, so a tool can offer it and you decide. The flag buys you the mechanical half and nothing else. - Why did my
$IFarm start failing? - Because an arm has to end its condition with a colon, and 0.10.0 says so at check time. Write
$IF @{plan} is equal to pluswith no colon and the line is neither an arm nor prose, so nothing selects it and nothing ships it. On 0.9.0 that file passedcheckwith exit 0 and thenbuildrefused it withcompiler invariant violated: control-plane scaffolding leaked into the artifact, reported as a typeglish bug. The fence was already there. 0.10.0 gives you a named diagnostic on the right line instead of a panic one command later. - Will 0.10.0 fire new errors on prompts I already wrote?
- Yes, and one change has a wide blast radius that the changelog does not mention:
WHEN,WHENEVERandWHILEguards now reach the contradiction prover, where 0.9.0 proved onlyIF. SoYou MUST NOT offer a credit.besideWHEN an outage lasted more than 24 hours THEN you MUST offer a credit.checks clean on 0.9.0 and is 2 blockinglogic/contradictionerrors on 0.10.0, while the identical pair written withIFis 2 errors on both builds. That is a blind spot closed rather than a new rule, but it lands on the default-and-exception shape, which is the most common shape in a real support prompt. The repair is mechanical: scope the default too, withUNLESS an outage lasted more than 24 hours THEN you MUST NOT offer a credit.That version checks clean on both builds. - Is
prompt/unparsed-rulea new diagnostic code? - It is new to 0.10.0 by behavior. On 0.9.0,
typeglish --explain prompt/unparsed-ruleanswersunknown code, and the same file that draws the info on 0.10.0 draws nothing. It fires at info severity on a rule-shaped line the rule reader had to treat as prose, so it earns no enforceability credit and no measurability check. One caveat worth knowing: it is not in the 157-row code table thattypeglish referenceprints, on either build, so--explainis the only documentation of it today. - Should I upgrade to 0.10.0?
- Yes, and run the check before you trust an exit code.
npm i -D typeglish@latest, thennpx typeglish@latest check prompts/across everything you have. Two families of finding will appear. The colon gate is pure gain: it catches at check time a file that used to reachbuildand panic. The guard change is the one to budget for, because every default-and-exception pair you wrote withWHENrather thanIFis a candidate, and the fix is to scope the default withUNLESS. Then adoptreviewas the command you run after every edit, because that is the release.