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,473 @@
1
+ ---
2
+ id: sqlalchemy
3
+ version: 1.0.0
4
+ name: SQLAlchemy
5
+ description: >
6
+ SQLAlchemy 2.x patterns for async FastAPI projects: models, sessions, queries,
7
+ bulk operations, transactions, and Alembic migrations.
8
+ Trigger: When writing models, queries, bulk operations, or DB-touching service code.
9
+
10
+ user-invokable: true
11
+ license: MIT
12
+ metadata:
13
+ author: lgzarturo
14
+ category: database
15
+
16
+ compatibility:
17
+ tools: [claude, codex, gemini, agy, opencode]
18
+ stacks:
19
+ languages: [python]
20
+ frameworks: [sqlalchemy, alembic, asyncpg, fastapi]
21
+
22
+ risk:
23
+ level: medium
24
+ can_execute_shell: true
25
+ can_modify_files: true
26
+ requires_network: false
27
+
28
+ inputs:
29
+ - model files (models/*.py)
30
+ - service files with DB queries (services/*.py)
31
+ - migration files (alembic/versions/*.py)
32
+ - database setup (db.py)
33
+
34
+ outputs:
35
+ - DeclarativeBase model definitions (SQLAlchemy 2.x style)
36
+ - AsyncSession setup and get_db dependency
37
+ - select() query patterns with scalars, scalar_one, scalar_one_or_none
38
+ - bulk insert/update via execute()
39
+ - transaction-safe async service methods
40
+ - Alembic async env.py configuration
41
+ - anti-pattern corrections (lazy loading, N+1, commit in service)
42
+
43
+ quality:
44
+ reviewed_by: codeconductor-core
45
+ version: 0.1.0
46
+ paths:
47
+ - "**/*.py"
48
+ ---
49
+ ## When to Use
50
+
51
+ - Defining new SQLAlchemy models
52
+ - Writing query logic in services
53
+ - Implementing bulk operations
54
+ - Setting up or modifying Alembic migrations
55
+ - Debugging N+1 queries or session issues
56
+
57
+ ## Engine and Session Setup
58
+
59
+ ```python
60
+ # db.py
61
+ from sqlalchemy.ext.asyncio import (
62
+ AsyncSession,
63
+ async_sessionmaker,
64
+ create_async_engine,
65
+ )
66
+ from src.config import settings
67
+
68
+ engine = create_async_engine(
69
+ settings.database_url, # postgresql+asyncpg://user:pass@host/db
70
+ echo=settings.debug,
71
+ pool_size=10,
72
+ max_overflow=20,
73
+ )
74
+
75
+ async_session_factory = async_sessionmaker(
76
+ engine,
77
+ class_=AsyncSession,
78
+ expire_on_commit=False, # required for async — objects usable after commit
79
+ )
80
+ ```
81
+
82
+ `expire_on_commit=False` is mandatory in async SQLAlchemy. Without it, accessing
83
+ attributes after commit triggers lazy load → `MissingGreenlet` error.
84
+
85
+ ## Model Base
86
+
87
+ ```python
88
+ # models/base.py
89
+ from datetime import datetime
90
+ from sqlalchemy import func
91
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
92
+
93
+
94
+ class Base(DeclarativeBase):
95
+ pass
96
+
97
+
98
+ class TimestampMixin:
99
+ created_at: Mapped[datetime] = mapped_column(
100
+ server_default=func.now(), nullable=False
101
+ )
102
+ updated_at: Mapped[datetime] = mapped_column(
103
+ server_default=func.now(), onupdate=func.now(), nullable=False
104
+ )
105
+ ```
106
+
107
+ All models inherit from `Base`. All persistent entities include
108
+ `TimestampMixin`.
109
+
110
+ ## Model Definition (SQLAlchemy 2.x style)
111
+
112
+ ```python
113
+ # models/product.py
114
+ from decimal import Decimal
115
+ from sqlalchemy import String, Numeric, ForeignKey, Index
116
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
117
+ from src.models.base import Base, TimestampMixin
118
+
119
+
120
+ class Product(Base, TimestampMixin):
121
+ __tablename__ = "products"
122
+ __table_args__ = (
123
+ Index("ix_products_is_active_stock", "is_active", "stock"),
124
+ )
125
+
126
+ id: Mapped[int] = mapped_column(primary_key=True)
127
+ name: Mapped[str] = mapped_column(String(255), nullable=False)
128
+ slug: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
129
+ price: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False)
130
+ stock: Mapped[int] = mapped_column(default=0, nullable=False)
131
+ is_active: Mapped[bool] = mapped_column(default=True, nullable=False)
132
+
133
+ category_id: Mapped[int | None] = mapped_column(ForeignKey("categories.id"))
134
+ category: Mapped["Category | None"] = relationship(
135
+ back_populates="products", lazy="raise"
136
+ )
137
+
138
+ def __repr__(self) -> str:
139
+ return f"<Product id={self.id} name={self.name!r}>"
140
+ ```
141
+
142
+ **Rules**:
143
+
144
+ - Use `Mapped[T]` and `mapped_column()` — never the old `Column()` style
145
+ - Always set `lazy="raise"` on relationships — prevents accidental lazy loads
146
+ - Always define `__tablename__` explicitly
147
+ - Add `Index` in `__table_args__` for composite or frequently filtered columns
148
+
149
+ ## Relationships
150
+
151
+ ```python
152
+ # One-to-many: Category → Products
153
+ class Category(Base, TimestampMixin):
154
+ __tablename__ = "categories"
155
+
156
+ id: Mapped[int] = mapped_column(primary_key=True)
157
+ name: Mapped[str] = mapped_column(String(100), nullable=False)
158
+
159
+ products: Mapped[list["Product"]] = relationship(
160
+ back_populates="category", lazy="raise"
161
+ )
162
+
163
+
164
+ # Many-to-many via association table
165
+ from sqlalchemy import Table, Column
166
+
167
+ product_tags = Table(
168
+ "product_tags",
169
+ Base.metadata,
170
+ Column("product_id", ForeignKey("products.id"), primary_key=True),
171
+ Column("tag_id", ForeignKey("tags.id"), primary_key=True),
172
+ )
173
+
174
+
175
+ class Product(Base, TimestampMixin):
176
+ ...
177
+ tags: Mapped[list["Tag"]] = relationship(
178
+ secondary=product_tags, back_populates="products", lazy="raise"
179
+ )
180
+ ```
181
+
182
+ Always use `back_populates` (explicit) — never `backref` (implicit, hard to
183
+ trace). `lazy="raise"` catches N+1 at runtime instead of silently degrading
184
+ performance.
185
+
186
+ ## Queries
187
+
188
+ ```python
189
+ from sqlalchemy import select, func
190
+ from sqlalchemy.ext.asyncio import AsyncSession
191
+ from sqlalchemy.orm import selectinload
192
+
193
+
194
+ # Get by PK — fastest for single object
195
+ product = await db.get(Product, product_id)
196
+
197
+ # Select with filter
198
+ result = await db.execute(select(Product).where(Product.is_active == True))
199
+ products = result.scalars().all()
200
+
201
+ # Single result — raises if 0 or >1
202
+ result = await db.execute(select(Product).where(Product.slug == slug))
203
+ product = result.scalar_one()
204
+
205
+ # Optional single result — returns None if not found
206
+ result = await db.execute(select(Product).where(Product.id == product_id))
207
+ product = result.scalar_one_or_none()
208
+
209
+ # With eager load (avoids lazy load with lazy="raise")
210
+ result = await db.execute(
211
+ select(Product)
212
+ .where(Product.is_active == True)
213
+ .options(selectinload(Product.category))
214
+ .order_by(Product.name)
215
+ )
216
+ products = result.scalars().all()
217
+
218
+ # Count
219
+ result = await db.execute(select(func.count()).select_from(Product))
220
+ total = result.scalar_one()
221
+ ```
222
+
223
+ **Eager loading options**:
224
+
225
+ | Method | Use |
226
+ | -------------- | --------------------------------------------- |
227
+ | `selectinload` | One-to-many, many-to-many — separate IN query |
228
+ | `joinedload` | Many-to-one (FK) — JOIN in same query |
229
+ | `subqueryload` | Large collections — subquery approach |
230
+
231
+ Default to `selectinload` for collections, `joinedload` for FK parents.
232
+
233
+ ## Write Operations
234
+
235
+ ### Create
236
+
237
+ ```python
238
+ # Single
239
+ product = Product(name="Laptop", price=Decimal("999.99"), stock=10)
240
+ db.add(product)
241
+ await db.flush() # assigns id without committing
242
+ await db.refresh(product) # reload after flush if needed
243
+ return product
244
+
245
+ # Bulk insert
246
+ products = [Product(name=n, price=p) for n, p in data]
247
+ db.add_all(products)
248
+ await db.flush()
249
+ ```
250
+
251
+ ### Update
252
+
253
+ ```python
254
+ from sqlalchemy import update
255
+
256
+ # Object-based (for single row with loaded object)
257
+ product.stock -= quantity
258
+ product.updated_at = datetime.utcnow()
259
+ await db.flush()
260
+
261
+ # Bulk update via execute (no objects loaded — efficient)
262
+ await db.execute(
263
+ update(Product)
264
+ .where(Product.category_id == category_id)
265
+ .values(is_active=False)
266
+ )
267
+ ```
268
+
269
+ ### Delete
270
+
271
+ ```python
272
+ from sqlalchemy import delete
273
+
274
+ # Single (object already loaded)
275
+ await db.delete(product)
276
+
277
+ # Bulk delete
278
+ await db.execute(delete(Product).where(Product.stock == 0))
279
+ ```
280
+
281
+ **Rule**: Never call `session.commit()` in services — commit belongs in
282
+ `get_db`. Use `await db.flush()` to flush to DB within a transaction without
283
+ committing.
284
+
285
+ ## Transactions
286
+
287
+ The `get_db` dependency owns the transaction. For nested operations requiring an
288
+ explicit savepoint:
289
+
290
+ ```python
291
+ from sqlalchemy.ext.asyncio import AsyncSession
292
+
293
+
294
+ async def transfer_stock(
295
+ db: AsyncSession, from_id: int, to_id: int, quantity: int
296
+ ) -> None:
297
+ async with db.begin_nested(): # SAVEPOINT
298
+ source = await db.get(Product, from_id)
299
+ target = await db.get(Product, to_id)
300
+
301
+ if source.stock < quantity:
302
+ raise ValueError("insufficient stock")
303
+
304
+ source.stock -= quantity
305
+ target.stock += quantity
306
+ await db.flush()
307
+ ```
308
+
309
+ Use `begin_nested()` for savepoints within an existing transaction. Never call
310
+ `db.begin()` inside a service — the session is already in a transaction.
311
+
312
+ ## Alembic Setup (Async)
313
+
314
+ ```python
315
+ # alembic/env.py
316
+ import asyncio
317
+ from logging.config import fileConfig
318
+ from sqlalchemy import pool
319
+ from sqlalchemy.ext.asyncio import async_engine_from_config
320
+ from alembic import context
321
+ from src.models.base import Base
322
+ from src.config import settings
323
+
324
+ config = context.config
325
+ config.set_main_option("sqlalchemy.url", settings.database_url)
326
+
327
+ if config.config_file_name is not None:
328
+ fileConfig(config.config_file_name)
329
+
330
+ target_metadata = Base.metadata
331
+
332
+
333
+ def run_migrations_offline() -> None:
334
+ url = config.get_main_option("sqlalchemy.url")
335
+ context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
336
+ with context.begin_transaction():
337
+ context.run_migrations()
338
+
339
+
340
+ def do_run_migrations(connection):
341
+ context.configure(connection=connection, target_metadata=target_metadata)
342
+ with context.begin_transaction():
343
+ context.run_migrations()
344
+
345
+
346
+ async def run_async_migrations() -> None:
347
+ connectable = async_engine_from_config(
348
+ config.get_section(config.config_ini_section, {}),
349
+ prefix="sqlalchemy.",
350
+ poolclass=pool.NullPool,
351
+ )
352
+ async with connectable.connect() as connection:
353
+ await connection.run_sync(do_run_migrations)
354
+ await connectable.dispose()
355
+
356
+
357
+ def run_migrations_online() -> None:
358
+ asyncio.run(run_async_migrations())
359
+
360
+
361
+ if context.is_offline_mode():
362
+ run_migrations_offline()
363
+ else:
364
+ run_migrations_online()
365
+ ```
366
+
367
+ ## Alembic Commands
368
+
369
+ ```bash
370
+ # Generate migration from model changes
371
+ alembic revision --autogenerate -m "add product slug"
372
+
373
+ # Apply all pending migrations
374
+ alembic upgrade head
375
+
376
+ # Rollback one migration
377
+ alembic downgrade -1
378
+
379
+ # Show current revision
380
+ alembic current
381
+
382
+ # Show migration history
383
+ alembic history --verbose
384
+ ```
385
+
386
+ **Always review autogenerated migrations** before applying. Alembic misses:
387
+
388
+ - `server_default` changes on existing columns
389
+ - Index changes inside `__table_args__`
390
+ - Column type changes on some dialects
391
+
392
+ ## Anti-Patterns
393
+
394
+ ### Lazy load with lazy="raise" (will crash)
395
+
396
+ ```python
397
+ # WRONG — lazy="raise" means this raises MissingGreenlet or InvalidRequestError
398
+ products = result.scalars().all()
399
+ for p in products:
400
+ print(p.category.name) # ERROR
401
+
402
+ # CORRECT — eager load upfront
403
+ result = await db.execute(
404
+ select(Product).options(selectinload(Product.category))
405
+ )
406
+ products = result.scalars().all()
407
+ for p in products:
408
+ print(p.category.name) # OK
409
+ ```
410
+
411
+ ### Commit in service
412
+
413
+ ```python
414
+ # WRONG — service owns the transaction
415
+ class ProductService:
416
+ @staticmethod
417
+ async def create(db: AsyncSession, payload: ProductCreate) -> Product:
418
+ product = Product(**payload.model_dump())
419
+ db.add(product)
420
+ await db.commit() # WRONG — get_db should commit
421
+ return product
422
+
423
+ # CORRECT — flush only, commit in get_db
424
+ class ProductService:
425
+ @staticmethod
426
+ async def create(db: AsyncSession, payload: ProductCreate) -> Product:
427
+ product = Product(**payload.model_dump())
428
+ db.add(product)
429
+ await db.flush()
430
+ await db.refresh(product)
431
+ return product
432
+ ```
433
+
434
+ ### expire_on_commit=True (default) in async
435
+
436
+ ```python
437
+ # WRONG — accessing attributes after commit triggers lazy load → MissingGreenlet
438
+ async_session_factory = async_sessionmaker(engine, class_=AsyncSession)
439
+ # expire_on_commit defaults to True
440
+
441
+ # After commit:
442
+ await db.commit()
443
+ print(product.name) # MissingGreenlet error
444
+
445
+ # CORRECT
446
+ async_session_factory = async_sessionmaker(
447
+ engine, class_=AsyncSession, expire_on_commit=False
448
+ )
449
+ ```
450
+
451
+ ### N+1 queries
452
+
453
+ ```python
454
+ # WRONG — 1 query for products + N queries for categories
455
+ products = (await db.execute(select(Product))).scalars().all()
456
+ for p in products:
457
+ print(p.category.name) # N queries
458
+
459
+ # CORRECT — 2 queries total
460
+ products = (
461
+ await db.execute(select(Product).options(selectinload(Product.category)))
462
+ ).scalars().all()
463
+ for p in products:
464
+ print(p.category.name) # no extra queries
465
+ ```
466
+
467
+ ## Resources
468
+
469
+ - **SQLAlchemy 2.x docs**: <https://docs.sqlalchemy.org/en/20/>
470
+ - **Async SQLAlchemy guide**:
471
+ <https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html>
472
+ - **Alembic docs**: <https://alembic.sqlalchemy.org/en/latest/>
473
+ - **asyncpg driver**: <https://magicstack.github.io/asyncpg/>
@@ -0,0 +1,32 @@
1
+ ---
2
+ id: tailwind-responsive-auditor
3
+ version: 1.0.0
4
+ name: Tailwind Responsive Auditor
5
+ description: >
6
+ Audits Tailwind CSS usage ensuring mobile-first responsive utilities and clean classes.
7
+ user-invokable: true
8
+ license: MIT
9
+ metadata:
10
+ author: lgzarturo
11
+ category: frontend
12
+ compatibility:
13
+ tools: [claude, codex, gemini, agy, opencode]
14
+ stacks:
15
+ languages: [typescript, javascript, html]
16
+ frameworks: [astro, nextjs, react, tailwindcss]
17
+ paths:
18
+ - "**/*.{tsx,jsx,vue,css}"
19
+ ---
20
+ # Tailwind Responsive Auditor
21
+
22
+ ## Core Principles
23
+
24
+ 1. **Mobile-First Design**: Always apply styles for mobile first (without breakpoints), then override them for larger screens (`sm:`, `md:`, `lg:`, `xl:`).
25
+ 2. **Class Deduplication**: Avoid redundant classes (e.g. `w-full w-auto` or `text-red-500 text-blue-500`).
26
+ 3. **No Arbitrary Values**: Avoid using arbitrary values like `w-[327px]` or `bg-[#f0f0f0]` unless absolutely necessary. Rely on the configured Tailwind theme.
27
+
28
+ ## Audit Checklist
29
+
30
+ - Do not use desktop-first design (e.g., max-width utilities should be avoided unless specified).
31
+ - Ensure that elements have responsive margins, padding, and layout flex/grid direction where needed.
32
+ - Alert on duplicate or conflicting utility classes in component files.
@@ -0,0 +1,28 @@
1
+ ---
2
+ id: tdd-mutation-tester
3
+ version: 1.0.0
4
+ name: TDD Mutation Tester
5
+ description: >
6
+ Performs simple mutation testing to verify test coverage and assertions.
7
+ user-invokable: true
8
+ license: MIT
9
+ metadata:
10
+ author: lgzarturo
11
+ category: testing
12
+ compatibility:
13
+ tools: [claude, codex, gemini, agy, opencode]
14
+ stacks:
15
+ languages: [typescript, javascript, python, kotlin, java, php]
16
+ ---
17
+ # TDD Mutation Tester
18
+
19
+ ## Core Principles
20
+
21
+ 1. **Verify Assertion Quality**: Coverage percentage can be misleading. Tests must fail when business logic changes.
22
+ 2. **Mutation Process**:
23
+ - Locate the core logic implemented by the `implementer`.
24
+ - Temporarily mutate a logical operator (e.g. swap `>` for `<`, `==` for `!=`, `+` for `-`, or invert a boolean).
25
+ - Run the test suite.
26
+ - **Expected Result**: At least one test MUST fail.
27
+ - **Clean Up**: Revert the mutated symbol immediately after running the test.
28
+ 3. **Scorecard**: If the test suite passes even with mutated business logic, report a "0 in Real Coverage" on the scorecard and return the task for better test assertions.