← Tidelines/Releases

TypeGlish 0.7.0: your prompts are one program

The cross-file release. Point typeglish check at a directory and every file joined by $IMPORT is checked as one program, so a NEVER in your shared policy file and a MUST in the agent that imports it finally collide at edit time instead of in production.

by TypeGlish team8 min read#releasestypeglish 0.7.0
One program.

TL;DR 0.7.0 makes typeglish check <dir> a program pass: files joined by $IMPORT compose before the proofs run, and a cross-file contradiction lands on its own (file, line) and blocks exactly like a local one (--no-program restores the old per-file behavior byte for byte). $REQUIRE section plus typeglish import --fix-citations clear the citation errors that kept composed production prompts out at the door, typeglish fmt is a new subcommand that joins hard-wrapped fragments in place, and typeglish reference now documents 99 diagnostic codes where 0.6.0 documented 48. Upgrade with npm i -D typeglish@latest.

0.6.0 opened the door for the prompt you already have: point import at it, get a checked .tg back, work the worklist down. Then everyone who walked through that door hit the same wall, because nobody ships one prompt. You ship a shared policy file, a chat agent, a voice agent, and an escalation module, and the reason you split them up was to write the refund policy exactly once. Up to 0.7.0 the checker read each of those files alone, which means the one defect that composition invites, a rule in one file that contradicts a rule in another, was the one defect it could not see. This release closes that. Upgrade with npm i -D typeglish@latest, or run it with no install at all: npx typeglish@latest check prompts/.

§1The join law: two files, one program

Here is the smallest version of the bug, and it is the shape every prompt set grows into. The policy team owns one file with the refund rules in it. The retention team owns the chat agent, which imports that file and adds a rule of its own. Both files are short, both are readable, and both are correct on their own terms.

prompts/refund-policy.tg✓ compiles
You are a support agent for Acme Analytics.
- NEVER waive a restocking fee.
The shared module, and the guard on this post proves it: checked by itself, this file is 0 error, 0 warning, 0 info. Nothing is wrong with it. Nothing will ever be wrong with it.
prompts/chat-agent.tg
$IMPORT file "./refund-policy.tg" as policy
@<policy>
- MUST waive a restocking fee.
The agent that composes it in. Checked by itself this file is also clean: 0 error. Somebody on the retention team wrote an absolute, in their own file, without reading the policy module they transclude on the line above. This is not carelessness. It is what file boundaries are for, and it is why the defect survives review.

Before 0.7.0, that was the end of the story: two green files, one agent that has been told both to always waive the fee and to never waive it, and a coin flip at runtime. Now point the checker at the directory.

typeglish check prompts/✗ exit 1
prompts/chat-agent.tg:3:1  error  logic/contradiction  Logical conflict — "waive a restocking fee" is both required and forbidden. … (conflicts with prompts/refund-policy.tg line 2)
prompts/chat-agent.tg:3:1  info   prompt/implied-doer  Nothing in this document says who follows its instructions - every imperative implies a doer …
prompts/refund-policy.tg:2:1  error  logic/contradiction  Logical conflict — "waive a restocking fee" is both required and forbidden. … (conflicts with prompts/chat-agent.tg line 3)

✗ 2 files — 2 error, 0 warning, 1 info
program: 1 root — prompts/chat-agent.tg (2 files)
Verbatim, with the fix-advice clause on each error line elided as for width. Read what the line numbers do. The error is reported twice, once on each file, and each report sits on the true line of its own offending rule: chat-agent.tg:3 is the MUST, refund-policy.tg:2 is the NEVER, and each names the other file and line as the conflict. Whichever file you have open, the error is in your gutter, pointing at the rule you can actually edit. That is the part that makes this usable rather than merely correct, and it is what the release means by cross-file findings that "block like local ones": exit code 1, and typeglish build refuses.

Two properties of the program pass are worth knowing before you put it in CI, because both of them are about restraint.

It is scoped to files that can actually collide. The pass does not diff every prompt against every other prompt. It composes what $IMPORT joins, and nothing else. Drop a support agent that says NEVER waive a restocking fee and a billing agent that says MUST waive a restocking fee into one directory with no import between them, and the checker reports 0 errors and tells you exactly why it stayed quiet: program: 2 independent files — no $IMPORT compositions, nothing co-checked. Those two agents never run in the same context, so there is no conflict to prove. Same soundness bar as always, now with a wider field of view: the compiler reports what it can prove and stays silent otherwise, which is the argument we made the first time in Your prompt argues with itself.

The composition is transitive. A root that imports a file that imports a third file is one program of three files, and the summary line says so: program: 1 root — tri/top.tg (3 files). The reference records the shift plainly. In 0.6.0, import/nested was documented as "an imported file itself imports another; nesting is not allowed". In 0.7.0 the same code means something else entirely, which you can read straight out of typeglish --explain import/nested: "A problem inside an imported file (imports compose transitively), reported at the $IMPORT line that pulled it in." The code went from naming a shape the language refused to naming a pointer at the real problem one level down.

One practical note that will save you an afternoon: point it at the directory. Naming a single file still checks that file alone, so typeglish check prompts/chat-agent.tg on the pair above exits 0 while typeglish check prompts/ exits 1. If your CI line names files, it is not running the program pass. And if you need the old behavior exactly, it is one flag: --no-program checks every file alone, which the help calls "the pre-program behavior, byte-identical".

§2The import funnel is whole

The second theme is the unglamorous work that decides whether the door in 0.6.0 actually leads anywhere. The changelog entry is blunt about the target: "the import funnel is whole, 50 blockers to 0 on a real production prompt set." That number is the compiler team's count on their own corpus, not a benchmark we can hand you, but the mechanism behind it is the interesting part, and you can reproduce it in a minute.

A composed production prompt cites sections that something else supplies. The host prepends a preamble, or the platform injects a handoff block, and your prompt says "follow <handoffs>" because at runtime <handoffs> is right there. The compiler cannot see the host, so it saw a pointer to nothing: structure/dangling-section-ref, a blocking error. Correct for a typo. Wrong, and unfixable, for a prompt whose runtime really does compose that section in. Multiply by a prompt set and you get a funnel that stops at the door for a reason the author cannot act on.

0.7.0 gives you the missing declaration. $REQUIRE section <names> says: a host composes these in, so treat a citation of them as resolved. The importer now finds those citations, prints the exact line to add, and will write it for you.

typeglish import legacy.md2 errors block build
  grammar
    L5 error structure/dangling-section-ref: Reference to section <handoffs>, which is not defined.
    L6 error structure/dangling-section-ref: Reference to section <escalation>, which is not defined.

✗ 2 errors block build — causes: structure/dangling-section-ref x2 · 2 citations (--fix-citations applies them)
  2 cited sections nothing declares. If a host composes them in (a prepended preamble), declare them at the top of the file (below any $CONFIG block) - if not, one is a typo:
      $REQUIRE section handoffs, escalation
  next: typeglish check legacy.tg for the full detail
Verbatim, with the absolute path of the written file shortened. Note the shape of the help: it does not guess. It names both readings, a host that composes the section in or a typo, hands you the line that fixes the first, and leaves the judgment with you. --gate makes that verdict a CI signal: plain import exits 0 because the conversion succeeded, import --gate exits 1 because errors would block build.

Add --fix-citations and the flag is the consent. Two blockers become zero, and the file it writes builds clean.

typeglish import legacy.md --fix-citations✓ builds clean
imported -> legacy.tg
  lifted: customer_name
  0 errors · 1 warn · 1 info (was 2 · 1 · 1 — 2 errors resolved by the repairs and lifts above)
  declared by --fix-citations: handoffs, escalation
    verify each is a section the composed agent really supplies - a typo gets declared too.
    A wrong name? delete it from the $REQUIRE line and quote the markup in the prose instead
The counts tell the whole story: 0 errors now, was 2 before. And read the two lines under the declaration, because they are the honest half. The flag applies what the report proposed; it cannot know whether handoffs is a section your host really supplies. A typo gets declared exactly as readily as a real citation, so the tool says so in its own output rather than letting you find out in production.

Here is the file it wrote. The citations resolve, the placeholder became a typed variable with a pointer at the point of use, and every word of the original is where the author left it.

legacy.tg - what import wrote✓ compiles
$REQUIRE variable customer_name

$REQUIRE section handoffs, escalation

You are a support agent for Acme Analytics.

Always be polite. Follow <handoffs> when the customer asks for a human.
Never state a specific price. Escalate per <escalation> if they push back.
Look up the order for @{customer_name}.
The guard on this post runs the real checker against this exact block and asserts no blocking error, which is the claim that matters: a composed prompt that cites what its host supplies now compiles.

And the declaration is scoped, not a blanket mute, which is the detail that keeps it honest. It resolves the names you listed and nothing else, so the typo case still fails exactly as it should.

typo.tgstructure/dangling-section-ref
$REQUIRE section handoffs, escalation
You are a support agent for Acme Analytics.
- ALWAYS follow <handofs> when a customer asks for a human.
One letter missing from the citation, and the blocking error is still there: Reference to section <handofs>, which is not defined. The guard on this post re-proves that too. Declaring handoffs did not teach the compiler to accept any tag it does not recognize; it taught it about one section, by name. You also get two structure/unused-import warnings, because now nothing cites the sections you declared, which is a second, quieter way of being told about the typo. If you have pasted a policy doc into an agent lately, this pairs with the pasted-policy teardown.

§3typeglish fmt, the command the finding names

0.7.0 adds a subcommand, and its charter is deliberately boring: typeglish fmt fixes shape, in place, mechanically. It joins hard-wrapped fragments, splits compound rules, and normalizes whitespace. Like import, it does not touch your words.

The reason it exists is that the checker was already telling people to do this by hand. A rule that got hard-wrapped by an editor at 80 columns reads, to a parser, as two statements where the second one is a sentence fragment.

support.tg - hard-wrapped by an editorstructure/wrapped-fragment
You are a support agent for Acme Analytics.
- ALWAYS confirm the order id before you promise a delivery date, and
  escalate to a human when the customer asks twice.
Three warnings on one wrapped rule: structure/missing-period on line 2, structure/bad-indent and structure/wrapped-fragment on line 3. The wrapped-fragment message does the diagnosis in full: "Reads as a hard-wrapped continuation of line 2 — it starts lowercase, classifies as plain prose, and line 2 has no terminator. If they are ONE statement, join them (typeglish fmt); if this line stands alone, capitalize its first word." In 0.6.0 that parenthetical named a command that did not exist yet.
typeglish fmt support.tg
$ typeglish fmt support.tg --check
support.tg: 1 change needed
✗ 1/1 file need formatting — run typeglish fmt

$ typeglish fmt support.tg
✓ formatted support.tg (1 change)
✓ 1 formatted, 0 unchanged
Verbatim from both runs. --check writes nothing and exits 1 when a file would change, so it drops into CI beside check the way any formatter's check mode does. The plain run rewrites the file and reports the count.
support.tg - after fmt✓ compiles
You are a support agent for Acme Analytics.
- ALWAYS confirm the order id before you promise a delivery date, and escalate to a human when the customer asks twice.
One join, and the file goes from three warnings to zero. What is left is a single info about "the order id" being a definite this document never introduced, which is a real observation about the prompt rather than an artifact of how it was typed. That is the split worth having: fmt takes the typing mistakes off the worklist so the findings that remain are about meaning.

§4The full-code ratchet: every code has a name you can look up

The last theme is one you will feel most if you have ever put an agent in front of this compiler. TypeGlish diagnostics are namespaced codes, and the promise of a code is that you can look it up: typeglish --explain logic/contradiction prints what it means, straight from the compiler. In 0.6.0 that promise had holes, and here is one, reproducible in two commands.

0.6.0: a code the compiler could not explain
$ npx typeglish@0.6.0 check unclosed.tg
unclosed.tg:2:1  error  structure/unclosed-tag  Tag <handoffs> is opened but never closed — add a </handoffs>.

$ npx typeglish@0.6.0 --explain structure/unclosed-tag
typeglish --explain: unknown code "structure/unclosed-tag" — codes look like category/name (e.g. logic/contradiction), or a score facet (e.g. enforceability)

$ npx typeglish@0.7.0 --explain structure/unclosed-tag
structure/unclosed-tag (error — blocks compilation)
  A section tag opened but never closed — the section extent is undefined.
The same checker, in the same release, blocking your build with a code its own explainer called unknown. Nothing was broken about the check; the code just was not declared in the registry --explain and reference read from. The changelog calls closing these the full-code ratchet, and ratchet is the right word: 0.7.0 landed the last two escapes, after an earlier change in the same release closed the first fifty.

The visible result is in the language reference. The diagnostics table that typeglish reference prints, the document the CLI tells agents to read first, goes from 48 codes in 0.6.0 to 99 in 0.7.0. Be clear about what that number is and is not: those are mostly not new checks. They are checks that already ran and already blocked builds, now with a documented name, a severity, and a one-line meaning you can look up. structure/unclosed-tag, structure/duplicate-tool, structure/no-default-branch, structure/dangling-service and dozens of others became addressable in this release rather than becoming real.

That matters to a human once a quarter and to an agent constantly. An agent that gets handed structure/bad-guard-expr and can resolve it to "a <tag when=…> guard that is not a valid condition, so the section could not resolve away soundly" can act. An agent that gets handed a code its tooling calls unknown will guess, and a guess about a compiler error is how a fix becomes a second bug. The same reasoning is why reference exists as a command at all.

§5Also in 0.7.0

  • Directory builds are partial and honest. typeglish build prompts/ compiles everything that passes, names each refusal on its own line with the diagnostics that caused it and nothing written for that file, reports the tally as built n of m with the refusals counted, and exits non-zero. You get the artifacts you earned and an accurate exit code, rather than one bad file blocking the set or a green run that quietly skipped things.
  • A new import error for a real ambiguity. import/ambiguous-file blocks when an import path could be answered by several files in the workspace, because a path that resolves to one of two files by luck is a defect you find much later. Qualify the path so exactly one resolves.
  • The Monaco editor works straight off npm. The published package now ships dist/lsp-worker.js, which was missing from the 0.6.0 tarball. If you embed typeglish/monaco, the language server worker is in the box.
  • A Voiceflow export is a door too. typeglish import on a .vf file writes a git-ready prompts/ fileset plus a manifest instead of a single file, and a fileset checks in its manifest reading order, which is exactly the directory-shaped program the rest of this release is about.
  • The last raw-ordinal escape. The also-list on clarity/duplicate now speaks line numbers instead of raw statement ordinals, so "already declared identically on line 4" points at the line your editor calls 4.
Field note

Energy check: this is a big release for anyone with more than one prompt file, and a quiet one if you have exactly one. Two honest caveats. First, upgrading can turn a green prompt set red, because a directory target now runs the program pass and a set of files that each passed alone can fail together. That is the release working, not a new rule to argue with, and every finding is still proven rather than guessed; --no-program is there if you need the old output byte for byte while you fix things. Second, the program pass only fires on directory targets, so a CI line that lists files one by one gets none of this. Change it to name the directory. As always there are no benchmark or adoption numbers here: the "50 blockers to 0" line is the compiler team's count on their own prompt set, quoted as what the release reports rather than as a measurement we ran. 0.7.0 also lands engine work we have not demonstrated, including the argument slot, the project default, and registry hardening, which show up as more precise readings rather than as new commands. Those are in the changelog on GitHub. For the release that opened the door these files walk through, read the 0.6.0 notes, and for why one prompt splits into several in the first place, Say it once, or say it's optional.

FAQQuestions about 0.7.0

How do I check prompts that import each other?
Point typeglish check at the directory, not at a file: typeglish check prompts/. A directory target expands to its .tg files and checks them as one program, so files joined by $IMPORT compose before the proofs run. Naming a single file still checks that file alone, which is why a cross-file contradiction can pass file by file and fail on the directory.
Does upgrading change how my existing files check?
It can, in one specific way: a directory target now runs the cross-file pass, so a set of files that each passed alone can fail together. The failure is a real defect rather than a new rule, and it is still proven, never guessed. If you need the old behavior exactly, typeglish check <dir> --no-program checks every file alone, byte-identical to the pre-program output.
Does the program pass check every prompt against every other?
No. It is scoped to files that can collide at runtime, meaning files joined by $IMPORT. Two unrelated prompts in one directory are never co-checked: a support agent with NEVER waive a restocking fee and a billing agent with MUST waive a restocking fee report 0 errors, and the summary line says it co-checked nothing: two independent files, no $IMPORT compositions between them. They never run in the same context, so there is nothing to prove.
What is $REQUIRE section for?
It declares a section a host composes in at runtime, usually a prepended preamble, so citing that tag resolves at compile time. Without it, a citation nothing declares is structure/dangling-section-ref, a blocking error, which is right for a typo and wrong for a prompt whose runtime really supplies the section. It is scoped to the names you list: with $REQUIRE section handoffs, escalation in the file, a misspelled citation still errors. typeglish import prints the line to add and --fix-citations applies it.
What does typeglish fmt do?
It fixes formatting in place: joins hard-wrapped fragments, splits compound rules, normalizes whitespace. Mechanical, shape only, never wording. It is the command structure/wrapped-fragment names in its own message. typeglish fmt --check writes nothing and exits 1 when a file would change, which is the CI mode.
Why did --explain not know some codes before 0.7.0?
Because some codes the checker could emit were never declared in the registry --explain reads. In 0.6.0, a file with an unclosed section tag failed with structure/unclosed-tag while --explain structure/unclosed-tag answered "unknown code". 0.7.0 closes those escapes, and the reference's diagnostics table goes from 48 codes to 99. Most are existing checks that now have a documented name an agent can look up, not new checks.
∿ washed up Jul 31, 2026 ∿