Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Release report — 4fb1554

Frozen evidence for one tested commit. This file is not updated as the project moves; it records what was verified at 4fb15542dfbf9af0bf34c42fe52783637b3daed4 and stays that way. Current implementation truth is ../capability-matrix.md.

Absorbed 2026-09-21: the release and limitation sections of the Era-1 ledger (goal-status.md), which is retired. Git retains the ledger.

What was built under the Era-1 execution contract (retired 2026-09-21; see Git history), what verifies it, and what it does not establish. Written to be checkable: every claim here names the command or file that supports it, and the limitations are in the same document as the results rather than in a different one.

Outcome

Nazm is a small, specified language with two implementations that agree — a tree-walking interpreter and a native compiler through LLVM IR — and a compiler for it written in Nazm that reproduces itself byte for byte — over a stated bootstrap subset, which covers concurrency and modules: the Nazm-written compiler resolves use and builds a multi-file program on its own. The language has structured concurrency, a canonical formatter, machine-applicable fixes under a versioned JSON schema, and measured performance baselines with one profile-driven optimisation pass behind them.

It is an experimental release. It has been verified on one target, it has no package ecosystem, no visibility control, no recursion in native code, and no cross-language performance comparison of any kind.

The checklist, and the evidence for each line

Definition of done (§13)StateEvidence
Scalar control flow, including return and transfers inside valuesdonecrates/nazm-cli/tests/build.rs, every case at -O0 and -O2 against the interpreter
A bootstrap subset with text, structured data, collections, memory behaviour, file I/O, errors and modulesdonedocs/bootstrap.md §1; the subset is what compiler/*.nz is written in, and use is resolved by the Nazm-written compiler itself — compiler/module.nz
Compiler frontend and lowering/emission in Nazm, external dependencies documenteddone for the bootstrap subsetcompiler/lex.nz, parse.nz, analyse.nz, emit.nz; dependencies in docs/bootstrap.md. Concurrency and module resolution are both implemented — compiler/module.nz, and the task and channel runtime in compiler/emit.nz
Native C1 → C2 → C3 bootstrap, conformance and reproducibilitydonecompiler/bootstrap.sh; C2 and C3 byte-identical in IR and executable, 5 conformance cases. Every stage compiles compiler/emit.nz itself and resolves its use lines with compiler/module.nz, so what is bootstrapped is the compiler as written rather than a concatenation of it
Ordinary independent programs compile with the Nazm-written compilerdone within the subsetcrates/nazm-cli/tests/selfhost.rs. Concurrent and multi-file programs both compile with it, imports and all
Initial structured concurrency: transfer/capture rules, cancellation, cleanupdonedocs/spec.md; 17 interpreter tests and 17 native tests, all deadline-bounded. Both implementations join a scope before reporting a failure, verified at -O0 and -O2 against the interpreter for a failing body, a failing task, a failure below the body, nested scopes, and body-over-task precedence
Structured developer interfaces, grammar/guide, formatting, repair workflowdonedocs/diagnostics.md, schema/, docs/grammar.ebnf, docs/guide.md, scripts/repair.sh
Performance baselines, a profiling/optimisation pass, no unsupported speed claimsdonedocs/performance.md, bench/baseline-linux-aarch64.json (current); bench/baseline.json (Darwin) is stale — see docs/performance.md
A clean rebuild of the integrated final state on a supported targetdonea fresh copy, --offline, full suite — see Reproducing this
A final local review/release packagedonescripts/release-candidate.sh, run contained at 4fb1554: every gate passed and the candidate was verified against its own manifest — 70 files, every digest matching — then extracted and re-digested independently on the host. The script validates what it assembled rather than trusting it, and compares file counts both ways so a file copied in but not listed fails

What is verified, and what that means

Six different kinds of evidence are involved, and they are not interchangeable.

ClaimEstablished byNot established
The two implementations agreeevery native test runs the same program under nazm run and comparesthat either matches the specification where both are wrong the same way
The compiler is self-hostingbootstrap.sh: C2 and C3 byte-identicalthat the compiler is correct. A compiler that miscompiles its own source in a way that reproduces the miscompilation is also a fixpoint — stated in docs/bootstrap.md
Concurrency works34 tests, each with a deadline so a hang fails by name; invariants and totals, never a schedulefreedom from deadlock, which the specification explicitly does not promise
The formatter is safeit re-lexes to the same token kinds, is idempotent, and keeps every commentthat its layout is the one you would choose
Fixes are applicablepreconditions on every fix; --apply refuses to write a file that would not parsethat a needs_review fix is right — that is what the label means
The optimisation helpeda profile before, a measurement against a recorded baseline after, same machine minutes apartanything about another machine, or about any other language

Supported platform and dependencies

Verified targetsaarch64-apple-darwin (Darwin arm64) and aarch64-unknown-linux-gnu (Linux aarch64, inside the contained runner). Other targets remain unverified rather than unsupported
Toolchainrustc 1.98.1, pinned in rust-toolchain.toml. Workspace MSRV floor 1.85; xtask declares 1.89 for itself
External at build timeclang, to assemble the emitted LLVM IR and link
External at run timelibc — malloc, realloc, write, exit, stdio, and pthreads for concurrency
Rust dependenciesclap, serde, serde_json, tempfile (dev). The benchmark harness has its own set; see Cargo.toml

The bootstrap needs the Rust implementation to exist: C1 is built by it. Nazm does not build itself from nothing, and docs/bootstrap.md says so at length.

Known limitations

These are properties of the release, not a backlog.

  • No visibility control. Every top-level function in a file is visible to anything that uses it. Adding private-by-default later will break existing multi-file programs; it already forced compiler/lex.nz to be split from compiler/lexer.nz, because a library carrying a main collides with its importer’s.
  • No recursion in native code. Refused by name (N0101), pending a stack contract — by both backends now. Until 2026-09-21 only the Rust one refused it: the Nazm-written compiler silently emitted native recursion that clang assembled and that ran, for programs nazm build rejects. compiler/emit.nz now makes the same refusal over every declared function, covering direct and mutual recursion, cycles that exist only across files, and cycles through a spawn. nazm run still runs recursive programs: this is a backend limit, not a language rule. The compiler written in Nazm is iterative throughout, which is why it compiles natively.
  • Ambient authority. read_file, write_file and the rest are available to any function with no capability to pass. Labelled as such in docs/spec.md; a capability parameter is the growth path and main(io: IO) was considered and not chosen.
  • A compiled program has no iteration budget. nazm run stops a runaway loop and reports N0402; the executable does not stop.
  • Concurrency is one OS thread per task. No M:N scheduling, no work stealing, no fairness, no cancellation beyond closing a channel, and no deadlock freedom. At roughly 4.5 µs per channel hand-off it is suited to coarse-grained work and not to fine-grained message passing.
  • Module resolution keys files by a textually normalised path, not a canonical one. ./x.nz, x.nz and a/../x.nz are one file; two names that reach one file through a symbolic link are two, and it would be read twice. The driver keys by the canonical path instead, because it can — the emitted runtime has no realpath.
  • lexer.nz, parser.nz and check.nz remain single-file stage probes. They exist to be compared token-for-token and node-for-node against the reference stages, so they read exactly the file they are given. Resolution belongs to the compiler driver, emit.nz, and that is where it is.
  • Almost no cross-language performance comparison. docs/performance.md measures this implementation against itself, plus one program — a sieve — against clang and python3 on one host. Nothing has been measured against Rust, Zig or Go, and one program is not a language comparison. (This entry read “No cross-language performance comparison” until 2026-09-21; the references were added at 87e1a47 and the disclaimer outlived them.)
  • The benchmark harness (nazm-bench) cannot produce a benchmark number. Its agent drivers, provider adapter and task corpus are unwritten, and run is disabled. It is unrelated to the language work above; see README.md.

Results

cargo test --workspace, run through cargo xtask contained on Linux: 549 passed, 0 failed, 6 ignored. Across 35 suites.

Frozen. This is the count at 4fb1554, and it is not updated as the suite grows — that is what “frozen evidence for one tested commit” means. capability-matrix.md carries the current figure. What they cover, grouped by the question each group answers:

countwhat it establishes
Language semantics (nazm-core/tests/language.rs)114each rule in docs/spec.md that a refactor could quietly reverse
Native end-to-end (nazm-cli/tests/build.rs)127every case compiled at -O0 and -O2, and compared against the interpreter
Self-hosting differentials (nazm-cli/tests/selfhost.rs)20the Nazm lexer, parser and checker against the Rust ones over every .nz in the tree, the full bootstrap chain, its own module resolution, concurrency compiled by it end to end, and its refusal of recursion matching the reference’s
Concurrency, interpreted (nazm-core/tests/concurrency.rs)17joins, capture, cancellation, cleanup, contention — every one with a deadline
Concurrency, native (nazm-cli/tests/concurrent.rs)17the same programs compiled, at -O0 and -O2, against a written expectation and the interpreter. Six of them establish that a failure joins its scope before it is reported, by giving a task an observable effect and ordering it with channel handshakes rather than by how fast anything runs
Machine interface (nazm-cli/tests/schemas.rs)5real command output validated against schema/*.json
Formatter (nazm-syntax/tests/format.rs)11token preservation, idempotence, comment preservation, over every source in the tree
Grammar (nazm-syntax/tests/grammar.rs)4every lexer token written down, every rule reachable, every example parsed by the real parser
Repair workflow (nazm-cli/tests/workflow.rs)10nazm fix, nazm fmt, nazm test and scripts/repair.sh end to end
Coverage of the guarantees themselves (codes.rs, builtins.rs, docs.rs, counts.rs, resource_regression.rs)10every diagnostic code asserted or named unreachable; every built-in exercised where an answer is asserted; every example run by a test; the README’s command table, the published schemas and the verified target checked against the build; and this table’s own rows required to sum to the total above; and the stage drivers required to assemble their output in one allocation, measured under a ceiling
The rest214unit tests across the six crates, the CLI’s own behaviour, and the benchmark harness — which is not part of this goal

The bootstrap: C2 and C3 byte-identical in IR and executable, with 5 conformance cases agreeing across C2, C3 and the interpreter. compiler/bootstrap.sh writes the record, including the source digest and line count.

Benchmarks: docs/performance.md, with the noise floor of the method stated alongside the numbers.

Mutation coverage

cargo xtask mutate injects each catalogued defect into a disposable copy and requires the suite to catch it. Run at final integration against 524cd24, which is the integrated state minus nazm test — an addition that touches none of the code the catalogue mutates.

48 caught, 0 survived, 0 unusable, against a catalogue that held 50 entries at the time and holds 51 now. Every catalogued defect that could be injected was detected by the suite.

Two were not injected, and both were already known: the run confirmed, in the runner’s own words, what checking the catalogue against the tree had found an hour earlier.

the runner saidwhy
the-conditional-selects-the-wrong-branchpattern occurs 2 times; it must be uniqueits anchor was the br i1 line alone, which appears in both statement_if and conditional
the-loop-reuses-its-first-conditionpattern not foundits anchor predated break and continue, which made the back edge conditional

Neither is a survivor, and both are worse than one: a mutation that does not apply is a problem only if somebody reads the log, and reads as coverage if they do not. Both are repaired, and the ambiguous one is split into two entries, because the statement and value forms of if are different code paths. cargo xtask mutate --only NAME exists so the repairs could be verified without re-running fifty.

A caught mutation establishes that the suite detects that defect, and nothing broader. Fifty-one entries is fifty-one facts, not a coverage percentage.

Skipped and conditional coverage

Reported rather than folded into a total, because a skipped test that looks like a passing one is the most misleading thing a suite can contain.

Two diagnostic codes, N0404 and N0406asserted by no test, and named as unprovokable in crates/nazm-cli/tests/codes.rs with the reason: one needs the machine to run out of threads, the other out of memory. That test requires every other code to be asserted somewhere, so the list is the only way out and it costs a sentence that has to be true
6 ignored tests in cargo test --workspacextask lifecycle tests. They spawn nested cargo builds and kill real process groups, so they run separately: cargo test -p xtask --test lifecycle -- --ignored --test-threads=1
10 docker_boundary testsexercise nothing without NAZM_REQUIRE_DOCKER=1. They belong to the benchmark harness, not the language
The mutation cataloguecargo xtask mutate is not in the default gate set: it injects defects into a copy and costs a full build per entry

The exact source state

Repositorylocal only; no remote is configured, which is not a blocker to anything here
Integrated commit4fb15542dfbf9af0bf34c42fe52783637b3daed4 — what the packaged candidate was built and tested at. scripts/release-candidate.sh records it in PROVENANCE.txt and refuses to assemble a candidate whose revision it cannot establish
Superseded candidatec3fb0789bc8b0731bed083f90ab998816ecd8a09, retained unchanged. Its bin/nazmc-selfhosted accepts recursion, the divergence closed at f0f9fc4 — it is the artefact tested at that revision, not a fallback
Remaining diffnone at 4fb1554. Corrected 2026-09-21: this row previously named c3fb078 and described everything committed after it as documentation. That was wrong, and it is the same mischaracterisation f0f9fc4 was written to correct: of the four commits between, f0f9fc4 changed compiler/emit.nz, ea6fc06 changed crates/nazm-cli/tests/selfhost.rs, and 4fb1554 changed scripts/release-candidate.sh. Only 3de05c9, after the tested commit, is documentation alone
Source digestPROVENANCE.txt carries one over the source files (.rs, .nz, .md, .json, .sh, .ebnf under seven roots — 140 files). It is not a whole-tree digest: Cargo.toml, Cargo.lock, rust-toolchain.toml, the Dockerfile, the mutation catalogue and every .expected fixture sit outside it

Reproducing this

From a clean checkout on Darwin arm64, with clang available:

cargo build --workspace
cargo xtask contained tests          # the whole suite
cargo lint                       # clippy, the same alias CI runs
cargo xtask check                # architectural gates and the guide against the grammar
cargo xtask contained bootstrap         # C1 -> C2 -> C3, byte-identical
cargo xtask contained bench      # measurements; --check compares to this host's baseline
cargo xtask contained run \'sh scripts/release-candidate.sh\'  # every gate, then the local artefact

# Not in the default set, and each says why in its own output:
cargo xtask contained mutate                       # one build and test run per catalogued defect
cargo test -p xtask --test lifecycle -- --ignored --test-threads=1
NAZM_REQUIRE_DOCKER=1 cargo test --test docker_boundary -- --test-threads=1

The compiler also runs a corpus of its own:

cargo run -p nazm-cli -- test compiler/conformance   # 5 cases, interpreted and compiled

Independent example programs are in examples/: max.nz, sum.nz, primes.nz, search.nz and pipeline.nz (structured concurrency). Each runs identically under nazm run and as a nazm build executable, and crates/nazm-cli/tests/build.rs asserts exactly that.

A repair workflow anyone can run:

printf 'fn main()->Int{\nlet x=1;\nx=2;\nx}\n' > broken.nz
NAZM=./target/debug/nazm sh scripts/repair.sh broken.nz
nazm run broken.nz     # 2

The package, as retained

Migrated from the Era-1 ledger, 2026-09-21. This is the authoritative record of the candidate’s identity and of exactly what it establishes.

Milestone G is complete and the goal’s Section 13 checklist is fully ticked. The release is experimental: one verified target, no package ecosystem, no visibility control, no native recursion, no cross-language performance claim.

Tested commit4fb15542dfbf9af0bf34c42fe52783637b3daed4, clean tree
Path~/nazm-release-4fb1554/ — retained outside the build tree. A copy also sits at target/release-candidate/, which is gitignored and is overwritten by the next release run
Archivenazm-release-candidate.tar.gz, 942,431 bytes, SHA-256 c75b02811904469c577a7cddc9e9a21feedec90cb2896d3adb4cfba8d1ddb820
MANIFEST.txt7,611 bytes, SHA-256 17f534f5af01d2bf9924db4d870d59b0bc0c8a6d856cc3f14ae5f5d2597a9ff3
PROVENANCE.txt3,175 bytes, SHA-256 c92715ad9230394b44915ae601c8cfeef58b3bcd7f9948a95b89c8f118c395bc
TargetLinux aarch64, inside nazm-contained:1.98.1 — rustc 1.98.1, Debian clang 19.1.7
Contents70 files: bin/nazm (the Rust implementation, 1,250,104 B, which builds C1) and bin/nazmc-selfhosted (the Nazm-written compiler as C2, 668,504 B, byte-identical to C3) — both ELF 64-bit LSB pie, ARM aarch64, neither portable off it — plus the bootstrap sources, examples/, schema/, docs/, both benchmark baselines, and the bootstrap record
Bootstrapir: identical, C2 and C3 digests both a8a16304…, runnable-bytes-differing: 0, 5 conformance cases, 5 sources / 4,647 lines
Suite549 passed, 0 failed, 6 ignored, 35 suites, 0 survivors
Verified twicethe runner checked the candidate against its own manifest before archiving — 70 files, every digest matching — and the exported archive was then extracted on the host and re-verified with shasum -c: 70 matching, 0 failed, and 70 files present against 70 listed, counted both ways

The previous candidate, kept

~/nazm-release-c3fb078/ is retained unchanged and re-verified after this run: 932,147 B, SHA-256 361347f63a36aaa2c9838c5685f2464f5ec985998e1f3a7af15e17ce45e357ed (MANIFEST.txt a121c2c4…, PROVENANCE.txt 5d52da22…). It is the candidate tested at c3fb078, and its bin/nazmc-selfhosted accepts recursion — the defect closed in f0f9fc4. Kept as the artefact that was tested at that revision, not as a fallback.

Test exclusions, stated rather than buried.

  • 6 ignored, all in xtask/tests/lifecycle.rs: they drive real mutation runs, nested cargo builds, and deliberately competing processes. Run with cargo test -p xtask --test lifecycle -- --ignored --test-threads=1.
  • 10 self-skipping, crates/nazm-bench/tests/docker_boundary.rs: without NAZM_REQUIRE_DOCKER=1 they print skipping: and exercise nothing, while still counting as passes in the 549.
  • The mutation catalogue is not in the gate set at all.

What the package establishes, and what it does not.

  • PROVENANCE.txt now carries source-digest with a source-digest-scope line beside it, so the field says what it covers: .rs, .nz, .md, .json, .sh, .ebnf under compiler crates xtask docs schema examples scripts bench/programs — 140 files. It is not a whole-tree digest. 69 tracked files fall outside it, including Cargo.toml and every crate manifest, Cargo.lock, rust-toolchain.toml, .cargo/config.toml, docker/contained.Dockerfile, xtask/mutations/mutations.toml, and every .expected fixture. It corroborates the commit for the sources; it cannot detect a changed dependency pin or a changed expectation. (The c3fb078 package still carries the old source-tree-digest label.)
  • Archive entry metadata is normalised; whole-archive reproducibility is not established. Different claims, and the package now says so itself. The tar is written --sort=name --owner=0 --group=0 --numeric-owner --mtime=@0 and the gzip stream carries no timestamp — verified on the archive: every entry reads Jan 1 1970, uid/gid 0, gzip MTIME zero. The archive is not bit-reproducible and as the pipeline stands cannot be: PROVENANCE.txt and the packaged docs/bootstrap-record.txt (a file inside the archive, not in the repository) both embed the run’s date-utc:, so two runs differ by construction. Neither packaged executable has been tested for bit-reproducibility. Byte-identity is established between C2 and C3, within one run and after the build-id is stripped — a third claim again.

The release gate earned its keep this run. The first attempt at this candidate failed: scripts/release-candidate.sh: 207: printf: Illegal option --. The reproducibility note added in c6a4215 wrapped so that one line began with --owner=0, and dash reads a leading -- in a format string as an option. bash does not, which is why sh -n and a host run said nothing — the container’s /bin/sh is dash. Fixed in 4fb1554 by passing the paragraph as %s\n arguments; the rest of scripts/ and compiler/ were swept for the same shape and have none. The candidate is assembled and then validated, so a PROVENANCE.txt that failed to write took the release down instead of shipping short.

Commits after the tested commit: none. 4fb1554 is the tip. Earlier, c6a4215 had described itself as “documentation only” and was not — it also edited scripts/release-candidate.sh, which is what assembles and attributes a candidate, and nothing tested that edit until the release gate rejected it two commits later.


The archive

scripts/release-candidate.sh assembles target/release-candidate/: both compilers (bin/nazm, the Rust implementation that builds C1, and bin/nazmc-selfhosted, the Nazm-written C2 — both Linux aarch64 ELF and neither portable off it), the sources the bootstrap needs, the documentation, the JSON schemas, the examples, every recorded baseline, the bootstrap record, a MANIFEST.txt with a SHA-256 of every file, and a PROVENANCE.txt saying which revision it is and what was checked against it. It builds nothing that has not passed every gate, and it does not tag, push or publish — that is a decision for a person.

The candidate built at 4fb1554 is described above under The package, as retained. The superseded candidate built at c3fb078, retained beside it and unchanged:

Pathtarget/release-candidate/ — under target/, so gitignored and rebuildable, not a durable store
nazm-release-candidate.tar.gz932,147 bytes, SHA-256 361347f63a36aaa2c9838c5685f2464f5ec985998e1f3a7af15e17ce45e357ed
MANIFEST.txt7,611 bytes, SHA-256 a121c2c46ef5423229427f64844308a5d6973266d32d1b4c52d8c31f5b259171
PROVENANCE.txt2,447 bytes, SHA-256 5d52da22c3b0dea8c670283e68a106fcb9a8e04ed0cd0cdf1446e15e82b0d773

Two different claims, and only one of them holds.

Entry metadata is normalised. The tar is written with --sort=name --owner=0 --group=0 --numeric-owner --mtime=@0, and the gzip stream carries no timestamp of its own — checked on the archive itself: every entry reads Jan 1 1970, uid and gid 0, and the gzip header’s MTIME field is zero. Entry order and metadata are therefore stable across runs.

Whole-archive reproducibility is not established — and as the pipeline stands it cannot be, because two packaged files embed the run’s wall clock: PROVENANCE.txt and docs/bootstrap-record.txt both carry date-utc:. Two runs differ by construction. Beyond that, neither packaged executable has been tested for bit-reproducibility; the bootstrap establishes only that C2 and C3 match each other within one run, and only after the linker’s build-id is stripped, which the packaged binary keeps.

What is established is that the contents match MANIFEST.txt — checked inside the container and again after extraction on the host.