Skip to content

Commit ede5ae0

Browse files
mfosterwclaude
andauthored
Add Claude Code configuration and skills (#291)
* Add Claude Code base configuration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add Claude Code skill definitions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cea6514 commit ede5ae0

3 files changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
name: new-bill-feature
3+
description: Guide for adding a new feature to the Bill/voting system in webiscite
4+
disable-model-invocation: true
5+
argument-hint: "[description of the feature]"
6+
---
7+
8+
## Task
9+
Implement a new feature for the Bill/voting system: $ARGUMENTS
10+
11+
## Checklist of files to consider
12+
13+
The Bill system spans these files. Review each to determine if it needs changes:
14+
15+
**Models & Logic:**
16+
- `democrasite/webiscite/models.py` — Bill, Vote, PullRequest models (fields, `vote()`, `submit()`, `_check_approval()`, Status choices)
17+
- `democrasite/webiscite/managers.py``BillManager.create_from_github()`, queryset annotations (yes_percent, no_percent, user_vote)
18+
- `democrasite/webiscite/constitution.py``is_constitutional()`, `update_constitution()` for protected file ranges
19+
- `democrasite/webiscite/tasks.py``submit_bill()` Celery task (approval check → GitHub merge → constitution update)
20+
- `democrasite/webiscite/webhooks.py``PullRequestHandler` (opened/reopened/closed) and HMAC-validated `GithubWebhookView`
21+
22+
**Views & API:**
23+
- `democrasite/webiscite/views.py` — BillListView, BillDetailView, BillUpdateView, vote_view (AJAX POST)
24+
- `democrasite/webiscite/api/views.py` — BillViewSet (list/retrieve/update + vote action), IsAuthorOrReadOnly permission
25+
- `democrasite/webiscite/api/serializers.py` — BillSerializer, PullRequestSerializer
26+
- `democrasite/webiscite/urls.py` — Template view URL patterns
27+
- `config/api_router.py` — DRF router registration
28+
29+
**Templates & Frontend:**
30+
- `democrasite/templates/webiscite/bill_list.html` — Card grid of bills
31+
- `democrasite/templates/webiscite/bill_detail.html` — Single bill page
32+
- `democrasite/templates/webiscite/snippets/vote.html` — Vote progress bar + yes/no buttons
33+
- `democrasite/templates/webiscite/bill_form.html` — Edit form (name, description)
34+
- `democrasite/static/js/vote.js` — AJAX vote handler, DOM updates for counts and progress bar
35+
36+
**Tests (mirror each area above):**
37+
- `democrasite/webiscite/tests/test_models.py`
38+
- `democrasite/webiscite/tests/test_views.py`
39+
- `democrasite/webiscite/tests/test_tasks.py`
40+
- `democrasite/webiscite/tests/test_webhooks.py`
41+
- `democrasite/webiscite/tests/test_constitution.py`
42+
- `democrasite/webiscite/tests/test_templates.py`
43+
- `democrasite/webiscite/tests/factories.py` — PullRequestFactory, BillFactory, TaskFactory
44+
45+
**Config & Admin:**
46+
- `democrasite/webiscite/admin.py` — SimpleHistoryAdmin for Bill, PullRequest
47+
- `democrasite/webiscite/context_processors.py` — Exposes `github_repo` to templates
48+
- `config/settings/base.py` — WEBISCITE_* settings (quorum, majority thresholds, voting period, GitHub token/repo)
49+
50+
## Key patterns to follow
51+
- Bill status choices: DRAFT, OPEN, APPROVED, REJECTED, FAILED, CLOSED
52+
- Votes are M2M through Vote model with unique constraint on (bill, user)
53+
- Bill.vote() toggles existing votes; raises ClosedBillVoteError if bill not OPEN
54+
- Constitutional bills need WEBISCITE_SUPERMAJORITY (66.67%), normal need WEBISCITE_NORMAL_MAJORITY (50%)
55+
- Each Bill has a OneToOne PeriodicTask for scheduled submission
56+
- Managers annotate querysets with vote percentages and user vote status
57+
- API vote endpoint expects {"support": true/false}, template vote_view expects POST with "vote" field
58+
- django-simple-history tracks Bill and Vote changes automatically
59+
- PullRequest has a `draft` boolean field tracking GitHub's draft state
60+
- Draft bills (from draft PRs) cannot be voted on or submitted; they transition to OPEN via Bill.publish() when the PR is marked ready for review
61+
- The `unique_active_pull_request` constraint prevents duplicate bills for the same PR in both `open` and `draft` statuses
62+
- PullRequest.close() closes both open and draft bills
63+
- The submit PeriodicTask is created disabled for draft bills; Bill.publish() enables it and resets last_run_at so the voting period starts from publication
64+
- GitHub's `ready_for_review` webhook action triggers PullRequestHandler.ready_for_review(), which updates the PR and publishes the draft bill
65+
66+
## Steps
67+
1. Read the relevant files from the checklist above
68+
2. Plan the changes needed across all layers (model → serializer → view → template → test)
69+
3. If adding a model field, create a migration with `just manage makemigrations`
70+
4. Implement changes
71+
5. Update or add factories in factories.py for any new model fields
72+
6. Write tests covering the new functionality
73+
7. Run `just run pytest democrasite/webiscite/tests/` to verify
74+
8. Run `just lint` to check style
75+
9. Update documentation:
76+
- Update the "Key patterns to follow" section in this skill file (`.claude/skills/new-bill-feature/SKILL.md`) with the new feature's patterns
77+
- Update `docs/webiscite.rst` if the feature changes the pull request processing pipeline or bill lifecycle
78+
- If files were created or deleted, run `just run make -C docs apidocs` to regenerate API docs
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
name: test-webhook
3+
description: Set up smee to forward GitHub webhooks to local Django for manual testing
4+
disable-model-invocation: true
5+
argument-hint: https://smee.io/2ckdUNpB3Qt0UvE7
6+
---
7+
8+
## Task
9+
Start a smee webhook proxy to forward GitHub webhook events to the local Django server for manual testing.
10+
11+
## Prerequisites
12+
- `smee` CLI installed (`npm install -g smee-client`)
13+
- Docker containers running (`just up`)
14+
- A smee channel URL from https://smee.io or a personal one
15+
- A GitHub webhook configured on your fork pointing to the smee URL, with the `GITHUB_WEBHOOK_SECRET` matching your `.envs/.local/.django` file
16+
17+
## Steps
18+
19+
1. Parse the smee URL from `$ARGUMENTS`
20+
2. Verify containers are running with `just up`
21+
3. Start smee in the background:
22+
```
23+
smee --url <SMEE_URL> --path /hooks/github/ --port 8000
24+
```
25+
4. Confirm smee connects successfully
26+
5. Inform the user they are ready to test by creating or updating pull requests on their GitHub fork
27+
28+
## Configuration reference
29+
- Webhook path: `/hooks/github/`
30+
- Local port: `8000` (Docker maps this to the Django container)
31+
- Webhook secret env var: `GITHUB_WEBHOOK_SECRET` in `.envs/.local/.django`
32+
- Django setting: `WEBISCITE_GITHUB_WEBHOOK_SECRET` in `config/settings/base.py`
33+
- Webhook handler: `democrasite/webiscite/webhooks.py``GithubWebhookView`
34+
- Supported events: `pull_request` (opened, reopened, closed, ready_for_review), `push`, `ping`
35+
36+
## Troubleshooting
37+
- If webhook returns 403: check that `GITHUB_WEBHOOK_SECRET` matches the secret configured on the GitHub webhook
38+
- If webhook returns 400: check the `x-github-event` and `x-hub-signature-256` headers are present
39+
- If smee doesn't connect: verify the smee URL is correct and the channel exists
40+
- Check Django logs with `docker compose -f docker-compose.local.yml logs django -f` for webhook processing errors

CLAUDE.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
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%).
8+
9+
## Tech Stack
10+
11+
- **Python 3.12 / Django 5.x** with PostgreSQL, Redis, Celery
12+
- **Docker Compose** for local development (all commands run inside containers)
13+
- **Django REST Framework** with JWT auth and drf-spectacular for API docs
14+
- **django-allauth** for GitHub/Google OAuth
15+
- **Ruff** (linter/formatter), **mypy** (type checker), **djLint** (template linter), **pre-commit** hooks
16+
17+
## Common Commands
18+
19+
All development uses Docker. The `justfile` sets `COMPOSE_FILE=docker-compose.local.yml` automatically.
20+
21+
```bash
22+
just build # Build Docker images
23+
just up # Start all containers
24+
just down # Stop containers
25+
just test # Run pytest suite
26+
just lint # Run pre-commit hooks
27+
just typecheck # Run mypy
28+
just manage <args> # Run manage.py (e.g., just manage makemigrations)
29+
just migrate # makemigrations + migrate
30+
just coverage # Run tests with coverage + open HTML report
31+
just shell # Bash shell in django container
32+
just pyshell # Django shell_plus (IPython)
33+
just run <cmd> # Execute arbitrary command in django container
34+
just loaddata # Load fixtures (democrasite + activitypub)
35+
```
36+
37+
To run a single test file or test:
38+
```bash
39+
just run pytest democrasite/webiscite/tests/test_models.py
40+
just run pytest democrasite/webiscite/tests/test_models.py::TestBill::test_method_name -v
41+
```
42+
43+
Pytest is configured with `--ds=config.settings.test --reuse-db` in `pyproject.toml`.
44+
45+
## Architecture
46+
47+
### Django Apps
48+
49+
- **`democrasite/webiscite/`** — Core app. Models: `PullRequest`, `Bill`, `Vote`. Handles GitHub webhooks, voting logic, constitution enforcement, and Celery tasks for auto-merging.
50+
- **`democrasite/users/`** — Custom `User` model (extends `AbstractUser` with single `name` field instead of first/last). OAuth integration.
51+
- **`democrasite/activitypub/`** — ActivityPub federation. Models: `Person` (linked to User with keypair), `Follow`.
52+
53+
### Configuration
54+
55+
- **`config/settings/`** — Split settings: `base.py`, `local.py`, `production.py`, `test.py`
56+
- **`config/urls.py`** — URL routing. Admin is only enabled in DEBUG mode.
57+
- **`config/api_router.py`** — DRF router registering `UserViewSet` and `BillViewSet`
58+
- **`config/celery_app.py`** — Celery configuration with Redis backend
59+
60+
### Key Patterns
61+
62+
- **Constitution system** (`webiscite/constitution.py`, `constitution.json`): Maps files to protected line ranges. PRs touching protected code become "constitutional" bills requiring supermajority. Line numbers auto-update after merges.
63+
- **Bill lifecycle**: OPEN → APPROVED/REJECTED/FAILED/CLOSED. Each Bill has a OneToOne to a Celery `PeriodicTask` that runs `submit_bill()` at voting period end.
64+
- **Webhook flow** (`webiscite/webhooks.py`): GitHub push events → HMAC validation → create/update `PullRequest` → create `Bill`.
65+
- **Vote constraints**: One vote per user per bill (DB unique constraint). Votes can be changed.
66+
- **Audit trail**: `django-simple-history` tracks changes on key models.
67+
68+
### API
69+
70+
- REST API at `/api/` with Swagger docs at `/api/docs/`
71+
- Auth: session, token, JWT (`/api/token/refresh/`), GitHub OAuth (`/api/auth/github/`)
72+
73+
## Code Style
74+
75+
- **Ruff**: 88-char lines, force single-line imports, double quotes, spaces for indentation
76+
- **djLint**: Django template profile, 119-char max line length, 2-space indent
77+
- **mypy**: Strict-ish config with Django/DRF stubs, migrations ignored
78+
- Env files live in `.envs/` (copied from `.envs.template/` on setup)

0 commit comments

Comments
 (0)