Development¶
Setup¶
You need CPython 3.12–3.15, uv 0.12.18 or later
in the 0.12 series, just and Rust 1.94.0, pinned in
rust-toolchain.toml. Use Python 3.12 or 3.13
for the complete reference suite. From the repository root:
uv sync --locked --no-install-project --group dev --extra jax --extra autograd
just build-ext
uv run --no-sync pre-commit install
uv syncinstalls the development tools and the test references: treams 0.4.7, SciPy and mpmath. treams-rs itself needs only NumPy. Historical comparisons against treams 0.4.5 retain that version in their evidence.- JAX, PyTorch and HIPS Autograd are optional; without them their adapter tests skip.
just build-extcompiles the extension into.venv. Run it again after every change undercrates/.
On Linux, add the locked CPU build of PyTorch as hosted CI does:
torch_version="$(
uv export --locked --extra torch --no-emit-project --no-hashes \
--no-header --no-annotate | sed -n 's/^torch==\([^ ;]*\).*/\1/p'
)"
uv pip install --index-url https://download.pytorch.org/whl/cpu "torch==${torch_version}+cpu"
This CPU index covers Python 3.15 in the release dependency set. On macOS,
add --extra torch to the initial uv sync command on Python 3.12–3.14;
that installs PyTorch from PyPI. The standard PyPI torch extra on 3.15
is not tested for this release.
Run Python commands as uv run --no-sync python ... or
uv run --no-sync pytest .... --no-sync keeps the environment and the
extension of just build-ext as they are.
The development dependency set omits the treams reference implementation on Python 3.14–3.15 and h5py on 3.15. Tests that require an unavailable dependency skip and appear in the test summary; independent tests still run. On 3.12 and 3.13, a missing treams installation is an error.
Repository hooks¶
uv run --no-sync pre-commit install installs both the pre-commit and
commit-msg hooks from .pre-commit-config.yaml.
Git worktrees share these hooks. Their first run downloads the hook tools.
| Hooks | Checks |
|---|---|
pre-commit-hooks |
Whitespace, final newlines, line endings, YAML and JSON, conflict markers, symlinks, file sizes and private keys |
markdownlint-cli2 |
Markdown formatting with .markdownlint-cli2.yaml; needs Node 22 or newer, which pre-commit downloads if Node is absent |
taplo-format |
TOML formatting |
typos |
Spelling, with exceptions in _typos.toml |
commitizen |
Conventional Commit messages: type(optional scope): subject |
Local just hooks |
rustfmt, Clippy, Ruff and Pyrefly using the locked tools |
Review and stage any automatic formatting changes before committing again.
just file-hygiene, part of just check and CI, runs the file hooks over the
whole repository. Commitizen checks messages; it does not manage versions.
Cargo.toml sets the package version (releasing). Fix
generated documentation in its source or in
scripts/generate_docs.py, then run
just docs.
Checks¶
just ci
just ci runs the local Rust and Python checks through the same recipes
used by hosted CI. Use HYPOTHESIS_PROFILE=ci for 100 examples per property
(the local default is 30). just ci has two halves:
| Recipe | Checks |
|---|---|
just ci-rust |
rustfmt, Clippy, the treams-core tests and rustdoc, with warnings as errors |
just ci-python |
file hygiene, lock files, Ruff format and lint, strict Pyrefly, and the Python tests against a fresh development extension |
just check-wheel |
builds an optimized wheel and checks it in a clean environment (clean wheel); not part of just ci |
The Python tests include the check that the generated reference pages and
llms.txt match the code. Tests live under tests/<domain>/; the
testing guide maps each directory to its subject.
just check runs the lint checks without tests: file hygiene, lock files,
rustfmt, Clippy, Ruff, Pyrefly and just docs-check. just check-static runs
all of them except just docs-check, which needs a built extension. Use
just check while you work and just ci before you open a pull request. The
justfile holds the exact commands.
Workflows¶
These are the configured checks; a release needs successful runs for its exact source revision, as described in releasing.
| Workflow | Runs on | Runs |
|---|---|---|
| CI | pull requests and pushes to main |
Python 3.12–3.15, supported dependency bounds, Rust checks, coverage, repository checks, the host release wheel, native wheels, documentation and a history secret scan |
| Native Wheels | called by CI and Release Candidate, or by hand | All supported wheels, installed-wheel checks and a source-distribution rebuild |
| Docs | called by CI, publication and manual Deploy docs | Strict Material site build and rustdoc; mike maintains dev, released versions and the latest alias, then the complete site is deployed through a GitHub Pages artifact |
| Release Candidate | called by Publish Release | Verify the source revision and build distribution files that cannot change during publication |
| Publish Release | repository dispatch with event type release |
Build and test the candidate, try it on TestPyPI (advisory), wait for the operator's tag and approval, then publish to PyPI and deploy release documentation |
| Formal | pull requests and pushes that change formal/, a Rust file with a Lean model, the justfile or the workflow |
just formal |
CI runs the complete reference suite on Python 3.12 and 3.13 and the available
tests on 3.14 and 3.15. Separate jobs cover NumPy 2.1–2.5, all declared
dependency floors together on 3.12, and current framework releases on 3.13
and 3.14. Rust checks cover 1.94.0 and stable. These checks, including the
coverage thresholds below, must pass for CI Success, which is required for
release.
The Coverage job checks the Python 3.12 report directly. Overall line coverage
may drop by at most 0.1 percentage points from the exact base commit, and
at least 90% of changed executable lines must be covered. The base is the
pull request's base SHA, or the previous main SHA for a push. CI reuses that
commit's cached report or its saved CI artifact, retained for 90 days. If both
are missing, the same Coverage job regenerates the report from that exact
commit using its locked dependencies and Python 3.12 tests, then caches it for
later runs of the pull request. Normal checks reuse the report without running
the base tests again. A regeneration failure fails the check; it never skips
the comparison.
To check two downloaded reports locally, run from the repository root:
uv run --script scripts/check_coverage.py coverage.xml base-coverage.xml --compare-branch BASE_SHA
Codecov receives a best-effort upload on main for the badge. Its PR comments
and status checks are disabled, and an upload failure cannot fail CI Success.
When a dependency range changes, update its CI job too. A change to a
Rust dependency linked into wheels also needs just licenses to refresh the
bundled license texts.
Development builds¶
cargo test, just rust-test and just build-ext use the dev profile. It
optimizes at level 1 (dependencies at level 2) and keeps debug assertions and
overflow checks. Its timings do not represent release performance; measure
with a release build as described in benchmarks.
Clean wheel¶
Run just check-wheel after a change to packaging or to public imports. It
builds an optimized wheel and installs it into a clean environment without
SciPy or treams. There it checks the core, Advect and HIPS Autograd behavior, compares native
results on threads that flush subnormals with those of ordinary threads bit for
bit, and checks the optional HDF5 interchange. It also scans the archive for
local paths.
The just recipes rewrite the checkout, home, Cargo and Rustup paths in
compiled binaries to /build, so no local path ends up in a wheel or in a
panic message. Direct cargo or maturin calls skip this step, so build
distributable files only through the recipes. The wheel has no Rust SBOM,
because maturin writes absolute paths into its package identifiers;
Cargo.lock lists the Rust dependencies.
Formal proofs¶
Lean models cover the diffraction-order enumeration, the lattice shells, the
translation selection rules, the LU equilibration and the gradients of
requested illuminations (formal proofs). After
changing one of those Rust functions, update its Lean definition and run
just formal. It needs elan and runs
in its own workflow, not in just ci. cargo test compares cube, degrees
and harmonics with the Lean output in
formal/golden/.