@smicolon/ai-kit 0.1.0 → 0.1.1
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.
- package/.claude-plugin/CLAUDE.md +7 -0
- package/.claude-plugin/marketplace.json +373 -0
- package/package.json +4 -3
- package/packs/architect/CHANGELOG.md +17 -0
- package/packs/architect/README.md +58 -0
- package/packs/architect/agents/system-architect.md +768 -0
- package/packs/architect/commands/diagram-create.md +300 -0
- package/packs/better-auth/.claude-plugin/plugin.json +14 -0
- package/packs/better-auth/.mcp.json +14 -0
- package/packs/better-auth/CHANGELOG.md +26 -0
- package/packs/better-auth/README.md +125 -0
- package/packs/better-auth/agents/auth-architect.md +278 -0
- package/packs/better-auth/commands/auth-provider-add.md +265 -0
- package/packs/better-auth/commands/auth-setup.md +298 -0
- package/packs/better-auth/skills/auth-security/SKILL.md +425 -0
- package/packs/better-auth/skills/better-auth-patterns/SKILL.md +455 -0
- package/packs/dev-loop/.claude-plugin/plugin.json +10 -0
- package/packs/dev-loop/CHANGELOG.md +69 -0
- package/packs/dev-loop/README.md +155 -0
- package/packs/dev-loop/commands/cancel-dev.md +21 -0
- package/packs/dev-loop/commands/dev-loop.md +72 -0
- package/packs/dev-loop/commands/dev-plan.md +351 -0
- package/packs/dev-loop/hooks/hooks.json +15 -0
- package/packs/dev-loop/hooks/stop-hook.sh +178 -0
- package/packs/dev-loop/scripts/setup-dev-loop.sh +194 -0
- package/packs/dev-loop/skills/tdd-planner/SKILL.md +249 -0
- package/packs/dev-loop/skills/tdd-planner/references/framework-patterns.md +874 -0
- package/packs/dev-loop/skills/tdd-planner/references/good-example.md +260 -0
- package/packs/dev-loop/skills/tdd-planner/references/plan-template.md +275 -0
- package/packs/django/CHANGELOG.md +39 -0
- package/packs/django/README.md +92 -0
- package/packs/django/agents/django-architect.md +182 -0
- package/packs/django/agents/django-builder.md +250 -0
- package/packs/django/agents/django-feature-based.md +420 -0
- package/packs/django/agents/django-reviewer.md +253 -0
- package/packs/django/agents/django-tester.md +230 -0
- package/packs/django/commands/api-endpoint.md +285 -0
- package/packs/django/commands/model-create.md +178 -0
- package/packs/django/commands/test-generate.md +325 -0
- package/packs/django/rules/migrations.md +138 -0
- package/packs/django/rules/models.md +167 -0
- package/packs/django/rules/serializers.md +126 -0
- package/packs/django/rules/services.md +131 -0
- package/packs/django/rules/tests.md +140 -0
- package/packs/django/rules/views.md +102 -0
- package/packs/django/skills/import-convention-enforcer/SKILL.md +226 -0
- package/packs/django/skills/import-convention-enforcer/patterns/django-imports.md +343 -0
- package/packs/django/skills/migration-safety-checker/SKILL.md +375 -0
- package/packs/django/skills/model-entity-validator/SKILL.md +298 -0
- package/packs/django/skills/performance-optimizer/SKILL.md +447 -0
- package/packs/django/skills/red-phase-verifier/SKILL.md +180 -0
- package/packs/django/skills/security-first-validator/SKILL.md +435 -0
- package/packs/django/skills/test-coverage-advisor/SKILL.md +394 -0
- package/packs/django/skills/test-validity-checker/SKILL.md +194 -0
- package/packs/failure-log/.claude-plugin/plugin.json +14 -0
- package/packs/failure-log/CHANGELOG.md +20 -0
- package/packs/failure-log/README.md +168 -0
- package/packs/failure-log/commands/failure-add.md +106 -0
- package/packs/failure-log/commands/failure-list.md +89 -0
- package/packs/failure-log/hooks/hooks.json +16 -0
- package/packs/failure-log/hooks/scripts/inject-failures.sh +64 -0
- package/packs/failure-log/skills/failure-log-manager/SKILL.md +164 -0
- package/packs/flutter/.claude-plugin/plugin.json +10 -0
- package/packs/flutter/CHANGELOG.md +19 -0
- package/packs/flutter/README.md +170 -0
- package/packs/flutter/agents/flutter-architect.md +166 -0
- package/packs/flutter/agents/flutter-builder.md +303 -0
- package/packs/flutter/agents/release-manager.md +355 -0
- package/packs/flutter/commands/fastlane-setup.md +188 -0
- package/packs/flutter/commands/flutter-build.md +90 -0
- package/packs/flutter/commands/flutter-deploy.md +133 -0
- package/packs/flutter/commands/flutter-test.md +117 -0
- package/packs/flutter/commands/signing-setup.md +209 -0
- package/packs/flutter/hooks/hooks.json +17 -0
- package/packs/flutter/skills/fastlane-knowledge/SKILL.md +193 -0
- package/packs/flutter/skills/flutter-architecture/SKILL.md +127 -0
- package/packs/flutter/skills/store-publishing/SKILL.md +163 -0
- package/packs/hono/.claude-plugin/plugin.json +19 -0
- package/packs/hono/CHANGELOG.md +19 -0
- package/packs/hono/README.md +143 -0
- package/packs/hono/agents/hono-architect.md +240 -0
- package/packs/hono/agents/hono-builder.md +285 -0
- package/packs/hono/agents/hono-reviewer.md +279 -0
- package/packs/hono/agents/hono-tester.md +346 -0
- package/packs/hono/commands/middleware-create.md +223 -0
- package/packs/hono/commands/project-init.md +306 -0
- package/packs/hono/commands/route-create.md +153 -0
- package/packs/hono/commands/rpc-client.md +263 -0
- package/packs/hono/hooks/hooks.json +4 -0
- package/packs/hono/skills/cloudflare-bindings/SKILL.md +408 -0
- package/packs/hono/skills/hono-patterns/SKILL.md +309 -0
- package/packs/hono/skills/rpc-typesafe/SKILL.md +388 -0
- package/packs/hono/skills/zod-validation/SKILL.md +332 -0
- package/packs/nestjs/CHANGELOG.md +29 -0
- package/packs/nestjs/README.md +75 -0
- package/packs/nestjs/agents/nestjs-architect.md +402 -0
- package/packs/nestjs/agents/nestjs-builder.md +301 -0
- package/packs/nestjs/agents/nestjs-tester.md +437 -0
- package/packs/nestjs/commands/module-create.md +369 -0
- package/packs/nestjs/rules/controllers.md +92 -0
- package/packs/nestjs/rules/dto.md +124 -0
- package/packs/nestjs/rules/entities.md +102 -0
- package/packs/nestjs/rules/services.md +106 -0
- package/packs/nestjs/skills/barrel-export-manager/SKILL.md +389 -0
- package/packs/nestjs/skills/import-convention-enforcer/SKILL.md +365 -0
- package/packs/nextjs/CHANGELOG.md +36 -0
- package/packs/nextjs/README.md +76 -0
- package/packs/nextjs/agents/frontend-tester.md +680 -0
- package/packs/nextjs/agents/frontend-visual.md +820 -0
- package/packs/nextjs/agents/nextjs-architect.md +331 -0
- package/packs/nextjs/agents/nextjs-modular.md +433 -0
- package/packs/nextjs/commands/component-create.md +398 -0
- package/packs/nextjs/rules/api-routes.md +129 -0
- package/packs/nextjs/rules/components.md +106 -0
- package/packs/nextjs/rules/hooks.md +132 -0
- package/packs/nextjs/skills/accessibility-validator/SKILL.md +445 -0
- package/packs/nextjs/skills/import-convention-enforcer/SKILL.md +399 -0
- package/packs/nextjs/skills/react-form-validator/SKILL.md +569 -0
- package/packs/nuxtjs/CHANGELOG.md +30 -0
- package/packs/nuxtjs/README.md +56 -0
- package/packs/nuxtjs/agents/frontend-tester.md +680 -0
- package/packs/nuxtjs/agents/frontend-visual.md +820 -0
- package/packs/nuxtjs/agents/nuxtjs-architect.md +537 -0
- package/packs/nuxtjs/commands/component-create.md +223 -0
- package/packs/nuxtjs/rules/components.md +101 -0
- package/packs/nuxtjs/rules/composables.md +118 -0
- package/packs/nuxtjs/rules/server-routes.md +127 -0
- package/packs/nuxtjs/skills/accessibility-validator/SKILL.md +183 -0
- package/packs/nuxtjs/skills/import-convention-enforcer/SKILL.md +196 -0
- package/packs/nuxtjs/skills/veevalidate-form-validator/SKILL.md +190 -0
- package/packs/onboard/CHANGELOG.md +22 -0
- package/packs/onboard/README.md +103 -0
- package/packs/onboard/agents/onboard-guide.md +118 -0
- package/packs/onboard/commands/onboard.md +313 -0
- package/packs/onboard/skills/onboard-context-provider/SKILL.md +98 -0
- package/packs/tanstack-router/.claude-plugin/plugin.json +14 -0
- package/packs/tanstack-router/CHANGELOG.md +30 -0
- package/packs/tanstack-router/README.md +113 -0
- package/packs/tanstack-router/agents/tanstack-architect.md +173 -0
- package/packs/tanstack-router/agents/tanstack-builder.md +360 -0
- package/packs/tanstack-router/agents/tanstack-tester.md +454 -0
- package/packs/tanstack-router/commands/form-create.md +313 -0
- package/packs/tanstack-router/commands/query-create.md +263 -0
- package/packs/tanstack-router/commands/route-create.md +190 -0
- package/packs/tanstack-router/commands/table-create.md +413 -0
- package/packs/tanstack-router/skills/ai-patterns/SKILL.md +370 -0
- package/packs/tanstack-router/skills/db-patterns/SKILL.md +346 -0
- package/packs/tanstack-router/skills/devtools-patterns/SKILL.md +415 -0
- package/packs/tanstack-router/skills/form-patterns/SKILL.md +425 -0
- package/packs/tanstack-router/skills/pacer-patterns/SKILL.md +341 -0
- package/packs/tanstack-router/skills/query-patterns/SKILL.md +359 -0
- package/packs/tanstack-router/skills/router-patterns/SKILL.md +285 -0
- package/packs/tanstack-router/skills/store-patterns/SKILL.md +351 -0
- package/packs/tanstack-router/skills/table-patterns/SKILL.md +531 -0
- package/packs/tanstack-router/skills/tanstack-conventions/SKILL.md +428 -0
- package/packs/tanstack-router/skills/virtual-patterns/SKILL.md +490 -0
- package/packs/worktree/.claude-plugin/plugin.json +19 -0
- package/packs/worktree/CHANGELOG.md +24 -0
- package/packs/worktree/README.md +110 -0
- package/packs/worktree/commands/wt.md +73 -0
- package/packs/worktree/scripts/wt.sh +396 -0
- package/packs/worktree/skills/worktree-manager/SKILL.md +68 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: performance-optimizer
|
|
3
|
+
description: This skill should be used when the user asks to "optimize queries", "fix slow queries", "improve performance", "detect N+1", "add indexes", or when writing Django ORM queries that access related objects. Detects and fixes performance issues.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Performance Optimizer
|
|
7
|
+
|
|
8
|
+
Auto-detects and fixes Django ORM performance issues before they reach production.
|
|
9
|
+
|
|
10
|
+
## Activation Triggers
|
|
11
|
+
|
|
12
|
+
This skill activates when:
|
|
13
|
+
- Writing ORM queries
|
|
14
|
+
- Accessing related objects (foreign keys, M2M)
|
|
15
|
+
- Creating views that fetch data
|
|
16
|
+
- Mentioning "slow", "performance", "optimization"
|
|
17
|
+
- Looping over querysets
|
|
18
|
+
- Creating list/detail views
|
|
19
|
+
|
|
20
|
+
## Performance Targets
|
|
21
|
+
|
|
22
|
+
- ✅ NO N+1 queries
|
|
23
|
+
- ✅ Appropriate indexes on frequently queried fields
|
|
24
|
+
- ✅ Pagination for list endpoints
|
|
25
|
+
- ✅ `select_related()` for foreign keys
|
|
26
|
+
- ✅ `prefetch_related()` for reverse foreign keys and M2M
|
|
27
|
+
- ✅ Caching for expensive operations
|
|
28
|
+
|
|
29
|
+
## Auto-Detection Patterns
|
|
30
|
+
|
|
31
|
+
### Pattern 1: N+1 Query Detection
|
|
32
|
+
|
|
33
|
+
**❌ INEFFICIENT (N+1 query):**
|
|
34
|
+
```python
|
|
35
|
+
# Bad: 1 query for users + N queries for organizations
|
|
36
|
+
users = User.objects.all() # 1 query
|
|
37
|
+
for user in users: # N queries
|
|
38
|
+
print(user.organization.name) # Hits DB each time!
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**✅ OPTIMIZED:**
|
|
42
|
+
```python
|
|
43
|
+
# Good: 2 queries total (1 for users + 1 JOIN for organizations)
|
|
44
|
+
users = User.objects.select_related('organization').all()
|
|
45
|
+
for user in users:
|
|
46
|
+
print(user.organization.name) # No DB hit!
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Pattern 2: Reverse Foreign Key N+1
|
|
50
|
+
|
|
51
|
+
**❌ INEFFICIENT:**
|
|
52
|
+
```python
|
|
53
|
+
# Bad: 1 + N queries
|
|
54
|
+
organizations = Organization.objects.all() # 1 query
|
|
55
|
+
for org in organizations: # N queries
|
|
56
|
+
users = org.users.all() # Hits DB each time!
|
|
57
|
+
print(f"{org.name}: {users.count()} users")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**✅ OPTIMIZED:**
|
|
61
|
+
```python
|
|
62
|
+
# Good: 2 queries total
|
|
63
|
+
organizations = Organization.objects.prefetch_related('users').all()
|
|
64
|
+
for org in organizations:
|
|
65
|
+
users = org.users.all() # Pre-fetched!
|
|
66
|
+
print(f"{org.name}: {users.count()} users")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Pattern 3: Many-to-Many N+1
|
|
70
|
+
|
|
71
|
+
**❌ INEFFICIENT:**
|
|
72
|
+
```python
|
|
73
|
+
# Bad: 1 + N queries
|
|
74
|
+
users = User.objects.all() # 1 query
|
|
75
|
+
for user in users: # N queries
|
|
76
|
+
roles = user.roles.all() # Hits DB each time!
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**✅ OPTIMIZED:**
|
|
80
|
+
```python
|
|
81
|
+
# Good: 2 queries total
|
|
82
|
+
users = User.objects.prefetch_related('roles').all()
|
|
83
|
+
for user in users:
|
|
84
|
+
roles = user.roles.all() # Pre-fetched!
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Pattern 4: Missing Indexes
|
|
88
|
+
|
|
89
|
+
**❌ SLOW:**
|
|
90
|
+
```python
|
|
91
|
+
# Frequent query without index
|
|
92
|
+
User.objects.filter(email='test@example.com') # Table scan if no index!
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**✅ FAST:**
|
|
96
|
+
```python
|
|
97
|
+
# Model with index
|
|
98
|
+
class User(models.Model):
|
|
99
|
+
email = models.EmailField(unique=True) # Unique creates index
|
|
100
|
+
# OR
|
|
101
|
+
class Meta:
|
|
102
|
+
indexes = [
|
|
103
|
+
models.Index(fields=['email']), # Explicit index
|
|
104
|
+
]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Pattern 5: Missing Pagination
|
|
108
|
+
|
|
109
|
+
**❌ DANGEROUS:**
|
|
110
|
+
```python
|
|
111
|
+
# Returns ALL records - memory issues with large tables
|
|
112
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
113
|
+
queryset = User.objects.all() # No pagination!
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**✅ SAFE:**
|
|
117
|
+
```python
|
|
118
|
+
# Paginated automatically
|
|
119
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
120
|
+
queryset = User.objects.all()
|
|
121
|
+
pagination_class = PageNumberPagination # DRF auto-paginates
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Auto-Fix Process
|
|
125
|
+
|
|
126
|
+
### Step 1: Detect Anti-Pattern
|
|
127
|
+
|
|
128
|
+
When detecting:
|
|
129
|
+
```python
|
|
130
|
+
def get_users_with_organizations(self):
|
|
131
|
+
users = User.objects.all()
|
|
132
|
+
return [
|
|
133
|
+
{'user': u.email, 'org': u.organization.name}
|
|
134
|
+
for u in users
|
|
135
|
+
]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Step 2: Analyze Query Pattern
|
|
139
|
+
|
|
140
|
+
Identify:
|
|
141
|
+
- Queryset: `User.objects.all()` (1 query)
|
|
142
|
+
- Foreign key access: `u.organization.name` (N queries)
|
|
143
|
+
- **Total:** 1 + N queries = N+1 problem!
|
|
144
|
+
|
|
145
|
+
### Step 3: Suggest Optimization
|
|
146
|
+
|
|
147
|
+
Provide:
|
|
148
|
+
|
|
149
|
+
> **N+1 Query Detected!**
|
|
150
|
+
>
|
|
151
|
+
> **Problem:**
|
|
152
|
+
> - Query: `User.objects.all()` → 1 query
|
|
153
|
+
> - Loop accesses `user.organization.name` → N queries
|
|
154
|
+
> - **Total:** 1 + N queries (inefficient!)
|
|
155
|
+
>
|
|
156
|
+
> **Impact:**
|
|
157
|
+
> - 100 users = 101 queries 😱
|
|
158
|
+
> - 1000 users = 1001 queries 🔥
|
|
159
|
+
>
|
|
160
|
+
> **Fix:**
|
|
161
|
+
> ```python
|
|
162
|
+
> users = User.objects.select_related('organization').all()
|
|
163
|
+
> # Now only 2 queries total!
|
|
164
|
+
> ```
|
|
165
|
+
>
|
|
166
|
+
> **Why:**
|
|
167
|
+
> - `select_related()` performs SQL JOIN
|
|
168
|
+
> - Fetches related data in single query
|
|
169
|
+
> - 500x faster for 1000 records!
|
|
170
|
+
|
|
171
|
+
### Step 4: Auto-Apply Fix
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
# Optimized version
|
|
175
|
+
def get_users_with_organizations(self):
|
|
176
|
+
users = User.objects.select_related('organization').all()
|
|
177
|
+
return [
|
|
178
|
+
{'user': u.email, 'org': u.organization.name} # No DB hit!
|
|
179
|
+
for u in users
|
|
180
|
+
]
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Common Optimization Patterns
|
|
184
|
+
|
|
185
|
+
### Foreign Key (One-to-One, Many-to-One)
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
# Use select_related() for foreign keys
|
|
189
|
+
User.objects.select_related('organization', 'created_by')
|
|
190
|
+
|
|
191
|
+
# Multiple levels
|
|
192
|
+
User.objects.select_related('organization__country')
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Reverse Foreign Key (One-to-Many)
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
# Use prefetch_related() for reverse FK
|
|
199
|
+
Organization.objects.prefetch_related('users')
|
|
200
|
+
|
|
201
|
+
# With filtering
|
|
202
|
+
from django.db.models import Prefetch
|
|
203
|
+
Organization.objects.prefetch_related(
|
|
204
|
+
Prefetch('users', queryset=User.objects.filter(is_active=True))
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Many-to-Many
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
# Use prefetch_related() for M2M
|
|
212
|
+
User.objects.prefetch_related('roles', 'permissions')
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Combined Optimization
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
# Optimize multiple relations
|
|
219
|
+
users = User.objects.select_related(
|
|
220
|
+
'organization', # FK
|
|
221
|
+
'created_by' # FK
|
|
222
|
+
).prefetch_related(
|
|
223
|
+
'roles', # M2M
|
|
224
|
+
'permissions' # M2M
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Index Recommendations
|
|
229
|
+
|
|
230
|
+
Suggest indexes for:
|
|
231
|
+
|
|
232
|
+
### Frequently Filtered Fields
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
class User(models.Model):
|
|
236
|
+
email = models.EmailField()
|
|
237
|
+
status = models.CharField(max_length=20)
|
|
238
|
+
|
|
239
|
+
class Meta:
|
|
240
|
+
indexes = [
|
|
241
|
+
models.Index(fields=['email']), # WHERE email = ...
|
|
242
|
+
models.Index(fields=['status']), # WHERE status = ...
|
|
243
|
+
]
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Composite Indexes
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
class Order(models.Model):
|
|
250
|
+
user = models.ForeignKey(User)
|
|
251
|
+
status = models.CharField(max_length=20)
|
|
252
|
+
created_at = models.DateTimeField()
|
|
253
|
+
|
|
254
|
+
class Meta:
|
|
255
|
+
indexes = [
|
|
256
|
+
# Composite index for common query pattern
|
|
257
|
+
models.Index(fields=['user', 'status']),
|
|
258
|
+
# Index for time-based queries
|
|
259
|
+
models.Index(fields=['-created_at']), # DESC order
|
|
260
|
+
]
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Foreign Key Indexes
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
# Django automatically creates indexes for:
|
|
267
|
+
# - Primary keys
|
|
268
|
+
# - Unique fields
|
|
269
|
+
# - Foreign keys
|
|
270
|
+
|
|
271
|
+
# Manual indexes needed for:
|
|
272
|
+
# - Composite queries
|
|
273
|
+
# - Ordering fields
|
|
274
|
+
# - Search fields
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Caching Strategies
|
|
278
|
+
|
|
279
|
+
### Query Caching
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from django.core.cache import cache
|
|
283
|
+
|
|
284
|
+
def get_active_users():
|
|
285
|
+
cache_key = 'active_users'
|
|
286
|
+
users = cache.get(cache_key)
|
|
287
|
+
|
|
288
|
+
if users is None:
|
|
289
|
+
users = list(User.objects.filter(is_active=True).values())
|
|
290
|
+
cache.set(cache_key, users, 300) # Cache 5 minutes
|
|
291
|
+
|
|
292
|
+
return users
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Model Method Caching
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
from django.utils.functional import cached_property
|
|
299
|
+
|
|
300
|
+
class User(models.Model):
|
|
301
|
+
@cached_property
|
|
302
|
+
def full_name(self):
|
|
303
|
+
"""Expensive computation cached per instance."""
|
|
304
|
+
return f"{self.first_name} {self.last_name}".strip()
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Pagination Patterns
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
# DRF Pagination
|
|
311
|
+
from rest_framework.pagination import PageNumberPagination
|
|
312
|
+
|
|
313
|
+
class StandardResultsSetPagination(PageNumberPagination):
|
|
314
|
+
page_size = 100
|
|
315
|
+
page_size_query_param = 'page_size'
|
|
316
|
+
max_page_size = 1000
|
|
317
|
+
|
|
318
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
319
|
+
pagination_class = StandardResultsSetPagination
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Database Query Analysis
|
|
323
|
+
|
|
324
|
+
Check queries using Django Debug Toolbar patterns:
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from django.db import connection
|
|
328
|
+
from django.test.utils import override_settings
|
|
329
|
+
|
|
330
|
+
@override_settings(DEBUG=True)
|
|
331
|
+
def test_user_list_queries(self):
|
|
332
|
+
"""Test that user list doesn't have N+1 queries."""
|
|
333
|
+
with self.assertNumQueries(2): # Should be exactly 2 queries
|
|
334
|
+
response = self.client.get('/api/users/')
|
|
335
|
+
# 1 query: Fetch users
|
|
336
|
+
# 1 query: Fetch organizations (prefetched)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Performance Checklist
|
|
340
|
+
|
|
341
|
+
For every queryset, verify:
|
|
342
|
+
|
|
343
|
+
- ✅ `select_related()` for accessed foreign keys
|
|
344
|
+
- ✅ `prefetch_related()` for reverse FKs and M2M
|
|
345
|
+
- ✅ `.only()` or `.defer()` if fetching many fields
|
|
346
|
+
- ✅ Indexes on filtered/ordered fields
|
|
347
|
+
- ✅ Pagination for lists
|
|
348
|
+
- ✅ `.count()` instead of `len(queryset)`
|
|
349
|
+
- ✅ `.exists()` instead of `if queryset`
|
|
350
|
+
- ✅ Bulk operations instead of loops
|
|
351
|
+
|
|
352
|
+
## Before/After Examples
|
|
353
|
+
|
|
354
|
+
### Example 1: User List with Organization
|
|
355
|
+
|
|
356
|
+
**❌ Before (N+1):**
|
|
357
|
+
```python
|
|
358
|
+
# views.py
|
|
359
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
360
|
+
queryset = User.objects.all() # 1 query
|
|
361
|
+
# serializer accesses user.organization.name → N queries
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**✅ After (Optimized):**
|
|
365
|
+
```python
|
|
366
|
+
# views.py
|
|
367
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
368
|
+
queryset = User.objects.select_related('organization').all() # 2 queries total
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Example 2: Organization with Users
|
|
372
|
+
|
|
373
|
+
**❌ Before (N+1):**
|
|
374
|
+
```python
|
|
375
|
+
def organization_summary(self):
|
|
376
|
+
orgs = Organization.objects.all() # 1 query
|
|
377
|
+
return [
|
|
378
|
+
{
|
|
379
|
+
'name': org.name,
|
|
380
|
+
'user_count': org.users.count() # N queries!
|
|
381
|
+
}
|
|
382
|
+
for org in orgs
|
|
383
|
+
]
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**✅ After (Optimized):**
|
|
387
|
+
```python
|
|
388
|
+
from django.db.models import Count
|
|
389
|
+
|
|
390
|
+
def organization_summary(self):
|
|
391
|
+
orgs = Organization.objects.annotate(
|
|
392
|
+
user_count=Count('users') # Single query with aggregation
|
|
393
|
+
).all()
|
|
394
|
+
return [
|
|
395
|
+
{
|
|
396
|
+
'name': org.name,
|
|
397
|
+
'user_count': org.user_count # No DB hit!
|
|
398
|
+
}
|
|
399
|
+
for org in orgs
|
|
400
|
+
]
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Integration with Testing
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
# Test that optimizations work
|
|
407
|
+
@pytest.mark.django_db
|
|
408
|
+
def test_user_list_performance(django_assert_num_queries):
|
|
409
|
+
"""User list should use select_related to avoid N+1."""
|
|
410
|
+
UserFactory.create_batch(100) # Create 100 users
|
|
411
|
+
|
|
412
|
+
with django_assert_num_queries(2): # Only 2 queries allowed
|
|
413
|
+
users = list(User.objects.select_related('organization').all())
|
|
414
|
+
for user in users:
|
|
415
|
+
_ = user.organization.name # Should not hit DB
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Success Criteria
|
|
419
|
+
|
|
420
|
+
✅ NO N+1 queries in codebase
|
|
421
|
+
✅ All foreign key access uses `select_related()`
|
|
422
|
+
✅ All reverse FK/M2M use `prefetch_related()`
|
|
423
|
+
✅ Appropriate indexes on all models
|
|
424
|
+
✅ Pagination on all list endpoints
|
|
425
|
+
✅ Bulk operations used instead of loops
|
|
426
|
+
|
|
427
|
+
## Behavior
|
|
428
|
+
|
|
429
|
+
**Proactive enforcement:**
|
|
430
|
+
- Detect N+1 queries automatically
|
|
431
|
+
- Suggest optimizations immediately
|
|
432
|
+
- Calculate performance impact (queries before/after)
|
|
433
|
+
- Add indexes to models
|
|
434
|
+
- Explain WHY the optimization matters
|
|
435
|
+
|
|
436
|
+
**Never:**
|
|
437
|
+
- Require explicit "optimize queries" request
|
|
438
|
+
- Wait for production slowness
|
|
439
|
+
- Just warn without fixing
|
|
440
|
+
|
|
441
|
+
**Block completion if:**
|
|
442
|
+
- N+1 queries detected
|
|
443
|
+
- Missing indexes on frequently queried fields
|
|
444
|
+
- No pagination on list endpoints
|
|
445
|
+
- Bulk creates/updates done in loops
|
|
446
|
+
|
|
447
|
+
This ensures code is performant from day one.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: red-phase-verifier
|
|
3
|
+
description: This skill should be used when the user asks to "write tests first", "run TDD", "verify red phase", "check failing tests", or when practicing test-driven development. Ensures tests fail before implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Red Phase Verifier
|
|
7
|
+
|
|
8
|
+
Ensures tests are written BEFORE implementation and fail initially.
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
In TDD:
|
|
13
|
+
1. **RED**: Write failing tests
|
|
14
|
+
2. **GREEN**: Implement to pass
|
|
15
|
+
3. **REFACTOR**: Improve while green
|
|
16
|
+
|
|
17
|
+
This skill enforces Step 1.
|
|
18
|
+
|
|
19
|
+
## Activation Triggers
|
|
20
|
+
|
|
21
|
+
This skill activates when:
|
|
22
|
+
- Running /dev-loop command
|
|
23
|
+
- Creating test files before implementation
|
|
24
|
+
- Mentioning "write tests first"
|
|
25
|
+
|
|
26
|
+
## Verification Process
|
|
27
|
+
|
|
28
|
+
### Step 1: Check Implementation Exists
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# If implementing UserService.create_user:
|
|
32
|
+
# Check if method exists and has logic
|
|
33
|
+
|
|
34
|
+
import inspect
|
|
35
|
+
from app.services import UserService
|
|
36
|
+
|
|
37
|
+
method = getattr(UserService, 'create_user', None)
|
|
38
|
+
if method:
|
|
39
|
+
source = inspect.getsource(method)
|
|
40
|
+
if 'pass' not in source and 'raise NotImplementedError' not in source:
|
|
41
|
+
# Implementation exists!
|
|
42
|
+
WARN: "Implementation found before tests"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Step 2: Run Tests
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pytest tests/test_new_feature.py -v --tb=short
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Step 3: Verify Failures
|
|
52
|
+
|
|
53
|
+
Expected output:
|
|
54
|
+
```
|
|
55
|
+
FAILED tests/test_new_feature.py::test_create - AssertionError
|
|
56
|
+
FAILED tests/test_new_feature.py::test_validate - AttributeError
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Step 4: Report
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
RED PHASE VERIFICATION
|
|
63
|
+
|
|
64
|
+
Tests written: 5
|
|
65
|
+
Tests failing: 5
|
|
66
|
+
|
|
67
|
+
Red phase confirmed!
|
|
68
|
+
Implementation may now proceed.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Warning Cases
|
|
72
|
+
|
|
73
|
+
### Case 1: Tests Pass Before Implementation
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
WARNING: Tests passed before implementation!
|
|
77
|
+
|
|
78
|
+
Failing tests: 0/5
|
|
79
|
+
|
|
80
|
+
This indicates:
|
|
81
|
+
1. Tests don't test new functionality
|
|
82
|
+
2. Tests have trivial assertions
|
|
83
|
+
3. Implementation already exists
|
|
84
|
+
|
|
85
|
+
Action: Regenerate stricter tests
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Case 2: Partial Failures
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
PARTIAL RED PHASE
|
|
92
|
+
|
|
93
|
+
Tests failing: 3/5
|
|
94
|
+
Tests passing: 2/5
|
|
95
|
+
|
|
96
|
+
Passing tests may be:
|
|
97
|
+
1. Testing existing functionality (OK)
|
|
98
|
+
2. Trivial assertions (NOT OK)
|
|
99
|
+
|
|
100
|
+
Review passing tests:
|
|
101
|
+
- test_helper_exists: assert helper <- TRIVIAL
|
|
102
|
+
- test_constant: assert X == X <- TRIVIAL
|
|
103
|
+
|
|
104
|
+
Action: Strengthen or remove trivial tests
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Case 3: Wrong Failure Type
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
UNEXPECTED FAILURE TYPE
|
|
111
|
+
|
|
112
|
+
test_create_user: SyntaxError in test file
|
|
113
|
+
|
|
114
|
+
Tests should fail due to:
|
|
115
|
+
- AssertionError (expected behavior not met)
|
|
116
|
+
- AttributeError (method doesn't exist yet)
|
|
117
|
+
- NotImplementedError (placeholder)
|
|
118
|
+
|
|
119
|
+
NOT due to:
|
|
120
|
+
- SyntaxError (test file broken)
|
|
121
|
+
- ImportError (missing dependency)
|
|
122
|
+
- TypeError (wrong arguments)
|
|
123
|
+
|
|
124
|
+
Action: Fix test file syntax
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Enforcement
|
|
128
|
+
|
|
129
|
+
When red phase not verified:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
CANNOT PROCEED TO GREEN PHASE
|
|
133
|
+
|
|
134
|
+
Red phase requirements not met:
|
|
135
|
+
- 2 tests passed before implementation
|
|
136
|
+
- 1 test has syntax error
|
|
137
|
+
|
|
138
|
+
Fix issues, then run verification again.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Expected Failure Types
|
|
142
|
+
|
|
143
|
+
### Good Failures (Proceed)
|
|
144
|
+
|
|
145
|
+
- `AssertionError` - Assertion failed (expected)
|
|
146
|
+
- `AttributeError` - Method/attribute doesn't exist yet
|
|
147
|
+
- `NotImplementedError` - Placeholder implementation
|
|
148
|
+
- `ModuleNotFoundError` - Module not created yet
|
|
149
|
+
|
|
150
|
+
### Bad Failures (Fix First)
|
|
151
|
+
|
|
152
|
+
- `SyntaxError` - Test code is broken
|
|
153
|
+
- `IndentationError` - Test code formatting issue
|
|
154
|
+
- `NameError` - Undefined variable in test
|
|
155
|
+
- `TypeError` - Wrong arguments in test setup
|
|
156
|
+
|
|
157
|
+
## Dev Loop Integration
|
|
158
|
+
|
|
159
|
+
When integrated with /dev-loop:
|
|
160
|
+
|
|
161
|
+
1. Tests written -> Run red phase verifier
|
|
162
|
+
2. If all fail correctly -> Proceed to implementation
|
|
163
|
+
3. If some pass -> Warn and suggest fixes
|
|
164
|
+
4. If wrong failures -> Block until fixed
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
TDD LOOP: RED PHASE
|
|
168
|
+
|
|
169
|
+
Running red phase verification...
|
|
170
|
+
|
|
171
|
+
test_create_user: FAILED (AttributeError)
|
|
172
|
+
test_validate_email: FAILED (AssertionError)
|
|
173
|
+
test_duplicate_email: FAILED (AssertionError)
|
|
174
|
+
test_get_user: FAILED (NotImplementedError)
|
|
175
|
+
test_list_users: FAILED (NotImplementedError)
|
|
176
|
+
|
|
177
|
+
Red phase: 5/5 tests failing correctly
|
|
178
|
+
|
|
179
|
+
Proceeding to GREEN phase...
|
|
180
|
+
```
|