Skip to content

Contributing

Local dev loop

API (Python / FastAPI)

python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'

# spin up the data services
docker compose up -d db redis

alembic upgrade head
uvicorn api.app:app --reload

Web (Next.js)

cd web
npm install
npm run dev

Full stack via Docker: make all or docker compose up --build.

Code style

  • Python: ruff (ruff check ., ruff format .) + mypy (mypy core api flows). 88-column line length. Google-style docstrings on public functions. Pydantic v2 at every I/O boundary.
  • TypeScript: eslint + prettier. Tailwind 4 utility-first; keep components dumb, put logic in hooks.
  • Commit messages: conventional-ish — feat(scope): ..., fix(scope): ..., chore(scope): .... Keep the subject ≤ 72 chars.

Tests

pytest                           # all tests
pytest tests/unit/               # unit only
pytest tests/integration/ -m integ   # integration (uses testcontainers)
pytest --cov=core --cov=api --cov=flows

Integration tests spin up a real Postgres via testcontainers. They're slower; CI runs them on PR.

Migrations

alembic revision --autogenerate -m "short description"
alembic upgrade head
alembic downgrade -1   # roll back last migration locally

Migrations live under core/db/migrations/versions/. Make sure the down-revision is clean — we keep migrations reversible where possible.

Evals

Scoring changes should be validated against the eval harness:

pytest tests/eval -m eval    # regression eval vs labeled data

Labeled samples live in tests/data/. If you change the scoring prompt in a way that moves scores, rebuild the golden set.

Docs

pip install -e '.[docs]'
mkdocs serve    # http://localhost:8000

Docs auto-deploy to GitHub Pages on push to master via .github/workflows/docs.yml. The config is mkdocs.yml at repo root; sources under docs/.

Where to put things

  • New LLM adaptercore/llm/backends/.
  • New ingest sourcecore/sources/<name>/.
  • API routeapi/routes/, register in api/app.py.
  • UI componentweb/src/components/ (shared) or web/src/app/(main)/<route>/_components/ (route-scoped).
  • Design specdev/specs/ (local only, gitignored).
  • Ticketdev/tickets/open/ → moved to dev/tickets/closed/ on merge (local only, gitignored).

Before opening a PR

  • ruff check . passes.
  • mypy clean (or justify the # type: ignore).
  • pytest -m 'not eval and not integ' green.
  • Migrations applied and reversible.
  • If you changed a scoring prompt or the profile schema, re-run the eval and note the delta in the PR description.