Skip to content

Contributing

Kaveh Razavi edited this page Jun 28, 2026 · 8 revisions

Contributing

NNx is a small library; the goal is to keep it small, tested, and useful for the existing notebook consumers while inviting new ones. Bug reports and PRs are welcome via GitHub issues.

For the full reference, see the canonical file: CONTRIBUTING.md.

Getting set up

git clone https://github.com/thekaveh/NNx.git
cd NNx
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install              # optional but recommended

Verify a clean baseline:

pytest                          # full suite (~15s on CPU)
ruff check src/ tests/ examples/  # lint
ruff format --check src/ tests/ examples/  # format check (matches CI + pre-commit)
mkdocs build --strict           # docs (gates CI)

Useful env var: NNX_TQDM_DISABLE=1 silences the training progress bar — set this in CI or non-TTY contexts. The test suite's conftest.py already does this session-wide.

Workflow

  1. Open an issue first for non-trivial changes — saves churn if the design is off. Tiny fixes can go straight to PR.
  2. Branch from main. Name branches descriptively (fix/..., feat/..., docs/..., refactor/...).
  3. Write tests. Every PR that changes behavior should land with a focused test that fails on main and passes on the branch. The existing tests/test_*_series.py files (organized by audit pass) are good models.
  4. Keep PRs small. One coherent change per PR is much easier to review than a sweeping mix.

Back-compat invariants

These are non-negotiable:

  • Strict back-compat for the existing notebook consumer. Don't rename, remove, or restructure public APIs without a migration path. Don't change the on-disk runs/<id>/ format.
  • Omit-when-default rule. New fields on params dataclasses must omit themselves from .state() when set to their defaults. This preserves run.id hashes for all existing runs — a non-default field shifts the hash and breaks warm-resume. See the regression tests in tests/test_params_round_trip.py (search for test_nn_*_state_omits_*_when_*) for the canonical pattern. The test must use an explicit assert "<field>" not in state() form — a round-trip test alone is insufficient.
  • State / from_state round-trip. Every params dataclass with a state() method must round-trip cleanly through from_state(state()). Enforced by tests/test_params_round_trip.py.
  • Breaking on-disk format changes require a versioned reader. Public API renames require a deprecation shim and a __getattr__ alias for at least one minor version.

Test policy

pytest                          # full suite
pytest tests/test_pass2_n_series.py::test_n7_evaluate_aggregates_across_batches
pytest -k "graph"               # name filter
pytest --cov=nnx --cov-report=term-missing  # with coverage

Tests live under tests/. Rules:

  • Tests run on CPU and must finish fast (keep new tests under a few seconds).
  • Use small TensorDataset fixtures from tests/conftest.py.
  • Code without tests will not be merged.
  • Dependencies added to the core [project.dependencies] list will not be merged if they can go under [project.optional-dependencies] instead.

Style

  • ruff enforces formatting and lint (E F W B I UP). Run ruff check --fix src/ tests/ examples/ and ruff format src/ tests/ examples/ before pushing. Pre-commit handles both automatically when installed.
  • Type annotations are encouraged on new code. pyright (basic mode) runs in CI, with --strict planned over time.
  • Docstrings on public functions / classes explain the why (constraints, edge cases) — not just the what.
  • Comments explain non-obvious decisions, hidden constraints, or surprising behavior.

Submitting a PR

  • Push to your fork and open a PR against main.
  • Fill in the PR template (Summary / Test plan).
  • Wait for CI to go green (lint + format + tests + mkdocs on Python 3.10 / 3.11 / 3.12).
  • Address review comments by pushing new commits — we squash on merge.
  • Add a one-line entry under [Unreleased] in CHANGELOG.md for any user-visible change.

Releases

NNx uses release-please for automated version bumps, changelog updates, and tagging. Contributors don't touch versions or tags — just write a Conventional Commit-style PR title (feat:, fix:, chore:, docs:, etc.).

The end-to-end release flow:

  1. Every merge to main updates a long-lived "Release" PR maintained by release-please.yml. The PR accumulates the next version + CHANGELOG.md diff. Pre-1.0: feat: triggers a minor bump (0.X.0); fix: and most other types trigger a patch bump (0.X.Y).
  2. A maintainer merges the Release PR when ready to ship. That merge pushes a v* tag.
  3. The tag push fires release.yml: full test matrix → build → OIDC-trusted publish to PyPI → verify-published confirms pip install thekaveh-nnx==X.Y.Z works from a clean venv.

License

By contributing you agree that your contribution will be licensed under the Apache License 2.0.

See also

Clone this wiki locally