3.8 KiB
Voting App - Agent Instructions
See ~/.config/opencode/AGENTS.md for global development principles (container-first, package managers, Dockerfile standards).
Repo Overview
Flask backend + React TypeScript frontend. Data flow: User → React Frontend → Flask Backend → Congress.gov v3 API, with SQLite caching and local GPT-OSS for bill summaries. Backend source is flat under src/ (no submodules). Vote data: House votes from Clerk XML (fast), Senate votes from senate.gov XML (slow, sequential scan).
Lockfiles Are Gitignored — Regenerate Before Building
Both uv.lock and frontend/pnpm-lock.yaml are in .gitignore. Dockerfiles copy them during build, so they must exist locally first:
uv sync # generates uv.lock
cd frontend && pnpm install # generates pnpm-lock.yaml
Skip regeneration only if you haven't changed dependencies.
Port Mapping
Host ports differ from container ports:
| Service | Host Port | Container Port |
|---|---|---|
| Backend | 8001 | 8000 |
| Frontend | 5174 | 5173 |
VITE_API_URL Is Build-Time Only
VITE_API_URL is passed as a Docker build arg (http://backend:8000) and baked into the frontend bundle at build time. It is NOT a runtime environment variable — changing .env after building has no effect. Rebuild the frontend container to pick up URL changes.
Frontend: Preview Mode, No HMR
docker-compose.yml runs npx vite preview, serving the production build from dist/. There is no hot module reload. For active frontend development, rebuild after every change:
docker compose down frontend && docker compose up -d frontend --build
The ../frontend:/app/frontend volume mount in docker-compose has no effect in preview mode (serves from baked-in dist/).
Backend Entry Point
Runs as python -m src.main --port 8000 inside the container. The --port 8000 flag is hardcoded in docker-compose (mapped to 8001 on host).
SQLite Cache
Backend caches Congress.gov API responses and GPT-OSS summaries in SQLite at data/voting_app.db. The data/ directory is mounted as a Docker volume to /app/data. Not committed to git. Delete stale DB after schema changes (e.g. adding start_date column).
Required External Services
- Congress.gov API key: Set
CONGRESS_API_KEYin.env - Local GPT-OSS server: Set
GPT_OSS_BASE_URLin.env(e.g., LM Studio, Ollama, vLLM)
Quality Gate Commands
Run before building any container:
# Backend
ruff format .
ruff check --fix .
# Frontend
cd frontend && pnpm lint --fix
cd frontend && tsc
Ruff: line-length 120, rules E W F I N UP B SIM.
Frontend ESLint: @typescript-eslint/no-unused-vars and no-explicit-any are warn.
Frontend pnpm build runs tsc && vite build — TypeScript check gates the build.
Testing
106 tests total: 103 unit/E2E tests (mocked services) + 3 integration tests (real API). All pass.
# Unit + E2E tests (fast, mocked)
uv run pytest tests/unit/ tests/e2e/
# Integration tests (slow, real API — requires CONGRESS_API_KEY)
uv run pytest tests/integration/
# In container
docker compose exec backend python -m pytest tests/unit/ tests/e2e/
The integration pytest mark is registered in pyproject.toml. Integration tests require a valid CONGRESS_API_KEY in .env and skip if unset. Senate vote fetching uses XML scanning from senate.gov and can take 2-4 minutes per legislator.
Backend Dockerfile Note
The backend Dockerfile installs uv via pip (the python:3.12-slim image doesn't include it). Build arg VITE_API_URL is set via docker-compose, not .env.
Build Context
.dockerignore excludes tests, tools, additionaldocs, data, and .env from Docker build context. A .dockerignore.backend file exists in docker/ but is not used by Docker (context is project root).