Check a model before you ship it
A model is not finished when it is written. It is finished when the checker accepts it. This chapter is the procedure for getting there — and the checker itself, running on this page, so you can put a document through it without installing anything.
The published validators
Two ES modules are published at fixed URLs on this site. They are bundled from
language/checker.ts and language/fixer.ts by
scripts/build-language-tools.ts — the same engines the command line runs, not a lighter
web edition — so a document that passes here is a document appwithai will accept.
| Module | URL | What it gives you |
|---|---|---|
checker.js |
https://appwithai.org/guide/checker.js |
Every diagnostic bun language/checker.ts prints, as check(source). |
fixer.js |
https://appwithai.org/guide/fixer.js |
The five auto-repairs, plus checkAndFix(source) — repair and re-check in one call. |
import { check, formatReport } from "https://appwithai.org/guide/checker.js";
import { checkAndFix } from "https://appwithai.org/guide/fixer.js";
const report = check(source);
// { ok, counts: { errors, warnings, infos }, issues: [...], languageVersion }
console.log(formatReport(report));
Both also attach themselves to globalThis as EMLChecker and
EMLFixer, so a page that loaded them with a bare import and no bindings can
still call them. They are built against EML 1.2.0, and every report says
which version produced it.
Check a model now
Everything below runs in this tab, against those two modules at those two URLs. Nothing is uploaded.
The document
Start from an example, break one on purpose, or paste your own.
The authoring protocol
This is the procedure the specification asks anyone — person or model — to follow when someone describes a business and asks for an application. Four steps, in order. The first is the one most often skipped, and skipping it is what produces a schema that validates and a business that does not run.
The deliverable is one file — <business-name>.mmd, every byte of it Mermaid.
Steps 1, 3 and 4 exist to make that file correct; only step 2 produces it. A reply that
describes the model instead — headings, an entity glossary, a lifecycle drawn as an arrow in a
sentence — is not a model however thorough it is. Paste one into the checker above and it answers
EML004, empty document: no entities, rules, or workflows found, because to the
parser it is empty.
-
Enhance the brief before modelling it
A business description is always thinner than the model it implies. Someone who asks for “a booking system for a dance studio” has not mentioned instructors, rooms, cancellation windows, waitlists, or what happens to a class when its instructor calls in sick — and every one of those is a table, a rule or a workflow.
So think the business through before writing a byte of Mermaid: an enhanced specification, stated back to the user in the reply. It is analysis, not the artifact — it never becomes the file, and it never carries the
.mmdname. It covers all five of:- The entity model — every entity the business needs, not only the ones named, with types, modifiers, keys, foreign keys and cardinalities, including the reference and lookup entities the described flow silently depends on.
- Each entity's workflow — the states a record moves through, the event on each edge, and who may make each move. An entity with a
statusfield has a state machine whether or not anyone drew it. - The business rules — what decides what, the inputs each reads, and whether it merely decides or must also act. A rule that must act needs
%%action. - Cross-entity effects — where changing one entity must create or update another: an order line decrementing stock, an approval writing a ledger entry, a cancellation releasing a slot. These are the ones most often missed.
- The multi-step workflows — any flow spanning more than one entity or actor, end to end, including what happens when a step fails and what compensates a step already taken.
State assumptions as assumptions. Where the description leaves a genuine fork — two plausible businesses, materially different models — name the fork, model the reading you recommend, and say what changes if the other was meant. A question raised instead of a file leaves the reader with nothing to correct; raised beside one they can already run, it costs them a sentence to answer.
-
Write the model file
Only now write the document: the ERD section, the rules sections, the workflow sections, and the directives that give them meaning. Every entity from step 1 appears; every rule is a
%%ruleflowchart or an%%action; every cross-entity effect is a hook, an action or a step; every multi-step flow is a saga.Name the file after the business —
acme-dance-studio.mmd, lower-case, hyphenated, one file. It opens on a%%line —%%meta name:before the first section keyword — and every line is a Mermaid statement, an EML%%directive, or blank: no Markdown headings, no bullets, no fences, no prose. Everything the language adds already rides in Mermaid comments, so anything you want to say goes in the reply or in a%%line.Check a directive's
statusbefore relying on it:%%entity,%%ruleand%%triggerare validated but not compiled, so do not promise behaviour that rides on them. -
Validate — three runs, three repairs
Run the checker at least three times. Not ceremony: a repair can uncover a problem the original error was masking, so a single clean pass immediately after an edit describes a document that no longer exists. The third pass over unchanged bytes is what makes “clean” mean clean.
If it fails, correct the cause in the model — not the symptom in the text — and re-run, up to three correction attempts. Do not deliver a document that still has errors after three. Deliver the best version you have, say plainly which diagnostics remain, and let the user decide.
-
Deliver
Hand back the finished
<business-name>.mmdas a downloadable file, not a fenced block someone has to copy out of a chat log — and the file that was validated, not a draft of it. Alongside it, in the reply rather than inside the file: the enhanced specification from step 1, so the user can correct what you inferred; the final checker result with counts; and anything you assumed or left unresolved.
// Pass 1 — repair the five auto-fixable codes, then re-check what survived.
let report = checkAndFix(model);
model = report.source;
// Passes 2 and 3 — check the repaired bytes, and confirm they are stable.
for (let pass = 2; pass <= 3 && report.ok; pass++) {
report = { ...check(model), source: model };
}
Silently shipping a model that fails validation is the one outcome worse than not finishing. If it does not come clean in three attempts, say so, with the diagnostics that remain and what you tried.
Reading a diagnostic
Every finding carries a code, a line and a hint, and the codes
are banded so the band alone tells you where the fault is.
| Band | Where the fault is |
|---|---|
EML0xx | The document — its structure, its sections, its metadata. |
EML1xx | Entities — attributes, types, keys, relationships. |
EML2xx | Directive-declared hooks, rules and workflows. |
EML3xx | Rule flowcharts. |
EML4xx | Workflow sections. |
EML5xx | Cross-section consistency — the two halves of the document disagreeing. |
Five codes can be repaired without being told what was meant, and the fixer repairs exactly those:
EML001, EML114, EML117, EML421 and
EML422. Everything else needs a decision, which is why checkAndFix reports
what it could not touch rather than guessing.
Warnings are not noise. Most describe something the generator accepts and quietly gets wrong — a dropped modifier, a state no enum backs, a rule that can decide but cannot act. Clear them, or be able to say why you left one.
The same check, from a command line
This page is one way in. If you have a shell — or you are a language model with one — the same three passes run in a single command, against the same two modules:
curl -sO https://appwithai.org/guide/check-model.mjs
node check-model.mjs my-business.mmd
It finds checker.js and fixer.js — beside itself if all three were
downloaded together, otherwise from this site — runs checkAndFix and then two
check passes over the repaired bytes, prints the report, and exits 0 when
the model is clean and 1 when the generator would refuse it. --write saves
the repaired document back over the input. It is a runner, not a second checker: every diagnostic it
prints comes from the published modules, which is the same reason this page has no validation logic
of its own.
The specification, for machines
The full machine-readable specification of the modelling language — every directive, type, modifier, cardinality, hook, step and diagnostic, plus the authoring protocol above in its authoritative form — is published as a single file. It describes the language and nothing else: a model is all you need to produce, so the generator, the framework and the shape of the emitted application are deliberately left out of it.
https://appwithai.org/llms-full.txt
Point a language model at it before asking for a model, and it has the language definition, the
directive table, the checker codes and this protocol without having to guess at any of them. When that
file and language/appwithai-language.json disagree, the definition wins; when the
definition and a shipped compiler disagree, the compiler wins and the definition is the bug.