Building & testing the docs#

The documentation is built with Sphinx. The build needs none of the compiled stack (NEML2, MOOSE/PUMA, NEPER, CUBIT); docs/conf.py mocks those imports, so it runs from a plain checkout.

System prerequisite: pandoc#

The tutorials are Jupyter notebooks, and nbsphinx converts them by shelling out to the pandoc binary. pandoc is a system program, not a Python package, so pip install -e ".[docs]" does not install it. Without it the build stops at the first notebook with:

nbsphinx.PandocMissing: Pandoc wasn't found.

Install it with your system package manager, or into the build environment:

sudo apt-get install pandoc     # Debian/Ubuntu (what CI uses)
brew install pandoc             # macOS
conda install -c conda-forge pandoc   # conda environments

Other platforms and the official installers are at https://pandoc.org/installing.html.

Install the docs toolchain#

pip install -e ".[docs]"

Build the HTML#

cd docs
make html
# open docs/_build/html/index.html

For a strict build that treats warnings (broken cross-references, import failures) as errors, the same check CI runs:

sphinx-build -W --keep-going -b html docs docs/_build/html

Doc tests#

Runnable snippets marked with the doctest directive are executed by the doctest builder, and external links are checked by the linkcheck builder:

make -C docs doctest
make -C docs linkcheck

A minimal doctest, verifying the package imports and exposes a version string:

>>> import graintrace
>>> isinstance(graintrace.__version__, str)
True

Continuous integration#

.github/workflows/build_docs.yml runs the strict HTML build plus doctest and linkcheck on every push and pull request to main and documentation. linkcheck is informational and does not fail the job; the strict build and doctest do.

Every run — any branch, any pull request — uploads the built HTML as a downloadable docs-html artifact, so a change can be reviewed without deploying. Only a push to main deploys, via GitHub’s own actions/deploy-pages (OIDC, pages: write + id-token: write). There is no gh-pages branch and pull requests get no live preview URL; download the artifact instead.

Writing new pages#

  • API pages under docs/api/ are thin automodule stubs; add one per new public module and list it in docs/api/api.rst.

  • Tutorial pages under docs/tutorials/ are Jupyter notebooks, listed in docs/tutorials/tutorials.rst. They carry their code inline rather than including the matching examples/ script; keep the two in step by hand.

  • New prose pages must be reStructuredText. myst_parser is not installed and source_suffix is not set, so a .md file dropped into docs/ is silently ignored rather than reported as an error.

  • Keep the prose plain and factual; let the code examples carry the detail.

  • Define every symbol you introduce, or point at Notation and conventions. Equations that a later paragraph refers to get a :label: and are cited with :eq:.

Definition of done (adding or changing code)#

A new feature or code change is not complete until all five of these are done in the same change:

  1. Tests pass. Add or extend tests under tests/ and keep the suite green: pytest, black --check graintrace tests, and pylint --rcfile=.pylintrc graintrace. Tests that need the external stack self-skip via pytest.importorskip/skipif.

  2. Docstrings updated. Every new or changed public class, method, and function has a docstring (Google or NumPy style). Document new constructor keyword arguments; they surface directly in the API reference.

  3. Docs updated. Update the relevant tutorial under docs/tutorials/, Configuration reference for new options, and add an automodule page under docs/api/ for a new public module. The strict build and doctest must pass (see above).

  4. CLAUDE.md updated. Update .claude/CLAUDE.md (the working reference for the codebase) so it stays the source of truth.

  5. MCP updated. If the change is a workflow segment or adds/changes user-facing parameters, update the MCP tool in graintrace/mcp/tools/, its recipe in graintrace/mcp/recipes/, and the tool table in graintrace/mcp/README.md. New external-tool dependencies must be reported by dependency_status.