How to build, run, test, and gate the Cadenza seed toolchain via `cargo xtask`. Read this whenever working in the Rust workspace under implementation/seed/crates (cadenza-syntax, rcdzc, cdz-run, cdz-runtime) or in xtask/ — compiling or running a .cdz/.sexp program, running the corpus behavior gate, checking library health, formatting, round-tripping the syntax surfaces, or inspecting a binary AST.
Install
npx skillscat add camshaft/cadenza/seed-workspace Install via the SkillsCat registry.
Driving the Cadenza seed workspace
cargo xtask is the ONE interface for everything in this workspace — building, running programs,
and gating. Prefer it over hand-rolled cargo run -p … pipelines: it choreographs the real tool
binaries, computes paths, and keeps results consistent. xtask itself pulls in no workspace crate as
a library; it builds and drives cdz-syntax, rcdzc, and cdz-run as processes.
The crates
cadenza-syntax— the front end. Lexer, parser, the two arenas AST + binary codec, printers.
Binary iscdz-syntax. Converts between surfaces:binary,sexpr,ml, and two output-only
debug views,debug(indented tree) andflat(arenas dumped literally).rcdzc— the reference compiler: binary AST → a WebAssembly component. Binary isrcdzc.cdz-run— the wasmtime host: runs a finished component, resolving the value-heap runtime by
content address from the store. Binary iscdz-run.cdz-runtime— the value-heap runtime, built ONLY forwasm32-unknown-unknown. It is
excluded from the native workspace, so a plaincargo buildskips it —cargo xtask build
andcargo xtask checkbuild it explicitly.
The pipeline is cdz-syntax | rcdzc | cdz-run; each tool streams stdin→stdout (a - arg reads
stdin), so they compose as a real pipe with no temp files.
The commands
Run from anywhere in the repo (cargo xtask resolves the workspace root itself).
| command | what it does |
|---|---|
cargo xtask build |
build the value-heap runtime component, content-address it, store it under target/cadenza-store |
cargo xtask run <file.cdz> |
compile-and-run one program end-to-end; prints the result to stdout |
cargo xtask emit <file.cdz> [-o out.wasm] |
compile only — write the component, don't run it |
cargo xtask gate [FILE.sexp…] |
run the corpus and grade each case (defaults to all of spec/semantics/*.sexp) |
cargo xtask check |
omnibus health check: build + test + clippy + wasm-runtime + gate |
cargo xtask roundtrip [FILE.sexp…] |
every corpus program must round-trip through the syntax surfaces |
cargo xtask fmt [--check] <file…> |
format program files through the printer |
cargo xtask bench [--save] |
runtime allocation benchmark: gross heap allocs per hot op, diffed against the committed spec/bench/.alloc-baseline (regression ⇒ non-zero exit); --save records the baseline |
cargo xtask miri [--filter F] |
run the cdz-runtime tests under Miri (the UB oracle for the refcount/FBIP heap core): flags use-after-free, OOB, uninit reads, and aliasing (Stacked Borrows) violations the normal run can't see. Sets -Zmiri-permissive-provenance (mandatory — the tagged-pointer Handle stuffs immediates into pointer bits). Defaults to the aliasing-critical subset (fbip); --filter "" runs the whole suite (slow, ~100-1000×). Needs rustup component add --toolchain nightly miri. |
Global --profile <name> picks the cargo profile the pipeline tools are built under. It defaults torelease-debug (optimized — so the ~900-case gate is fast). Pass --profile dev for a quick
unoptimized build when iterating on the tools themselves.
Common tasks
Run a program:
cargo xtask run path/to/prog.cdz # → the rendered result on stdout
echo '(module m (def (main) 42) (export main))' > /tmp/p.cdz && cargo xtask run /tmp/p.cdz # → 42Is the library healthy? One command — build, test, clippy, the wasm runtime, and the gate:
cargo xtask checkEach step's full output is captured to target/xtask-logs/check-<timestamp>.log; the console shows
just a ✓ per step. On the first failing step it prints the whole captured log inline (and the path),
so read that — don't re-run with | tail; the log has everything.
Run the behavior gate (compile+run each corpus case, compare to the recorded outcome):
cargo xtask gate # whole corpus: "N pass, M todo, K fail"
cargo xtask gate spec/semantics/01-literals.sexp # one file- pass — ran and matched the recorded value.
- todo — the compiler can't compile it yet (declined), or the expectation needs machinery not
wired (error-code matching, traps). NOT a failure. - fail — a real disagreement (ran to a wrong value, or ran where a rejection was expected).
Only a fail makes the gate exit non-zero.
Debug ONE failing case — prints its normalized program, expected, and actual:
cargo xtask gate --case "substring of the case description"Guard against a compiler regression with the committed baseline (spec/semantics/.gate-baseline):
cargo xtask gate --save # record the current per-case verdicts as the baseline
cargo xtask gate --check # fail ONLY if a case that used to pass now doesn't (totals may drift)Refresh the baseline (--save) after the compiler gains real ground, so "newly passing" doesn't
grow unbounded. check uses --check automatically when a baseline exists.
Track runtime allocation performance against the committed baseline (spec/bench/.alloc-baseline):
cargo xtask bench # measure hot-op heap allocs; fail if any op REGRESSED past baseline+2%
cargo xtask bench --save # record the current counts as the new baseline (after an improvement)Allocation COUNT — not wall-clock — is the tracked metric: it is identical native↔wasm and
deterministic, so it's a stable regression signal (wall-clock on the native build would measure the
system allocator, not the shipped talc/wasm path). The measurements come from thehot_op_allocation_ceilings test in cdz-runtime (one source of truth, shared with its in-crate
allocation-ceiling asserts). After landing a runtime allocation win, run --save to record the new
floor; a later change that pushes an op back up fails the bench.
Inspect a binary AST:
cargo xtask emit prog.cdz -o /tmp/p.wasm # get a component to poke at with wasm-tools
cdz-syntax --from binary --to debug /tmp/p.ast # indented tree of the AST
cdz-syntax --from binary --to flat /tmp/p.ast # the arenas literally (leaf pool + structure + root)(cdz-syntax is at target/<profile>/cdz-syntax after a build; or cargo run -p cadenza-syntax --bin cdz-syntax --.)
Plain cargo still works
For crate-level work, ordinary cargo is fine and often faster to reach for:
cargo test -p rcdzc # test one crate
cargo build --workspace # native build (does NOT build cdz-runtime — that's wasm-only)
cargo clippy -p cadenza-syntaxUse cargo xtask check when you want the single green/red signal that also covers the wasm runtime
and the behavior gate — the things a plain cargo build/test silently misses.
Gotchas
- The wasm runtime is invisible to
cargo build.cdz-runtimeis workspace-excluded and builds
only forwasm32-unknown-unknown.cargo xtask build/checkare the only things that build it;
a greencargo build --workspacesays nothing about it. - The gate's
todois honest, not hidden failure. rcdzc is climbing; most cases aretodo
because it can't compile them yet. Diff the FAIL set (or usegate --check), not the raw counts —
pass/todo totals drift as the compiler grows. .cdz/.sexpfiles carry the s-expression surface (--from sexpr, the default forrun).
Themlsurface exists but has known round-trip gaps (seeroundtrip); sexpr is the reliable one.- Corpus
inputis normalized on the fly to the export shape ((do (def (main) E) (export main)))
bycdz-syntax corpus; the.sexpfiles themselves are not yet migrated.