Skip to main content

Contributing

The repository accepts pull requests against main for new work, against release/0.2.x for the active line, and against release/0.1.x for maintenance fixes to the 0.1.0 export-format line. All three branches require the test suite to pass before merge; CI runs on PRs to main, develop, and release/**.

Development setup

git clone https://github.com/aaronstevenwhite/fovea.git
cd fovea
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

The dev compose file binds the source directories into the backend and frontend containers so a save triggers a rebuild. The model service is built once and reused; re-build when its Python dependencies change.

Backend

The backend lives under server/. Run the test suite with:

cd server
npm install
npm test

Schema changes go through Prisma:

npx prisma migrate dev --name <migration_name>

Never edit a landed migration. Add a new migration that performs the desired transformation; see Project > Stability.

Frontend

The frontend lives under annotation-tool/. Run:

cd annotation-tool
npm install
npm run dev # vite dev server
npm test # vitest unit tests
npm run lint # eslint

End-to-end tests live under annotation-tool/e2e/ and run via Playwright against the e2e compose stack:

docker compose -f docker-compose.yml -f docker-compose.e2e.yml up -d
npx playwright test

Model service

The model service lives under model-service/. Use uv:

cd model-service
uv sync
uv run pytest

Tests live under model-service/test/ (not tests/). pytest.ini has coverage addopts; locally run with --override-ini="addopts=" to skip coverage.

Commit messages

The convention is a single sentence with no conventional-commit prefix. PR titles do use prefixes (fix:, feat:, docs:). The PR template lives at .github/PULL_REQUEST_TEMPLATE.md; PR bodies must keep every checkbox section, checked or unchecked.

Type rules

The model-service Python code does not use Any or bare object type annotations. Use Protocols, generics, or concrete types instead. Never soften a type to silence a lint.

The backend and frontend TypeScript follow strict mode. Add a TypeBox schema for every route; the request and response shapes are part of the API contract.

RBAC test baseline

Integration tests that exercise multi-user isolation (test/integration/multi-user-isolation.test.ts, import-export-cross-user.test.ts, issue-121-real-fixture.test.ts) call the shared helper test/integration/_rbac-baseline.ts from their beforeEach. The helper deletes the test-helper's blanket-grant RolePermission rows under the system scope and re-seeds an ownership-aware production-like baseline: every action on every content type (persona, annotation, summary, claim, world_state) is seeded as ownOnly: true for the user system role, plus an unconditional video read. This makes CASL's per-row condition the gate the test exercises rather than an unconditional grant.

Without _rbac-baseline.ts, the test-helper's blanket grants hide ownership leaks: the matrix would falsely pass even when v0.2.0's permission state would have allowed cross-user access in production. New isolation tests should pull in the helper.

Filing issues

Bugs go to the GitHub issues tracker. Reproduction steps that hit the actual stack (docker compose up, then a sequence of curl commands) are easier to act on than UI-only descriptions. Multi-user isolation regressions should reference test/integration/multi-user-isolation.test.ts so the matrix gains a new test for the affected route.