The Zipp story
Zipp's repository has 2,780 commits over 146 days, written almost entirely by one person, and it narrates its own mistakes in commit subjects. This is that history in eight acts, with dates and hashes. The journal has the day-by-day entries.
By the numbers
- Commits from 29 May to 21 October 2026
- 2,780
- Tagged releases in 55 days
- 29
- First-party lines of Rust
- 452,901
- Pull requests
- 3
247 in May, 1,039 in June, 253 in July, 230 in August, 366 in September, 645 in October
v0.0.1 on 30 August to v0.0.29 on 23 October
501,978 with the regex engine that began as a fork, counted at v0.0.24
Everything else landed on a linear trunk
Act one: a typed language with four back ends (29 to 30 May)
The first commit, a71a1bdc on 29 May 2026, is "Initial commit - ZIPP language v0": a tree-walking interpreter for a small statically typed language. Within the same day it had a Cranelift JIT for the integer subset that beat V8 on a hot loop, an LLVM release tier that matched V8 on dense doubles, a conservative mark-sweep collector, a deterministic gas-metered WebAssembly contract profile, a zero-knowledge back end that bound proofs to a program hash, and a TypeScript front end via oxc. By 30 May it was fusing map, filter and reduce chains.
None of that code survives. It is worth recording because the deletion, on 25 July, is one of the project's defining decisions.
Act two: the pivot to JavaScript (30 to 31 May)
On 30 May the subjects change: "prototypes, new, instanceof, in (the ES5 object model)", then "wire the zipp js <file.js> command". Two JavaScript engines were tried and discarded within a week. The one that stuck is 87174f64, "zipp-vm: clean-sheet JS engine v2 — explicit-frame register VM". The same day also contains "FIX broken build + RETRACT the false perf claims", the first of several retractions by name.
By 31 May the register VM had a native x86-64 JIT for hot integer functions, on-stack replacement for loop regions, call-free inline caches (object.js from 14 ms to 3 ms), scalar replacement of objects, rope strings, promises with a microtask loop, and its first legitimate win over V8: loop.js at 26.5 ms against 28.0.
Act three: the conformance march (1 to 14 June)
June has 1,039 commits. It opens with "tools: add test262 conformance runner" and "async generators — the biggest conformance lever". On 2 June alone: RegExp via a forked engine, TypedArrays, Proxy traps, Temporal type by type, the Intl namespace with all eleven services, splitting a 17,400-line vm.rs into a module folder, and a mark-sweep collector for the new VM. Then eval ("the #1 conformance lever"), ShadowRealm, ES modules with top-level await, blocking Atomics, WTF-8 strings, arbitrary-precision BigInt, proper tail calls, the honest ten-workload benchmark pack, six JIT soundness bugs from an adversarial audit, and on 14 June a function-level JIT on by default plus a checkable performance roadmap.
Then six weeks of silence.
Act four: the reckoning (25 July to 4 August)
The project returns on 25 July with the single most quotable commit in its history, 1217d683: "Fix the test262 runner; real pass rate is 96.97%, not 100%". The runner had been scoring one execution mode. The same day, 9ef187f9 "Focus the workspace on zipp-vm; rewrite the docs against measurements" deleted the AOT, Cranelift, LLVM, zero-knowledge and TypeScript crates. The Cargo.toml still explains it: those crates were removed rather than left to rot alongside code they no longer share anything with.
What followed was measurement: mimalloc (4.20× to 3.34× Node), fat LTO (about 2%), the zipp-wasm persistent embedding, a new in-house front end that took Test262 to 99.0%, Intl.DurationFormat and the IANA database, 30 failures to 5 with 22 of them runner defects, a sampling profiler, the first benchmark capture that could be attributed to a commit, PGO adopted at −13.3%, and a nursery whose first stage was refuted by its own prover before the third stage landed. The landing page went up on 4 August.
Act five: parity, audits, Python (19 August to 14 September)
The nursery went default-on on 19 August. On 20 August the headline dipped under 1.20× Node. On 21 August a tier-differential fuzzer found five live wrong-answer classes. On 23 August wave 19 decomposed the three rows that were 60% of the remaining gap, and on 24 August c6ffa200 recorded "cross Node parity at 0.9695x". ARM64 got a JIT and the sandboxes got hardened on 24 August; the hostile corpus went under parity on 27 August; a 2.16× regression on 29 August was root-caused to a de-fused call lowering and reversed the same day.
v0.0.1 shipped on 30 August. Ten more tags followed in three days as real embedders hit real limits: a Game Boy cartridge that could not fit in a 1 MB ArrayBuffer, an emulator killed by a lifetime instruction budget, a framebuffer encoder that outran the collector. 2 September produced the canonical capture, 0.728× Node across thirty rows.
September then turned to correctness. External audits on 11 and 12 September produced 38 tickets, closed in v0.0.15 through v0.0.17; making specification-order method calls the default cost the headline benchmark its lead, and the ledger says so. On 13 September, in one day, the repository gained a Python 3 frontend compiling to the same VM, WebAssembly build variants, a folder-based playground, GPU compute from Python through WebGPU and WebGL2, and a Torch subset with GPU training. On 14 September the pinned Test262 corpus passed 95,671 of 95,680 unmodified and 95,680 with five documented corrections, and v0.0.18 was published. Between 15 and 16 September a review of the whole project found 324 defects, all fixed; torch.compile learned the graph protocol it sits on, GPU sessions kept weights and optimizer state on the device, version-2 graphs got native kernels, and v0.0.19 was published with the two longest gate jobs run locally on the tagged tree.
Act six: models, torch and speed (17 to 25 September)
On 17 September a local-model plugin overlay arrived, and the GPU path learned to keep a checkpoint's own Q4_K and Q6_K blocks and decode them inside the matmul, so Qwen3-0.6B holds at 373 MB instead of 2,274. The GGUF reader moved out to its own project, a model split into stages that reproduce the whole model's logits bit for bit across worker threads, matmul_fixed computed a product in integers, and the kernels became Rust that builds to the same bytes on Linux and Windows. v0.0.20 was published on 18 September.
From 22 September a round of fixes preceded a torch layer checked against PyTorch 2.11 area by area: tensors and autograd, nn, optimizers and schedulers, half precision, linalg, fft, distributions, complex, sparse and quantized tensors. On 23 September zipp py ran torch.compile graphs on a hardware GPU through wgpu. Then the Python frontend went from about 22× CPython 3.13's time to about 1.75× in three days, through fused instructions, a JIT tier, small integers in the value word and native dict, set and instance storage, while a prepared GPU step fell below PyTorch's eager CUDA on the same card. The Python parser became Zipp's own and the RustPython fork was deleted, and v0.0.21 was published on 25 September.
Act seven: smaller, faster, and outside reports (26 September to 14 October)
The fortnight after v0.0.21 went into size and speed. An application bridge let hosts answer synchronous calls, and ZIPP Lite, the JavaScript core without Intl and Temporal built for size, arrived on 29 September. From 9 to 12 October four size passes and the first profile of the WebAssembly interpreter made four string workloads that were quadratic linear, won back the member-call speed the strict call order had cost, and took the browser modules 11% to 15% below v0.0.21's. v0.0.22 was published on 10 October with Lite as an asset, and v0.0.23 on 12 October, once memory that grows in place finally reached the collector and Python stood at 1.30× CPython.
On 13 and 14 October the sixteenth pass compiled most of the loop shapes that still fell back to the interpreter, while four agents worked beside it on Python speed, torch, promises and module size: Python reached 1.09× CPython 3.13 and torch 4.2× PyTorch's warm time. The repository's first eight GitHub issues, all from @takahashim, each with a repro and a pointer into the source, were fixed with tests and answered, and v0.0.24 was published on 14 October.
Act eight: embedders, releases and large programs (15 to 21 October)
The week after v0.0.24 belonged to embedders. @takahashim, who had filed the first eight issues, embedded Zipp the way a browser-like host does and sent ten more reports, from a Proxy on the global object's prototype chain to a VM that could not allocate again after reaching its heap ceiling; v0.0.25 fixed them on 15 October, with the safe-sandbox interpreter at 0.84× Bellard's QuickJS's time. On 17 October the interpreter learned superinstructions and a second, smaller loop that keeps its state in registers, and a new suite of 21 workloads went looking for where QuickJS still won. Its first record was corrected the same afternoon: the build it measured had been trained on the rows it measured. The same day each language's source moved under src/lang/, and a language became one entry of a table.
18 October shipped two releases. The Test262 pin moved to the corpus of 13 October and ran once, unmodified: 96,229 of 96,230 executions, with one documented expected failure and no correction patches. v0.0.26 changed what a release is: plain native files for Windows and for Linux on x86-64, arm64 and armv7, one .wasm per WebAssembly engine that carries its own bindings, and one loader, zipp.mjs, that runs any of them in a sandbox that starts with nothing permitted. That evening v0.0.27 fixed seven more reports from @takahashim, among them a regex that never finished compiling, and added a Brotli copy of every engine.
The last three days went to the interpreter and to large programs. Generators began to run their steps as frames of the loop that asks for them, and bundled applications became the yardstick: Babel, the TypeScript compiler, Prettier, and Vue, Preact and React rendering on the server. Each found a limit sized for small programs: a module that compiled in quadratic time, a cap of 256 planned object literals per compiler, string literals left uncached past a bundle's 4,096th function. Every bundle also ran slower with the JIT on than off, by 4% to 36%, so a program of 256 or more functions now runs with its JIT off. Babel's workload went from 13.8 s with v0.0.27's default JIT, or 11.3 s interpreted, to 5.2 s, and the TypeScript compiler's from 2.86 s interpreted to 1.17 s; in a separate interleaved run of the published executables, Bellard's QuickJS took 0.72× and 0.87× of v0.0.28's time on them. React's server renderer, which needed TextEncoder, ran for the first time.
Eleven more reports came from @takahashim over those days, the last four as designs for new embedding API: import.meta.url, unhandled-rejection reports, V8-format Error.prototype.stack, and a host call that runs a classic script. With them fixed and every ES5, ES6, ES2016+ and Intl test of the kangax compat-table passing, v0.0.28 was published on 21 October. The first Linux CI run of the week's work, dispatched before the tag because every commit skips CI, found three failures, all fixed first; one was a stale cache key the previous release's site refresh had left behind. The canonical capture of the 0.0.28 engine the same day put its JIT at 0.652× Node's time over the 30 benchmark rows and its interpreter at 0.685× Bellard's QuickJS's (benchmarks).
How the work was done
The archived handoff notes describe the August performance campaign as orchestrated workflows of AI coding agents: parallel scouts to refute the obvious suspects with measurements, parallel implementers with disjoint file ownership, and a verification gate that re-measures rather than trusting the lanes. The notes are candid about what that bought and what it cost: scouts refuted more than they proposed, which was the point; implementers disproved their briefings; honest under-delivery was reported as such; session limits killed mid-run workflows twice; and one verification pass was silently vacuous because prompt interpolation was over-escaped, so every verdict came back meaningless. The rule that came out of it: if you delegate verification, check that the agent actually received the finding.
The working rules that survive in the repository are the same ones a careful human team would write. Scout before building. Never promote a dirty, filtered, non-PGO or incomplete artifact. Record neutral and refuted ideas so they are not repeated; most entries in the 881 KB performance ledger are negative results, and the notes say they saved more time than the wins.
The honest thread
What makes this history unusual is that the corrections are in the subjects, not buried in bodies. "RETRACT the false perf claims." "Real pass rate is 96.97%, not 100%." "The INT tier was silently returning wrong answers on ordinary loops." "It is the COLLECTOR, not the allocator — pool built, measured zero, reverted." "The profiler was lying about a quarter of json-large." "B95 shipped a 19x pathology and the benchmarks could not see it." "The harness could name a commit it had never measured." "Correct the expansion record: its build was trained on the rows it measured." Every one of those is a commit you can open.
- Initial commit: ZIPP language v0
a71a1bdc. Cranelift, LLVM, WASM and zero-knowledge back ends by the end of the day. - The clean-sheet explicit-frame register VM
87174f64, the engine that survives. - Test262 runner added
79d10480. - 96.97%, not 100%
1217d683; the workspace narrowed tozipp-vmthe same day. - PGO adopted at −13.3%
5cafcb10. - Node parity crossed at 0.9695×
c6ffa200. - v0.0.1
First native and WebAssembly distribution.
- The canonical capture
0.728× Node across 30 rows at
8229b3fc. - Python, the playground and GPU compute
378b18de,5fd22bcb,8bb14918,9651dd40. - 95,680 of 95,680 corrected core; v0.0.18
1539eb4b,fc474d15. - Resident GPU sessions,
torch.compileprepare and v0.0.19bc9ce154,71dab087,65468539,18961128,431b7177,2e6a39c3. - Quantized weights,
matmul_fixedand v0.0.20ad64fce3,50ec6a98,c4409b6f,fdc639c5. - The torch layer against PyTorch 2.11, and native GPU through wgpu
1f95d6f9,cfc8e990,b24c6b81,7e2434e5. - Python at about 1.75× CPython; v0.0.21
f9347730,cff586d2,fd18aaf7,50e490b0,f954857c. - ZIPP Lite and the application bridge
13c33194,065446f2,76045145. - The WebAssembly interpreter profiled; v0.0.22
739603bc,4e8cb084,09bf2c18,4a9a9124. - Memory grown in place collected; v0.0.23
d24fe3e8,784e3eb2,621f798b,407b81cd. - Compiled loops, eight reported bugs fixed; v0.0.24
4515054f,9470f615,acca1331,6333c421,a82ad0bb. - Ten embedding reports fixed; v0.0.25
cbea93aa,ca0091bc,3e8a5d07,ddbd0272. - Superinstructions, a second interpreter loop, one folder per language
b2b8f5ca,702ad388,da8427b6,37da4030. - The latest Test262, unmodified; plain files and one-file engines in v0.0.26; v0.0.27
67a7e1cc,543917b8,5f1c0578,74d92e70. - Generators as frames; eleven more reports begin
732a0855,6f730a8d,c0d9527b. - Large programs run interpreted;
Error.stack; v0.0.282e83f5e0,130640b0,79ddfd28,ca511f54,fced020a,da0fe6b1.