Nucleide

Contributing to Nucleide

Thank you for contributing to Nucleide. This document describes the workflow, conventions, and checks expected for code changes.

Before you start

  1. Read the root AGENTS.md and any AGENTS.md files in directories you plan to touch.
  2. Open an issue first if your change is large, architectural, or introduces new dependencies.
  3. Make sure you can run the local development stack: Local development.

Development workflow

  1. Create a feature branch from main:

    git checkout main
    git pull
    git checkout -b feature/your-feature-name
  2. Make your changes following the conventions below.

  3. Add or update tests for new behavior.

  4. 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
  5. Commit with a clear message explaining what changed and why.

  6. 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/python or on Python.
  • Add tests next to changed code (#[cfg(test)] modules or tests/ files).

Python

  • Target Python >= 3.10.
  • Format with ruff format.
  • Lint with ruff check.
  • Type-check with mypy --strict against the .pyi stubs.
  • The compiled _internal module is checked via its stub; do not add business logic to the pure-Python facade.
  • Facade naming in the flat nucleide._internal namespace: parse_* takes text, read_* takes a path, and a name carries a domain prefix (alara_*, origen_*, …) wherever a collision across modules is plausible. nucleide.cccc ships both naming eras side by side (isotxs_parse plus prefixed cccc_parse_isotxs aliases) — the dual exports are grandfathered; document, don’t rename.

Shell scripts

  • Run shellcheck and shfmt where 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:

  1. Record every breaking change under CHANGELOG.md → [Unreleased] → Changed in the same PR that makes it.
  2. Prefer deprecating over removing: #[deprecated(since = "…", note = "…")] on Rust items (removed no earlier than the next minor) and warnings.warn(..., DeprecationWarning) on the Python facade for the same window.
  3. Pure additions (new functions, modules, struct fields, error enum variants) need no deprecation period: every public error enum carries #[non_exhaustive], so downstream matches 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.
  4. Error types follow one of two naming patterns: a single crate-root Error, or per-module Error types re-exported at the crate root under domain names where the type matters cross-module (the CompendiumError / CramError precedent). nucleide-mcnp-io is the sanctioned exception — its nine per-module errors stay module-path-only.
  5. Fallible paths return the crate Result alias (pub type Result<T>), exposed at the crate root, or per error module where errors are per-module — no user-reachable panics (private expect/unwrap on validated invariants must cite why the input cannot occur).
  6. 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

  1. 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.
  2. Regenerate the reference tables. After changing python/nucleide/_internal.pyi, the python/nucleide/*.py facades, crate manifests/descriptions, or adding fixtures (document the area README), run python3 scripts/gen-reference.py --write and commit the result (CI enforces freshness).
  3. 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.
  4. Do not duplicate details that live elsewhere. Link to the project README, API stubs, and fixture READMEs instead of copying them.
  5. Use relative links. Internal links must be relative so documentation stays usable offline and in branches.
  6. Use .mdx for component-heavy pages. Pages that use @nukehub/docs-kit shortcodes such as <Mermaid>, <Callout>, or <DataTable> must have an .mdx extension. Plain .md is fine for prose-only pages.
  7. Every page sets title and sidebar.order in 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.
  8. 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 so currentColor and CSS custom properties follow the site theme. Keep every figure theme-aware: no background panel and no root color attribute; near-black strokes/text as currentColor, muted grays as var(--muted-foreground), panel fills as var(--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 when alt is 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, so currentColor follows the site theme and var(--primary) follows the accent picker — no background panel needed. Inline SVGs take role="img" and an aria-label on 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 .md sources: 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 .md siblings.
  9. 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 ## References heading (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 --check passes
  • cargo clippy --all-targets -- -D warnings passes
  • cargo test --workspace passes
  • maturin develop succeeds
  • pytest tests/ passes
  • ruff format --check python tests && ruff check python tests passes
  • mypy passes
  • 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.