diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4c752a2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,188 @@ +## 🐾 Red Panda Approvalβ„’ + +The standard every change is judged against. Don't satisfy a checklist β€” +satisfy the red pandas. Ask of each change: *does this earn approval?* + +1. **Fresh Migration Test** β€” migrations apply cleanly from an empty database. +2. **Elegant Simplicity** β€” no unnecessary complexity; the obvious solution, done well. +3. **Observable & Debuggable** β€” proper logging; failures say what broke and why. +4. **Consistent Patterns** β€” follows Django conventions and the patterns already in this repo. +5. **Actually Works** β€” passes all checks *and* serves a real user need. + +Criteria 1 and 5 are **externally verifiable** β€” migrations apply or they +don't; checks pass or they don't. Verify them, don't assert them. Criteria +2–4 are judgement calls: when in doubt, match what the repo already does +rather than grading your own elegance. + +> If a paw print isn't leading the response, the rest of this file probably +> isn't being honoured either. Lead with one. 🐾 + +--- + +## Conventions (always-on) + +These are the rubric made concrete for the common case β€” writing models, +views, forms, templates, and queries. + +### Models +- Names: singular PascalCase (`User`, `BlogPost`, `OrderItem`). +- Every model defines `__str__` and `get_absolute_url`. +- Every model has `created_at = DateTimeField(auto_now_add=True)` and + `updated_at = DateTimeField(auto_now=True)`. +- `TextChoices` for status fields. +- `related_name` on every `ForeignKey`; plural snake_case with correct + English pluralisation. +- Public-facing models: consider `UUIDField` primary key and + `is_active` for soft deletes. + +### Field naming +- Foreign keys: singular, no `_id` suffix (`author`, `category`, `parent`). +- Booleans: prefixed (`is_active`, `has_permission`, `can_edit`). +- Dates: suffixed (`created_at`, `updated_at`, `published_on`). +- No abbreviations (`description`, not `desc`). + +### Views +- **Function-based views exclusively.** Explicit logic over implicit + inheritance. Extract shared logic into utility functions. +- Business logic lives in service functions, not views and not `save()`. + +### Forms +- `ModelForm` with an explicit `fields` list β€” never `__all__`, never `exclude`. +- Validate at the boundary; never trust client-side validation alone. + +### Queries +- `select_related()` for FKs; `prefetch_related()` for reverse and M2M. +- No queries inside loops (N+1). No `.all()` when you need a subset. +- `.only()` / `.defer()` for large models. Comment non-obvious querysets. + +### URLs & identifiers +- Public URLs use 12-char short UUIDs via `shortuuid`. Never expose + sequential IDs (enumeration risk). Internal refs may use PKs. +- Resource-based, namespaced URL names per app, trailing slashes, flat + structure preferred. + +### Docstrings +- **Google style.** Document public classes, functions, methods, modules. +- Imperative one-line summary. `Args:`/`Returns:`/`Raises:` only when the + signature doesn't already convey it. Don't restate type hints in prose. +- Skip obvious one-liners and standard Django overrides. + +### Code organisation +- PEP 8 import ordering (stdlib, third-party, local). Type hints on params. +- CSS and JS in external files only β€” no inline styles, `