Learn the smallest MEL surface you need before wiring UI, server routes, or agents.
MEL defines the domain rules. TypeScript activates those rules and calls them.
domain.mel -> createManifesto(...).activate() -> app.action.x.submit() -> snapshot()
Start with the pieces below. You can build the first web app and trusted agent integration without reading the full language reference.
| MEL Piece | Use It For | First Example |
|---|---|---|
domain |
Name the app model | domain TodoApp { ... } |
type |
Name reusable data shapes | type Todo = { id: string, title: string } |
state |
Store source-of-truth app data | todos: Array<Todo> = [] |
computed |
Derive read-only values from state | computed todoCount = len(todos) |
action |
Define a change callers can submit | action addTodo(title: string) { ... } |
dispatchable when |
Reject invalid input before the action runs | dispatchable when trim(title) != "" |
available when |
Hide or disable actions that do not apply | available when hasCompleted |
onceIntent |
Run the action body once for one submitted action | onceIntent { patch ... } |
patch |
Write the next app state | patch filterMode = newFilter |
effect |
Later: ask TypeScript to do external work | effect api.fetchUser({ id: id }) |
If a page introduces more than these pieces, treat it as a lookup page or an advanced topic.
The example below uses three MEL helpers that app developers see often:
| Name | Meaning |
|---|---|
$item |
The current item inside filter(...) or map(...) |
$runtime.random.uuid |
A deterministic runtime-provided id for the submitted action |
dispatchable when |
Rejects invalid input before the action body runs |
domain TodoApp {
type Todo = {
id: string,
title: string,
completed: boolean
}
state {
todos: Array<Todo> = []
filterMode: "all" | "active" | "completed" = "all"
}
computed todoCount = len(todos)
computed completedCount = len(filter(todos, $item.completed))
computed activeCount = todoCount - completedCount
computed hasCompleted = completedCount > 0
action addTodo(title: string)
dispatchable when trim(title) != "" {
onceIntent {
patch todos = append(todos, {
id: $runtime.random.uuid,
title: trim(title),
completed: false
})
}
}
action setFilter(newFilter: "all" | "active" | "completed")
dispatchable when filterMode != newFilter {
onceIntent {
patch filterMode = newFilter
}
}
}
That file is enough for app code to submit actions and read Snapshots:
const app = createManifesto(TodoMel, {}).activate();
await app.action.addTodo.submit("Ship docs");
await app.action.setFilter.submit("active");
const snapshot = app.snapshot();
console.log(snapshot.state.todos);
console.log(snapshot.computed["activeCount"]);Put data in state when users, UI, server routes, or agents need to see it
later.
state {
todos: Array<Todo> = []
draftTitle: string = ""
lastSavedAt: number | null = null
}
Keep state concrete. Prefer names that match the product: todos,
selectedProjectId, syncStatus, lastAgentSummary.
Use computed for values that can be derived from state.
computed todoCount = len(todos)
computed completedTodos = filter(todos, $item.completed)
computed hasCompleted = len(completedTodos) > 0
Computed values are read from snapshot().computed. Do not patch them directly.
Use actions for app, route, script, and agent commands.
action clearCompleted()
available when hasCompleted {
onceIntent {
patch todos = filter(todos, !$item.completed)
}
}
Submit that action from TypeScript:
await app.action.clearCompleted.submit();Use available when when the UI should hide or disable an action. Use
dispatchable when when an input must be rejected even if the caller submits it.
action renameTodo(id: string, title: string)
available when todoCount > 0
dispatchable when trim(title) != "" && len(filter(todos, $item.id == id)) > 0 {
onceIntent {
patch todos = map(todos,
$item.id == id
? { id: $item.id, title: trim(title), completed: $item.completed }
: $item
)
}
}
You can build the first Todo app, React UI, and local agent wiring without
effects. Use effect only when the work leaves the Manifesto domain: API
calls, database writes, model calls, queues, storage, email, or other services.
domain Profile {
state {
userName: string | null = null
loading: boolean = false
}
action fetchUser(id: string) {
onceIntent {
patch loading = true
effect api.fetchUser({ id: id })
}
}
}
The TypeScript handler lives outside the .mel file and returns patches that
update state. Read Effects or Effect Handlers
after the first app path works.
Skip these until a guide points you there:
- raw
DomainSchemaobjects - low-level runtime assembly
- full internal snapshots
- proposal, approval, and durable history runtime setup
- full MEL grammar tables
- raw codegen plugin internals, unless you are customizing generated output
Read Creating an App and Reading Snapshots before the app-code examples in State, Computed Values, and Actions. Then build the Tutorial or wire React. Use the MEL Reference only when you need complete syntax lookup.