Contributing to Murk
What to Contribute
There are many ways to help improve Murk:
- Bug reports – found something broken? Open an issue with reproduction steps.
- Feature requests – have an idea for a new capability? Open an issue to discuss it.
- Documentation – fix typos, clarify explanations, add examples.
- New propagators – implement domain-specific propagators (see Adding a new propagator below).
- New space backends – add spatial topologies (see Adding a new space backend below).
- Performance improvements – benchmark, profile, and optimize hot paths.
- Tests – increase coverage, add property tests, stress-test edge cases.
Look for issues labeled good-first-issue for accessible entry points.
Development Environment
Requirements:
- Rust stable (1.87+) via rustup
- Rust nightly (for Miri only):
rustup toolchain install nightly --component miri - Python 3.12+
- maturin:
pip install maturin
Setup:
git clone https://github.com/tachyon-beep/murk.git
cd murk
# Build Rust workspace
cargo build --workspace
# Build Python extension (development mode)
cd crates/murk-python
maturin develop --release
cd ../..
Project Structure
murk/
├── crates/
│ ├── murk-core/ # Leaf crate: IDs, field definitions, commands, traits
│ ├── murk-arena/ # Double-buffered ping-pong arena allocator
│ ├── murk-space/ # Space trait + 7 lattice backends
│ ├── murk-propagator/ # Propagator trait, pipeline validation, step context
│ ├── murk-propagators/ # Reference propagators (diffusion, agent movement)
│ ├── murk-obs/ # Observation specification and tensor extraction
│ ├── murk-engine/ # LockstepWorld, RealtimeAsyncWorld, TickEngine
│ ├── murk-replay/ # Deterministic replay recording/verification
│ ├── murk-ffi/ # C ABI with handle tables
│ ├── murk-python/ # Python/PyO3 bindings + Gymnasium adapters
│ ├── murk-bench/ # Benchmark profiles
│ └── murk-test-utils/ # Shared test fixtures
├── examples/ # Python examples (heat_seeker, hex_pursuit, crystal_nav)
└── docs/ # Design documents and concepts guide
Dependency graph (simplified):
murk-core
↑
murk-arena, murk-space
↑
murk-propagator, murk-obs
↑
murk-engine
↑
murk-ffi → murk-python
Running Tests
# Full workspace test suite (700+ tests)
cargo test --workspace
# Single crate
cargo test -p murk-space
# Python tests
cd crates/murk-python
pytest tests/ -v
# Memory safety (requires nightly)
cargo +nightly miri test -p murk-arena
# Clippy lints (must pass with zero warnings)
cargo clippy --workspace -- -D warnings
# Format check
cargo fmt --all -- --check
CI Expectations
Every push and PR triggers:
| Job | What it checks |
|---|---|
cargo check | Compilation across all crates |
cargo test | Full test suite |
clippy | Lint warnings (zero tolerance) |
rustfmt | Formatting |
miri | Memory safety for murk-arena |
All five must pass before merging.
Code Style
Rust
#![deny(missing_docs)]on all crates — every public item needs a doc comment.#![forbid(unsafe_code)]on all crates exceptmurk-arenaandmurk-ffi. If your change needsunsafe, it belongs in one of those two crates.- Clippy with
-D warnings— all clippy suggestions must be resolved. cargo fmt— standard rustfmt formatting, no custom config.
Python
- Type annotations on all public functions.
- Docstrings on all public classes and methods.
- Type stubs (
.pyi) must be updated when the Python API changes.
Adding a New Space Backend
Space backends implement the Space trait in murk-space. Follow the pattern
of Square4 or Hex2D:
- Create
crates/murk-space/src/your_space.rs. - Implement the
Spacetrait:ndim(),cell_count(),neighbours(),distance()compile_region(),iter_region(),map_coord_to_tensor_index()canonical_ordering(),canonical_rank()instance_id()
- Add
pub mod your_space;andpub use your_space::YourSpace;tolib.rs. - Run the compliance test suite — this is critical:
#![allow(unused)]
fn main() {
// In your_space.rs, at the bottom:
#[cfg(test)]
mod tests {
use super::*;
use crate::compliance::compliance_tests;
compliance_tests!(YourSpace, || YourSpace::new(4, 4, EdgeBehavior::Absorb).unwrap());
}
}
The compliance test suite (crates/murk-space/src/compliance.rs) automatically
tests all Space trait invariants: canonical ordering consistency, neighbor
symmetry, distance triangle inequality, region compilation, and more. If your
backend passes compliance tests, it works with the rest of Murk.
- Add FFI support in
murk-ffiand Python bindings inmurk-pythonif needed.
Adding a New Propagator
Propagators implement the Propagator trait in murk-propagator:
- Create your propagator struct (must be
Send + 'static). - Implement:
name()— human-readable namereads()— fields read via in-tick overlay (Euler style)reads_previous()— fields read from frozen tick-start (Jacobi style)writes()— fields written, withWriteMode::FullorIncrementalstep(&self, ctx: &mut StepContext)— the per-tick logic
- Optionally implement
max_dt()for CFL stability constraints.
See crates/murk-engine/examples/quickstart.rs for a complete example, or
crates/murk-test-utils/src/fixtures.rs for minimal test propagators.
Key rules:
step()must be deterministic (same inputs → same outputs).&selfonly — propagators are stateless. All mutable state goes through fields.- Copy read data to a local buffer before grabbing the write handle
(split-borrow limitation in
StepContext).
Pull Request Process
- Fork the repository and create a branch.
- Make your changes with tests.
- Ensure all CI checks pass locally (
cargo test --workspace && cargo clippy --workspace -- -D warnings). - Open a PR with a clear description of what changed and why.
Commit Messages
This project uses Conventional Commits:
| Prefix | When to use |
|---|---|
feat: | New feature |
fix: | Bug fix |
docs: | Documentation only |
ci: | CI/CD changes |
chore: | Maintenance (deps, config) |
refactor: | Code change that neither fixes nor adds |
test: | Adding or updating tests |
perf: | Performance improvement |
Use a scope when helpful: feat(space):, fix(python):, ci(release):.
Releasing
Releases are manual, triggered by pushing a git tag:
- Bump the version in root
Cargo.tomlunder[workspace.package], and update all inter-crateversion = "x.y.z"dependency strings in each crate’sCargo.toml. The Python package version is derived automatically (viadynamic = ["version"]inpyproject.toml). - Commit and push to
main. - Tag and push the tag:
git tag murk-v<version> git push origin murk-v<version> - The release workflow runs CI, publishes Rust crates to crates.io, builds Python wheels, and publishes them to PyPI. It also creates a GitHub Release with auto-generated release notes.
- If something fails: fix the issue, then re-trigger from the GitHub Actions
UI using
workflow_dispatchwith the same tag (CI is skipped on re-runs).
Dry-run a release locally:
cargo publish --dry-run -p murk-core
Secrets required (set in GitHub repo settings > Secrets):
CARGO_REGISTRY_TOKEN— crates.io API tokenCODECOV_TOKEN— Codecov upload token