|
| 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 |
0 commit comments