Chapter 08

Reference

Everything the language offers, on one page. The authority is language/appwithai-language.json — when this page and that file disagree, the file wins.

Directives

DirectiveFormPurpose
%%meta%%meta <key>: <value>Section metadata: name, kind, version, entity, stack.
%%enum%%enum Name: a, b, cA closed vocabulary, reusable by fields and states.
%%field%%field E.col enum: NameField metadata: enum, ui, default, min, max, help, format.
%%entity%%entity E audited: trueEntity metadata: prefix, audited, softDelete, label, icon.
%%index%%index E(a, b) [unique]An index over one or more columns.
%%category%%category name: X; icon: Y; entities: A, BDashboard grouping. ;-separated keys; only name required.
%%rule%%rule name on E event: <hook> priority: <n>Binds a decision flowchart to an entity and lifecycle event.
%%action%%action name <type> when: <expr> …A side effect a rule emits. See below.
%%workflow%%workflow Name entity: E kind: <hook|state|saga>Names and classifies a workflow section.
%%hook%%hook <type> <handler> on E[field: c]Binds a handler to a lifecycle event.
%%step%%step <node> <StepType> <k>: <v> …Binds a saga flowchart node to an executable step.
%%rbac%%rbac role:a|b on E.opRestricts an operation or transition to roles.
%%trigger%%trigger cron:0 7 * * * -> handler on EAn event source: cron:, webhook:, message:.
%%guard%%guard field <op> <json>An automation condition (the builder's dialect).
%%loop%%loop L1 while: f <op> v max: nRepeat-while inside an automation. max: is required.

Types

CanonicalAliases that normalize to it
stringvarchar, char, uuid, guid, id, email, url, phone, password, color
textlongtext
integerint, bigint, smallint
decimalnumber, float, double, money, amount
booleanbool
date
datetimetimestamp, time
jsonjsonb, object, array

Length is written on the type: string(120) display_name.

Modifiers

ModifierEffect
PKPrimary key; unique; auto-added as string id PK if absent.
FKForeign key; the target is derived from the column name.
UK / UNIQUEUnique index.
OPTIONAL / NULLNullable. Everything is required by default.

Cardinalities

OperatorKindExample
||--||one to oneUser ||--|| Profile : has
||--o{one to manyAccount ||--o{ Contact : employs
||--|{one to many (at least one)Order ||--|{ OrderItem : contains
}o--||many to oneDeal }o--|| Stage : in_stage
}|--||many to one (at least one)OrderItem }|--|| Order : belongs_to
}o--o{many to manyStudent }o--o{ Course : enrolls
}|--|{many to many (at least one)Author }|--|{ Book : writes
|o--o|zero or one, both sidesEmployee |o--o| Desk : assigned

Lifecycle hooks

HookPhase · opTypical use
beforeCreatebefore · createNormalize, mint a number, set defaults
afterCreateafter · createWelcome email, emit an event
beforeUpdatebefore · updateSnapshot, transform, recompute
afterUpdateafter · updateSync outward, invalidate a cache
beforeDeletebefore · deleteBlock a delete — return false
afterDeleteafter · deleteClean up files or related rows
beforeQuery / afterQueryqueryTenant scoping; post-process results
beforeRead / afterReadreadGuard a record; redact fields
beforeList / afterListlistFilter or sort; annotate a page
customValidatevalidate · anyCross-field validation — throw to reject

Rule actions

TypeRequiredOptional
trigger-workflowworkflowmessage
validation-errormessage
transformfield, valuemessage

Saga step types

TypeRequiredOptionalPublishes
Decisionone of decisionTable / rulepublishOne variable per output column of the matching row
CreateEntityentity, fieldsasThe new row's id under as
UpdateEntityfield + one of source/valueentity, targetField, targetSource
DeleteEntityentity, targetField, targetSource, hard
Formulatarget, operationsource, operand, valueIts result under target
RESTurlmethod, bodyTemplate

Formula operations: multiply, divide, add, subtract (need source + operand), set (needs value, stored unchanged — the only way to pass text), copy (needs source).

Two syntax rules that bite

fields and decisionTable carry JSON and must be the last key on the line. A value with a // in it — a URL — must have no space after its key:, because properties split at whitespace before the next key:.

Checker codes worth recognising

CodeMeansDo
EML114FK column does not end in _idRename it, or accept a plain string with no lookup
EML117Entity has no primary keyAdd string id PK (auto-fixable)
EML262A step is missing a required propertyCheck the table above
EML264A step reads a variable nothing publishedPublish it with as: / target:, or it is a column of the triggering row
EML265Cross-entity write with no row targetingAdd targetSource or targetField
EML272A decision row leaves an output unsetGive every row every column — the engine discards incomplete rows silently
EML284An action names a workflow that does not existFix the name, or declare the workflow
EML286A trigger: rule saga nothing namesAdd the action, or make it automatic
EML421 / EML424No initial state / a state with no path to a terminalFix the machine — a record will get stuck there
EML426 / EML427Enum and state machine disagreeMake the value sets identical
EML502An FK with no matching relationshipDeclare the relationship; lookups depend on it

Commands

CommandDoes
bun language/checker.ts <file>Check a model. --strict fails on warnings, --json for CI.
bun language/cli/eml.ts info -i <file>Summarise a model: entity, rule, workflow, hook and index counts.
bun packages/generator/src/cli/generate.ts generate …Generate an application.
bun run db:setupIn a generated app: migrate, then seed.
bun run devIn a generated app: API and web together.
bun run test:e2e:fastThe generated end-to-end suite, minus the volume tests.

Where things live

PathWhat
language/appwithai-language.jsonThe canonical language definition — the authority for everything on this page
language/spec/The prose spec: ERD, rules, workflows, types, directives
language/examples/crm.eml.mmdThe model this guide builds
language/checker.tsThe checker
packages/generator/src/parsers/mermaid.parser.tsThe ERD parser
packages/generator/templates/Every template the generated app is made of
generated-projects/crm/The application, as generated