jz
AGENTS.md183 starsChanged 3 days agoMITView on GitHub
Operational guide for AI agents working in this repo. Architecture, code layout, and conventions live in CONTRIBUTING.md — read it first. This file is only the things that bite automated edits. These are the product's promises. They are CI-gated (test/bench.js), not aspirations: 1. Fastest wasm. On every bench case, jz's wasm is the fastest among wasm producers — ahead of clang→wasm, rustc→wasm, tinygo→wasm, AssemblyScript. Native C (clang -O3) is the only target allowed to be faster per-case within its documented…
We split this AGENTS.md into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 71
No stale dist/ reuse. test:battery -- fast excludes bootstrap.
- Very specific: line 147
Don't push/pull unless asked.
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md
Operational guide for AI agents working in this repo. Architecture, code layout, and conventions live in CONTRIBUTING.md — read it first. This file is only the things that bite automated edits.
Performance claims (the bar every change is held to)
These are the product's promises. They are CI-gated (test/bench.js), not aspirations:
- Fastest wasm. On every bench case, jz's wasm is the fastest among wasm producers —
ahead of
clang→wasm,rustc→wasm,tinygo→wasm, AssemblyScript. Native C (clang -O3) is the only target allowed to be faster per-case within its documented parity band. Porffor'sporf native(its 2026 rewrite emits no wasm) is a hard floor: jz may not be slower or larger on any comparable case and must lead on both geomeans (committed evidence). A case where another wasm target wins is a bug to fix, tracked astodoin the gate until jz takes the lead — never silently accepted (vm, whose same code leads or trails by CPU, is the one named exception below). - Faster than V8-family JITs, everywhere; faster than bun/JSC, almost everywhere.
jz beats V8-family engines (Node, Deno) on every case but two it does not lead yet,
watrand thejzself-compile: no band holds them, they are named as unmet (CLAIM_UNMETinbench/claims.mjs) and tracked as todo in the gate. It beats bun/JSC too, except two classes held to named bands: the tight-integer-loop class (vm,dict,crc32), where JSC's adaptive JIT out-executes an AOT wasm module on code already shown optimal; and the dynamic programs (watr,jessie, thejzself-compile), where the JIT speculates on what each site has seen and jz's wasm carries only what it proves. The second class is open work, not a proven limit.
The exceptions are one table, CLAIM_EXCEPTIONS in bench/claims.mjs: case, claim
class, band, a one-line measured cause and an evidence pointer. An entry is earned by
measurement only: the same code shown optimal against that class, or the same machine
code leading on some CPUs and trailing on others (vm against the wasm class: 0.75× on
Zen 4, 0.76× on the M4, 1.07× on Zen 3, 1.10× on Zen 5). Any other gap stays red until
fixed, unless the owner accepts it for a release: such an entry is marked open, names
its cause and stays a target (today the dynamic programs against bun/JSC, and webaudio's peak RSS
against V8 under the class rss). The gate (test/bench-claims.js) holds an
excepted case to its band instead of leadership and fails past it: every exception is a
tripwire, not a claim, removed the commit jz leads on every runner.
Evidence comes from the lean reference protocol, not a full matrix: bench.yml's
manual reference run (and node bench/bench.mjs --contenders --paired=6 --json=…)
measures jz against the nearest rivals of each claim class per case; a full
rerun re-ranks the rivals weekly.
What this implies for HOW we optimize:
- General techniques, never per-bench tweaks. The other wasm producers win where they win because they have general passes (loop autovectorization, SLP, alias-based load CSE). To match them, each jz optimization must eradicate a whole CLASS of cases — phrase the fix as "any program shaped like X," prove it on a minimal kernel, and pin that kernel (not the bench). If a fix only helps one named bench, it's the wrong fix.
- The compiler reasons; the source gives no hints. jz must infer integer-ness, ranges,
shapes and aliasing on its own. Do NOT make a kernel faster by editing its source (adding
|0, splitting arrays, hoisting by hand). If a var needs a|0to narrow, the inference is too weak — fix the inference. Bench sources are fixed inputs, identical across targets.
Verify before claiming done
npm test— core suite (in-process compiler), without hidden bootstrap builds or generated population sweeps. Run it before reporting any change. Focus withnpm test -- dyn-keys strings; inspect ownership withnode test/index.js --suite=all --list.npm run test:matrix— core opt0/opt2/opt3/wasi matrix. Jobs default to one;JZ_TEST_JOBS=Nopts into concurrency. Do not launch overlapping batteries.npm run test:extended— integration matrix plus generated correctness/WAT/ codegen populations on native and WASI. These checks moved out of core, not out of CI.test:integration/test:generatedrun just the current leg.npm run test:bootstrap -- --full— one fresh ordinary build shared across round-trip, O0/O2/O3 parity/oracles, checkpoint tests, the hosted suite and recursive compilation. The checkpoint overlay still has its own build. No staledist/reuse.test:battery -- fastexcludes bootstrap; the full battery runs it alone after native checks. Subprocess logs are disk-backed.npm run test:self— standalone self-compile gate: builds a fresh compiler in a temporary directory and round-trips real programs through the wasm-hosted compiler. Codegen changes can break the bootstrap; so does compiler source outside the self-compilable subset: hosted, an index into a Number reads it as a pointer (a walker asksArray.isArraybefore indexing a child), and nothing is freed within a compile but a summary solve's arena, which its phase checkpoint releases (a walker shares what it can, a chain of links rather than a copy per node). The build has 20 minutes; on a loaded machineJZ_SELF_BUILD_TIMEOUT=3600000gives it an hour (aSIGTERMbuild exit is the limit, not a failure).npm run test:262/test:262:builtins— conformance subset.npm run test:three— three.js's math layer (thethreedevDependency, unmodified): every module compiles,test/three/api.jsandtest/three/kernels.jsanswer what Node answers, the kernels' V8 ratios print (V8 timing each kernel in a process of its own), a bar atLEAD(1.2×) for the kernels jz leads by it (PINNED) and at parity for every kernel underJZ_THREE_PIN=1, and the three.js bench cases (bench/three*) answer what Node answers. A group jz gets wrong is listed inKNOWNthere with its cause; the gate fails when it starts to agree.
A test that passes standalone but fails in the full suite is almost always a
compile-state leak — global mutable state that isn't reset between compile()
calls (see src/ctx.js). Reproduce cold (node -e "import…compile(src)")
vs warm (after other compiles) before assuming a logic bug.
Never hand-edit generated artifacts
These are compiler/build output — edit the source, then regenerate:
| Artifact | Regenerate with | Source |
examples/**/*.wasm | npm run build:examples | the example's *.js |
examples/thumbs/*.webp | node examples/gen-thumb.mjs <name> | the example |
dist/jz.js, dist/interop.js | npm run build | index.js / instantiate.js + src/ |
assets/sprae.js | npm run build | the sprae package (web asset for the landing, not compiler dist) |
assets/grid-current.wasm | npm run build | assets/grid-current.js (the landing's header animation) |
bench/bench.svg, bench/results.json | npm run bench | the bench corpus |
These build outputs are gitignored, not committed (see below). You never commit
.wasm/dist — just make sure they still build: CI runs build:examples as a
smoke test and test:self for the bundle. Edit source, not output.
Build artifacts & deploy (no artifacts in git)
dist/ and example .wasm are gitignored build output:
- Local dev —
npm installrunsprepare→build:web, which builds the JavaScriptdist/assets (all the repl needs), not the self compiler. Usenpm run buildexplicitly when you needdist/jz.wasm. For examples, runnpm run build:examplesonce. The repl/examples import these by relative path, so they must exist on disk — they do after a build, they're just untracked. A fresh clone needs a build before they run locally; nothing is served "from master." - Deploy —
pages.ymlbuildsdist/+ example wasm and uploads them alongside the tracked files as the Pages artifact. Pages Source = GitHub Actions (not a branch); URL https://jz.js.org/ (dy.github.io/jz redirects there). It installs without root lifecycle scripts, builds browser assets withscripts/build-dist.mjs --js-only, and gates the gallery plus REPL/hero smoke. The guide's code blocks are highlighted byassets/highlight.js, the tracked tokenizer the REPL shares (nothing built); its copyable examples are also tested before deployment. Full-suite, self-compile and benchmark failures belong to their separate gates; they do not block static site updates. Example compilation still must succeed. - npm —
preparebuilds the JavaScriptdist/before pack, so the published tarball shipsdist/jz.js+dist/interop.js(inpackage.json"files").dist/jz.wasmis the self-compile artifact — never served, never published.
bench/results.json (+ the geomean bench/bench.svg) is committed — measured
data the bench workflow refreshes. bench/web/*.wasm is not: like dist/
and the example wasm, it's build output that pages.yml rebuilds from source at
deploy (node bench/bench.mjs --emit-web) and serves from the Pages artifact.
Git
- Stage specific files by name. Never
git add -A/git add .. - Don't
push/pullunless asked. Don't run repo-widecheckout/reset/stash/clean. - Build artifacts (
dist/, example.wasm) are gitignored — never commit them; CI andpreparebuild them. Commit source only.
Semantics check
Valid jz is valid JS — run the same source under Node and diff results to catch
miscompiles, minding the documented divergences (f64 numbers,
no GC, etc.). --wat (or compile(src, { wat: true })) shows emitted WAT;
grep v128 to confirm vectorization, __dyn_get/__ext_call for dynamic fallbacks.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repositoryOne other file it gives its agents.
- llms.txtllms.txt
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

