cc-codeconductor 0.4.2 → 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 (172) hide show
  1. package/README.md +75 -4
  2. package/dist/index.js +2698 -230
  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-pipeline.md +69 -0
  8. package/presets/agy/workflows/cc-scorecard.md +17 -0
  9. package/presets/claude/commands/cc/openspec.md +144 -0
  10. package/presets/claude/commands/cc/review.md +13 -2
  11. package/presets/claude/commands/cc/scorecard.md +65 -0
  12. package/presets/claude/skills/evaluation/SKILL.md +42 -0
  13. package/presets/claude/skills/openspec/SKILL.md +54 -0
  14. package/presets/codex/AGENTS.md +57 -0
  15. package/presets/cursor/.cursorignore +15 -0
  16. package/presets/cursor/AGENTS.md +504 -0
  17. package/presets/cursor/agents/architect.md +211 -0
  18. package/presets/cursor/agents/complexity-auditor.md +76 -0
  19. package/presets/cursor/agents/contract-builder.md +75 -0
  20. package/presets/cursor/agents/docs.md +180 -0
  21. package/presets/cursor/agents/goal-planner.md +71 -0
  22. package/presets/cursor/agents/implementer.md +161 -0
  23. package/presets/cursor/agents/orchestrator.md +377 -0
  24. package/presets/cursor/agents/repo-explorer.md +100 -0
  25. package/presets/cursor/agents/reviewer.md +237 -0
  26. package/presets/cursor/agents/security-reviewer.md +113 -0
  27. package/presets/cursor/agents/task-coach.md +145 -0
  28. package/presets/cursor/agents/tester.md +241 -0
  29. package/presets/cursor/commands/cc/api-contract.md +58 -0
  30. package/presets/cursor/commands/cc/db-migration.md +58 -0
  31. package/presets/cursor/commands/cc/feature.md +115 -0
  32. package/presets/cursor/commands/cc/fix.md +121 -0
  33. package/presets/cursor/commands/cc/openspec.md +144 -0
  34. package/presets/cursor/commands/cc/pagespeed.md +103 -0
  35. package/presets/cursor/commands/cc/refactor.md +148 -0
  36. package/presets/cursor/commands/cc/review.md +137 -0
  37. package/presets/cursor/commands/cc/scorecard.md +65 -0
  38. package/presets/cursor/commands/cc/tdd-cycle.md +226 -0
  39. package/presets/cursor/commands/cc/test-plan.md +138 -0
  40. package/presets/cursor/rules/behavioral-discipline.mdc +14 -0
  41. package/presets/cursor/rules/context-budget.mdc +12 -0
  42. package/presets/cursor/rules/orchestration.mdc +12 -0
  43. package/presets/cursor/rules/yagni-stdlib.mdc +11 -0
  44. package/presets/cursor/skills/android/SKILL.md +122 -0
  45. package/presets/cursor/skills/api-versioning/SKILL.md +394 -0
  46. package/presets/cursor/skills/astro/SKILL.md +322 -0
  47. package/presets/cursor/skills/auth-token-inspector/SKILL.md +33 -0
  48. package/presets/cursor/skills/code-review/SKILL.md +208 -0
  49. package/presets/cursor/skills/conductor-setup/SKILL.md +127 -0
  50. package/presets/cursor/skills/django-orm/SKILL.md +463 -0
  51. package/presets/cursor/skills/django-testing/SKILL.md +417 -0
  52. package/presets/cursor/skills/django-uv/SKILL.md +409 -0
  53. package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +54 -0
  54. package/presets/cursor/skills/evaluation/SKILL.md +8 -0
  55. package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +46 -0
  56. package/presets/cursor/skills/find-skills/SKILL.md +144 -0
  57. package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +49 -0
  58. package/presets/cursor/skills/jpa-postgres/SKILL.md +626 -0
  59. package/presets/cursor/skills/laravel-specialist/SKILL.md +267 -0
  60. package/presets/cursor/skills/laravel-specialist/references/eloquent.md +351 -0
  61. package/presets/cursor/skills/laravel-specialist/references/livewire.md +512 -0
  62. package/presets/cursor/skills/laravel-specialist/references/queues.md +423 -0
  63. package/presets/cursor/skills/laravel-specialist/references/routing.md +362 -0
  64. package/presets/cursor/skills/laravel-specialist/references/testing.md +522 -0
  65. package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +39 -0
  66. package/presets/cursor/skills/multi-agent-orchestration/README.md +144 -0
  67. package/presets/cursor/skills/multi-agent-orchestration/SKILL.md +579 -0
  68. package/presets/cursor/skills/multi-agent-orchestration/examples/framework_implementations.py +362 -0
  69. package/presets/cursor/skills/multi-agent-orchestration/examples/orchestration_patterns.py +411 -0
  70. package/presets/cursor/skills/multi-agent-orchestration/scripts/agent_communication.py +334 -0
  71. package/presets/cursor/skills/multi-agent-orchestration/scripts/benchmarking.py +341 -0
  72. package/presets/cursor/skills/multi-agent-orchestration/scripts/workflow_management.py +334 -0
  73. package/presets/cursor/skills/nextjs-typescript/SKILL.md +394 -0
  74. package/presets/cursor/skills/openspec/SKILL.md +52 -0
  75. package/presets/cursor/skills/pagespeed-insights/SKILL.md +445 -0
  76. package/presets/cursor/skills/pagespeed-insights/reference.md +50 -0
  77. package/presets/cursor/skills/pagespeed-perf/SKILL.md +281 -0
  78. package/presets/cursor/skills/php-pro/SKILL.md +210 -0
  79. package/presets/cursor/skills/php-pro/references/async-patterns.md +412 -0
  80. package/presets/cursor/skills/php-pro/references/laravel-patterns.md +377 -0
  81. package/presets/cursor/skills/php-pro/references/modern-php-features.md +323 -0
  82. package/presets/cursor/skills/php-pro/references/symfony-patterns.md +466 -0
  83. package/presets/cursor/skills/php-pro/references/testing-quality.md +466 -0
  84. package/presets/cursor/skills/python/SKILL.md +613 -0
  85. package/presets/cursor/skills/python-django-stack/SKILL.md +500 -0
  86. package/presets/cursor/skills/python-fastapi-stack/SKILL.md +464 -0
  87. package/presets/cursor/skills/security/SKILL.md +384 -0
  88. package/presets/cursor/skills/seo-analytics-injector/SKILL.md +44 -0
  89. package/presets/cursor/skills/spring-auth-auditor/SKILL.md +33 -0
  90. package/presets/cursor/skills/spring-boot-feature/SKILL.md +566 -0
  91. package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +408 -0
  92. package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +479 -0
  93. package/presets/cursor/skills/sqlalchemy/SKILL.md +473 -0
  94. package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +32 -0
  95. package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +28 -0
  96. package/presets/cursor/skills/testing-tdd/SKILL.md +592 -0
  97. package/presets/cursor/skills/workflow-orchestration-patterns/SKILL.md +98 -0
  98. package/presets/cursor/skills/workflow-orchestration-patterns/references/details.md +223 -0
  99. package/presets/laravel-tall/agents/architect.md +8 -0
  100. package/presets/laravel-tall/agents/implementer.md +12 -0
  101. package/presets/laravel-tall/laravel-tall.yml +38 -0
  102. package/presets/opencode/agents/architect.md +153 -61
  103. package/presets/opencode/agents/complexity-auditor.md +1 -0
  104. package/presets/opencode/agents/contract-builder.md +93 -0
  105. package/presets/opencode/agents/docs.md +125 -40
  106. package/presets/opencode/agents/goal-planner.md +82 -0
  107. package/presets/opencode/agents/implementer.md +107 -38
  108. package/presets/opencode/agents/orchestrator.md +90 -71
  109. package/presets/opencode/agents/repo-explorer.md +1 -2
  110. package/presets/opencode/agents/reviewer.md +164 -75
  111. package/presets/opencode/agents/security-reviewer.md +129 -0
  112. package/presets/opencode/agents/task-coach.md +110 -59
  113. package/presets/opencode/agents/tester.md +1 -2
  114. package/presets/opencode/commands/cc-openspec.md +61 -0
  115. package/presets/opencode/commands/cc-scorecard.md +16 -0
  116. package/presets/opencode/prompts/v0.4.0/architect.md +221 -0
  117. package/presets/opencode/prompts/v0.4.0/complexity-auditor.md +89 -0
  118. package/presets/opencode/prompts/v0.4.0/docs.md +189 -0
  119. package/presets/opencode/prompts/v0.4.0/implementer.md +162 -0
  120. package/presets/opencode/prompts/v0.4.0/orchestrator.md +348 -0
  121. package/presets/opencode/prompts/v0.4.0/repo-explorer.md +110 -0
  122. package/presets/opencode/prompts/v0.4.0/reviewer.md +225 -0
  123. package/presets/opencode/prompts/v0.4.0/task-coach.md +155 -0
  124. package/presets/opencode/prompts/v0.4.0/tester.md +251 -0
  125. package/presets/opencode/prompts/v0.5.0/architect.md +222 -0
  126. package/presets/opencode/prompts/v0.5.0/complexity-auditor.md +91 -0
  127. package/presets/opencode/prompts/v0.5.0/contract-builder.md +84 -0
  128. package/presets/opencode/prompts/v0.5.0/docs.md +190 -0
  129. package/presets/opencode/prompts/v0.5.0/goal-planner.md +80 -0
  130. package/presets/opencode/prompts/v0.5.0/implementer.md +171 -0
  131. package/presets/opencode/prompts/v0.5.0/orchestrator.md +388 -0
  132. package/presets/opencode/prompts/v0.5.0/repo-explorer.md +111 -0
  133. package/presets/opencode/prompts/v0.5.0/reviewer.md +248 -0
  134. package/presets/opencode/prompts/v0.5.0/security-reviewer.md +123 -0
  135. package/presets/opencode/prompts/v0.5.0/task-coach.md +156 -0
  136. package/presets/opencode/prompts/v0.5.0/tester.md +252 -0
  137. package/presets/opencode/skills/auth-token-inspector/SKILL.md +31 -0
  138. package/presets/opencode/skills/drizzle-schema-architect/SKILL.md +51 -0
  139. package/presets/opencode/skills/evaluation/SKILL.md +6 -0
  140. package/presets/opencode/skills/fastapi-pydantic-strict/SKILL.md +44 -0
  141. package/presets/opencode/skills/jpa-nplusone-detector/SKILL.md +46 -0
  142. package/presets/opencode/skills/livewire-alpine-bridge/SKILL.md +36 -0
  143. package/presets/opencode/skills/openspec/SKILL.md +50 -0
  144. package/presets/opencode/skills/seo-analytics-injector/SKILL.md +44 -0
  145. package/presets/opencode/skills/spring-auth-auditor/SKILL.md +30 -0
  146. package/presets/opencode/skills/tailwind-responsive-auditor/SKILL.md +30 -0
  147. package/presets/opencode/skills/tdd-mutation-tester/SKILL.md +28 -0
  148. package/presets/python-data-api/agents/architect.md +8 -0
  149. package/presets/python-data-api/agents/implementer.md +9 -0
  150. package/presets/python-data-api/python-data-api.yml +37 -0
  151. package/presets/spring-kotlin-jpa/agents/architect.md +8 -0
  152. package/presets/spring-kotlin-jpa/agents/implementer.md +9 -0
  153. package/presets/spring-kotlin-jpa/spring-kotlin-jpa.yml +38 -0
  154. package/presets/templates/BACKLOG.md +33 -0
  155. package/presets/templates/execution-profile.yml +6 -0
  156. package/presets/templates/model-comparison.md +11 -0
  157. package/presets/templates/regression-checklist.yml +10 -0
  158. package/presets/ts-next-drizzle/agents/architect.md +8 -0
  159. package/presets/ts-next-drizzle/agents/implementer.md +10 -0
  160. package/presets/ts-next-drizzle/ts-next-drizzle.yml +41 -0
  161. package/src/presets/manifests/agy.yml +2 -2
  162. package/src/presets/manifests/claude.yml +2 -2
  163. package/src/presets/manifests/codex.yml +2 -2
  164. package/src/presets/manifests/cursor.yml +19 -3
  165. package/src/presets/manifests/gemini.yml +2 -2
  166. package/src/presets/manifests/opencode.yml +2 -2
  167. package/src/presets/models/agy.yml +21 -0
  168. package/src/presets/models/claude.yml +18 -0
  169. package/src/presets/models/codex.yml +18 -0
  170. package/src/presets/models/cursor.yml +39 -9
  171. package/src/presets/models/gemini.yml +18 -0
  172. package/src/presets/models/opencode.yml +18 -0
@@ -0,0 +1,613 @@
1
+ ---
2
+ id: python
3
+ version: 1.0.0
4
+ name: Python
5
+ description: >
6
+ Python development best practices: clean code, patterns,
7
+ type hints, decorators, context managers, and architecture.
8
+ Trigger: When writing Python code anywhere in the project.
9
+
10
+ user-invokable: true
11
+ license: MIT
12
+ metadata:
13
+ author: lgzarturo
14
+ category: python
15
+
16
+ compatibility:
17
+ tools: [claude, codex, gemini, agy, opencode]
18
+ stacks:
19
+ languages: [python]
20
+ frameworks: [django, fastapi, flask]
21
+
22
+ risk:
23
+ level: low
24
+ can_execute_shell: false
25
+ can_modify_files: true
26
+ requires_network: false
27
+
28
+ inputs:
29
+ - Python source files
30
+ - existing functions, classes, or modules
31
+ - business logic implementations to review or refactor
32
+
33
+ outputs:
34
+ - clean Python functions and classes with meaningful names
35
+ - type-annotated public API signatures
36
+ - custom domain exceptions
37
+ - value objects and data classes
38
+ - service and repository pattern implementations
39
+
40
+ quality:
41
+ reviewed_by: codeconductor-core
42
+ version: 0.1.0
43
+ paths:
44
+ - "**/*.py"
45
+ ---
46
+ ## When to Use
47
+
48
+ - Writing any Python code in the project
49
+ - Reviewing code for quality and maintainability
50
+ - Designing new functions, classes, or modules
51
+ - Implementing business logic or utilities
52
+
53
+ ## Clean Code Principles
54
+
55
+ ### Readability > Brevity
56
+
57
+ Code is read more times than it's written. Prioritize clarity over cleverness:
58
+
59
+ ```python
60
+ # WRONG — clever but obscure
61
+ def f(x): return x if x else 0
62
+
63
+ # CORRECT — readable
64
+ def calculate_discount(price, has_discount):
65
+ if not has_discount:
66
+ return 0
67
+ return price * DISCOUNT_RATE
68
+ ```
69
+
70
+ ### Meaningful Names
71
+
72
+ | What | Convention | Example |
73
+ | --------- | ---------------------- | -------------------------------------------- |
74
+ | Variables | descriptive snake_case | `total_price`, `products_list` |
75
+ | Functions | verb snake_case | `get_active_products()`, `calculate_total()` |
76
+ | Classes | PascalCase | `OrderService`, `CartController` |
77
+ | Constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT`, `DEFAULT_PAGE_SIZE` |
78
+ | Modules | snake_case | `order_service.py`, `cart_utils.py` |
79
+
80
+ ```python
81
+ # WRONG — cryptic names
82
+ d = 1500
83
+ p = products.filter(a=True)
84
+
85
+ # CORRECT — names that say what they are
86
+ discount_amount = 1500
87
+ active_products = Product.objects.filter(is_active=True)
88
+ ```
89
+
90
+ ### Small Functions
91
+
92
+ A function should do ONE thing. Ideally under 30 lines:
93
+
94
+ ```python
95
+ # WRONG — function does many things
96
+ def process_order(order_data):
97
+ # validates
98
+ # creates order
99
+ # creates items
100
+ # updates stock
101
+ # sends email
102
+ # updates analytics
103
+ ...
104
+
105
+ # CORRECT — small, focused functions
106
+ def process_order(order_data):
107
+ validated = validate_order_data(order_data)
108
+ order = create_order(validated)
109
+ create_order_items(order, validated["items"])
110
+ update_product_stock(validated["items"])
111
+ send_order_confirmation(order)
112
+ track_order_analytics(order)
113
+ return order
114
+ ```
115
+
116
+ ### DRY — Don't Repeat Yourself
117
+
118
+ Extract repeated code into functions or classes:
119
+
120
+ ```python
121
+ # WRONG — repeat logic
122
+ if order.status == "pending":
123
+ send_email(order.customer, "Your order is pending")
124
+ log_event("order_pending", order.id)
125
+
126
+ if order.status == "paid":
127
+ send_email(order.customer, "Your order was paid")
128
+ log_event("order_paid", order.id)
129
+
130
+ # CORRECT — extract to function
131
+ def notify_order_status(order):
132
+ status_messages = {
133
+ "pending": "Your order is pending",
134
+ "paid": "Your order was paid",
135
+ }
136
+ send_email(order.customer, status_messages[order.status])
137
+ log_event(f"order_{order.status}", order.id)
138
+ ```
139
+
140
+ ## Type Hints
141
+
142
+ ### When to Use Them
143
+
144
+ Use type hints on:
145
+
146
+ - Public function signatures (APIs, services)
147
+ - Functions with complex parameters
148
+ - Non-obvious returns
149
+
150
+ ```python
151
+ from typing import Optional, List, Dict, Any
152
+
153
+ # Public functions — use hints
154
+ def calculate_total(items: List[Dict[str, Any]], tax_rate: float) -> float:
155
+ ...
156
+
157
+ def find_product(product_id: int) -> Optional[Product]:
158
+ ...
159
+
160
+ # Private helpers — optional
161
+ def _build_cart_response(cart):
162
+ ...
163
+ ```
164
+
165
+ ### Basic Types
166
+
167
+ ```python
168
+ # Primitives
169
+ name: str = "Product"
170
+ quantity: int = 5
171
+ price: float = 1500.99
172
+ is_active: bool = True
173
+
174
+ # Collections
175
+ products: List[Product] = []
176
+ product_ids: list[int] = []
177
+ metadata: Dict[str, Any] = {}
178
+ items: tuple[str, int] = ("sku", 5)
179
+
180
+ # Optionals
181
+ user: Optional[User] = None
182
+ description: str | None = None
183
+ ```
184
+
185
+ ### Advanced Types
186
+
187
+ ```python
188
+ from typing import Union, Optional, Callable, Any
189
+ from decimal import Decimal
190
+
191
+ # Union for multiple types
192
+ def process_payment(amount: float, method: str) -> Union[dict, None]:
193
+ if method == "cash":
194
+ return {"change": amount - 1000}
195
+ return None
196
+
197
+ # Callable for functions as parameters
198
+ def execute_callback(callback: Callable[[str], None], message: str):
199
+ callback(message)
200
+
201
+ # TypeAlias for complex types
202
+ CartData = Dict[str, Dict[str, Union[int, str]]]
203
+
204
+ def process_cart(cart: CartData):
205
+ ...
206
+ ```
207
+
208
+ ## Common Patterns
209
+
210
+ ### Context Managers
211
+
212
+ For resources needing cleanup (files, connections, transactions):
213
+
214
+ ```python
215
+ # WRONG — no guaranteed cleanup
216
+ def write_report(data):
217
+ f = open("report.txt", "w")
218
+ f.write(data)
219
+ f.close() # Won't run if exception occurs
220
+
221
+ # CORRECT — context manager
222
+ def write_report(data):
223
+ with open("report.txt", "w") as f:
224
+ f.write(data)
225
+ # Automatic cleanup
226
+
227
+ # Custom context manager
228
+ class Transaction:
229
+ def __init__(self):
230
+ self.entered = False
231
+
232
+ def __enter__(self):
233
+ db.begin()
234
+ self.entered = True
235
+ return self
236
+
237
+ def __exit__(self, exc_type, exc_val, exc_tb):
238
+ if exc_type:
239
+ db.rollback()
240
+ else:
241
+ db.commit()
242
+ return False # Don't suppress exceptions
243
+ ```
244
+
245
+ ### Decorators
246
+
247
+ Functions that modify the behavior of other functions:
248
+
249
+ ```python
250
+ import functools
251
+ import time
252
+
253
+ # Basic decorator
254
+ def require_auth(view):
255
+ @functools.wraps(view)
256
+ def wrapper(request, *args, **kwargs):
257
+ if not request.user.is_authenticated:
258
+ return JsonResponse({"error": "Unauthorized"}, status=401)
259
+ return view(request, *args, **kwargs)
260
+ return wrapper
261
+
262
+ # Decorator with parameters
263
+ def require_role(role: str):
264
+ def decorator(view):
265
+ @functools.wraps(view)
266
+ def wrapper(request, *args, **kwargs):
267
+ if request.user.role != role:
268
+ return JsonResponse({"error": "Forbidden"}, status=403)
269
+ return view(request, *args, **kwargs)
270
+ return wrapper
271
+ return decorator
272
+
273
+ # Decorator with timing
274
+ def timing(view):
275
+ @functools.wraps(view)
276
+ def wrapper(*args, **kwargs):
277
+ start = time.perf_counter()
278
+ result = view(*args, **kwargs)
279
+ elapsed = time.perf_counter() - start
280
+ print(f"{view.__name__} took {elapsed:.4f}s")
281
+ return result
282
+ return wrapper
283
+ ```
284
+
285
+ ### Data Classes
286
+
287
+ For simple data structures:
288
+
289
+ ```python
290
+ from dataclasses import dataclass
291
+ from datetime import datetime
292
+ from decimal import Decimal
293
+
294
+ @dataclass
295
+ class CartItem:
296
+ product_id: int
297
+ quantity: int
298
+ price: Decimal
299
+ name: str = ""
300
+
301
+ @property
302
+ def subtotal(self) -> Decimal:
303
+ return self.price * self.quantity
304
+
305
+ # Usage
306
+ item = CartItem(product_id=1, quantity=2, price=Decimal("1500.00"))
307
+ print(item.subtotal) # 3000.00
308
+ ```
309
+
310
+ ### Enums
311
+
312
+ For limited values:
313
+
314
+ ```python
315
+ from enum import Enum
316
+
317
+ class OrderStatus(str, Enum):
318
+ PENDING = "pending"
319
+ PAID = "paid"
320
+ SHIPPED = "shipped"
321
+ CANCELLED = "cancelled"
322
+
323
+ @property
324
+ def is_final(self):
325
+ return self in (OrderStatus.SHIPPED, OrderStatus.CANCELLED)
326
+
327
+ # Usage
328
+ order.status = OrderStatus.PAID
329
+ if order.status.is_final:
330
+ ...
331
+ ```
332
+
333
+ ## Errors and Exceptions
334
+
335
+ ### Exception Handling
336
+
337
+ ```python
338
+ # WRONG — catch generic Exception
339
+ try:
340
+ product = Product.objects.get(id=product_id)
341
+ except Exception:
342
+ return JsonResponse({"error": "Not found"}, status=404)
343
+
344
+ # CORRECT — catch specific exception
345
+ try:
346
+ product = Product.objects.get(id=product_id)
347
+ except Product.DoesNotExist:
348
+ return JsonResponse({"error": "Product not found"}, status=404)
349
+
350
+ # With fallback
351
+ product = Product.objects.filter(id=product_id).first()
352
+ if not product:
353
+ return JsonResponse({"error": "Product not found"}, status=404)
354
+ ```
355
+
356
+ ### Custom Exceptions
357
+
358
+ ```python
359
+ class BusinessException(Exception):
360
+ """Exception for business logic errors."""
361
+ def __init__(self, message: str, code: str = "BUSINESS_ERROR"):
362
+ self.message = message
363
+ self.code = code
364
+ super().__init__(self.message)
365
+
366
+ class InsufficientStockException(BusinessException):
367
+ def __init__(self, product_id: int, requested: int, available: int):
368
+ super().__init__(
369
+ message=f"Insufficient stock: requested {requested}, available {available}",
370
+ code="INSUFFICIENT_STOCK",
371
+ )
372
+ self.product_id = product_id
373
+ self.requested = requested
374
+ self.available = available
375
+
376
+ # Usage
377
+ try:
378
+ if requested > available:
379
+ raise InsufficientStockException(product_id, requested, available)
380
+ except InsufficientStockException as e:
381
+ return JsonResponse({"error": e.message, "code": e.code}, status=400)
382
+ ```
383
+
384
+ ## Architecture Patterns
385
+
386
+ ### Repository Pattern
387
+
388
+ Abstract data access:
389
+
390
+ ```python
391
+ class ProductRepository:
392
+ @staticmethod
393
+ def get_active() -> QuerySet:
394
+ return Product.objects.filter(is_active=True)
395
+
396
+ @staticmethod
397
+ def get_by_category(category_id: int) -> QuerySet:
398
+ return Product.objects.filter(
399
+ category_id=category_id,
400
+ is_active=True,
401
+ ).select_related("category")
402
+
403
+ @staticmethod
404
+ def search(query: str) -> QuerySet:
405
+ return Product.objects.filter(
406
+ Q(name__icontains=query) | Q(sku__icontains=query),
407
+ is_active=True,
408
+ )
409
+ ```
410
+
411
+ ### Service Layer
412
+
413
+ Encapsulated business logic:
414
+
415
+ ```python
416
+ class ProductService:
417
+ @staticmethod
418
+ def get_products_for_pos(store: Store) -> List[Dict]:
419
+ products = ProductRepository.get_active().select_related(
420
+ "category", "tax"
421
+ )
422
+ return [
423
+ {
424
+ "id": p.id,
425
+ "name": p.name,
426
+ "price": str(p.get_display_price()),
427
+ "stock": p.stock if not p.is_service else None,
428
+ "is_service": p.is_service,
429
+ }
430
+ for p in products
431
+ ]
432
+
433
+ @staticmethod
434
+ def check_stock(product_id: int, quantity: int) -> bool:
435
+ try:
436
+ product = Product.objects.get(id=product_id)
437
+ except Product.DoesNotExist:
438
+ return False
439
+
440
+ if product.is_service:
441
+ return True
442
+ return product.stock >= quantity
443
+ ```
444
+
445
+ ### Value Objects
446
+
447
+ Immutable objects representing values:
448
+
449
+ ```python
450
+ from dataclasses import dataclass
451
+ from decimal import Decimal
452
+
453
+ @dataclass(frozen=True)
454
+ class Money:
455
+ amount: Decimal
456
+ currency: str = "ARS"
457
+
458
+ def __add__(self, other: "Money") -> "Money":
459
+ if self.currency != other.currency:
460
+ raise ValueError("Cannot add different currencies")
461
+ return Money(self.amount + other.amount, self.currency)
462
+
463
+ def __str__(self):
464
+ return f"{self.currency} {self.amount:.2f}"
465
+
466
+ # Usage
467
+ subtotal = Money(Decimal("1000.00"))
468
+ tax = Money(Decimal("210.00"))
469
+ total = subtotal + tax # Money(amount=Decimal('1210.00'), currency='ARS')
470
+ ```
471
+
472
+ ## Utility Functions
473
+
474
+ ### Common Helper Functions
475
+
476
+ ```python
477
+ import os
478
+ import json
479
+ from datetime import datetime
480
+ from decimal import Decimal
481
+ from typing import Any, Dict
482
+
483
+ def safe_int(value: Any, default: int = 0) -> int:
484
+ """Safely convert to int."""
485
+ try:
486
+ return int(value)
487
+ except (ValueError, TypeError):
488
+ return default
489
+
490
+ def safe_decimal(value: Any, default: Decimal = Decimal("0")) -> Decimal:
491
+ """Safely convert to Decimal."""
492
+ try:
493
+ return Decimal(str(value))
494
+ except (ValueError, TypeError):
495
+ return default
496
+
497
+ def format_money(amount: Decimal, currency: str = "ARS") -> str:
498
+ """Format money for display."""
499
+ return f"{currency} {amount:,.2f}".replace(",", ".")
500
+
501
+ def parse_json(body: bytes) -> Dict[str, Any]:
502
+ """Parse JSON from request body."""
503
+ try:
504
+ return json.loads(body)
505
+ except json.JSONDecodeError:
506
+ raise ValueError("Invalid JSON")
507
+
508
+ def truncate(text: str, length: int = 100, suffix: str = "...") -> str:
509
+ """Truncate text to length."""
510
+ if len(text) <= length:
511
+ return text
512
+ return text[:length - len(suffix)] + suffix
513
+ ```
514
+
515
+ ### Testing Utilities
516
+
517
+ ```python
518
+ # tests/test_utils.py
519
+ import pytest
520
+ from decimal import Decimal
521
+
522
+ class TestSafeInt:
523
+ def test_valid_int(self):
524
+ assert safe_int("42") == 42
525
+
526
+ def test_invalid_returns_default(self):
527
+ assert safe_int("not a number") == 0
528
+
529
+ def test_custom_default(self):
530
+ assert safe_int("invalid", 100) == 100
531
+
532
+ class TestMoney:
533
+ def test_add_same_currency(self):
534
+ a = Money(Decimal("100"))
535
+ b = Money(Decimal("50"))
536
+ assert a + b == Money(Decimal("150"))
537
+
538
+ def test_different_currency_raises(self):
539
+ a = Money(Decimal("100"), "ARS")
540
+ b = Money(Decimal("50"), "USD")
541
+ with pytest.raises(ValueError):
542
+ _ = a + b
543
+ ```
544
+
545
+ ## Code to Avoid
546
+
547
+ ### Mutable Default Arguments
548
+
549
+ ```python
550
+ # WRONG — mutable default
551
+ def add_items(items=[]):
552
+ items.append(1)
553
+ return items
554
+
555
+ # CORRECT
556
+ def add_items(items=None):
557
+ if items is None:
558
+ items = []
559
+ items.append(1)
560
+ return items
561
+ ```
562
+
563
+ ### Shadowing Builtins
564
+
565
+ ```python
566
+ # WRONG
567
+ list = [1, 2, 3] # shadows built-in
568
+ id = "abc" # shadows built-in
569
+
570
+ # CORRECT
571
+ items = [1, 2, 3]
572
+ product_id = "abc"
573
+ ```
574
+
575
+ ### Magic Numbers
576
+
577
+ ```python
578
+ # WRONG
579
+ if price > 1000:
580
+ discount = price * 0.1
581
+
582
+ # CORRECT
583
+ DISCOUNT_THRESHOLD = Decimal("1000")
584
+ DISCOUNT_RATE = Decimal("0.1")
585
+
586
+ if price > DISCOUNT_THRESHOLD:
587
+ discount = price * DISCOUNT_RATE
588
+ ```
589
+
590
+ ### Modifying Collections in Loop
591
+
592
+ ```python
593
+ # WRONG — modifies while iterating
594
+ for item in items:
595
+ if item.deleted:
596
+ items.remove(item)
597
+
598
+ # CORRECT
599
+ items = [item for item in items if not item.deleted]
600
+ # or
601
+ items_to_keep = []
602
+ for item in items:
603
+ if not item.deleted:
604
+ items_to_keep.append(item)
605
+ items = items_to_keep
606
+ ```
607
+
608
+ ## Resources
609
+
610
+ - **PEP 8** — Style Guide: <https://peps.python.org/pep-0008/>
611
+ - **PEP 484** — Type Hints: <https://peps.python.org/pep-0484/>
612
+ - **Real Python**: <https://realpython.com/>
613
+ - **Python Docs**: <https://docs.python.org/3/>