More
Status
Every number here comes from the repository as the site is written, or out of the document that recorded the measurement. Five figures here used to be typed by hand instead. Four of them were wrong by the time anybody looked — the worst by a factor of two — and the fifth, “0 open defects”, was a claim no gate on this page makes. The scorecard has all 11 goals and where each one stands.
What holds
Interpreter, bundle and native builds agree byte for byte on stdout, exit code and diagnostics. All 511 are also compiled for x86_64 and run under Rosetta, every one of them agreeing, on 15 October 2026. Every generated program is checked for what it keeps as well as for what it prints.
Which run wrote which figure
The corpus is 520 standalone programs, counted as this page is
written and by difftest's own filter — so a program added today is in that
number today. Everything below it needs a run, and these are not all from one
run. site/facts.json used to carry no date at all, which is how a
September figure sat on this page under a sentence claiming every number came
from the run that proved it; it carries one per figure now, and this is the
whole record:
| Gate | What it compared | Figure | Taken |
|---|---|---|---|
difftest | interpreter, bundle and native identical | 511 | 15 October 2026 |
wasmtest | identical in the browser engine | 415 | 15 October 2026 |
wasmtest | skipped: no filesystem, socket, process or thread | 96 | 15 October 2026 |
crosstest | cross-compiled to x86_64, identical under Rosetta | 511 | 15 October 2026 |
ProjectGold | whole programs agreeing on every engine | 44 | 15 October 2026 |
fuzz_laws | laws that held against arithmetic | 205 | 15 October 2026 |
depth | nested calls the interpreter reaches, against 10,000 promised | 31,444 | 15 October 2026 |
Every figure in this table was taken today, which has
not been true before and is the only reason the paragraphs that used to sit
here are gone. crosstest had two records and neither was the
sentence anybody wants: a clean run over the corpus as it stood in September,
against an October one that attempted 224 of the corpus on a
machine at load 38 rising to 134, which docs/binary-size.md asked
to have retaken on a quiet machine. It has been — at load 12.4, the whole
corpus, nothing declined, in twenty-four minutes. The October row stays in that
document as the record of what a machine under that load could measure at all,
which was always its point.
difftest used to be the other one, and is not any
more: its row is a run of today over all 511 programs, which is
every program the corpus holds, and it agrees exactly with the count this page
takes from the tree. Three earlier attempts at it were stopped at a two-hour
limit, which read as the gate being too big for the window — and was not. The
run that finished took nineteen minutes on a machine at load 13. The
same work had not finished in two hours at load 100, so what those attempts
measured was the load, not the gate.
The depth figure is a capacity, not a limit, and
it is only comparable with itself. A frame holds the locals of whatever
function is recursing, so the number moves with the shape of the
program measuring it: the gate's own function reaches 31444, where the
one in ProjectGold/08-fibonacci reached about 42,000 on the same
build. That is why the gate pins one shape and asks not to have it tidied.
What the specification promises is 10,000, and what the gate fails on is
the margin falling under twice that — the smallest margin in which a frame
doubling is caught here rather than by the guarantee breaking. It is in the
table because the margin moved once already with nothing noticing: see
F99.
What does not work
| Limit | Detail |
|---|---|
| 660 libraries, all first-party | See Packages; the plan for the rest is in docs/packages-plan.md. Anything else you need, you write, or bind to C with extern fn. |
| Blade is local only | A path dependency and a local Blade registry both work. Nothing fetches from or publishes to a remote one yet. |
| Downloads are not code-signed | macOS binaries for both architectures are published on the releases page with SHA-256 sums, but they carry no Apple Developer signature, so Gatekeeper will ask before the first run. Notarisation is release work, not language work. |
| Not faster than Rust | The geomean is 0.965× of it, 0.955× with the harness floor taken out of both sides, against a 0.95× target — so the goal is met and the claim is not available. tree at 0.51× is the worst row. On the systems suite Grasm loses etl by 3.6×, and wins memory everywhere — 8× on chat. Every row → |
| One integer width, one float width | i8…u64, usize and int are aliases for i64; f32 is an alias for f64 (spec/types.md [T-INT-ALIAS]). No unsigned and no wrapping arithmetic. bytes is writable in place at one byte each, so a byte buffer is no longer eightfold — but a buffer of samples is still [f64] at eight bytes apiece. |
| No shared mutable state across threads | par_map and par_filter demand an empty effect row and will not reach a ref from outside; a task cannot be spawned per list element. No mutable globals, statics or thread-locals, and a handler holds no state between calls — which is what makes a recording replay. |
| No SIMD, no file handles, no string builder | No intrinsics or autovectoriser. Whole-file and ranged reads only — no seek, no mmap, no streaming encoder. str is immutable, so appending in a loop is quadratic; collect and join. |
| A window, but no widgets | packages/sdl binds SDL2 — window, renderer, streaming texture, event poll — and scripts/sdltest.sh drives the whole path at 0.4 ms a frame at 256×256 RGBA, gated with no display. What is missing above that is the toolkit: no buttons, sliders, text fields or layout, and no glyph rasteriser, so text means binding SDL2_ttf. Nothing addresses the GPU, and the wasm build ships the interpreter without DOM reach. |
| A union error cannot be narrowed | (A|B) is raised, propagated and printed, never destructured. Declare one sum type and match it. |
| Polymorphic recursion | Refused by name at build time. It is what monomorphization is; no work removes it. |
| Small standard library | 84 methods. Enough for 44 whole programs and 12 applications; you will find gaps in something larger. |
| Linux never linked or run here | The 2,100 lines of the operating-system layer written for Linux went through a compiler on this machine for the first time on 15 October 2026: cargo check --release --target x86_64-unknown-linux-gnu --workspace passes for every crate. Nothing has been linked or run, and a type-check does not catch a wrong constant — which is how AF_INET6 was 30 on Linux for four commits. CI compiles it properly. |
| Tasks refuse every effect | 1,000,000 tasks run, and none of them may do I/O. G6's uncolored half is the open one. |
| One gate is red, on purpose | The specification is 71 pages against the 50 §19.2 binds it to, and crates/grasm_spec/tests/core.rs fails past 70 because that is where recording the gap stops being enough. It crossed on 12 October when [E-SCHEDULE] was rewritten. The ceiling is not being raised and the metric is not being re-scoped, because spec/core.md rules both out: a number that is met by redefining what counts is not a number. docs/known-issues.md has what closing it costs. |
| No query engine | Edit-to-diagnostic is p95 40 ms by re-checking the whole program, which is fast enough at this repository's size and will not be at a million lines. |
| No users but this project | Nobody has hit it from an angle nobody here thought of. |
| Region sweep not implemented | Tier 1's rule is enforced; the bulk deallocation it makes sound is not built. |
Deliberate, and still surprising
Four things an outside port tripped over in its first week. None is a defect and each one refuses with a diagnostic that explains itself — they are here because a reader arriving from Rust, npm or Go will write all four by reflex, and finding out one at a time costs an afternoon.
| You write | What happens | Why |
|---|---|---|
pub type Shape = … | refused | Types are one namespace across the whole program, so every type is already reachable from every module. There is nothing for pub to change. |
let own = 1 | refused | own is a parameter mode and one of the 37 Core constructs, so it is reserved. It is a common variable name, and that is a real cost of spending the word here. |
name = "my-pkg" | refused | A consumer writes import my_pkg with the name bare, so the name has to be a Grasm identifier. Registries whose import syntax quotes the name — npm, PyPI — can afford hyphens; this one cannot. |
let x = fallible() | refused | A failure cannot be dropped on the floor. Use try to propagate, match to handle both arms, or annotate the binding : T ! E to hold the failure as a value. |
What is not claimed
Grasm is not faster than Rust, Go or Zig, and no benchmark here says it is. It is not battle-tested. Defects are still being found at roughly one per working session — falling, but not zero, and a language with zero known defects and no users has told you more about its test suite than about itself.
The no_std conversion that took a hello-world from 351.1 KB
to 85.7 KB found nine defects on its way, and no gate on this page
would have caught any of them: they were in the operating-system layer,
below everything the corpus exercises. Two would have shipped silently —
a variadic open passing a garbage mode, and an RwLock
releasing the wrong lock. The list is on the
scorecard.
How that is kept honest
All 101 findings are written down in ProjectGold/FINDINGS.md,
including the ones that were the test harness's fault, the ones withdrawn as
wrong, and the ones caught before they shipped. Every gate is verified by
breaking the thing it guards and confirming it fails — and one of them had to
be, because replaytest was matching 0 of 39
journalled builtins and exiting 0 for weeks, its pattern expecting a struct
written on one line. It now asserts its own count, which is the check that
would have caught it on day one.