Contributing
This page covers the development workflow. Use the companion references for documentation conventions, the release process, and current architecture contracts.
Development setup¶
git clone https://github.com/awwesomeman/factrix.git
cd factrix
uv sync --extra dev
python scripts/setup_dev.py
uv run pytest
uv sync --extra dev is sufficient for uv run pytest to finish green: the
tests that build or resolve the MkDocs site skip themselves, with a reason, when
the docs extra is absent. Add --extra docs (or --all-extras) to make those
checks run instead of skip; uv run pytest -rs lists the skip reasons.
pyproject.toml and uv.lock define the environment. Do not install packages
directly into .venv; use uv add, update the project metadata, and commit the
lockfile change together.
Extras¶
| Command | Purpose |
|---|---|
uv sync |
Runtime dependencies only |
uv sync --extra dev |
Tests, lint, typing, and commit tooling |
uv sync --extra docs |
MkDocs and API-documentation tooling |
uv sync --extra pandas |
pandas/pyarrow input adapter |
uv sync --extra jupyter |
Notebook environment |
uv sync --frozen --all-extras |
Full CI or release verification |
The named all extra is the optional runtime bundle; it does not include the
development and docs toolchains. Use --all-extras when every declared extra
is required.
CI exercises both sides of the docs-theme boundary. The all-extras test matrix
runs the two tests that resolve mkdocs.yml. A separate docs-no-theme cell
installs mkdocstrings without mkdocs-material and asserts that exactly those
two tests skip for the requires_theme reason while the other 19 module tests
run. This is distinct from dev-extra, where the module skips during
collection because the complete docs machinery is absent.
On Windows, enable UTF-8 for Markdown and diagnostic tests if the console does not already do so:
uv sync installs factrix in editable mode. Restart a Jupyter kernel after
changing module imports, dataclasses, or module-level constants; autoreload is
not sufficient for those changes.
Environment troubleshooting¶
Each clone normally owns its .venv; another project does not mix packages
into it. The uv download cache is shared by default, so a reported cache error
can affect more than one clone without implying that their environments were
combined.
When several toolchains are needed on a development machine, install them in one sync and retain existing extras:
On a centrally managed Windows machine, use the organisation-approved Python and certificate profile. A typical invocation with an existing system Python is:
Use this diagnostic order instead of repeatedly restarting an installation:
- Confirm the interpreter and environment with
.\.venv\Scripts\python.exe -c "import sys; print(sys.executable); print(sys.prefix)". - Run
py -m uv pip check --python .\.venv\Scripts\python.exeand inspectpy -m uv cache dirwhen uv reports cache or package-metadata errors. - If dependency resolution has finished but the process shows no sustained CPU, disk, or network activity for about a minute, stop it and investigate proxy, certificate, antivirus, or endpoint file-scanning controls. Waiting longer does not repair an idle finalisation step.
- When uv names a damaged cache package, remove only that package with
py -m uv cache clean <package>, then run one sync. Do not clear unrelated caches or run concurrent repair attempts. - If imports succeed but package metadata or plugin discovery is missing,
treat
.venvas incomplete. Recreate only the repository-local environment through the approved setup rather than editingsite-packagesmanually.
Do not disable TLS verification or endpoint security to make an install pass. Use system certificates, an approved package mirror, or an administrator- managed exception for the project environment and cache paths. Clean CI can provide an independent verification gate, but it does not make a partially installed local environment healthy.
Git hooks¶
python scripts/setup_dev.py installs the pre-commit, commit-msg, and
pre-push stages declared in .pre-commit-config.yaml. Installation is local
to each clone.
| Stage | Checks |
|---|---|
pre-commit |
Ruff lint and format checks for staged Python/notebook changes |
commit-msg |
Commit-message and contributor-trailer policy |
pre-push |
Strict docs build for docs-relevant paths, mypy for package paths, and release-note checks where applicable |
The pre-push gates run through uv run, which resolves the environment of
the directory the push is issued from. A git worktree has no environment of
its own, so the gates fall back to the main checkout's .venv; where neither
can start a tool the hook says the gate did not run and lets the push
through, rather than reporting it as a gate failure. Nothing was measured in
that case, and CI still runs the same check on the pull request — but if you
want the local feedback back, uv sync --extra docs inside the worktree gives
it one of its own. Reach for git push --no-verify only when you mean to skip
gates that can run; it drops all of them, including the release-note check.
Run the lint stage directly with:
When updating hook versions, run uv run pre-commit autoupdate. Keep the Ruff
hook revision equal to the ruff== development pin in pyproject.toml; a test
enforces the match.
Development workflow¶
git switch main
git pull --ff-only origin main
git switch -c <type>/<short-description>
# edit and test
uv run pytest tests/test_<area>.py -v
uv run pytest
git add <specific-files>
cz commit
git push -u origin <type>/<short-description>
gh pr create
Use lowercase, hyphenated branches under feat/, fix/, docs/,
refactor/, or chore/. Do not commit directly to main.
Commits follow Conventional Commits. Keep the subject concise and use the body
for the reason and behavioural effect. Do not add AI co-author or
Signed-off-by trailers unless a future repository policy explicitly requires
them.
Open one pull request for one reviewable concern. The PR body should contain a
short summary, the verification performed, and Closes #<issue> when it
resolves an issue. Version bumps and tags follow the independent
release process, not each merged PR.
Testing rules¶
- Use synthetic fixtures from
tests/conftest.pyorfactrix.datasets; tests do not read private or live market data. - Add tests for new metrics, public result fields, parameters, error paths, and documented warnings.
- Characterise statistical size separately from ordinary unit behaviour. A reduced-replication guard can pin a measured band, while the larger sweep is documented in Inference calibration.
- Run focused tests while iterating, then the full suite before opening a PR.
- Do not bypass hooks or tests to make a branch appear green.
The baseline local checks are:
uv run pre-commit run --all-files --hook-stage pre-commit
uv run mypy factrix
uv run pytest
uv run pytest --doctest-modules factrix
uv run mkdocs build --strict
Use uv sync --frozen --all-extras before the complete run so optional adapter
tests do not execute against a partially installed pandas/pyarrow environment,
and so the docs-site tests run rather than skip — uv sync --extra dev alone
leaves both the pandas adapter and the docs checks unexercised. uv run pytest
-rs prints the reason for every skip, so a missing extra is visible rather than
silent.
Python and API changes¶
- Public functions and classes use type annotations and Google-style docstrings.
datanames a DataFrame-like input; reservedf_*for degrees of freedom.- A new metric registers one
MetricSpec; do not add a parallel applicability or routing table. - A formal p-value and
alternativemust be published together throughMetricResult.p_value; metadata must not duplicate the canonical p-value. - Public metric names describe the statistic. Avoid
_testsuffixes, and add a method token when several estimators produce the same concept.
For the complete dispatch, result, guard, and naming invariants, see Architecture. For user-visible prose, citations, examples, and generated pages, see Documentation conventions.
Design proposals¶
architecture.md describes the current state, not design history. Use a
GitHub issue for a proposed behavioural or architectural change, record the
decision and definition of done there, and link the implementing PR. Update the
architecture page only when the current contract changes.
The files under docs/plans/archive/ are frozen historical plans and are not
published. Do not create a new plan file for ordinary development work; use an
issue unless the design genuinely requires a long-lived external artifact.
Communication and language¶
Issues, pull requests, commit messages, changelog entries, package docstrings, and published docs use English. Planning archives may remain bilingual because they are excluded from the site.
Small decisions belong in the PR rationale. Invariant-level changes must also update the relevant architecture section. If a decision changes a public contract, state the migration path explicitly.
Licensing¶
factrix is licensed under Apache-2.0. Contributions are licensed to the project under the same terms unless the contributor states otherwise. Submit only code you have the right to license, identify third-party sources and licences, and disclose relevant patent claims. Strong-copyleft code is not accepted into the main package because it would change downstream obligations.