Developing
How to build the project from a checkout and what the local gate runs before a push. This is the contributor’s page; if you are looking for what the application does, start at Running the app.
Building
A recent stable Rust toolchain (the workspace is edition 2024 — Rust 1.85+) and, for the documentation gates, Node. From the repo root:
cargo build # the everyday buildcargo run -p standalone --release # the appcargo nextest run --workspace # the whole suitecargo clippy --workspace --all-targets -- -D warningscargo fmt --all --check--workspace is load-bearing on the test and lint rows rather than a flourish: the C ABI crate sits
outside the workspace’s default members, so a bare cargo nextest run skips the ABI conformance
suite and a bare cargo clippy stops linting the C ABI, and both come back green while covering
nothing.
The headless capture CLI and the visual-QA harness have their own page, Headless capture and video.
The pre-push gate
A checked-in .githooks/pre-push runs the fast subset of CI before a push, so a
broken push costs seconds locally instead of minutes in CI. It is opt-in per
clone — enable it with:
git config core.hooksPath .githooksAn uninstalled clone has no gate. Git will not run a hook from a tracked directory without that config, so until you set it nothing below happens. There is deliberately no auto-install (ADR-0033 Alternative H).
What it runs, stopping at the first failure and naming the step that failed:
| Step | Command |
|---|---|
| Doc links | node scripts/check-doc-links.mjs |
| Index rows | node scripts/check-index-rows.mjs |
| Index rows (self-test) | node scripts/check-index-rows.mjs --self-test |
| Backlog claims | node scripts/check-backlog-claims.mjs |
| Filter figures | node scripts/check-filter-figures.mjs |
| Comment hygiene | node scripts/check-comment-hygiene.mjs |
| Contents blocks | node scripts/toc.mjs --check |
| Contents blocks (self-test) | node scripts/toc.mjs --self-test |
| Reader prose | node scripts/check-reader-prose.mjs |
| Format | cargo fmt --all --check |
| Lint | cargo clippy --workspace --all-targets -- -D warnings |
| Tests | cargo nextest run --workspace -P fast (narrowed — see below) |
The Node steps come first because they are the cheapest (tens of milliseconds
between them): every relative markdown link in the repo must resolve, every row
inside a marked roster region must stay under 320 bytes
(ADR-0116),
every live design-backlog.md entry must carry a probe that still holds
(ADR-0108),
the diffusion filter’s cost figures must live in one file
(ADR-0122), no .rs
comment may carry a relative link or plan-relative narration
(ADR-0127),
every citation in a reader document must sit inside a link
(ADR-0168),
and every generated contents block must still match the headings beneath it
(ADR-0163).
The two that could go green on a rule that had quietly stopped working — a roster
detector matching nothing, an anchor rule that is merely plausible — carry a
--self-test beside their check.
If node is not on your PATH they all skip with a notice rather than
failing the push; nothing else here needs Node. That skip is about the hook only
— CI’s links job runs the same checks on ubuntu-latest, where they cannot
skip and are not bypassable.
Measured warm wall time: ~48.6 s (2026-08-08; dominated by the tests — fmt
and clippy are under two seconds between them). The hook excludes the nine
GPU-heavy suites that iterate every shipped preset or scene through a real
adapter. Which nine is not written here — since
ADR-0156
the list is the fast profile’s default-filter in .config/nextest.toml, and the hook and CI’s
check job both cite -P fast rather than restating it.
The test runner names the skipped binaries itself on every run, on the profile’s
authority, so the narrowing is never silent, and CI runs
all of them regardless — though since ADR-0073
it runs those nine in the coverage job alone rather than in two Windows jobs, so
the promise is now underwritten by one job instead of a redundancy between two.
cargo deny, doctests, Miri, and the coverage job are deliberately not in the
hook — they push it into minutes, and a gate that hurts gets disabled
(ADR-0033 Alternative F).
They are also the checks least likely to break from a local edit.
The spout compile job (ADR-0181) is outside the hook too, and for neither of those reasons.
It stages a third-party SDK over the network before it compiles anything, which does not fit the
hook’s budget at all — and it is the one CI gate that is likely to break from an ordinary local
edit, because code behind #[cfg(feature = "spout")] is not type-checked when the feature is off,
so every cargo build, clippy --all-targets and nextest run here is blind to it by
construction. If you are refactoring anything standalone/src/stream.rs reaches, compile it
yourself before pushing:
powershell -File packaging/spout/fetch-sdk.ps1 # once per checkoutcargo check -p standalone --features spoutBypass once with git push --no-verify.
What else there is to read
- Testing and visual QA — the
core/tests/harness that hard-tests every preset for reactivity, animation, shape sanity and beat response. - Headless capture and video — the
shotCLI,--renderand the live video-out. - MilkDrop conversion — reading what
milkconvproduced. - Embedding the core — putting the engine inside another application.
- On-device validation — the manual checklist for what CI cannot run: real GPUs, live loopback, installing the foobar2000 component.
- Releasing — how the version moves and what a
v*tag builds. - Non-functional requirements — the quantified budgets behind every “lightweight” and “real-time” claim.
- The architecture decisions — start with ADR-0001, the founding decision, with the rejected alternatives recorded.
Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.