Chapter 11

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.

ModuleURLWhat 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.
javascript
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.

1

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.

  1. 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 .mmd name. 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 status field 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.

  2. 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 %%rule flowchart 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 status before relying on it: %%entity, %%rule and %%trigger are validated but not compiled, so do not promise behaviour that rides on them.

  3. 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.

  4. Deliver

    Hand back the finished <business-name>.mmd as 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.

javascript
// 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 };
}
Never hand over a model you have not checked

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.

BandWhere the fault is
EML0xxThe document — its structure, its sections, its metadata.
EML1xxEntities — attributes, types, keys, relationships.
EML2xxDirective-declared hooks, rules and workflows.
EML3xxRule flowcharts.
EML4xxWorkflow sections.
EML5xxCross-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.

url
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.

A clean model is not the same as the right model

This chapter answers one question: would the generator accept this document? It cannot answer the other one — is this the business you meant. Nothing here is wrong in a model whose Invoice lifecycle can reach paid without passing through approved, or whose support agents can read every entity in the company.

The model viewer draws what the checker only validates: every entity with its columns and their controls, each state machine with the moves the generated API will allow, each business rule’s branches and what it emits, each multi-step process as an ordered ladder, and the roles with the number of entities each one can read. It carries this same checker, so the verdict on this page is the verdict there.

Open the model viewer →