Contributing to Nucleide
Thank you for contributing to Nucleide. This document describes the workflow, conventions, and checks expected for code changes.
Before you start
- Read the root
AGENTS.mdand anyAGENTS.mdfiles in directories you plan to touch. - Open an issue first if your change is large, architectural, or introduces new dependencies.
- Make sure you can run the local development stack: Local development.
Development workflow
-
Create a feature branch from
main:git checkout main git pull git checkout -b feature/your-feature-name -
Make your changes following the conventions below.
-
Add or update tests for new behavior.
-
Run the canonical checks:
cargo fmt --check cargo clippy --all-targets -- -D warnings cargo test --workspace maturin develop pytest tests/ ruff format --check python tests && ruff check python tests mypy -
Commit with a clear message explaining what changed and why.
-
Push and open a pull request against
main.
Code conventions
Rust
- Workspace pins
rust-version = "1.83"; avoid unstable features. - Format with
cargo fmt(rustfmt defaults). - Lint with
clippy --all-targets -- -D warnings; zero warnings tolerated. - Keep crate layering clean: workspace crates must not depend on
bindings/pythonor on Python. - Add tests next to changed code (
#[cfg(test)]modules ortests/files).
Python
- Target Python >= 3.10.
- Format with
ruff format. - Lint with
ruff check. - Type-check with
mypy --strictagainst the.pyistubs. - The compiled
_internalmodule is checked via its stub; do not add business logic to the pure-Python facade. - Facade naming in the flat
nucleide._internalnamespace:parse_*takes text,read_*takes a path, and a name carries a domain prefix (alara_*,origen_*, …) wherever a collision across modules is plausible.nucleide.ccccships both naming eras side by side (isotxs_parseplus prefixedcccc_parse_isotxsaliases) — the dual exports are grandfathered; document, don’t rename.
Shell scripts
- Run
shellcheckandshfmtwhere available. - Prefer
#!/usr/bin/env bash. - Use
set -euo pipefail.
API stability (pre-1.0 deprecation policy)
All workspace crates are 0.x, so minor releases may break the Rust and
Python APIs. Breaking changes are signaled, never silent:
- Record every breaking change under
CHANGELOG.md → [Unreleased] → Changedin the same PR that makes it. - Prefer deprecating over removing:
#[deprecated(since = "…", note = "…")]on Rust items (removed no earlier than the next minor) andwarnings.warn(..., DeprecationWarning)on the Python facade for the same window. - Pure additions (new functions, modules, struct fields, error enum
variants) need no deprecation period: every public error enum carries
#[non_exhaustive], so downstreammatches must name a wildcard arm and adding a variant stops being breaking. Data structs and non-error enums stay exhaustive — new fields or variants there are breaking. - Error types follow one of two naming patterns: a single crate-root
Error, or per-moduleErrortypes re-exported at the crate root under domain names where the type matters cross-module (theCompendiumError/CramErrorprecedent).nucleide-mcnp-iois the sanctioned exception — its nine per-module errors stay module-path-only. - Fallible paths return the crate
Resultalias (pub type Result<T>), exposed at the crate root, or per error module where errors are per-module — no user-reachable panics (privateexpect/unwrapon validated invariants must cite why the input cannot occur). - Crates carry
#![warn(missing_docs)]: every public item ships documented, and types with invariants construct through validating constructors (record-style structs may keep public fields, validated at use). All 18 crates carry the lint — new crates must ship the lint from the start.
The 0.5.0 API freeze covered (nucleide-kinetics,
nucleide-spectroscopy, the nucleide-mcnp-io endl/fortran modules,
and the numpy bridges): doc-comment and attribute edits only, no signature
or behavior changes.
Documentation
Documentation is a first-class deliverable. Update docs when your change affects:
- Public Rust or Python API →
docs/reference/ - Tutorials or worked examples →
docs/tutorials/ - Architecture, component boundaries, or layering →
docs/architecture/ - Developer workflow →
docs/development/
Do not duplicate information that already lives in README.md, AGENTS.md,
generated API docs, or fixture READMEs. Link instead.
Maintaining the docs
- Keep docs in sync with code. A PR that changes a public API, parser output, or crate boundary must update the matching tutorial or reference page.
- Regenerate the reference tables. After changing
python/nucleide/_internal.pyi, thepython/nucleide/*.pyfacades, crate manifests/descriptions, or adding fixtures (document the area README), runpython3 scripts/gen-reference.py --writeand commit the result (CI enforces freshness). - Prefer deletion over stale historical notes. If a section no longer reflects current behavior, delete it or move it to an explicit “Historical” appendix with a removal date.
- Do not duplicate details that live elsewhere. Link to the project README, API stubs, and fixture READMEs instead of copying them.
- Use relative links. Internal links must be relative so documentation stays usable offline and in branches.
- Use
.mdxfor component-heavy pages. Pages that use@nukehub/docs-kitshortcodes such as<Mermaid>,<Callout>, or<DataTable>must have an.mdxextension. Plain.mdis fine for prose-only pages. - Every page sets
titleandsidebar.orderin frontmatter. Without an explicit order the site sidebar falls back to alphabetical sorting. Keep the order values aligned with the reading order in the section index tables, and do not repeat the title as an in-body#heading (the site renders the frontmatter title as the page heading). Register every new page in the docs home full index as well as in its section index. - Figures are hand-authored SVGs. Theory figures live in
docs/theory/figures/and are embedded with the kit’s<SvgFigure>shortcode, which inlines the markup at build time socurrentColorand CSS custom properties follow the site theme. Keep every figure theme-aware: no background panel and no rootcolorattribute; near-black strokes/text ascurrentColor, muted grays asvar(--muted-foreground), panel fills asvar(--muted); accent hues stay hardcoded. Figure files keep their<title>element: the shortcode hoists it into the kit’s styled tooltip on the figure’s info button and uses it as the accessible name whenaltis omitted, so no browser-native tooltip appears when inlined; it also helps standalone viewing of the raw file. Landing-page visuals may instead be inline SVG in the page source (as on the docs home page): inline markup inherits the page CSS, socurrentColorfollows the site theme andvar(--primary)follows the accent picker — no background panel needed. Inline SVGs takerole="img"and anaria-labelon the<svg>tag for accessibility; do not use a<title>element, which browsers show as a hover tooltip. Keep the<svg ...>opening tag on a single line in.mdsources: the markdown parser only recognizes a raw-HTML block when the opening tag is complete on its line — a wrapped tag splits the SVG out of its element and dumps the inner text into the page and the generated.mdsiblings. - Citations live in frontmatter. Pages with sources declare a
references:array in frontmatter and cite inline with<Citation id="…"/>; the site kit renders the bibliography at the end of the page automatically. Cite each source at the claim it backs — the body carries no reference sections: never write a body## Referencesheading (it duplicates the rendered one) nor per-source annotation lists.
Testing
Rust tests
cargo test --workspace
cargo test -p nuclei
The cargo llvm-cov TOTAL denominator includes the binding facades plus
test/bench code.
Python tests
maturin develop
pytest tests/ -v
Parser parity regression tests
Parsers validated against golden-byte fixtures must continue to reproduce those fixtures byte-for-byte. If you intentionally change output, update the fixture and all consumers in the same PR and explain why in the commit message.
Commit messages
Use clear, imperative commit messages:
Add support for custom mesh tally formats
- Adds UsrbinTally parser for Cartesian and cylindrical meshes
- Adds golden-byte fixture for cylindrical USRBIN
- Updates tutorial with cylindrical example
Pull request checklist
- Branch is based on the latest
main -
cargo fmt --checkpasses -
cargo clippy --all-targets -- -D warningspasses -
cargo test --workspacepasses -
maturin developsucceeds -
pytest tests/passes -
ruff format --check python tests && ruff check python testspasses -
mypypasses - Documentation updated for user-facing or architectural changes
- No secrets, credentials, or personal data committed
- Commit messages explain the change
Getting help
- Open a discussion for questions.
- Open an issue for bugs or feature requests.
- Tag maintainers on security-related changes.
License
By contributing, you agree that your contributions will be licensed under the BSD-2-Clause license.