mz is the compiler that implements the Mzizi language: toolchain, not the language itself.
Today it is the Phase 0 front end: lex → parse → resolve → lower → IR, plus a contract
evaluator (“lower” here means into the IR for a component). One thing lowers to Rust: a
service, which mz build turns into a local Rust + axum
package. No component lowers to Rust yet. It is the centre of the
toolchain. It is a single Rust binary with
no dependencies at all, which is a deliberate house convention rather than an oversight
— a compiler whose check loop must stay sub-second on a modest laptop should not start life
with a dependency tree to build first, and the small things it needs (JSON emission,
SHA-256) are fully specified and therefore hand-rollable with verifiable correctness.
Getting it
There is no release, no published crate, and no installer. The crate isversion = "0.0.0"
with publish = false.
cargo test --workspace from the repository root for the whole suite: 425 tests in 18
suites at 6da2170, 294 of them in the compiler crate and the rest in the benchmark harness,
runner and probe crate. compiler/src is 12,644 lines.
The commands
With no command given, the first positional argument is treated as a file and
check is
assumed.
Documented elsewhere, not implemented here. RFC-0003 §5 lists
mz refs, mz path,
mz patch and mz diff under “designed here, next in implementation order”. They are
not built, and neither are mz run, mz test or mz fmt (what still has to be
built). The binary dispatches exactly the commands in the table above: check,
fix, contract, outline and build, plus hash and ir for the IR. Asking for anything
else prints the usage line and exits 2. mz outline, mz hash and mz ir refuse a service
with exit status 2, because services have no IR yet.Exit status is a contract
So the loop can branch on status without parsing output:mz check --agent: the protocol
This is the surface an agent should use. RFC-0001 §4 states it as a versioned contract, and
four properties define it.
1
Whole-program, all at once, deterministic order
Statement-per-line plus
end-anchored blocks let the parser resynchronise at every
line. The recovery target is at most one diagnostic per true author error — never a
cascade, and never “fix one to see the next”. That is the direct answer to FM-5: a
compiler that stops at the first parse error turns one mistake into five agent turns.2
NDJSON, one diagnostic per line
Each line is a complete JSON object carrying
code, severity, file, span as
[start_line, start_col, end_line, end_col], say, and an optional fix.3
say is written for a reader with zero file context
It quotes the offending source inline, so the agent needn’t re-read the file to
understand the error. The stated target is ≤ 200 characters — density is the budget.
4
Fixes are data
Every diagnostic carries a machine-applicable
fix when one is unambiguous, tagged
exact, guess or none. mz fix applies every
exact fix in one pass, which takes a whole class of mechanical error out of the loop.compiler/src/diagnostic.rs writes the same fields with severity added and
confidence nested inside the fix object:
mz: 3 errors (2 exact-fixable), 480ms.
Diagnostic codes
Codes are grouped by phase, and the ranges are stable:
Two are worth quoting because they show what the messages are trying to be.
MZ0402, when
something that cannot start a view line does:
MZ0501, which is a warning rather than an error, and is how RFC-0001 §1.6’s rule
(“a component without a contract block compiles with a warning”) is actually enforced:
end-echo diagnostics (MZ0206–MZ0208) are the payoff for the name echo: they can
say which block on which line an end actually closes, and hand back the exact text to
write instead. Each fix rewrites the whole closer, not only the end word.
Codes added after pilot 2
Pilot 2’s 7B model brought React habits the compiler did not name, and several view mistakes gave a cascade instead of one error on the right line. These codes are the fixes, audited inbenchmarks/READINESS.md.
Each message below is quoted from compiler/src/lex.rs or compiler/src/parse.rs.
MZ0313 quotes both numbers, so the agent needn’t work out the scale itself:
mz fix: apply every exact fix
mz fix <file.mz> checks the file, applies every exact fix to it in place, then checks the
result again. The output and the exit status describe the file as it now is.
- One pass. It applies the
exactfixes the first check reported, once. It does not loop until nothing changes. The second check says what is left. guessfixes are never applied. Where twoexactfixes would overlap, the checker keeps one asexactand demotes the other toguess, so the edits never collide. A demoted fix may come back asexacton the next run.--agentworks here too.mz fix --agent <file.mz>prints the second check as NDJSON, exactly asmz check --agentwould.- The exit status is the second check’s:
0with no errors left,1with errors left,2if the file could not be read or written.
READINESS.md
records that applying mz fix to any Mzizi candidate from either pilot never raised its
error count. Whether it helps an agent converge is for the benchmark to measure, and it has
not been measured.
mz contract: does it do what it says
check and contract are separate on purpose. The charter measures two different things:
“a syntax/compile error is not itself a defect for this metric… the defect rate measures
what gets past the compiler wrong.” One exit code covering both would make the Phase 0
defect metric unreadable.
mz contract <file> evaluates the component’s own contract block against its own
declarations: enum variants and their data columns, defaults, slots and the view. A failed
clause exits 1, exactly as a compile error does, so a harness can branch on status alone.
Across the nine primitives there are 29 clauses, and CI evaluates every one.
benchmarks/harness).
For a service, mz contract runs it in process (RFC-0011 §6–§7). Each example
clause is one request, and each ensure clause is tested over a generated set of requests:
tested, not proven. Over the example service:
icon class "size-14" height 48: the class renders 56px, the contract read the 48, and
mz contract passed. Now a variant row may leave height out, and the evaluator takes the
height its first plain h-N or size-N class token renders (N × 4 pixels). A row that
writes a height which disagrees with its class fails mz check with MZ0313. The rule is
the one the benchmark harness scores with, and both test suites check it on the same
examples. Tokens with a prefix or an arbitrary value, such as md:h-10 or h-[56px], are not
read. See the pilot results.
mz build: lower a service
mz build <file.mz> --out <dir> lowers a service to Rust (RFC-0011 §8). It is
the first thing in Mzizi that lowers to Rust, and the only one: it takes a service, and refuses
a component or a file with errors.
#[tokio::test] per example. It pins
axum =0.8.9 and tokio =1.53.1. CI’s lowering job builds the example service, runs its 18
generated tests, starts the server and sends a request over a socket.
What it is not: there is no Workers, Containers or WebAssembly target, no mz run, and no
component lowering. A generated package runs locally as a native server. See
what still has to be built.
mz outline: the representation for dependencies
When editing component A that uses B, an agent needs B’s interface, not B’s body. Reading
the file gets both, plus comments and unrelated functions — RFC-0003’s barrier RB-2.
The outline carries: the first doc line, the component name, declared capabilities, every
prop with its type and default, every enum with its variant names only, the names of
fn declarations, and a marker for whether a contract exists. It drops variant column data,
view bodies and function bodies — which is where the bulk of a component’s bytes are.
It is emitted as valid Mzizi, deliberately: a reader who can read the language can
already read this, so there is no second format to learn and no second parser to keep in
step.
Measured over the nine primitives and the two component examples, the worst case is 38% of source
(spinner.mz); the test in compiler/tests/ir_measured.rs fails above 75%.
Compile speed is a protocol property
RFC-0001 §4.6 makes this explicit: “A slow compiler fails Phase 0 no matter how good its errors are.” The stated budget is sub-second incremental for a single-component change, and the benchmark is specified to record it per iteration. What has been measured so far is much narrower: parse and lower of all eleven component.mz files in
the repository takes about 10 ms in a debug build, against a 400 ms test budget. That is a
floor on a tiny corpus, not the incremental-compile figure the charter asks for. See
Status.
Paths in the agent output
mz check --agent prints each diagnostic’s file exactly as it was given. In pilot 2 the
benchmark runner passed absolute paths, so the 7B model read about 13,500 tokens of path
strings. That was the first fix pilot 2 listed, and it is fixed in the runner rather than the
compiler: the runner’s default file-name normaliser replaces the candidate’s path with its
bare file name before the model sees it, for every arm. If you drive mz from your own
loop, pass a relative path.