Skip to main content
RFC-0001 designed the syntax against nine failure modes in writing code. RFC-0003 does the same for reading a codebase and retaining what was read — the other half of the loop, and the half that decides whether a small model can work in a large project at all.

The eight reading barriers

Content addressing answers seven of the eight from a single decision. That is the entire argument for it. RB-5 is the exception only in the sense that the syntax already solved it: the view grammar is native rather than a macro, so the IR inherits the property rather than fixing it.

The node model

A node’s hash covers its canonical serialization, which includes its children’s hashes. That makes the store a Merkle DAG: change a leaf and every ancestor’s hash changes, while every untouched sibling keeps its own.

Canonical serialization

Hashing is only stable if serialization is exact, so the form is fully specified and length-prefixed rather than delimiter-separated — a delimiter can appear inside a value and silently merge two different nodes into one hash:
Fields are sorted by key so that authoring order cannot change identity. Children are not sorted, because their order is meaning — a view’s children are a sequence.

The hash function

SHA-256, hand-rolled, verified against the published NIST vectors. The house convention is to hand-roll small, fully-specified things rather than take a dependency, and SHA-256 qualifies precisely because it is completely specified with official test vectors, so correctness is verifiable rather than assumed. RFC-0003 states the limit of that plainly, and so does this page:
This is a content-identity function, not a security boundary. Collision resistance matters; side channels do not. If a hash ever becomes a trust boundary — signed releases, a shared public store — swap in a reviewed crate at that point.
Hashes display as the first 12 hex characters.

Names are metadata, which is what makes renames free

The store maps Hash → Node. A separate namespace maps name → Hash. Nothing inside a node records the name of anything it references — only hashes.
  • A rename touches one namespace entry. No call site changes, because no call site ever held the name. That removes the whole class of exhaustive multi-site refactor, which is precisely what small models perform worst at.
  • Source carries no import lines. This is the mechanism underneath the syntax-level rule: source says what, the namespace says which.
  • Two components can be compared for identity, not similarity. Same hash means genuinely the same logic, wherever it came from.

What is implemented, and what is designed

Implemented

mz hash — the root hash and node count after sharing.mz outline — the interface form.mz ir — the flat node listing with hashes and structural paths.

Designed, not built

mz refs <hash> — every node referencing this one (RB-3).mz path <hash> — the structural path.mz patch <path> <source> — the write half of RB-1.mz diff <hash-a> <hash-b> — node-level added / removed / changed, with moves reported as moves.
The store is in-memory only. On-disk format, and whether the store is shared between projects, is an open question with a caching payoff and a trust question attached.

A correction the implementation forced

This is worth reading because it is the shape of the whole project in one paragraph: a claim was made, a measured test caught it, and the claim was narrowed rather than defended. RFC-0003’s first draft called structural paths “stable across edits”. Two row siblings inside alert’s view collided on the same path, which would have made a patch address ambiguous. Two things came out of it. Paths now disambiguate same-named siblings — by slot where one exists (alert/view/element:notice/element:row@alert-title, readable, and slot is already the design system’s identity attribute), by ordinal otherwise. And the claim was cut back to what is true:
Paths are unique and readable and survive reformatting; the permanently stable identity is the hash. Inserting a sibling can shift an ordinal-disambiguated path, so anything holding a reference across edits should hold the hash, not the path.

What the IR unlocks — none of it done yet

  • Contract evaluation. Contract bodies parse and do nothing. With an IR they can be evaluated against the tree, which is what finally makes the Phase 0 defect metric (compiles clean, behaviourally wrong) measurable by the toolchain rather than by a Rust test standing in for it.
  • Incremental compilation keyed on hashes. Only changed hashes and their ancestors recompile — the cheapest route to the sub-second check loop the charter names as a Phase 0 success metric.
  • A cache that cannot lie. The store is derived, not authored, so it cannot drift from the code the way prose documentation does.

Measured

From compiler/tests/ir_measured.rs, over the nine primitives plus the corpus example, so these are reproducible rather than asserted:
The sharing number is small and RFC-0003 labels it as such: ten files is a tiny corpus, and sharing pays in proportion to repetition. At the scale of the 571-component registry the ratio should improve substantially — “but that is a prediction, and it stays labelled as one until the registry is lowered.”

Still open

  1. Local state. Signal semantics must survive content addressing.
  2. The patch API’s conflict model. Two agents patching sibling nodes should compose; patching the same node must not silently pick a winner.
  3. Store persistence. In-memory today.
  4. Contract evaluation semantics — the subject language for assertions like every button_size height at_least 48.