- Fork and clone.
cargo testcargo clippy --all-targets— the lint wall (no unwrap/expect/panic/unsafe) is CI-enforced, so run it before pushing.cargo fmt.
That's it. You do not need QEMU or bare-metal toolchains for most contributions — CI runs the multi-target embedded matrix on your PR for you. If you want the bare-metal targets locally anyway, the setup lives in ci/README.md (optional).
- Fork, branch in your fork, PR against
main. One focused change per PR. - Every behavior-facing change adds a line under
## [Unreleased]in CHANGELOG.md (groupedAdded/Changed/Fixed/Removed), in the same PR. - CI must be green: host tests (x86_64 + aarch64), clippy, the four bare-metal QEMU
smoke runs, and the flash/stack budget gates. If a budget gate trips, see the raise
protocol in ci/README.md — never raise
tolerance_pct.
good first issue— scoped and mentored; each states what to do, where in the code, and how to test it. Comment on the issue to claim it so nobody double-works.help wanted— a PR here will be accepted if it meets the bar below.
- No panics on library paths — fallible ops return typed per-module errors (
LinalgError,DiffError,IntegrateError,SolveError), neverunwrap. Clippy enforces this. - Stay generic over the scalar — inside generic code, never call an
f64-only function (it silently drops the autodiff payload). Use theNumerictrait surface. - Tests: f64 assertions may use golden values; f32 correctness is asserted via mathematical identities (e.g. reconstruction, round-trips), not goldens.
- Docs: public APIs get a doc example; behavior notes (NaN policy, iteration budgets) live on the item.
The workspace has several test and demo layers. Each has one job; adding a check means picking the layer that matches and not duplicating another's.
| Layer | Purpose | Must not |
|---|---|---|
| doctests | one minimal runnable demo per public item | become the correctness suite |
src/**/test.rs inline |
white-box tests of pub(crate) internals only (LU/lmpar) |
test public API |
tests/suite/ |
the correctness suite: public API, edge cases, proptests | re-declare problems/helpers inline |
demos/examples/basics/ |
copy-pasteable, headless, terminating demos; multicalc-only imports | exit 0 without ≥1 sanity assert!; touch a sink |
demos/examples/showcase/ |
live Rerun demos; measured numbers only | panic on edge cases (errors render as demo states); hardcode a perf claim |
tools/qa |
cross-implementation goldens (numpy/mpmath/MINPACK); .md accuracy tables generated from the fixtures |
duplicate self-consistency tests; hand-edit the generated tables |
tools/embedded-smoke |
on-target FP-path + stack/text budgets; goldens only via generated fixtures.rs |
hand-write golden values |
Shared problem definitions and tolerance helpers live in tools/testkit, so a problem is
declared once and reused across tests/suite/, the QA crate, and embedded-smoke.
The QA fixtures are regenerated with tools/qa/gen/generate.sh, which needs Python 3.12. See Regeneration for what it sets up and when
to rebuild the lock file.
Releases are automated from main:
- When that PR merges, the release workflow publishes to crates.io and creates a
vX.Y.Ztag and GitHub release whose notes come from the matching changelog section. - The
Cargo.tomlversion and the top dated changelog heading must match; a bump with no changelog entry fails the release.
Before step 1, build the docs the way docs.rs will and skim the result — docs.rs only rebuilds on a publish, so a broken page stays broken until the next release:
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc -p multicalc --no-deps --all-featuresThe --cfg docsrs flag is nightly-only and turns on the "Available on crate feature alloc only" badges. Check that the alloc-gated items (ParticleFilter, Jacobian::get_on_heap) are present and badged.
Thank you for all the contributions!