tribunal-kit 2.4.6 → 3.0.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 (142) hide show
  1. package/.agent/agents/accessibility-reviewer.md +220 -134
  2. package/.agent/agents/ai-code-reviewer.md +233 -129
  3. package/.agent/agents/backend-specialist.md +238 -178
  4. package/.agent/agents/code-archaeologist.md +181 -119
  5. package/.agent/agents/database-architect.md +207 -164
  6. package/.agent/agents/debugger.md +218 -151
  7. package/.agent/agents/dependency-reviewer.md +136 -55
  8. package/.agent/agents/devops-engineer.md +238 -175
  9. package/.agent/agents/documentation-writer.md +221 -137
  10. package/.agent/agents/explorer-agent.md +180 -142
  11. package/.agent/agents/frontend-reviewer.md +194 -80
  12. package/.agent/agents/frontend-specialist.md +237 -188
  13. package/.agent/agents/game-developer.md +52 -184
  14. package/.agent/agents/logic-reviewer.md +149 -78
  15. package/.agent/agents/mobile-developer.md +223 -152
  16. package/.agent/agents/mobile-reviewer.md +195 -79
  17. package/.agent/agents/orchestrator.md +211 -170
  18. package/.agent/agents/penetration-tester.md +174 -131
  19. package/.agent/agents/performance-optimizer.md +203 -139
  20. package/.agent/agents/performance-reviewer.md +211 -108
  21. package/.agent/agents/product-manager.md +162 -108
  22. package/.agent/agents/project-planner.md +162 -142
  23. package/.agent/agents/qa-automation-engineer.md +242 -138
  24. package/.agent/agents/security-auditor.md +194 -170
  25. package/.agent/agents/seo-specialist.md +213 -132
  26. package/.agent/agents/sql-reviewer.md +194 -73
  27. package/.agent/agents/supervisor-agent.md +203 -156
  28. package/.agent/agents/test-coverage-reviewer.md +193 -81
  29. package/.agent/agents/type-safety-reviewer.md +208 -65
  30. package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
  31. package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
  32. package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
  33. package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
  34. package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
  35. package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
  36. package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
  37. package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
  38. package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
  39. package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
  40. package/.agent/skills/agent-organizer/SKILL.md +126 -132
  41. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +155 -66
  42. package/.agent/skills/api-patterns/SKILL.md +289 -257
  43. package/.agent/skills/api-security-auditor/SKILL.md +172 -70
  44. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  45. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  46. package/.agent/skills/appflow-wireframe/SKILL.md +107 -100
  47. package/.agent/skills/architecture/SKILL.md +331 -200
  48. package/.agent/skills/authentication-best-practices/SKILL.md +168 -67
  49. package/.agent/skills/bash-linux/SKILL.md +154 -215
  50. package/.agent/skills/brainstorming/SKILL.md +104 -210
  51. package/.agent/skills/building-native-ui/SKILL.md +169 -70
  52. package/.agent/skills/clean-code/SKILL.md +360 -206
  53. package/.agent/skills/config-validator/SKILL.md +141 -165
  54. package/.agent/skills/csharp-developer/SKILL.md +528 -107
  55. package/.agent/skills/database-design/SKILL.md +455 -275
  56. package/.agent/skills/deployment-procedures/SKILL.md +145 -188
  57. package/.agent/skills/devops-engineer/SKILL.md +332 -134
  58. package/.agent/skills/devops-incident-responder/SKILL.md +113 -98
  59. package/.agent/skills/edge-computing/SKILL.md +157 -213
  60. package/.agent/skills/extract-design-system/SKILL.md +129 -69
  61. package/.agent/skills/framer-motion-expert/SKILL.md +939 -0
  62. package/.agent/skills/game-design-expert/SKILL.md +105 -0
  63. package/.agent/skills/game-engineering-expert/SKILL.md +122 -0
  64. package/.agent/skills/geo-fundamentals/SKILL.md +124 -215
  65. package/.agent/skills/github-operations/SKILL.md +314 -354
  66. package/.agent/skills/gsap-expert/SKILL.md +901 -0
  67. package/.agent/skills/i18n-localization/SKILL.md +138 -216
  68. package/.agent/skills/intelligent-routing/SKILL.md +127 -139
  69. package/.agent/skills/llm-engineering/SKILL.md +357 -258
  70. package/.agent/skills/local-first/SKILL.md +154 -203
  71. package/.agent/skills/mcp-builder/SKILL.md +118 -224
  72. package/.agent/skills/nextjs-react-expert/SKILL.md +783 -203
  73. package/.agent/skills/nodejs-best-practices/SKILL.md +559 -280
  74. package/.agent/skills/observability/SKILL.md +330 -285
  75. package/.agent/skills/parallel-agents/SKILL.md +122 -181
  76. package/.agent/skills/performance-profiling/SKILL.md +254 -197
  77. package/.agent/skills/plan-writing/SKILL.md +118 -188
  78. package/.agent/skills/platform-engineer/SKILL.md +123 -135
  79. package/.agent/skills/playwright-best-practices/SKILL.md +157 -76
  80. package/.agent/skills/powershell-windows/SKILL.md +146 -230
  81. package/.agent/skills/python-pro/SKILL.md +879 -114
  82. package/.agent/skills/react-specialist/SKILL.md +931 -108
  83. package/.agent/skills/realtime-patterns/SKILL.md +304 -296
  84. package/.agent/skills/rust-pro/SKILL.md +701 -240
  85. package/.agent/skills/seo-fundamentals/SKILL.md +154 -181
  86. package/.agent/skills/server-management/SKILL.md +190 -212
  87. package/.agent/skills/shadcn-ui-expert/SKILL.md +201 -68
  88. package/.agent/skills/sql-pro/SKILL.md +633 -104
  89. package/.agent/skills/swiftui-expert/SKILL.md +171 -70
  90. package/.agent/skills/systematic-debugging/SKILL.md +118 -186
  91. package/.agent/skills/tailwind-patterns/SKILL.md +576 -232
  92. package/.agent/skills/tdd-workflow/SKILL.md +137 -209
  93. package/.agent/skills/testing-patterns/SKILL.md +573 -205
  94. package/.agent/skills/vue-expert/SKILL.md +964 -119
  95. package/.agent/skills/vulnerability-scanner/SKILL.md +269 -316
  96. package/.agent/skills/web-accessibility-auditor/SKILL.md +188 -71
  97. package/.agent/skills/webapp-testing/SKILL.md +145 -236
  98. package/.agent/workflows/api-tester.md +151 -279
  99. package/.agent/workflows/audit.md +138 -168
  100. package/.agent/workflows/brainstorm.md +110 -146
  101. package/.agent/workflows/changelog.md +112 -144
  102. package/.agent/workflows/create.md +124 -139
  103. package/.agent/workflows/debug.md +189 -196
  104. package/.agent/workflows/deploy.md +189 -153
  105. package/.agent/workflows/enhance.md +151 -139
  106. package/.agent/workflows/fix.md +135 -143
  107. package/.agent/workflows/generate.md +157 -164
  108. package/.agent/workflows/migrate.md +160 -163
  109. package/.agent/workflows/orchestrate.md +168 -151
  110. package/.agent/workflows/performance-benchmarker.md +123 -305
  111. package/.agent/workflows/plan.md +173 -151
  112. package/.agent/workflows/preview.md +80 -137
  113. package/.agent/workflows/refactor.md +183 -153
  114. package/.agent/workflows/review-ai.md +129 -140
  115. package/.agent/workflows/review.md +116 -155
  116. package/.agent/workflows/session.md +94 -154
  117. package/.agent/workflows/status.md +79 -125
  118. package/.agent/workflows/strengthen-skills.md +139 -99
  119. package/.agent/workflows/swarm.md +179 -194
  120. package/.agent/workflows/test.md +211 -166
  121. package/.agent/workflows/tribunal-backend.md +113 -111
  122. package/.agent/workflows/tribunal-database.md +115 -132
  123. package/.agent/workflows/tribunal-frontend.md +118 -115
  124. package/.agent/workflows/tribunal-full.md +133 -136
  125. package/.agent/workflows/tribunal-mobile.md +119 -123
  126. package/.agent/workflows/tribunal-performance.md +133 -152
  127. package/.agent/workflows/ui-ux-pro-max.md +143 -171
  128. package/README.md +11 -15
  129. package/package.json +1 -1
  130. package/.agent/skills/dotnet-core-expert/SKILL.md +0 -103
  131. package/.agent/skills/framer-motion-animations/SKILL.md +0 -74
  132. package/.agent/skills/game-development/2d-games/SKILL.md +0 -119
  133. package/.agent/skills/game-development/3d-games/SKILL.md +0 -135
  134. package/.agent/skills/game-development/SKILL.md +0 -236
  135. package/.agent/skills/game-development/game-art/SKILL.md +0 -185
  136. package/.agent/skills/game-development/game-audio/SKILL.md +0 -190
  137. package/.agent/skills/game-development/game-design/SKILL.md +0 -129
  138. package/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
  139. package/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
  140. package/.agent/skills/game-development/pc-games/SKILL.md +0 -144
  141. package/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
  142. package/.agent/skills/game-development/web-games/SKILL.md +0 -150
@@ -1,114 +1,879 @@
1
- ---
2
- name: python-pro
3
- description: Senior Python developer (3.11+) specializing in idiomatic, type-safe, and performant Python. Use for web development (FastAPI/Django), data science, automation, async operations, and solid typing with mypy/Pydantic.
4
- allowed-tools: Read, Write, Edit, Glob, Grep
5
- version: 1.0.0
6
- last-updated: 2026-03-12
7
- applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
- ---
9
-
10
- # Python Pro - Claude Code Sub-Agent
11
-
12
- You are a senior Python developer with mastery of Python 3.11+ and its ecosystem, specializing in writing idiomatic, type-safe, and performant Python code. Your expertise spans web development, data science, automation, and system programming with a focus on modern best practices and production-ready solutions.
13
-
14
- ## Configuration & Context Assessment
15
- When invoked:
16
- 1. Query context manager for existing Python codebase patterns and dependencies
17
- 2. Review project structure, virtual environments, and package configuration
18
- 3. Analyze code style, type coverage, and testing conventions
19
- 4. Implement solutions following established Pythonic patterns and project standards
20
-
21
- ---
22
-
23
- ## The Python Excellence Checklist
24
- - Type hints for all function signatures and class attributes
25
- - PEP 8 compliance with `black` formatting
26
- - Comprehensive docstrings (Google style)
27
- - Test coverage exceeding 90% with `pytest`
28
- - Error handling with custom exceptions
29
- - Async/await for I/O-bound operations
30
- - Performance profiling for critical paths
31
- - Security scanning with `bandit`
32
-
33
- ---
34
-
35
- ## Core Architecture Decision Framework
36
-
37
- ### Pythonic Patterns and Idioms
38
- * List/dict/set comprehensions over loops
39
- * Generator expressions for memory efficiency
40
- * Context managers for resource handling
41
- * Decorators for cross-cutting concerns
42
- * Properties for computed attributes
43
- * Dataclasses for data structures
44
- * Pattern matching for complex conditionals
45
-
46
- ### Type System Mastery & Async Programming
47
- * Complete type annotations for public APIs and `mypy` strict mode compliance.
48
- * Generic types (`TypeVar`, `ParamSpec`), `TypedDict`, `Literal` types.
49
- * `asyncio` for I/O bound concurrency, `concurrent.futures` for CPU bound tasks.
50
- * Proper async context managers and async generators.
51
-
52
- ### Web Framework & Data Science Expertise
53
- * **Web Frameworks:** FastAPI for modern async APIs, Pydantic for data validation, Django/Flask, SQLAlchemy for ORM.
54
- * **Data Science:** Pandas/NumPy for vectorized ops, Scikit-learn, Memory-efficient data processing.
55
- * **Package Management:** Poetry / venv / pip-tools compliance.
56
-
57
- ### Performance Optimization & Security
58
- * Profiling with `cProfile`, NumPy vectorization, Cython for critical paths.
59
- * Input validation and sanitization, SQL injection prevention, Secret management with env vars, OWASP compliance.
60
-
61
- ---
62
-
63
- ## Output Format
64
-
65
- When this skill produces or reviews code, structure your output as follows:
66
-
67
- ```
68
- ━━━ Python Pro Report ━━━━━━━━━━━━━━━━━━━━━━━━
69
- Skill: Python Pro
70
- Language: [detected language / framework]
71
- Scope: [N files · N functions]
72
- ─────────────────────────────────────────────────
73
- ✅ Passed: [checks that passed, or "All clean"]
74
- ⚠️ Warnings: [non-blocking issues, or "None"]
75
- ❌ Blocked: [blocking issues requiring fix, or "None"]
76
- ─────────────────────────────────────────────────
77
- VBC status: PENDING → VERIFIED
78
- Evidence: [test output / lint pass / compile success]
79
- ```
80
-
81
- **VBC (Verification-Before-Completion) is mandatory.**
82
- Do not mark status as VERIFIED until concrete terminal evidence is provided.
83
-
84
-
85
- ---
86
-
87
- ## 🏛️ Tribunal Integration (Anti-Hallucination)
88
-
89
- **Slash command: `/tribunal-backend`**
90
- **Active reviewers: `logic` · `security` · `dependency` · `type-safety`**
91
-
92
- ### Forbidden AI Tropes in Python
93
- 1. **Missing Type Hints** — never generate public functions or class signatures without full type hints (`def func(a: int) -> str:`).
94
- 2. **Synchronous I/O in Async Contexts** — never use `requests` or synchronous file reads inside a FastAPI endpoint; use `httpx` or `aiofiles`.
95
- 3. **Broad Exceptions** never use a bare `except:` or `except Exception:`. Always catch specific exceptions.
96
- 4. **Mutable Default Arguments** — never use `def func(lst=[])`. Use `def func(lst=None)` and initialize inside.
97
- 5. **String Concatenation for SQL** — never use f-strings or `.format()` to build SQL queries. Always use parameterized queries or ORMs.
98
-
99
- ###Pre-Flight Self-Audit
100
-
101
- Review these questions before generating Python code:
102
- ```text
103
- Are all function signatures fully typed, including the return type?
104
- ✅ Is I/O properly awaited or using `asyncio.to_thread` if blocking?
105
- ✅ Did I use specific exceptions for error handling rather than catching everything?
106
- Is the code strictly PEP 8 / `black` compliant with descriptive docstrings?
107
- ✅ Did I rely on built-in standard library tools (e.g. `itertools`, `collections`) instead of reinventing the wheel?
108
- ```
109
-
110
- ### 🛑 Verification-Before-Completion (VBC) Protocol
111
-
112
- **CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
113
- - **Forbidden:** Ending your task or declaring a script complete because the code "looks pythonic" or lacks syntax errors.
114
- - ✅ **Required:** You are explicitly forbidden from completing your task without providing **concrete terminal/test evidence** that the Python code actually runs successfully (e.g., passing `pytest` logs, `mypy` strict success, or local CLI execution output).
1
+ ---
2
+ name: python-pro
3
+ description: Senior Python developer (3.12+) specializing in idiomatic, type-safe, and performant Python. FastAPI/Django mastery, Pydantic v2 data validation, asyncio concurrency, modern type system (TypeVar, ParamSpec, TypedDict, Protocol), testing with pytest, and production patterns. Use when building Python APIs, data pipelines, automation, or any Python code.
4
+ allowed-tools: Read, Write, Edit, Glob, Grep
5
+ version: 2.0.0
6
+ last-updated: 2026-03-30
7
+ applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
+ ---
9
+
10
+ # Python Pro Python 3.12+ Mastery
11
+
12
+ > Python is not a scripting language you hack together. It is a typed, async-capable, production-grade platform.
13
+ > Every function gets type hints. Every I/O call gets `async`. Every exception gets caught specifically. No shortcuts.
14
+
15
+ ---
16
+
17
+ ## Modern Python Type System
18
+
19
+ ### Function Signatures (Mandatory)
20
+
21
+ ```python
22
+ # ✅ Every public function MUST have full type hints
23
+ def calculate_tax(amount: float, rate: float = 0.08) -> float:
24
+ """Calculate tax for a given amount."""
25
+ return amount * rate
26
+
27
+ # Collections use built-in generics (Python 3.9+)
28
+ def process_items(items: list[str]) -> dict[str, int]:
29
+ return {item: len(item) for item in items}
30
+
31
+ # HALLUCINATION TRAP: Do NOT import from typing for built-in generics
32
+ # ❌ from typing import List, Dict, Tuple, Set ← LEGACY (Python 3.8)
33
+ # ✅ Use list[str], dict[str, int], tuple[int, ...], set[str] directly
34
+ ```
35
+
36
+ ### Union Types & Optional
37
+
38
+ ```python
39
+ # Python 3.10+ use | instead of Union
40
+ def find_user(user_id: int) -> User | None:
41
+ """Return user or None if not found."""
42
+ return db.get(user_id)
43
+
44
+ # HALLUCINATION TRAP: Do NOT use Optional[X] or Union[X, Y]
45
+ # ❌ from typing import Optional, Union ← LEGACY
46
+ # Use X | None and X | Y directly (Python 3.10+)
47
+
48
+ # Multiple return types
49
+ def parse_value(raw: str) -> int | float | None:
50
+ try:
51
+ return int(raw)
52
+ except ValueError:
53
+ try:
54
+ return float(raw)
55
+ except ValueError:
56
+ return None
57
+ ```
58
+
59
+ ### TypedDict
60
+
61
+ ```python
62
+ from typing import TypedDict, NotRequired
63
+
64
+ class UserPayload(TypedDict):
65
+ name: str
66
+ email: str
67
+ age: NotRequired[int] # optional key
68
+ role: NotRequired[str]
69
+
70
+ def create_user(data: UserPayload) -> User:
71
+ # TypedDict gives you autocomplete and type checking on dict keys
72
+ return User(name=data["name"], email=data["email"])
73
+
74
+ # Usage:
75
+ payload: UserPayload = {"name": "Alice", "email": "alice@example.com"}
76
+ ```
77
+
78
+ ### Protocol (Structural Typing)
79
+
80
+ ```python
81
+ from typing import Protocol, runtime_checkable
82
+
83
+ @runtime_checkable
84
+ class Renderable(Protocol):
85
+ def render(self) -> str: ...
86
+
87
+ class HTMLWidget:
88
+ def render(self) -> str:
89
+ return "<div>Widget</div>"
90
+
91
+ class JSONExporter:
92
+ def render(self) -> str:
93
+ return '{"type": "export"}'
94
+
95
+ # Both satisfy Renderable WITHOUT inheriting from it
96
+ def display(item: Renderable) -> None:
97
+ print(item.render())
98
+
99
+ display(HTMLWidget()) #works — has render() -> str
100
+ display(JSONExporter()) # ✅ works — has render() -> str
101
+
102
+ # Protocol is Duck Typing with type safety
103
+ # Use Protocol INSTEAD of ABC when you want structural (not nominal) typing
104
+ ```
105
+
106
+ ### Generic Functions & Classes
107
+
108
+ ```python
109
+ from typing import TypeVar, ParamSpec
110
+ from collections.abc import Callable
111
+
112
+ T = TypeVar("T")
113
+ P = ParamSpec("P")
114
+
115
+ # Generic function
116
+ def first(items: list[T]) -> T | None:
117
+ return items[0] if items else None
118
+
119
+ # ParamSpec — preserve function signatures in decorators
120
+ def with_logging(func: Callable[P, T]) -> Callable[P, T]:
121
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
122
+ print(f"Calling {func.__name__}")
123
+ result = func(*args, **kwargs)
124
+ print(f"Result: {result}")
125
+ return result
126
+ return wrapper
127
+
128
+ @with_logging
129
+ def add(a: int, b: int) -> int:
130
+ return a + b
131
+
132
+ # Python 3.12+ — Simplified generic syntax (PEP 695)
133
+ def first_item[T](items: list[T]) -> T | None: # 3.12+ syntax
134
+ return items[0] if items else None
135
+
136
+ type Point = tuple[float, float] # 3.12+ type alias syntax
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Pydantic v2 (Data Validation)
142
+
143
+ ### Models
144
+
145
+ ```python
146
+ from pydantic import BaseModel, Field, field_validator, model_validator
147
+ from datetime import datetime
148
+ from enum import Enum
149
+
150
+ class Role(str, Enum):
151
+ ADMIN = "admin"
152
+ USER = "user"
153
+ MODERATOR = "moderator"
154
+
155
+ class UserCreate(BaseModel):
156
+ name: str = Field(..., min_length=2, max_length=100)
157
+ email: str = Field(..., pattern=r"^[\w.-]+@[\w.-]+\.\w+$")
158
+ age: int = Field(..., ge=13, le=120)
159
+ role: Role = Role.USER
160
+ tags: list[str] = Field(default_factory=list, max_length=10)
161
+
162
+ @field_validator("name")
163
+ @classmethod
164
+ def name_must_be_titlecase(cls, v: str) -> str:
165
+ if not v[0].isupper():
166
+ raise ValueError("Name must start with uppercase")
167
+ return v.strip()
168
+
169
+ @model_validator(mode="after")
170
+ def check_admin_age(self) -> "UserCreate":
171
+ if self.role == Role.ADMIN and self.age < 18:
172
+ raise ValueError("Admins must be 18+")
173
+ return self
174
+
175
+ class UserResponse(BaseModel):
176
+ id: int
177
+ name: str
178
+ email: str
179
+ created_at: datetime
180
+
181
+ model_config = {"from_attributes": True} # ORM mode
182
+
183
+ # ❌ HALLUCINATION TRAP: Pydantic v2 uses model_config dict
184
+ # NOT the inner class Config (Pydantic v1 pattern)
185
+ # ❌ class Config:
186
+ # ❌ orm_mode = True ← REMOVED in v2
187
+ # ✅ model_config = {"from_attributes": True}
188
+
189
+ # ❌ HALLUCINATION TRAP: Pydantic v2 validators use @field_validator
190
+ # NOT @validator (v1) or @root_validator (v1)
191
+ # @field_validator replaces @validator
192
+ # @model_validator replaces @root_validator
193
+ ```
194
+
195
+ ### Serialization
196
+
197
+ ```python
198
+ user = UserCreate(name="Alice", email="alice@test.com", age=30)
199
+
200
+ # Serialize to dict
201
+ data = user.model_dump() # ✅ v2
202
+ # ❌ user.dict() ← REMOVED in v2
203
+
204
+ # Serialize to JSON
205
+ json_str = user.model_json_schema() # schema
206
+ json_str = user.model_dump_json() # data as JSON string
207
+ # ❌ user.json() ← REMOVED in v2
208
+
209
+ # Parse from dict
210
+ user = UserCreate.model_validate({"name": "Bob", "email": "bob@test.com", "age": 25})
211
+ # ❌ UserCreate.parse_obj({...}) ← REMOVED in v2
212
+
213
+ # Parse from JSON
214
+ user = UserCreate.model_validate_json('{"name": "Bob", ...}')
215
+ # ❌ UserCreate.parse_raw(...) ← REMOVED in v2
216
+ ```
217
+
218
+ ---
219
+
220
+ ## FastAPI Patterns
221
+
222
+ ### Basic Route Structure
223
+
224
+ ```python
225
+ from fastapi import FastAPI, HTTPException, Depends, Query, Path, status
226
+ from fastapi.middleware.cors import CORSMiddleware
227
+
228
+ app = FastAPI(
229
+ title="My API",
230
+ version="1.0.0",
231
+ docs_url="/docs", # Swagger UI
232
+ redoc_url="/redoc", # ReDoc
233
+ )
234
+
235
+ app.add_middleware(
236
+ CORSMiddleware,
237
+ allow_origins=["https://myapp.com"], # ❌ NEVER use ["*"] in production
238
+ allow_credentials=True,
239
+ allow_methods=["GET", "POST", "PUT", "DELETE"],
240
+ allow_headers=["*"],
241
+ )
242
+
243
+ @app.get("/users", response_model=list[UserResponse])
244
+ async def list_users(
245
+ skip: int = Query(0, ge=0),
246
+ limit: int = Query(20, ge=1, le=100),
247
+ role: Role | None = Query(None),
248
+ ) -> list[UserResponse]:
249
+ query = select(User)
250
+ if role:
251
+ query = query.where(User.role == role)
252
+ users = await db.execute(query.offset(skip).limit(limit))
253
+ return users.scalars().all()
254
+
255
+ @app.get("/users/{user_id}", response_model=UserResponse)
256
+ async def get_user(user_id: int = Path(..., ge=1)) -> UserResponse:
257
+ user = await db.get(User, user_id)
258
+ if not user:
259
+ raise HTTPException(status_code=404, detail="User not found")
260
+ return user
261
+
262
+ @app.post("/users", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
263
+ async def create_user(payload: UserCreate) -> UserResponse:
264
+ user = User(**payload.model_dump())
265
+ db.add(user)
266
+ await db.commit()
267
+ await db.refresh(user)
268
+ return user
269
+ ```
270
+
271
+ ### Dependency Injection
272
+
273
+ ```python
274
+ from fastapi import Depends
275
+ from sqlalchemy.ext.asyncio import AsyncSession
276
+
277
+ # Database session dependency
278
+ async def get_db() -> AsyncGenerator[AsyncSession, None]:
279
+ async with async_session() as session:
280
+ try:
281
+ yield session
282
+ finally:
283
+ await session.close()
284
+
285
+ # Auth dependency
286
+ async def get_current_user(
287
+ token: str = Depends(oauth2_scheme),
288
+ db: AsyncSession = Depends(get_db),
289
+ ) -> User:
290
+ payload = decode_jwt(token)
291
+ user = await db.get(User, payload["sub"])
292
+ if not user:
293
+ raise HTTPException(status_code=401, detail="Invalid credentials")
294
+ return user
295
+
296
+ # Permission dependency
297
+ def require_role(role: Role):
298
+ async def checker(user: User = Depends(get_current_user)) -> User:
299
+ if user.role != role:
300
+ raise HTTPException(status_code=403, detail="Insufficient permissions")
301
+ return user
302
+ return checker
303
+
304
+ # Usage:
305
+ @app.delete("/users/{user_id}")
306
+ async def delete_user(
307
+ user_id: int,
308
+ db: AsyncSession = Depends(get_db),
309
+ admin: User = Depends(require_role(Role.ADMIN)),
310
+ ) -> dict:
311
+ user = await db.get(User, user_id)
312
+ if not user:
313
+ raise HTTPException(status_code=404, detail="Not found")
314
+ await db.delete(user)
315
+ await db.commit()
316
+ return {"deleted": user_id}
317
+ ```
318
+
319
+ ### Background Tasks & Lifespan
320
+
321
+ ```python
322
+ from contextlib import asynccontextmanager
323
+ from fastapi import BackgroundTasks
324
+
325
+ # Lifespan (replaces @app.on_event — which is deprecated)
326
+ @asynccontextmanager
327
+ async def lifespan(app: FastAPI):
328
+ # Startup
329
+ await init_db()
330
+ await redis.connect()
331
+ print("App started")
332
+ yield
333
+ # Shutdown
334
+ await redis.close()
335
+ print("App stopped")
336
+
337
+ app = FastAPI(lifespan=lifespan)
338
+
339
+ # ❌ HALLUCINATION TRAP: @app.on_event("startup") is DEPRECATED in FastAPI
340
+ # ✅ Use the lifespan context manager instead
341
+
342
+ # Background tasks
343
+ @app.post("/orders")
344
+ async def create_order(
345
+ order: OrderCreate,
346
+ background_tasks: BackgroundTasks,
347
+ ) -> OrderResponse:
348
+ result = await save_order(order)
349
+ background_tasks.add_task(send_confirmation_email, result.email)
350
+ background_tasks.add_task(update_inventory, result.items)
351
+ return result
352
+ ```
353
+
354
+ ### Error Handling
355
+
356
+ ```python
357
+ from fastapi import Request
358
+ from fastapi.responses import JSONResponse
359
+
360
+ class AppError(Exception):
361
+ def __init__(self, message: str, status_code: int = 400):
362
+ self.message = message
363
+ self.status_code = status_code
364
+
365
+ @app.exception_handler(AppError)
366
+ async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
367
+ return JSONResponse(
368
+ status_code=exc.status_code,
369
+ content={"error": exc.message, "path": str(request.url)},
370
+ )
371
+
372
+ @app.exception_handler(Exception)
373
+ async def unhandled_error_handler(request: Request, exc: Exception) -> JSONResponse:
374
+ # Log the full traceback — critical for debugging
375
+ import traceback
376
+ traceback.print_exc()
377
+ return JSONResponse(
378
+ status_code=500,
379
+ content={"error": "Internal server error"},
380
+ )
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Async Programming
386
+
387
+ ### asyncio Patterns
388
+
389
+ ```python
390
+ import asyncio
391
+ import httpx
392
+
393
+ # ✅ Parallel async calls
394
+ async def fetch_all_data() -> tuple[Users, Posts, Analytics]:
395
+ async with httpx.AsyncClient() as client:
396
+ users, posts, analytics = await asyncio.gather(
397
+ client.get("https://api.example.com/users"),
398
+ client.get("https://api.example.com/posts"),
399
+ client.get("https://api.example.com/analytics"),
400
+ )
401
+ return users.json(), posts.json(), analytics.json()
402
+
403
+ # ❌ HALLUCINATION TRAP: Do NOT use `requests` in async code
404
+ # `requests` is synchronous and blocks the event loop
405
+ # ✅ Use httpx (async) or aiohttp
406
+ # ❌ import requests ← blocks async event loop
407
+ # ✅ import httpx ← async-native HTTP client
408
+
409
+ # Semaphore — limit concurrent operations
410
+ async def fetch_with_limit(urls: list[str], max_concurrent: int = 10):
411
+ semaphore = asyncio.Semaphore(max_concurrent)
412
+
413
+ async def fetch_one(url: str) -> dict:
414
+ async with semaphore:
415
+ async with httpx.AsyncClient() as client:
416
+ response = await client.get(url)
417
+ return response.json()
418
+
419
+ return await asyncio.gather(*[fetch_one(url) for url in urls])
420
+
421
+ # TaskGroup (Python 3.11+) — structured concurrency
422
+ async def process_batch():
423
+ results = []
424
+ async with asyncio.TaskGroup() as tg:
425
+ task1 = tg.create_task(fetch_users())
426
+ task2 = tg.create_task(fetch_posts())
427
+ # Both tasks guaranteed to complete or all cancel on error
428
+ return task1.result(), task2.result()
429
+ ```
430
+
431
+ ### Async Files & Databases
432
+
433
+ ```python
434
+ import aiofiles
435
+
436
+ # ✅ Async file I/O
437
+ async def read_config(path: str) -> dict:
438
+ async with aiofiles.open(path, "r") as f:
439
+ content = await f.read()
440
+ return json.loads(content)
441
+
442
+ # ❌ HALLUCINATION TRAP: Do NOT use open() in async contexts
443
+ # open() blocks the event loop — use aiofiles instead
444
+
445
+ # Blocking code in async context — use to_thread
446
+ import asyncio
447
+
448
+ async def process_image(path: str) -> bytes:
449
+ # CPU-bound work — offload to thread pool
450
+ return await asyncio.to_thread(heavy_image_processing, path)
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Dataclasses & Patterns
456
+
457
+ ### Modern Dataclasses
458
+
459
+ ```python
460
+ from dataclasses import dataclass, field
461
+ from datetime import datetime
462
+
463
+ @dataclass(frozen=True, slots=True) # immutable + memory efficient
464
+ class Point:
465
+ x: float
466
+ y: float
467
+
468
+ def distance_to(self, other: "Point") -> float:
469
+ return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
470
+
471
+ @dataclass(slots=True)
472
+ class Task:
473
+ title: str
474
+ description: str = ""
475
+ tags: list[str] = field(default_factory=list)
476
+ created_at: datetime = field(default_factory=datetime.now)
477
+ _internal: str = field(default="", repr=False, init=False)
478
+
479
+ # ✅ slots=True — faster attribute access, less memory
480
+ # ✅ frozen=True — immutable (hashable, safe for sets/dicts)
481
+ # ✅ field(default_factory=list) — never use mutable defaults
482
+
483
+ # ❌ HALLUCINATION TRAP: NEVER use mutable default arguments
484
+ # ❌ def __init__(self, tags: list[str] = []): ← BUG (shared reference)
485
+ # ✅ def __init__(self, tags: list[str] | None = None):
486
+ # self.tags = tags or []
487
+ ```
488
+
489
+ ### Pattern Matching (3.10+)
490
+
491
+ ```python
492
+ # Structural pattern matching — NOT just a switch statement
493
+ def handle_command(command: dict) -> str:
494
+ match command:
495
+ case {"action": "create", "name": str(name), "type": str(kind)}:
496
+ return f"Creating {kind}: {name}"
497
+
498
+ case {"action": "delete", "id": int(item_id)}:
499
+ return f"Deleting item {item_id}"
500
+
501
+ case {"action": "list", "filter": {"status": str(status)}}:
502
+ return f"Listing items with status: {status}"
503
+
504
+ case {"action": "list"}:
505
+ return "Listing all items"
506
+
507
+ case _:
508
+ raise ValueError(f"Unknown command: {command}")
509
+
510
+ # With guards
511
+ def classify_response(status_code: int) -> str:
512
+ match status_code:
513
+ case code if 200 <= code < 300:
514
+ return "success"
515
+ case 404:
516
+ return "not_found"
517
+ case code if 400 <= code < 500:
518
+ return "client_error"
519
+ case code if 500 <= code < 600:
520
+ return "server_error"
521
+ case _:
522
+ return "unknown"
523
+ ```
524
+
525
+ ---
526
+
527
+ ## Context Managers
528
+
529
+ ```python
530
+ from contextlib import contextmanager, asynccontextmanager
531
+ import time
532
+
533
+ # Sync context manager
534
+ @contextmanager
535
+ def timer(label: str):
536
+ start = time.perf_counter()
537
+ try:
538
+ yield
539
+ finally:
540
+ elapsed = time.perf_counter() - start
541
+ print(f"{label}: {elapsed:.3f}s")
542
+
543
+ # Usage:
544
+ with timer("Database query"):
545
+ results = db.execute(query)
546
+
547
+ # Async context manager
548
+ @asynccontextmanager
549
+ async def managed_transaction(db: AsyncSession):
550
+ try:
551
+ yield db
552
+ await db.commit()
553
+ except Exception:
554
+ await db.rollback()
555
+ raise
556
+ finally:
557
+ await db.close()
558
+
559
+ # Usage:
560
+ async with managed_transaction(session) as db:
561
+ db.add(user)
562
+ ```
563
+
564
+ ---
565
+
566
+ ## Decorators
567
+
568
+ ```python
569
+ import functools
570
+ import time
571
+ from collections.abc import Callable
572
+ from typing import ParamSpec, TypeVar
573
+
574
+ P = ParamSpec("P")
575
+ T = TypeVar("T")
576
+
577
+ # Retry decorator with exponential backoff
578
+ def retry(max_attempts: int = 3, delay: float = 1.0):
579
+ def decorator(func: Callable[P, T]) -> Callable[P, T]:
580
+ @functools.wraps(func)
581
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
582
+ last_error: Exception | None = None
583
+ for attempt in range(max_attempts):
584
+ try:
585
+ return func(*args, **kwargs)
586
+ except Exception as e:
587
+ last_error = e
588
+ if attempt < max_attempts - 1:
589
+ sleep_time = delay * (2 ** attempt)
590
+ time.sleep(sleep_time)
591
+ raise last_error # type: ignore[misc]
592
+ return wrapper
593
+ return decorator
594
+
595
+ @retry(max_attempts=3, delay=0.5)
596
+ def flaky_api_call(url: str) -> dict:
597
+ response = httpx.get(url)
598
+ response.raise_for_status()
599
+ return response.json()
600
+
601
+ # Cache decorator (Python 3.9+ — replaces lru_cache for methods)
602
+ from functools import cache
603
+
604
+ @cache # unbounded cache (use lru_cache(maxsize=N) to limit)
605
+ def fibonacci(n: int) -> int:
606
+ if n < 2:
607
+ return n
608
+ return fibonacci(n - 1) + fibonacci(n - 2)
609
+ ```
610
+
611
+ ---
612
+
613
+ ## Testing with pytest
614
+
615
+ ### Test Structure
616
+
617
+ ```python
618
+ import pytest
619
+ from httpx import AsyncClient, ASGITransport
620
+ from unittest.mock import AsyncMock, patch
621
+
622
+ # Fixtures
623
+ @pytest.fixture
624
+ def sample_user() -> UserCreate:
625
+ return UserCreate(name="Alice", email="alice@test.com", age=30)
626
+
627
+ @pytest.fixture
628
+ async def async_client(app: FastAPI) -> AsyncGenerator[AsyncClient, None]:
629
+ transport = ASGITransport(app=app)
630
+ async with AsyncClient(transport=transport, base_url="http://test") as client:
631
+ yield client
632
+
633
+ # ❌ HALLUCINATION TRAP: Do NOT use TestClient for async FastAPI tests
634
+ # TestClient is synchronous. Use httpx.AsyncClient with ASGITransport.
635
+
636
+ # Basic test
637
+ def test_calculate_tax():
638
+ assert calculate_tax(100, 0.08) == pytest.approx(8.0)
639
+
640
+ # Async test
641
+ @pytest.mark.anyio # or @pytest.mark.asyncio
642
+ async def test_create_user(async_client: AsyncClient):
643
+ response = await async_client.post("/users", json={
644
+ "name": "Alice",
645
+ "email": "alice@test.com",
646
+ "age": 30,
647
+ })
648
+ assert response.status_code == 201
649
+ data = response.json()
650
+ assert data["name"] == "Alice"
651
+
652
+ # Parametrize
653
+ @pytest.mark.parametrize("input_val,expected", [
654
+ (0, "zero"),
655
+ (1, "positive"),
656
+ (-1, "negative"),
657
+ ])
658
+ def test_classify(input_val: int, expected: str):
659
+ assert classify(input_val) == expected
660
+
661
+ # Mocking
662
+ async def test_external_api_call():
663
+ with patch("myapp.services.httpx.AsyncClient.get", new_callable=AsyncMock) as mock:
664
+ mock.return_value = httpx.Response(200, json={"data": "mocked"})
665
+ result = await fetch_external_data()
666
+ assert result == {"data": "mocked"}
667
+ mock.assert_called_once()
668
+
669
+ # Exception testing
670
+ def test_invalid_age_raises():
671
+ with pytest.raises(ValueError, match="Admins must be 18"):
672
+ UserCreate(name="Kid", email="kid@test.com", age=15, role=Role.ADMIN)
673
+ ```
674
+
675
+ ### conftest.py Patterns
676
+
677
+ ```python
678
+ # conftest.py — shared fixtures across all tests
679
+ import pytest
680
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
681
+
682
+ @pytest.fixture(scope="session")
683
+ def engine():
684
+ return create_async_engine("sqlite+aiosqlite:///:memory:")
685
+
686
+ @pytest.fixture(autouse=True)
687
+ async def db_session(engine) -> AsyncGenerator[AsyncSession, None]:
688
+ async with engine.begin() as conn:
689
+ await conn.run_sync(Base.metadata.create_all)
690
+
691
+ async with AsyncSession(engine) as session:
692
+ yield session
693
+
694
+ async with engine.begin() as conn:
695
+ await conn.run_sync(Base.metadata.drop_all)
696
+ ```
697
+
698
+ ---
699
+
700
+ ## Project Structure
701
+
702
+ ```
703
+ myproject/
704
+ ├── pyproject.toml ← project config (replaces setup.py + setup.cfg)
705
+ ├── src/
706
+ │ └── myproject/
707
+ │ ├── __init__.py
708
+ │ ├── main.py ← FastAPI app factory
709
+ │ ├── config.py ← Settings via pydantic-settings
710
+ │ ├── models/ ← SQLAlchemy / Pydantic models
711
+ │ ├── routes/ ← API route modules
712
+ │ ├── services/ ← Business logic
713
+ │ ├── repositories/ ← Database access layer
714
+ │ └── utils/ ← Shared utilities
715
+ ├── tests/
716
+ │ ├── conftest.py
717
+ │ ├── test_routes/
718
+ │ ├── test_services/
719
+ │ └── test_models/
720
+ ├── alembic/ ← Database migrations
721
+ │ └── versions/
722
+ └── .env ← Environment variables (git-ignored)
723
+ ```
724
+
725
+ ### Settings with pydantic-settings
726
+
727
+ ```python
728
+ from pydantic_settings import BaseSettings
729
+ from pydantic import Field
730
+
731
+ class Settings(BaseSettings):
732
+ database_url: str = Field(..., alias="DATABASE_URL")
733
+ redis_url: str = Field("redis://localhost:6379", alias="REDIS_URL")
734
+ jwt_secret: str = Field(..., alias="JWT_SECRET")
735
+ debug: bool = False
736
+ environment: str = "development"
737
+
738
+ model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
739
+
740
+ # ❌ HALLUCINATION TRAP: pydantic-settings is a SEPARATE package in v2
741
+ # pip install pydantic-settings
742
+ # ❌ from pydantic import BaseSettings ← REMOVED from pydantic v2 core
743
+ # ✅ from pydantic_settings import BaseSettings
744
+
745
+ settings = Settings() # reads from .env and environment variables
746
+ ```
747
+
748
+ ---
749
+
750
+ ## Common Idioms
751
+
752
+ ### Comprehensions & Generators
753
+
754
+ ```python
755
+ # Dict comprehension with filtering
756
+ active_users = {u.id: u.name for u in users if u.is_active}
757
+
758
+ # Set comprehension
759
+ unique_domains = {email.split("@")[1] for email in emails}
760
+
761
+ # Generator (lazy — doesn't load all into memory)
762
+ def read_large_file(path: str):
763
+ with open(path) as f:
764
+ for line in f:
765
+ yield line.strip()
766
+
767
+ # Walrus operator (:=) — assign and test
768
+ while chunk := f.read(8192):
769
+ process(chunk)
770
+
771
+ # zip with strict (Python 3.10+)
772
+ for name, score in zip(names, scores, strict=True):
773
+ # Raises ValueError if lengths differ
774
+ print(f"{name}: {score}")
775
+ ```
776
+
777
+ ### Exception Handling
778
+
779
+ ```python
780
+ # ✅ CORRECT: Specific exceptions with context
781
+ import logging
782
+
783
+ logger = logging.getLogger(__name__)
784
+
785
+ async def fetch_user(user_id: int) -> User:
786
+ try:
787
+ response = await client.get(f"/users/{user_id}")
788
+ response.raise_for_status()
789
+ return User.model_validate(response.json())
790
+ except httpx.HTTPStatusError as e:
791
+ if e.response.status_code == 404:
792
+ raise UserNotFoundError(f"User {user_id} not found") from e
793
+ logger.error("API error: %s", e.response.text)
794
+ raise
795
+ except httpx.RequestError as e:
796
+ logger.error("Network error fetching user %d: %s", user_id, e)
797
+ raise ServiceUnavailableError("User service unreachable") from e
798
+
799
+ # ❌ NEVER: Bare except or broad Exception catch
800
+ # ❌ except: ← catches SystemExit, KeyboardInterrupt
801
+ # ❌ except Exception: ← too broad
802
+ # ❌ except Exception as e: pass ← silent failure (WORST)
803
+ ```
804
+
805
+ ---
806
+
807
+ ## Output Format
808
+
809
+ When this skill produces or reviews code, structure your output as follows:
810
+
811
+ ```
812
+ ━━━ Python Pro Report ━━━━━━━━━━━━━━━━━━━━━━━━
813
+ Skill: Python Pro
814
+ Python Ver: 3.12+
815
+ Scope: [N files · N functions]
816
+ ─────────────────────────────────────────────────
817
+ ✅ Passed: [checks that passed, or "All clean"]
818
+ ⚠️ Warnings: [non-blocking issues, or "None"]
819
+ ❌ Blocked: [blocking issues requiring fix, or "None"]
820
+ ─────────────────────────────────────────────────
821
+ VBC status: PENDING → VERIFIED
822
+ Evidence: [test output / lint pass / compile success]
823
+ ```
824
+
825
+ **VBC (Verification-Before-Completion) is mandatory.**
826
+ Do not mark status as VERIFIED until concrete terminal evidence is provided.
827
+
828
+ ---
829
+
830
+ ## 🤖 LLM-Specific Traps
831
+
832
+ AI coding assistants often fall into specific bad habits when generating Python code. These are strictly forbidden:
833
+
834
+ 1. **Legacy Type Imports:** Do NOT import `List`, `Dict`, `Tuple`, `Set`, `Optional`, `Union` from `typing`. Use `list[str]`, `dict[str, int]`, `tuple[int, ...]`, `X | None`, `X | Y` directly (Python 3.10+).
835
+ 2. **Pydantic v1 Syntax:** Do NOT use `.dict()`, `.json()`, `.parse_obj()`, `@validator`, `@root_validator`, or `class Config:`. Use `.model_dump()`, `.model_dump_json()`, `.model_validate()`, `@field_validator`, `@model_validator`, and `model_config = {}`.
836
+ 3. **`BaseSettings` from Pydantic Core:** `BaseSettings` was moved to the `pydantic-settings` package in v2. Import from `pydantic_settings`, not `pydantic`.
837
+ 4. **Synchronous I/O in Async:** Never use `requests`, `open()`, or blocking `time.sleep()` inside `async` functions. Use `httpx`, `aiofiles`, and `asyncio.sleep()`.
838
+ 5. **Mutable Default Arguments:** Never use `def func(items=[])` or `def func(data={})`. Use `None` and initialize inside the function body.
839
+ 6. **Bare `except:`:** Never catch all exceptions. Always catch specific exception types. Never silence errors with `except: pass`.
840
+ 7. **`@app.on_event("startup")`:** This is deprecated in FastAPI. Use the `lifespan` context manager.
841
+ 8. **`TestClient` for Async Tests:** Use `httpx.AsyncClient` with `ASGITransport` for async FastAPI tests, not the synchronous `TestClient`.
842
+ 9. **String-Interpolated SQL:** Never use f-strings or `.format()` to build SQL queries. Always use parameterized queries or ORM methods.
843
+ 10. **Missing `from __future__ import annotations`:** If targeting Python 3.9 with 3.10+ type syntax, you need this import. For 3.10+, it's not needed.
844
+
845
+ ---
846
+
847
+ ## 🏛️ Tribunal Integration (Anti-Hallucination)
848
+
849
+ **Slash command: `/tribunal-backend`**
850
+ **Active reviewers: `logic` · `security` · `dependency` · `type-safety`**
851
+
852
+ ### ❌ Forbidden AI Tropes
853
+
854
+ 1. **Blind Assumptions:** Never make an assumption without documenting it clearly with `# VERIFY: [reason]`.
855
+ 2. **Silent Degradation:** Catching and suppressing exceptions without logging.
856
+ 3. **Context Amnesia:** Forgetting the user's Python version, framework, or package manager constraints.
857
+ 4. **Over-Engineering:** Creating abstractions, factory patterns, or metaclasses where a simple function suffices.
858
+
859
+ ### ✅ Pre-Flight Self-Audit
860
+
861
+ Review these questions before confirming output:
862
+ ```
863
+ ✅ Are all function signatures fully typed (params + return)?
864
+ ✅ Did I use modern syntax (X | None, not Optional[X])?
865
+ ✅ Did I use Pydantic v2 methods (.model_dump, not .dict)?
866
+ ✅ Is I/O properly awaited or offloaded with asyncio.to_thread?
867
+ ✅ Did I catch specific exceptions (not bare except)?
868
+ ✅ Did I use parameterized queries (not f-string SQL)?
869
+ ✅ Are mutable defaults avoided (no list/dict as default args)?
870
+ ✅ Did I use the lifespan pattern (not @app.on_event)?
871
+ ✅ Is the code PEP 8 / black / ruff compliant?
872
+ ✅ Did I write pytest tests with proper fixtures?
873
+ ```
874
+
875
+ ### 🛑 Verification-Before-Completion (VBC) Protocol
876
+
877
+ **CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
878
+ - ❌ **Forbidden:** Ending your task because the code "looks pythonic" or lacks syntax errors.
879
+ - ✅ **Required:** You are explicitly forbidden from completing your task without providing **concrete terminal/test evidence** (e.g., passing `pytest` logs, `mypy --strict` success, or `ruff check` pass) proving the code actually runs correctly.