This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Democrasite is a Django web application that implements democratic voting on GitHub pull requests. Users vote on "bills" (proposals to merge PRs), and approved bills are automatically merged. Core functionality is protected by a "constitution" system that requires supermajority votes (66.67%) to modify protected files/line ranges, while normal bills need simple majority (>50%).
- Python 3.13 / Django 5.x with PostgreSQL, Redis, Celery
- Docker Compose for local development (all commands run inside containers)
- Django REST Framework with JWT auth and drf-spectacular for API docs
- django-allauth for GitHub/Google OAuth
- Ruff (linter/formatter), mypy (type checker), djLint (template linter), pre-commit hooks
All development uses Docker. The justfile sets COMPOSE_FILE=docker-compose.local.yml automatically.
just build # Build Docker images
just up # Start all containers
just down # Stop containers
just test <args> # Run pytest suite
just lint # Run pre-commit hooks
just typecheck # Run mypy
just manage <args> # Run manage.py (e.g., just manage makemigrations)
just migrate # makemigrations + migrate
just coverage # Run tests with coverage + open HTML report
just shell # Bash shell in django container
just pyshell # Django shell_plus (IPython)
just run <cmd> # Execute arbitrary command in django container
just loaddata # Load fixtures (democrasite + social)To run a single test:
just test democrasite/webiscite/tests/test_models.py::TestBill::test_method_name -vFor any just command, prefer running outside the sandbox to avoid docker.sock
permission errors. If just is not found, run conda env democrasite first.
Pytest is configured with --ds=config.settings.test --reuse-db in pyproject.toml.
democrasite/webiscite/— Core app. Models:PullRequest,Bill,Vote. Handles GitHub webhooks, voting logic, constitution enforcement, and Celery tasks for auto-merging.democrasite/users/— CustomUsermodel (extendsAbstractUserwith singlenamefield instead of first/last). OAuth integration.democrasite/social/— Social network for short notes. Models:Person(linked to User with keypair),Follow.
config/settings/— Split settings:base.py,local.py,production.py,test.pyconfig/urls.py— URL routing. Admin is only enabled in DEBUG mode.config/api_router.py— DRF router registeringUserViewSetandBillViewSetconfig/celery_app.py— Celery configuration with Redis backend
- Constitution system (
democrasite/webiscite/constitution.py, repo-rootconstitution.json): Maps files to protected line ranges. PRs touching protected code become "constitutional" bills requiring supermajority. Line numbers auto-update after merges. - Bill lifecycle: Draft PRs yield
DRAFTbills untilready_for_review; thenOPEN→ APPROVED/REJECTED/FAILED/CLOSED (orAMENDEDif the PR branch changes during voting). Each Bill has a OneToOne to a Celery BeatPeriodicTaskthat runssubmit_bill()at voting period end. - Webhook flow (
democrasite/webiscite/webhooks.py): GitHubpull_requestwebhooks (open, reopen, close, synchronize, ready_for_review, etc.) → HMAC validation → create/updatePullRequestandBillas appropriate.pushandpingare accepted with minimal handling. - Vote constraints: One vote per user per bill (DB unique constraint). Votes can be changed.
- Audit trail:
django-simple-historytracks changes on key models.
- REST API at
/api/with Swagger docs at/api/docs/ - Auth: session, token, JWT (
/api/token/refresh/), GitHub OAuth (/api/auth/github/)
- Ruff: 88-char lines, force single-line imports, double quotes, spaces for indentation
- djLint: Django template profile, 119-char max line length, 2-space indent
- mypy: Strict-ish config with Django/DRF stubs, migrations ignored
- Env files live in
.envs/(copied from.envs.template/on setup)