cc-codeconductor 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +2642 -226
  3. package/package.json +1 -1
  4. package/presets/agy/skills/evaluation/SKILL.md +6 -0
  5. package/presets/agy/skills/openspec/SKILL.md +32 -0
  6. package/presets/agy/workflows/cc-openspec.md +62 -0
  7. package/presets/agy/workflows/cc-scorecard.md +17 -0
  8. package/presets/claude/commands/cc/openspec.md +144 -0
  9. package/presets/claude/commands/cc/review.md +13 -2
  10. package/presets/claude/commands/cc/scorecard.md +65 -0
  11. package/presets/claude/skills/evaluation/SKILL.md +42 -0
  12. package/presets/claude/skills/openspec/SKILL.md +54 -0
  13. package/presets/codex/AGENTS.md +57 -0
  14. package/presets/cursor/.cursorignore +15 -0
  15. package/presets/cursor/AGENTS.md +504 -0
  16. package/presets/cursor/agents/architect.md +211 -0
  17. package/presets/cursor/agents/complexity-auditor.md +76 -0
  18. package/presets/cursor/agents/contract-builder.md +75 -0
  19. package/presets/cursor/agents/docs.md +180 -0
  20. package/presets/cursor/agents/goal-planner.md +71 -0
  21. package/presets/cursor/agents/implementer.md +161 -0
  22. package/presets/cursor/agents/orchestrator.md +377 -0
  23. package/presets/cursor/agents/repo-explorer.md +100 -0
  24. package/presets/cursor/agents/reviewer.md +237 -0
  25. package/presets/cursor/agents/security-reviewer.md +113 -0
  26. package/presets/cursor/agents/task-coach.md +145 -0
  27. package/presets/cursor/agents/tester.md +241 -0
  28. package/presets/cursor/commands/cc/api-contract.md +58 -0
  29. package/presets/cursor/commands/cc/db-migration.md +58 -0
  30. package/presets/cursor/commands/cc/feature.md +115 -0
  31. package/presets/cursor/commands/cc/fix.md +121 -0
  32. package/presets/cursor/commands/cc/openspec.md +144 -0
  33. package/presets/cursor/commands/cc/pagespeed.md +103 -0
  34. package/presets/cursor/commands/cc/refactor.md +148 -0
  35. package/presets/cursor/commands/cc/review.md +137 -0
  36. package/presets/cursor/commands/cc/scorecard.md +65 -0
  37. package/presets/cursor/commands/cc/tdd-cycle.md +226 -0
  38. package/presets/cursor/commands/cc/test-plan.md +138 -0
  39. package/presets/cursor/rules/behavioral-discipline.mdc +14 -0
  40. package/presets/cursor/rules/context-budget.mdc +12 -0
  41. package/presets/cursor/rules/orchestration.mdc +12 -0
  42. package/presets/cursor/rules/yagni-stdlib.mdc +11 -0
  43. package/presets/cursor/skills/android/SKILL.md +122 -0
  44. package/presets/cursor/skills/api-versioning/SKILL.md +394 -0
  45. package/presets/cursor/skills/astro/SKILL.md +322 -0
  46. package/presets/cursor/skills/auth-token-inspector/SKILL.md +33 -0
  47. package/presets/cursor/skills/code-review/SKILL.md +208 -0
  48. package/presets/cursor/skills/conductor-setup/SKILL.md +127 -0
  49. package/presets/cursor/skills/django-orm/SKILL.md +463 -0
  50. package/presets/cursor/skills/django-testing/SKILL.md +417 -0
  51. package/presets/cursor/skills/django-uv/SKILL.md +409 -0
  52. package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +54 -0
  53. package/presets/cursor/skills/evaluation/SKILL.md +8 -0
  54. package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +46 -0
  55. package/presets/cursor/skills/find-skills/SKILL.md +144 -0
  56. package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +49 -0
  57. package/presets/cursor/skills/jpa-postgres/SKILL.md +626 -0
  58. package/presets/cursor/skills/laravel-specialist/SKILL.md +267 -0
  59. package/presets/cursor/skills/laravel-specialist/references/eloquent.md +351 -0
  60. package/presets/cursor/skills/laravel-specialist/references/livewire.md +512 -0
  61. package/presets/cursor/skills/laravel-specialist/references/queues.md +423 -0
  62. package/presets/cursor/skills/laravel-specialist/references/routing.md +362 -0
  63. package/presets/cursor/skills/laravel-specialist/references/testing.md +522 -0
  64. package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +39 -0
  65. package/presets/cursor/skills/multi-agent-orchestration/README.md +144 -0
  66. package/presets/cursor/skills/multi-agent-orchestration/SKILL.md +579 -0
  67. package/presets/cursor/skills/multi-agent-orchestration/examples/framework_implementations.py +362 -0
  68. package/presets/cursor/skills/multi-agent-orchestration/examples/orchestration_patterns.py +411 -0
  69. package/presets/cursor/skills/multi-agent-orchestration/scripts/agent_communication.py +334 -0
  70. package/presets/cursor/skills/multi-agent-orchestration/scripts/benchmarking.py +341 -0
  71. package/presets/cursor/skills/multi-agent-orchestration/scripts/workflow_management.py +334 -0
  72. package/presets/cursor/skills/nextjs-typescript/SKILL.md +394 -0
  73. package/presets/cursor/skills/openspec/SKILL.md +52 -0
  74. package/presets/cursor/skills/pagespeed-insights/SKILL.md +445 -0
  75. package/presets/cursor/skills/pagespeed-insights/reference.md +50 -0
  76. package/presets/cursor/skills/pagespeed-perf/SKILL.md +281 -0
  77. package/presets/cursor/skills/php-pro/SKILL.md +210 -0
  78. package/presets/cursor/skills/php-pro/references/async-patterns.md +412 -0
  79. package/presets/cursor/skills/php-pro/references/laravel-patterns.md +377 -0
  80. package/presets/cursor/skills/php-pro/references/modern-php-features.md +323 -0
  81. package/presets/cursor/skills/php-pro/references/symfony-patterns.md +466 -0
  82. package/presets/cursor/skills/php-pro/references/testing-quality.md +466 -0
  83. package/presets/cursor/skills/python/SKILL.md +613 -0
  84. package/presets/cursor/skills/python-django-stack/SKILL.md +500 -0
  85. package/presets/cursor/skills/python-fastapi-stack/SKILL.md +464 -0
  86. package/presets/cursor/skills/security/SKILL.md +384 -0
  87. package/presets/cursor/skills/seo-analytics-injector/SKILL.md +44 -0
  88. package/presets/cursor/skills/spring-auth-auditor/SKILL.md +33 -0
  89. package/presets/cursor/skills/spring-boot-feature/SKILL.md +566 -0
  90. package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +408 -0
  91. package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +479 -0
  92. package/presets/cursor/skills/sqlalchemy/SKILL.md +473 -0
  93. package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +32 -0
  94. package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +28 -0
  95. package/presets/cursor/skills/testing-tdd/SKILL.md +592 -0
  96. package/presets/cursor/skills/workflow-orchestration-patterns/SKILL.md +98 -0
  97. package/presets/cursor/skills/workflow-orchestration-patterns/references/details.md +223 -0
  98. package/presets/opencode/agents/architect.md +1 -2
  99. package/presets/opencode/agents/complexity-auditor.md +1 -0
  100. package/presets/opencode/agents/contract-builder.md +93 -0
  101. package/presets/opencode/agents/docs.md +1 -2
  102. package/presets/opencode/agents/goal-planner.md +82 -0
  103. package/presets/opencode/agents/implementer.md +9 -2
  104. package/presets/opencode/agents/orchestrator.md +50 -12
  105. package/presets/opencode/agents/repo-explorer.md +0 -1
  106. package/presets/opencode/agents/reviewer.md +23 -2
  107. package/presets/opencode/agents/security-reviewer.md +129 -0
  108. package/presets/opencode/agents/task-coach.md +1 -2
  109. package/presets/opencode/agents/tester.md +1 -2
  110. package/presets/opencode/commands/cc-openspec.md +61 -0
  111. package/presets/opencode/commands/cc-scorecard.md +16 -0
  112. package/presets/opencode/prompts/v0.5.0/architect.md +222 -0
  113. package/presets/opencode/prompts/v0.5.0/complexity-auditor.md +91 -0
  114. package/presets/opencode/prompts/v0.5.0/contract-builder.md +84 -0
  115. package/presets/opencode/prompts/v0.5.0/docs.md +190 -0
  116. package/presets/opencode/prompts/v0.5.0/goal-planner.md +80 -0
  117. package/presets/opencode/prompts/v0.5.0/implementer.md +171 -0
  118. package/presets/opencode/prompts/v0.5.0/orchestrator.md +388 -0
  119. package/presets/opencode/prompts/v0.5.0/repo-explorer.md +111 -0
  120. package/presets/opencode/prompts/v0.5.0/reviewer.md +248 -0
  121. package/presets/opencode/prompts/v0.5.0/security-reviewer.md +123 -0
  122. package/presets/opencode/prompts/v0.5.0/task-coach.md +156 -0
  123. package/presets/opencode/prompts/v0.5.0/tester.md +252 -0
  124. package/presets/opencode/skills/evaluation/SKILL.md +6 -0
  125. package/presets/opencode/skills/openspec/SKILL.md +50 -0
  126. package/presets/templates/BACKLOG.md +33 -0
  127. package/presets/templates/execution-profile.yml +6 -0
  128. package/presets/templates/model-comparison.md +11 -0
  129. package/presets/templates/regression-checklist.yml +10 -0
  130. package/src/presets/manifests/agy.yml +2 -2
  131. package/src/presets/manifests/claude.yml +2 -2
  132. package/src/presets/manifests/codex.yml +2 -2
  133. package/src/presets/manifests/cursor.yml +19 -3
  134. package/src/presets/manifests/gemini.yml +2 -2
  135. package/src/presets/manifests/opencode.yml +2 -2
  136. package/src/presets/models/agy.yml +21 -0
  137. package/src/presets/models/claude.yml +18 -0
  138. package/src/presets/models/codex.yml +18 -0
  139. package/src/presets/models/cursor.yml +39 -9
  140. package/src/presets/models/gemini.yml +18 -0
  141. package/src/presets/models/opencode.yml +18 -0
@@ -0,0 +1,500 @@
1
+ ---
2
+ id: python-django-stack
3
+ version: 1.0.0
4
+ name: Python Django Stack
5
+ description: >
6
+ Python and Django conventions for multi-tenant SaaS POS projects:
7
+ views, services, naming, JSON APIs, PDFs, and cart patterns.
8
+ Trigger: When writing any view, service, API endpoint, or model code in apps/.
9
+
10
+ user-invokable: true
11
+ license: MIT
12
+ metadata:
13
+ author: lgzarturo
14
+ category: django
15
+
16
+ compatibility:
17
+ tools: [claude, codex, gemini, agy, opencode]
18
+ stacks:
19
+ languages: [python]
20
+ frameworks: [django, django-tenants, django-rest-framework, reportlab]
21
+
22
+ risk:
23
+ level: medium
24
+ can_execute_shell: true
25
+ can_modify_files: true
26
+ requires_network: false
27
+
28
+ inputs:
29
+ - view files (views.py)
30
+ - service files (services.py)
31
+ - model files (models.py)
32
+ - URL configuration files (urls.py)
33
+ - existing app structure under apps/
34
+
35
+ outputs:
36
+ - FBV and CBV view implementations
37
+ - service layer classes with static methods
38
+ - JSON API responses with correct status codes
39
+ - pagination with offset/limit pattern
40
+ - PDF generation functions (reportlab)
41
+ - cart session implementations
42
+ - URL configurations with namespaces
43
+ - model definitions with indexes and choices
44
+
45
+ quality:
46
+ reviewed_by: codeconductor-core
47
+ version: 0.1.0
48
+ paths:
49
+ - "**/manage.py"
50
+ - "**/*.py"
51
+ ---
52
+ ## When to Use
53
+
54
+ - Writing any new view, service method, or API endpoint
55
+ - Designing a new feature and deciding where logic lives
56
+ - Adding a JSON API response
57
+ - Implementing a PDF report or cart operation
58
+ - Structuring the architecture of a new app
59
+
60
+ ## Django Project Structure
61
+
62
+ ### Recommended Apps
63
+
64
+ A well-structured Django project follows modular app patterns:
65
+
66
+ ```
67
+ apps/
68
+ ├── core/ # Configuration, shared models (Store)
69
+ ├── users/ # User model, authentication
70
+ ├── employees/ # Employees (tenant)
71
+ ├── catalog/ # Products, categories (tenant)
72
+ ├── cart/ # Storefront cart (tenant)
73
+ ├── orders/ # Orders (tenant)
74
+ ├── pos/ # Point of Sale (tenant)
75
+ ├── storefront/ # Public store (tenant)
76
+ └── analytics/ # Reports and metrics (tenant)
77
+ ```
78
+
79
+ ### Per-App File Structure
80
+
81
+ ```
82
+ apps/catalog/
83
+ ├── __init__.py
84
+ ├── models.py # Models (Product, Category, ProductImage)
85
+ ├── views.py # Views
86
+ ├── urls.py # Routes
87
+ ├── services.py # Business logic
88
+ ├── serializers.py # Serialization (if using DRF)
89
+ ├── reports.py # PDF reports
90
+ ├── signals.py # Signals
91
+ ├── admin.py # Admin config
92
+ ├── tests/
93
+ │ ├── __init__.py
94
+ │ └── test_{feature}.py
95
+ └── migrations/
96
+ ```
97
+
98
+ ## View Patterns
99
+
100
+ ### FBV vs CBV
101
+
102
+ | Type | When to Use | Examples |
103
+ | ------------------------ | ---------------------------------------------------------------------- | ----------------------------------------------------- |
104
+ | **FBV** (Function-Based) | All API endpoints, all POS/manager views, any view with business logic | `api_cart_add`, `api_search_products`, `pos_checkout` |
105
+ | **CBV** (Class-Based) | Only basic storefront list/detail | `HomeView`, `CategoryListView`, `ProductDetailView` |
106
+
107
+ **Rule**: CBV for generic read-only views with simple templates. FBV for
108
+ everything else (APIs, complex logic, POS).
109
+
110
+ ```python
111
+ # FBV — always for APIs
112
+ @require_pos_access
113
+ @require_POST
114
+ def api_cart_add(request):
115
+ cart = POSCart(request)
116
+ data = json.loads(request.body)
117
+ cart.add(data["product_id"], data["quantity"])
118
+ return JsonResponse({"success": True, "cart_count": cart.count()})
119
+
120
+ # CBV — only for basic storefront
121
+ class ProductDetailView(TemplateView):
122
+ template_name = "storefront/product.html"
123
+
124
+ def get_context_data(self, **kwargs):
125
+ ctx = super().get_context_data(**kwargs)
126
+ ctx["product"] = get_object_or_404(Product, slug=self.kwargs["slug"])
127
+ return ctx
128
+ ```
129
+
130
+ ### Decorator Order
131
+
132
+ Custom access decorator outermost, HTTP method decorator innermost:
133
+
134
+ ```python
135
+ @require_pos_access # outermost — runs first, guards access
136
+ @require_POST # innermost — closest to the function
137
+ def api_cart_add(request):
138
+ ...
139
+ ```
140
+
141
+ **Always use** `@require_POST` / `@require_GET` explicitly. Never
142
+ `if request.method == "POST"` inside an API view.
143
+
144
+ ## Service Layer
145
+
146
+ Business logic lives in `services.py` modules. Services are classes with
147
+ `@staticmethod` only — no instance state, no `__init__`:
148
+
149
+ ```python
150
+ class OrderService:
151
+ @staticmethod
152
+ def create_order(order: Order, cart: Cart) -> Order:
153
+ with transaction.atomic():
154
+ order.save()
155
+ OrderItem.objects.bulk_create(items)
156
+ Product.objects.bulk_update(products, ["stock"])
157
+ cart.clear()
158
+ return order
159
+
160
+ @staticmethod
161
+ def cancel_order(order_id: int, reason: str) -> Order:
162
+ ...
163
+ ```
164
+
165
+ Views call `OrderService.create_order(...)`. **No business logic in views.**
166
+
167
+ ### When to Create a Service
168
+
169
+ - Complex business logic involving multiple models
170
+ - Transactions spanning multiple DB operations
171
+ - Logic reusable across multiple views
172
+ - Anything that isn't "delegate to another component"
173
+
174
+ ## Naming Conventions
175
+
176
+ | Pattern | Convention | Examples |
177
+ | ---------------------- | -------------------------------------------- | --------------------------------------------------------------- |
178
+ | AJAX API views | `api_{resource}_{action}` | `api_cart_add`, `api_search_products`, `api_temporal_cart_save` |
179
+ | Module-private helpers | leading `_` | `_build_pos_order`, `_sort_by_category` |
180
+ | Module constants | `UPPER_SNAKE_CASE` | `VALID_PAYMENT_METHODS = {"cash", "transfer", "card"}` |
181
+ | URL names | `{scope}_{resource}_{action}` | `api_catalog_products`, `pos_checkout` |
182
+ | Service methods | `camelCase` (PEP8 inconsistent but accepted) | `createOrder`, `calculateTotal` |
183
+ | Model methods | `snake_case` | `get_display_price()`, `is_available()` |
184
+
185
+ ## JSON API Response Shapes
186
+
187
+ ### List Endpoint with Pagination
188
+
189
+ ```python
190
+ return JsonResponse({
191
+ "results": [...],
192
+ "total": N,
193
+ "has_more": bool,
194
+ })
195
+ ```
196
+
197
+ ### Successful Mutation
198
+
199
+ ```python
200
+ return JsonResponse({"success": True, "cart_count": N})
201
+ ```
202
+
203
+ ### Error Response
204
+
205
+ ```python
206
+ return JsonResponse({"success": False, "error": "error message"}, status=400)
207
+ ```
208
+
209
+ ### HTTP Status Codes
210
+
211
+ - `200` — OK, success
212
+ - `201` — Created
213
+ - `400` — Client error (invalid data, missing fields)
214
+ - `404` — Not found
215
+ - `500` — Server error
216
+
217
+ **Never return** `200` with `"success": false`. Use the correct status code.
218
+
219
+ ## Pagination
220
+
221
+ ### Offset/Limit — No Page Numbers
222
+
223
+ Always offset + limit, never page numbers. Default 20 items, max 100:
224
+
225
+ ```python
226
+ offset = int(request.GET.get("offset", 0))
227
+ limit = min(int(request.GET.get("limit", 20)), 100)
228
+
229
+ total = qs.count()
230
+ page = qs[offset:offset + limit]
231
+
232
+ return JsonResponse({
233
+ "results": list(page.values("id", "name", "price")),
234
+ "total": total,
235
+ "has_more": (offset + limit) < total,
236
+ })
237
+ ```
238
+
239
+ ## PDF Patterns
240
+
241
+ Pure functions, no classes. Take primitive data, return `bytes`. Internal
242
+ helpers prefixed with `_`:
243
+
244
+ ```python
245
+ import io
246
+ from reportlab.lib.pagesizes import A4
247
+ from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
248
+
249
+ def generate_restock_pdf(store_name: str, products_raw: list) -> bytes:
250
+ buffer = io.BytesIO()
251
+ doc = _make_doc(buffer)
252
+ story = _build_story(store_name, products_raw)
253
+ doc.build(story)
254
+ buffer.seek(0)
255
+ return buffer.read()
256
+
257
+ def _make_doc(buffer: io.BytesIO) -> SimpleDocTemplate:
258
+ return SimpleDocTemplate(
259
+ buffer,
260
+ pagesize=A4,
261
+ rightMargin=30,
262
+ leftMargin=30,
263
+ topMargin=30,
264
+ bottomMargin=30,
265
+ )
266
+
267
+ def _build_story(store_name: str, products_raw: list) -> list:
268
+ data = [["SKU", "Product", "Stock", "Min"]]
269
+ for p in products_raw:
270
+ data.append([p["sku"], p["name"], p["stock"], p["min_stock"]])
271
+ table = Table(data, colWidths=[60, 200, 50, 50])
272
+ table.setStyle(TableStyle([...]))
273
+ return [table]
274
+ ```
275
+
276
+ The view wraps the result in `HttpResponse`:
277
+
278
+ ```python
279
+ def api_restock_pdf(request):
280
+ products = Product.objects.filter(stock__lte=F("min_stock"))
281
+ pdf_bytes = generate_restock_pdf(request.store.name, list(products.values()))
282
+ response = HttpResponse(pdf_bytes, content_type="application/pdf")
283
+ response["Content-Disposition"] = 'inline; filename="restock.pdf"'
284
+ return response
285
+ ```
286
+
287
+ ## Cart Patterns
288
+
289
+ Two independent carts coexist per request. Session key is the only separator:
290
+
291
+ | Cart | Session Key | Module |
292
+ | ---------- | ----------- | ---------------------- |
293
+ | Storefront | `cart` | `apps/cart/cart.py` |
294
+ | POS | `pos_cart` | `apps/pos/pos_cart.py` |
295
+
296
+ Both follow the same contract:
297
+
298
+ - Initialize with `request`
299
+ - Store as `{str(product_id): {"quantity": int, "price": str}}`
300
+ - Price stored as `str` (Decimal is not JSON-serializable)
301
+ - Always call `self.session.modified = True` in `save()`
302
+
303
+ ```python
304
+ class POSCart:
305
+ def __init__(self, request):
306
+ self.session = request.session.get("pos_cart", {})
307
+
308
+ def add(self, product_id, quantity):
309
+ key = str(product_id)
310
+ if key in self.session:
311
+ self.session[key]["quantity"] += quantity
312
+ else:
313
+ self.session[key] = {"quantity": quantity, "price": "0.00"}
314
+ self.save()
315
+
316
+ def save(self):
317
+ self.session["pos_cart"] = self.session
318
+ self.session.modified = True
319
+ ```
320
+
321
+ ## Type Hints
322
+
323
+ **Minimal**: Only on public service/utility function signatures, not on views or
324
+ helpers:
325
+
326
+ ```python
327
+ # Service — annotate
328
+ def generate_restock_pdf(store_name: str, products_raw: list) -> bytes:
329
+ ...
330
+
331
+ # View — no annotations
332
+ def api_cart_add(request):
333
+ ...
334
+
335
+ # Private helpers — no annotations
336
+ def _build_cart_item(product, quantity):
337
+ ...
338
+ ```
339
+
340
+ ## Access Decorators
341
+
342
+ ### require_pos_access
343
+
344
+ Verifies the employee has POS access:
345
+
346
+ ```python
347
+ def require_pos_access(view):
348
+ def wrapper(request, *args, **kwargs):
349
+ if not request.user.is_authenticated:
350
+ return JsonResponse({"error": "Auth required"}, status=401)
351
+ if not request.user.is_superuser:
352
+ emp = EmployeeProfile.objects.get(user=request.user)
353
+ if not emp.has_pos_access:
354
+ return JsonResponse({"error": "No POS access"}, status=403)
355
+ return view(request, *args, **kwargs)
356
+ return wrapper
357
+ ```
358
+
359
+ ### require_manager_access
360
+
361
+ Verifies the employee is a manager:
362
+
363
+ ```python
364
+ def require_manager_access(view):
365
+ def wrapper(request, *args, **kwargs):
366
+ if not request.user.is_authenticated:
367
+ return JsonResponse({"error": "Auth required"}, status=401)
368
+ if not request.user.is_superuser:
369
+ emp = EmployeeProfile.objects.get(user=request.user)
370
+ if not emp.is_manager:
371
+ return JsonResponse({"error": "Manager required"}, status=403)
372
+ return view(request, *args, **kwargs)
373
+ return wrapper
374
+ ```
375
+
376
+ ## URLs and Routing
377
+
378
+ ### URL Structure
379
+
380
+ ```python
381
+ # apps/catalog/urls.py
382
+ from django.urls import path
383
+
384
+ urlpatterns = [
385
+ # API
386
+ path("api/products/", api_catalog_products, name="api_catalog_products"),
387
+ path("api/products/<int:pk>/", api_product_detail, name="api_product_detail"),
388
+ # Web
389
+ path("", product_list, name="catalog_product_list"),
390
+ path("<slug:slug>/", product_detail, name="catalog_product_detail"),
391
+ ]
392
+ ```
393
+
394
+ ### Namespaces
395
+
396
+ ```python
397
+ # config/urls.py
398
+ urlpatterns = [
399
+ path("catalog/", include("apps.catalog.urls", namespace="catalog")),
400
+ path("pos/", include("apps.pos.urls", namespace="pos")),
401
+ ]
402
+ ```
403
+
404
+ ## Models — Best Practices
405
+
406
+ ### Model Fields
407
+
408
+ ```python
409
+ class Product(models.Model):
410
+ name = models.CharField(max_length=255)
411
+ slug = models.SlugField(max_length=255, unique=True)
412
+ description = models.TextField(blank=True)
413
+ price = models.DecimalField(max_digits=10, decimal_places=2)
414
+ stock = models.PositiveIntegerField(default=0)
415
+ is_active = models.BooleanField(default=True)
416
+ created_at = models.DateTimeField(auto_now_add=True)
417
+ updated_at = models.DateTimeField(auto_now=True)
418
+
419
+ class Meta:
420
+ ordering = ["name"]
421
+ indexes = [
422
+ models.Index(fields=["is_active", "stock"]),
423
+ models.Index(fields=["slug"]),
424
+ ]
425
+
426
+ def __str__(self):
427
+ return self.name
428
+
429
+ @property
430
+ def is_available(self):
431
+ return self.is_active and (self.is_service or self.stock > 0)
432
+ ```
433
+
434
+ ### Choices
435
+
436
+ ```python
437
+ class Order(models.Model):
438
+ class Status(models.TextChoices):
439
+ PENDING = "pending", "Pending"
440
+ PAID = "paid", "Paid"
441
+ SHIPPED = "shipped", "Shipped"
442
+ CANCELLED = "cancelled", "Cancelled"
443
+
444
+ status = models.CharField(
445
+ max_length=20,
446
+ choices=Status.choices,
447
+ default=Status.PENDING,
448
+ )
449
+ ```
450
+
451
+ ## Code Quality
452
+
453
+ ### Line Length
454
+
455
+ 88 characters (Black + Ruff). Run before every commit:
456
+
457
+ ```bash
458
+ make format # black + ruff --fix
459
+ make lint # ruff check
460
+ uv run djlint . --reformat # templates
461
+ ```
462
+
463
+ Pre-commit hooks enforce Black and Ruff automatically.
464
+
465
+ ### Imports
466
+
467
+ ```python
468
+ # Standard library
469
+ import os
470
+ import json
471
+ from datetime import datetime
472
+
473
+ # Third party
474
+ from django.db import models
475
+ from django.http import JsonResponse
476
+ from django.views.decorators.http import require_POST
477
+
478
+ # Local
479
+ from apps.catalog.models import Product
480
+ from apps.pos.services import CartService
481
+ ```
482
+
483
+ ## Commands
484
+
485
+ ```bash
486
+ make run # dev server on 0.0.0.0:8000
487
+ make lint # ruff check
488
+ make format # black + ruff fix
489
+ make tests # pytest (--reuse-db --nomigrations by default)
490
+ make migrate # migrate_schemas --shared + --tenant
491
+ ```
492
+
493
+ ## Resources
494
+
495
+ - **FBV API examples**: `apps/pos/views.py`
496
+ - **Service layer**: `apps/orders/services.py`
497
+ - **PDF pattern**: `apps/catalog/reports.py`
498
+ - **Cart pattern**: `apps/pos/pos_cart.py`, `apps/cart/cart.py`
499
+ - **Pagination**: `apps/catalog/views.py` (api_catalog_products)
500
+ - **Django docs**: <https://docs.djangoproject.com/en/5.2/>