> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mzizi.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Syntax

> Every syntax decision in Mzizi traces to a named failure mode an agent actually hits writing Rust UI code. This is the implemented grammar, not the RFC's draft form.

<Info>
  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](/status#where-the-rfcs-and-the-implementation-disagree)
  records the divergence.
</Info>

## 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."*

| ID       | Failure mode                                                                                                                                                                                               |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **FM-1** | **Idiom sampling.** N equivalent ways to express one intent means generation samples over them, and every choice point is somewhere to be subtly wrong.                                                    |
| **FM-2** | **Anonymous-delimiter mismatch.** A `}` closing something 200 lines up carries no information about *what* it closes. One missing brace cascades into dozens of phantom errors.                            |
| **FM-3** | **Non-local fixes.** Errors whose repair site is far from the report site. Rust lifetimes are the extreme case.                                                                                            |
| **FM-4** | **Edit-anchor collisions.** Agents mostly *edit*, by exact-string match. Repeated boilerplate makes the anchor non-unique, so edits fail or land on the wrong copy.                                        |
| **FM-5** | **Round-trip starvation.** A compiler that stops at the first parse error turns one mistake into five compile cycles, each a full agent turn.                                                              |
| **FM-6** | **Token burn.** Ceremony carrying no decision — imports, derive lists, repeated visibility keywords. (The inverse trap is APL-terseness; the optimum is *plain words, no ceremony*, not *few characters*.) |
| **FM-7** | **Macro opacity.** When the visible source differs from what compiles, errors point into generated code the author cannot see.                                                                             |
| **FM-8** | **Formatting entropy.** Formatting freedom mixes cosmetic and semantic change in every diff.                                                                                                               |
| **FM-9** | **Hidden capability.** To reason locally about a function you must know what it *can do* without reading its transitive callees.                                                                           |

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

```mz examples/connectivity_bar.mz theme={null}
## A status strip announcing the app's network state.
## Corpus reference: n7-shell/nyuchi-connectivity-bar.
component connectivity_bar

  use motion

  enum connection_state
    online   label "Back online"      color "bg-malachite"
    syncing  label "Syncing changes"  color "bg-gold"
    cached   label "Viewing cached"   color "bg-sodalite"
    offline  label "You are offline"  color "bg-terracotta"
  end

  prop state: connection_state
  prop visible: bool = true
  prop on_state_change: event(connection_state)

  view
    when not visible
      nothing
    end
    strip
      slot = "nyuchi-connectivity-bar"
      class = "fixed inset-x-0 top-0 z-50 {state.color}"
      role = "status"
      text = state.label
      when state is offline
        button
          text = "Retry"
          class = "min-h-[48px]"
          tap = retry
        end
      end
    end
  end

  fn retry
    emit on_state_change(syncing)
  end

  contract
    online.label is "Back online"
    offline.color is "bg-terracotta"
    when offline shows button "Retry"
    button "Retry" min_height 48
  end

end component connectivity_bar
```

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

| Intent                | The only form                                                      | Deliberately absent                          |
| --------------------- | ------------------------------------------------------------------ | -------------------------------------------- |
| Conditional           | `when <cond> … [else …] end`                                       | `if`, ternary, `unless`, expression-`if`     |
| Enumeration           | `enum … end` with per-variant data columns                         | parallel const maps keyed by variant         |
| Iteration             | `for each x in xs … end`                                           | `while`, `loop`, iterator-chain/loop duality |
| Branching on variants | `match x / case a … / case b … / end`, exhaustive, no guards in v0 | `if`-chains over variants                    |
| Events out            | `emit <event>(args)`                                               | callback-calling conventions                 |
| Absence               | `nothing` (in views), `none` (as a value)                          | `null`/`undefined` duality                   |

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:

```text theme={null}
component  end     use    enum   prop   view   fn     contract
when       else    not    is     match  case   for    each
in         emit    nothing none  event  true   false
```

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:

```mz primitives/alert.mz (excerpt) theme={null}
  enum alert_variant
    default      class "bg-card text-card-foreground"                    announce "status"
    destructive  class "bg-card text-destructive"                        announce "alert"
    warning      class "bg-card text-[var(--status-warning,#FFD740)]"     announce "alert"
    info         class "bg-card text-[var(--status-info,#00B0FF)]"        announce "status"
  end
```

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.

<Note>
  This is a design commitment, not a demonstrated one — there is no lowering yet. See
  [Status](/status).
</Note>

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

<Warning>
  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.
</Warning>

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