Contributing

This page describes how to set up a local development environment, run tests, and understand the CI gates for Typus.

Environment

  • Python 3.10+ (CI runs on 3.10, 3.11, 3.12)
  • Use uv and Makefile shortcuts (no plain pip in scripts/CI):
  • Preferred bootstrap path for contributors:
./dev/scripts/bootstrap-dev.sh   # ensures uv + Python 3.10, syncs the full dev/docs .venv, installs hooks
  • Lower-level env sync targets remain available when you only need to refresh part of the setup:
make dev-setup      # creates .venv (py310) and installs -e .[dev,sqlite,loader]
make dev-install    # installs pre-commit hooks

Formatting & Lint

make format         # uv run ruff format .
make lint           # uv run ruff check --fix .
make lint-check     # uv run ruff format --check . && uv run ruff check .

Type checking

make typecheck      # uv run ty check (warnings fail)

Docs

make docs           # creates a py310 .venv if missing, ensures docs extras, then builds the MkDocs site

Canonical local quality gate

make check-all      # lint-check + typecheck + docs + schemas-check + ci

Tests

Lightweight SQLite-backed tests that match the CI gate:

make ci

Broader default local suite (SQLite + tests that do not require optional Postgres access):

make test

Postgres-backed tests (optional, requires a running DB):

export TYPUS_TEST_DSN=postgresql+asyncpg://<user>:<pass>@<host>:5432/ibrida-v0
export POSTGRES_DSN=$TYPUS_TEST_DSN
export ELEVATION_DSN=$TYPUS_TEST_DSN
export ELEVATION_TABLE=elevation_raster

# Smoke-check DSN / DB / required table before optional PG tests
make test-pg-smoke

# Optional Postgres marker subset (dataset-dependent)
make test-pg

# Elevation smoke tests (guarded; requires raster table)
TYPUS_ELEVATION_TEST=1 uv run pytest -q -k elevation_service

Important: make test and CI exclude pg_optional tests by marker. Run make test-pg-smoke + make test-pg explicitly when you want live Postgres coverage. Some tests assert counts that are specific to the SQLite sample fixture and will not match the full production dataset. The recommended workflow is:

  1. Run make ci / make test to exercise the SQLite fixture path.
  2. Run make test-pg-smoke and then make test-pg for Postgres-specific coverage.

Notes:

  • Ensure the Postgres database contains the required expanded_taxa table with the recommended indexes. You can bootstrap indexes with:
uv run typus-pg-ensure-indexes

Pre-commit

make dev-install

Hooks run on commit and in CI: - ruff (lint) - ruff-format (check-only in CI) - whitespace/yaml fixers - pytest-lite (fast subset; mirrors make ci test selection)

JSON Schemas

If you add or change public Pydantic models, append the dotted path to typus/export_schemas.py and regenerate schemas:

uv run python -m typus.export_schemas

Generated JSON files live under typus/schemas/ and are committed to the repo. CI and release workflows enforce schema freshness with make schemas-check.

Perf Harness (optional)

make perf

Writes a report to dev/agents/perf_report.md. You can enable TYPUS_PERF_VERIFY=1 for strict checks, and TYPUS_PERF_EXPLAIN=1 to append EXPLAIN snippets for Postgres.

Service Backends

  • SQLite: for offline/dev; loader creates indexes by default.
  • Postgres: for production or full datasets; typus-pg-ensure-indexes adds recommended indexes.
  • Elevation: Postgres-only; use ELEVATION_DSN and ELEVATION_TABLE (default elevation_raster).

CI/CD Overview

  • CI workflow (.github/workflows/ci.yml):
  • Matrix on Python 3.10/3.11/3.12.
  • Syncs dependencies from uv.lock with uv sync --locked, then runs make check-all.
  • make check-all covers make lint-check, make typecheck, make docs, make schemas-check, and make ci.
  • Triggers on PRs and pushes to main (to avoid duplicate branch + PR runs for the same commit).
  • Publish workflow (.github/workflows/publish.yml):
  • Blocks build/publish on the same make check-all gate used locally and in CI.
  • Uses locked project tooling for twine and docs builds instead of uvx or live dependency resolution.
  • Builds and uploads wheels, then deploys docs on tag.

Supply-chain guardrails:

  • Typus keeps normal runtime dependency ranges for downstream consumers, but repo-owned CI, docs, build, and publish environments are lockfile-governed.
  • pyproject.toml configures a seven-day uv exclude-newer cooldown. Lock refreshes should be intentional, reviewed, and committed with the code or workflow change that needs them.
  • make check-all includes make lock-age-check, which fails when uv.lock references artifacts uploaded inside the cooldown window.
  • Release workflows should use uv sync --locked / uv run --locked; avoid uvx for tools already present in the project lock.