Contributing to solprop#
Contributions can improve the models, numerical methods, interfaces, tests, or
manual. Begin with a reproducible example of the problem or a clear description
of the proposed change. The repository’s AGENTS.md and CONTRIBUTING.md define
the working agreement; the detailed design and conventions are in this guide.
Set up a development environment#
solprop uses uv and Python 3.14. Clone the repository and synchronise its declared development dependencies:
git clone https://github.com/LupoLab/solprop.git
cd solprop
uv sync
The native backend lives in native/ in the same repository and is a second distribution,
solprop-native, at the same version and a required dependency. A plain uv sync therefore
builds it and installs it editable into the same environment, which needs its C++
toolchain, and uv run poe check-all runs solprop’s gate and then the native gate; a change
that touches native/ runs both and follows native/AGENTS.md.
Building the native backend
covers its builds; the architecture and
kernel contract describe the boundary between the two packages.
uv sync --extra gui --group gui-test adds the desktop front end from gui/ with its test
plugin. A sync removes every extra and hand-installed package it does not name, Pyroraptor
included; uv sync --inexact, or uv run --no-sync, keeps them.
A checkout has three states, and only the first satisfies the exact requirement a release carries:
setup |
native |
metadata |
|---|---|---|
|
built from this tree |
consistent: a workspace member, at the same version |
the lightweight setup |
none |
unmet, deliberately |
|
CI’s wheel from this commit or a compatible ancestor |
mismatched, deliberately: |
The lightweight setup serves Python and documentation contributors, with no native package and no compiler:
uv run --no-project tools/setup_light.py
It runs uv sync --locked --no-install-package solprop-native and says how to continue. It is
a development workflow, not a way to run solprop in production. It supports the reference
backend and the lint, type, test and documentation checks. uv can’t be configured otherwise:
uv run has no flag to skip a package, and uv sync ignores UV_NO_SYNC, so a later plain
uv run or uv sync builds native. Run the tools with the environment activated, or with
UV_NO_SYNC=1. There backend="auto" refuses, so scripts name backend="numpy_ref"; tests
take SOLPROP_TEST_BACKEND=numpy_ref, and native-only tests skip. A native change still needs
a native build and its qualification. Automation that needs no local build, such as Read the
Docs and the lanes on CI’s wheel, uses the same two settings.
uv run --no-sync poe fetch-wheel installs a wheel CI built, for Linux x86-64, macOS arm64,
Windows x64 or Windows on Arm, using gh and no compiler. With no compiler, run the lightweight
setup first, because a plain uv run would try to build native:
uv run --no-project tools/setup_light.py
uv run --no-sync poe fetch-wheel
It takes the newest artifact from this checkout’s
history only if the difference between that artifact’s commit and the working tree, including
uncommitted changes, touches neither native/ nor the solprop modules that solprop_native
imports. Anything else is refused, and so is an expired artifact; each refusal names uv sync
and the lightweight setup. A later plain uv sync or uv run builds native in its place, so
run with uv run --no-sync. The wheel keeps the commit it was built from, and check_build’s
lookup of the seam’s names remains the guard at run time; the comparison of source digests is
for an editable checkout, and a wheel has no sources beside it to compare.
Agree and implement a change#
Work on a topic branch and keep changes focused. New physics, numerical schemes, public-interface redesigns, and substantive contract changes require an accepted enhancement proposal. Other changes larger than a focused fix start with an agreed issue or written plan.
Keep the manual, docstrings, tests, and release-note fragments consistent with the
behaviour. Add a unique changelog.d/<slug>.<category>.md fragment for a notable change;
leave the shared changelog to the rolling release PR.
Check physical claims against their primary sources and report uncertainties.
Run fast checks and directly affected tests during development. Continuous
integration runs the full gate on every pull request,
uv run poe check
together with the slow and coverage lanes (poe test-slow, poe cov); a change is
complete when they pass there. A change to the documentation alone needs less; see
documentation-only changes.
Add a field to the run record#
A run record holds every field of every dataclass it serialises, at its default too. Add the field and nothing else: the configuration hash moves, and a file written before it reads but is not resumed (ADR-066). Whether the output schema is bumped follows the schema rule.
Work on the documentation#
Build the manual with uv run poe docs. It executes no gallery script: every gallery has
committed output, which it reuses, and it fails naming any script whose source differs from
its committed output. There are three galleries:
Introductory lessons,
docs/introduction/tutorials/plot_*.py, rendered intodocs/introduction/lessons/, and focused tutorials,docs/tutorials/**/plot_*.py, rendered intodocs/auto_tutorials/. Both are deterministic and large enough to show the physics, and may run their real problem at full length when that is what they demonstrate. CI’s docs job runs every one again on each change (SOLPROP_RUN_EXAMPLES=tutorials).Worked examples,
docs/examples/**/plot_*.py: full-size problems of minutes to half an hour, rendered intodocs/auto_examples/and run only by hand.
After changing a gallery script, including its prose, regenerate its output with
uv run poe docs-examples <name> (or with no name, every stale script) on an idle machine
and commit the generated files with the source, stating a worked example’s run time in the
commit message. Never edit generated gallery or API pages by hand.