Skip to main content
Everything on this page is the implemented grammar — cross-checked against compiler/src/lex.rs, compiler/src/parse.rs, and the .mz files CI gates on every push. Where RFC-0001’s illustrative examples differ, Status records the divergence.

The method: design against named failure modes

RFC-0001 does not open with a syntax proposal. It opens with a table of nine things that go wrong when a language model writes code, and then justifies every decision against one of them. Its own rule: “If a decision doesn’t trace to a failure mode, it doesn’t belong in the language.”

A whole component

This is examples/connectivity_bar.mz verbatim — a hand port of a real registry component (n7-shell/nyuchi-connectivity-bar) whose TypeScript and Rust implementations both exist, so every line is checkable against known ground truth. RFC examples come from the benchmark corpus, never invented.
examples/connectivity_bar.mz
Note what is absent: no import lines, no use statements for the button it renders, no lifetimes, no &, no .to_string(), no #[derive], no pub, no braces, no semicolons.

end, with the name echoed back — FM-2

Blocks close with end. Top-level declarations close with end <kind> <name>, and the parser cross-checks the echo. A mismatched or missing closer produces one precisely located diagnostic rather than a cascade. RFC-0001 originally justified this as parser-side error localisation and then criticised itself for it. RFC-0002 withdrew the self-criticism and supplied the real reason:
The echo is an error-correcting code for weak long-range attention. A parameter-constrained model cannot reliably track nesting depth across 200 lines; the closer hands it the answer locally instead of requiring it to reconstruct the stack.
Indentation is canonical but not significant — the parser reads end, not whitespace, so whitespace mangling in transit cannot change meaning.

One construct per intent — FM-1

There is no statement/expression duality. Logic is statements; only view and contract have their own declarative sub-grammars. The keyword vocabulary is 23 common English words, each a single token in every mainstream tokenizer:
Type names are deliberately not reserved. The lexer’s own note explains why: reserving them bought nothing and cost a collision, because text is also a view attribute, so lexing it as a keyword made a valid view line unparseable. Types resolve by position.

Variant data lives on the variant — FM-1, FM-4

enum connection_state carries label and color as columns on the variants. The TypeScript corpus kept these in parallel Record maps, and porting that corpus to Rust found exactly the drift you would predict: nodes missing from a map, a wrong fallback colour. In Mzizi the enum is the table, a missing cell is a compile error, and state.label is total by construction. alert.mz shows the version of this that is not about styling at all:
primitives/alert.mz (excerpt)
The ARIA role and the colour are one decision per severity, so they live in the same row and cannot drift apart — the same structural fix, applied to accessibility.

Props, events and defaults are declarations — FM-6

prop name: type [= default], one per line. Events are props of type event(payload), and emit is the only way to fire one. No destructuring ceremony, and no interface-plus-signature double declaration — the TypeScript pattern declares every prop twice, which is pure ceremony and a real drift site.

The view grammar is native, not a macro — FM-7

view … end is part of the language grammar. Elements are words (strip, row, button, control, notice, busy, rule); attributes are name = value lines; children are nested blocks. Every diagnostic inside a view points at source the author actually wrote. Interpolation is {expr} inside strings, and it is the only string-building mechanism. Attributes the corpus proved load-bearing are first-class and checked: slot (the data-slot identity contract), role (ARIA — and an explicit role replaces implicit semantics, which is a real corpus defect class), and class (statically scanned, so dynamic class construction is impossible).

Capabilities are declared at the top — FM-9

use motion, use net, use storage, use ml. An agent reading the first five lines of any component knows its blast radius. use ml is the Phase 3 Candle seam: it parses today and is specified to error as “not yet available”.

No ownership at the surface — FM-3

Mzizi lowers to Rust, but no borrow, lifetime or ownership concept exists in the surface language. Values have value semantics; the compiler owns the Rc/signal plumbing in lowered code. The class of non-local-fix errors that makes Rust hard for agents is spent once, by the compiler authors, instead of on every generation.
This is a design commitment, not a demonstrated one — there is no lowering yet. See Status.

Canonical form — FM-8, FM-4

There is meant to be exactly one rendering of any Mzizi program: two-space indent, one attribute per line past two, fixed column alignment in enum tables, and a fixed declaration order inside a component (doc → use → enum/type → prop → view → fn → contract). No configuration. The point is that diffs are always semantic and any unique line is a unique edit anchor — and that the benchmark’s token counts are measured against one canonical surface rather than a formatting lottery.
RFC-0001 §3 specifies that mz rewrites every file it touches to canonical form. There is no formatter in the binary today; the .mz files in the repository follow the convention by hand. mz dispatches check, outline, hash and ir and nothing else.

Still open

Local state — state count: int = 0 plus assignment semantics that lower to signals — is unresolved. It was open in RFC-0001 §7, still open in RFC-0002 §6, and RFC-0003 §8 adds a constraint to it: signal semantics must survive content addressing. state is not a keyword in the lexer today.