Contributing

This guide covers the development setup, conventions, and workflow for contributing to meridian-tools.

Development setup

Clone and install

git clone <repo-url> meridian-tools
cd meridian-tools
pip install -e ".[dev]"

The [dev] extra installs pytest, ruff, and mypy.

Verify the install

meridian-tools --help
python -m compileall src tests
ruff check src tests
mypy src
pytest tests/ -v

Acceptance gate

Before submitting any change, run the full acceptance sequence from the repository root:

python -m compileall src tests
ruff check src tests
ruff format --check src tests
mypy src
python -m pip install -e . --no-deps
meridian-tools --help
pytest tests/ -v

See acceptance.md for the expected results and how to interpret failures.

Code style

Formatting and linting

The project uses Ruff for both linting and formatting:

# Check
ruff check src tests
ruff format --check src tests

# Auto-fix
ruff check --fix src tests
ruff format src tests

Configuration is in pyproject.toml:

[tool.ruff]
line-length = 120
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "C90", "SIM", "RUF"]

Type annotations

All public functions and classes use type annotations. The codebase uses from __future__ import annotations for forward-reference support.

Import conventions

  • Standard library imports first, then third-party, then local.
  • Heavy dependencies (Meridian, TensorFlow, ArviZ) are imported lazily inside functions, not at module level, in the config/CLI/validation layers.
  • Ruff rule I enforces import sorting.

Configuration models

All Pydantic models use ConfigDict(extra="forbid"). New config fields must be added with appropriate types, defaults, and validators.

Testing

Running tests

# Full suite
pytest tests/ -v

# Specific file
pytest tests/test_runner.py -v

# Specific test
pytest tests/test_runner.py::test_run_pipeline_writes_manifest -v

Test conventions

  • Tests use pytest with tmp_path for temporary directories.
  • monkeypatch is used extensively to mock Meridian internals and isolate unit tests from real MCMC sampling.
  • Module-scoped fixtures (scope="module") are used for expensive model construction in test_log_likelihood.py and test_model_selection.py.
  • Shared test infrastructure is defined inline in individual test modules. There is no top-level conftest.py.

Live Meridian verification

One opt-in command exercises the bounded real Meridian seam:

MERIDIAN_TOOLS_ENABLE_REAL_FIT=1 pytest tests/test_demo_integration.py::test_real_pipeline_refresh_smoke tests/test_log_likelihood.py::test_compute_log_likelihood_dataset_real_posterior_smoke -m real_fit -v

This is not part of the default suite. It proves one reduced real pipeline run over bundled demo data, one stored-run refresh after the original YAML is removed, and the lower-level live log-likelihood seam. Run it after Meridian version upgrades and before release-candidate handoff when you want extra confidence beyond the fast suite.

Writing new tests

  • Place tests in the appropriate tests/test_<module>.py file.
  • Use monkeypatch to avoid real MCMC sampling in unit tests.
  • Test both success paths and error conditions.
  • Verify artefact file contents, not just their existence.
  • Use tmp_path for all filesystem operations.

Project structure

meridian-tools/
├── src/meridian_tools/       # Package source
│   ├── __init__.py           # Lazy-loading exports
│   ├── artifacts.py          # Manifest helpers
│   ├── cli.py                # CLI entry point
│   ├── config.py             # Pydantic models
│   ├── cv.py                 # Validation splits
│   ├── demo.py               # Demo discovery
│   ├── diagnostics.py        # Diagnostics export
│   ├── exports.py            # Meridian export wrappers
│   ├── launcher.py           # Run execution wrapper
│   ├── lifecycle.py          # Post-run management
│   ├── log_likelihood.py     # Log-likelihood adapter
│   ├── model_selection.py    # LOO/WAIC wrappers
│   ├── terminal.py           # CLI presentation
│   └── version.py            # Static version
├── tests/                    # Test suite
│   ├── _demo_data/           # Bundled demo data (packaged)
├── docs/                     # Documentation
├── runme.py                  # Source-tree demo launcher
└── pyproject.toml            # Build and dependency config

Versioning

The version is defined in src/meridian_tools/version.py:

__version__ = "0.3.0"

Version bumps are manual edits. Update this file when preparing a release.

Documentation

Documentation lives in docs/. When adding new features:

  1. Update relevant guide or reference pages.
  2. Add API documentation for new public functions or classes.
  3. Update the YAML schema reference if config fields changed.
  4. Update the output schema if new artefacts are produced.

Common pitfalls

  • Do not import Meridian at module level in config, CLI, or validation modules. This breaks CLI responsiveness.
  • Do not add extra="allow" to Pydantic models. The extra="forbid" policy prevents silent misconfiguration.
  • Do not modify source run directories in lifecycle operations. Always create new sibling directories.
  • Do not weaken or delete existing tests without explicit direction.