agent-skill-kit 3.9.135
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/.agent/global.d.ts +80 -0
- package/.agent/rules/GEMINI.md +210 -0
- package/.agent/rules/autopilot.md +287 -0
- package/.agent/rules/code-rules.md +227 -0
- package/.agent/scripts/audit_workflows.ts +23 -0
- package/.agent/scripts/auto_preview.ts +170 -0
- package/.agent/scripts/checklist.ts +180 -0
- package/.agent/scripts/compile-agents.ts +237 -0
- package/.agent/scripts/fix_skills.ts +49 -0
- package/.agent/scripts/session_manager.ts +222 -0
- package/.agent/scripts/skill-audit.ts +255 -0
- package/.agent/scripts/sync_workflows.ts +54 -0
- package/.agent/scripts/utils/colors.ts +58 -0
- package/.agent/scripts/utils/process-manager.ts +131 -0
- package/.agent/scripts/utils/reporter.ts +192 -0
- package/.agent/scripts/utils/runner.ts +128 -0
- package/.agent/scripts/verify_all.ts +243 -0
- package/.agent/scripts/version-sync.ts +256 -0
- package/.agent/skills/SKILL_INDEX.md +129 -0
- package/.agent/skills/agent-browser/AGENTS.md +728 -0
- package/.agent/skills/agent-browser/SKILL.md +193 -0
- package/.agent/skills/agent-browser/rules/_sections.md +15 -0
- package/.agent/skills/agent-browser/rules/_template.md +32 -0
- package/.agent/skills/agent-browser/rules/engineering-spec.md +528 -0
- package/.agent/skills/agent-browser/scripts/browser_cli.ts +52 -0
- package/.agent/skills/agent-browser/scripts/session_manager.ts +166 -0
- package/.agent/skills/ai-artist/AGENTS.md +1082 -0
- package/.agent/skills/ai-artist/SKILL.md +186 -0
- package/.agent/skills/ai-artist/rules/_sections.md +30 -0
- package/.agent/skills/ai-artist/rules/_template.md +32 -0
- package/.agent/skills/ai-artist/rules/domain-code.md +118 -0
- package/.agent/skills/ai-artist/rules/domain-marketing.md +105 -0
- package/.agent/skills/ai-artist/rules/engineering-spec.md +519 -0
- package/.agent/skills/ai-artist/rules/image-prompts.md +195 -0
- package/.agent/skills/ai-artist/rules/model-syntax.md +115 -0
- package/.agent/skills/ai-artist/scripts/prompt_compiler.ts +72 -0
- package/.agent/skills/ai-artist/templates/image-core.txt +1 -0
- package/.agent/skills/ai-artist/templates/llm-core.txt +6 -0
- package/.agent/skills/api-architect/AGENTS.md +1896 -0
- package/.agent/skills/api-architect/SKILL.md +173 -0
- package/.agent/skills/api-architect/rules/_sections.md +35 -0
- package/.agent/skills/api-architect/rules/_template.md +32 -0
- package/.agent/skills/api-architect/rules/api-style.md +115 -0
- package/.agent/skills/api-architect/rules/auth.md +134 -0
- package/.agent/skills/api-architect/rules/documentation.md +131 -0
- package/.agent/skills/api-architect/rules/engineering-spec.md +505 -0
- package/.agent/skills/api-architect/rules/graphql.md +154 -0
- package/.agent/skills/api-architect/rules/rate-limiting.md +76 -0
- package/.agent/skills/api-architect/rules/response.md +138 -0
- package/.agent/skills/api-architect/rules/rest.md +113 -0
- package/.agent/skills/api-architect/rules/security-testing.md +146 -0
- package/.agent/skills/api-architect/rules/trpc.md +129 -0
- package/.agent/skills/api-architect/rules/versioning.md +100 -0
- package/.agent/skills/api-architect/scripts/api_validator.ts +413 -0
- package/.agent/skills/auth-patterns/AGENTS.md +1830 -0
- package/.agent/skills/auth-patterns/SKILL.md +163 -0
- package/.agent/skills/auth-patterns/rules/_sections.md +30 -0
- package/.agent/skills/auth-patterns/rules/_template.md +32 -0
- package/.agent/skills/auth-patterns/rules/engineering-spec.md +515 -0
- package/.agent/skills/auth-patterns/rules/jwt-deep.md +196 -0
- package/.agent/skills/auth-patterns/rules/mfa.md +174 -0
- package/.agent/skills/auth-patterns/rules/oauth2.md +134 -0
- package/.agent/skills/auth-patterns/rules/passkey.md +243 -0
- package/.agent/skills/auth-patterns/rules/rbac-abac.md +206 -0
- package/.agent/skills/auth-patterns/rules/session.md +183 -0
- package/.agent/skills/auth-patterns/scripts/auth_validator.ts +121 -0
- package/.agent/skills/chrome-devtools/AGENTS.md +952 -0
- package/.agent/skills/chrome-devtools/SKILL.md +160 -0
- package/.agent/skills/chrome-devtools/rules/_sections.md +25 -0
- package/.agent/skills/chrome-devtools/rules/_template.md +32 -0
- package/.agent/skills/chrome-devtools/rules/aria-snapshot.md +95 -0
- package/.agent/skills/chrome-devtools/rules/engineering-spec.md +510 -0
- package/.agent/skills/chrome-devtools/rules/scripts-guide.md +174 -0
- package/.agent/skills/chrome-devtools/scripts/aria-snapshot.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/click.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/console.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/core_cli.ts +79 -0
- package/.agent/skills/chrome-devtools/scripts/evaluate.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/fill.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/navigate.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/network.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/performance.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/screenshot.ts +3 -0
- package/.agent/skills/chrome-devtools/scripts/select-ref.ts +3 -0
- package/.agent/skills/cicd-pipeline/AGENTS.md +809 -0
- package/.agent/skills/cicd-pipeline/SKILL.md +164 -0
- package/.agent/skills/cicd-pipeline/rules/_sections.md +15 -0
- package/.agent/skills/cicd-pipeline/rules/_template.md +32 -0
- package/.agent/skills/cicd-pipeline/rules/engineering-spec.md +477 -0
- package/.agent/skills/cicd-pipeline/scripts/flag-manager.ts +253 -0
- package/.agent/skills/cicd-pipeline/scripts/pipeline_validator.ts +133 -0
- package/.agent/skills/code-constitution/AGENTS.md +597 -0
- package/.agent/skills/code-constitution/CHANGELOG.md +216 -0
- package/.agent/skills/code-constitution/SKILL.md +191 -0
- package/.agent/skills/code-constitution/VERSION +3 -0
- package/.agent/skills/code-constitution/examples/violation-backend-mutation/after.tsx +59 -0
- package/.agent/skills/code-constitution/examples/violation-backend-mutation/before.tsx +42 -0
- package/.agent/skills/code-constitution/examples/violation-backend-mutation/explanation.md +91 -0
- package/.agent/skills/code-constitution/examples/violation-chart-injection/after.tsx +99 -0
- package/.agent/skills/code-constitution/examples/violation-chart-injection/before.tsx +57 -0
- package/.agent/skills/code-constitution/examples/violation-chart-injection/explanation.md +120 -0
- package/.agent/skills/code-constitution/knowledge/lessons-learned.yaml +3 -0
- package/.agent/skills/code-constitution/metadata/precedence.yaml +117 -0
- package/.agent/skills/code-constitution/metadata/scope-map.yaml +156 -0
- package/.agent/skills/code-constitution/proposals/v1.1-change-proposal-template.md +201 -0
- package/.agent/skills/code-constitution/resources/AUTHORITY_MODEL.md +115 -0
- package/.agent/skills/code-constitution/resources/ENFORCEMENT_GUIDE.md +246 -0
- package/.agent/skills/code-constitution/resources/LOAD_ORDER.md +86 -0
- package/.agent/skills/code-constitution/rules/_sections.md +15 -0
- package/.agent/skills/code-constitution/rules/_template.md +32 -0
- package/.agent/skills/code-constitution/rules/constitution/master-constitution.md +210 -0
- package/.agent/skills/code-constitution/rules/doctrines/architecture/architecture-doctrine.md +188 -0
- package/.agent/skills/code-constitution/rules/doctrines/backend/backend-data-engine-doctrine.md +218 -0
- package/.agent/skills/code-constitution/rules/doctrines/commercial/commercial-guardrails-doctrine.md +196 -0
- package/.agent/skills/code-constitution/rules/doctrines/data/data-integrity-doctrine.md +202 -0
- package/.agent/skills/code-constitution/rules/doctrines/frontend/frontend-mobile-doctrine.md +169 -0
- package/.agent/skills/code-constitution/rules/doctrines/frontend/interaction-patterns-doctrine.md +176 -0
- package/.agent/skills/code-constitution/rules/doctrines/learning/learning-engine-doctrine.md +192 -0
- package/.agent/skills/code-constitution/rules/doctrines/performance/performance-doctrine.md +180 -0
- package/.agent/skills/code-constitution/rules/doctrines/review/code-review-doctrine.md +174 -0
- package/.agent/skills/code-constitution/rules/enforcement/agents/agent-enforcement-protocol.md +218 -0
- package/.agent/skills/code-constitution/rules/enforcement/agents/agent-system-prompt.md +196 -0
- package/.agent/skills/code-constitution/rules/enforcement/checklists/backend-api-review-checklist.md +131 -0
- package/.agent/skills/code-constitution/rules/enforcement/checklists/chart-component-review-checklist.md +147 -0
- package/.agent/skills/code-constitution/rules/enforcement/checklists/frontend-review-checklist.md +194 -0
- package/.agent/skills/code-constitution/rules/enforcement/playbooks/doctrine-violation-playbook.md +236 -0
- package/.agent/skills/code-constitution/rules/engineering-spec.md +561 -0
- package/.agent/skills/code-constitution/scripts/audit_pr.ts +219 -0
- package/.agent/skills/code-constitution/scripts/check_boundaries.ts +134 -0
- package/.agent/skills/code-constitution/scripts/learn.ts +202 -0
- package/.agent/skills/code-constitution/scripts/validate_doctrine.ts +287 -0
- package/.agent/skills/code-craft/AGENTS.md +803 -0
- package/.agent/skills/code-craft/SKILL.md +170 -0
- package/.agent/skills/code-craft/rules/_sections.md +20 -0
- package/.agent/skills/code-craft/rules/_template.md +32 -0
- package/.agent/skills/code-craft/rules/engineering-spec.md +447 -0
- package/.agent/skills/code-craft/rules/verification-scripts.md +83 -0
- package/.agent/skills/code-craft/scripts/code_quality_checker.ts +193 -0
- package/.agent/skills/code-review/AGENTS.md +1664 -0
- package/.agent/skills/code-review/SKILL.md +152 -0
- package/.agent/skills/code-review/rules/_sections.md +15 -0
- package/.agent/skills/code-review/rules/_template.md +32 -0
- package/.agent/skills/code-review/rules/engineering-spec.md +466 -0
- package/.agent/skills/code-review/scripts/lint_runner.ts +213 -0
- package/.agent/skills/code-review/scripts/type_coverage.ts +118 -0
- package/.agent/skills/context-engineering/AGENTS.md +499 -0
- package/.agent/skills/context-engineering/SKILL.md +147 -0
- package/.agent/skills/context-engineering/rules/_sections.md +15 -0
- package/.agent/skills/context-engineering/rules/_template.md +32 -0
- package/.agent/skills/context-engineering/rules/engineering-spec.md +463 -0
- package/.agent/skills/context-engineering/scripts/context_analyzer.ts +127 -0
- package/.agent/skills/copywriting/AGENTS.md +501 -0
- package/.agent/skills/copywriting/SKILL.md +188 -0
- package/.agent/skills/copywriting/rules/_sections.md +15 -0
- package/.agent/skills/copywriting/rules/_template.md +32 -0
- package/.agent/skills/copywriting/rules/engineering-spec.md +465 -0
- package/.agent/skills/copywriting/scripts/copy_validator.ts +185 -0
- package/.agent/skills/data-modeler/AGENTS.md +814 -0
- package/.agent/skills/data-modeler/SKILL.md +195 -0
- package/.agent/skills/data-modeler/rules/_sections.md +15 -0
- package/.agent/skills/data-modeler/rules/_template.md +32 -0
- package/.agent/skills/data-modeler/rules/database-selection.md +124 -0
- package/.agent/skills/data-modeler/rules/engineering-spec.md +479 -0
- package/.agent/skills/data-modeler/rules/indexing.md +166 -0
- package/.agent/skills/data-modeler/rules/migrations.md +176 -0
- package/.agent/skills/data-modeler/rules/optimization.md +161 -0
- package/.agent/skills/data-modeler/rules/orm-selection.md +155 -0
- package/.agent/skills/data-modeler/rules/schema-design.md +162 -0
- package/.agent/skills/data-modeler/scripts/schema_validator.ts +357 -0
- package/.agent/skills/debug-pro/AGENTS.md +798 -0
- package/.agent/skills/debug-pro/SKILL.md +193 -0
- package/.agent/skills/debug-pro/defense-in-depth/SKILL.md +148 -0
- package/.agent/skills/debug-pro/root-cause-tracing/SKILL.md +196 -0
- package/.agent/skills/debug-pro/root-cause-tracing/find-polluter.sh +63 -0
- package/.agent/skills/debug-pro/rules/_sections.md +15 -0
- package/.agent/skills/debug-pro/rules/_template.md +32 -0
- package/.agent/skills/debug-pro/rules/engineering-spec.md +491 -0
- package/.agent/skills/debug-pro/scripts/debug_verifier.ts +148 -0
- package/.agent/skills/debug-pro/verification-before-completion/SKILL.md +160 -0
- package/.agent/skills/design-system/AGENTS.md +4216 -0
- package/.agent/skills/design-system/SKILL.md +186 -0
- package/.agent/skills/design-system/rules/_sections.md +65 -0
- package/.agent/skills/design-system/rules/_template.md +32 -0
- package/.agent/skills/design-system/rules/animation-guide.md +355 -0
- package/.agent/skills/design-system/rules/color-system.md +335 -0
- package/.agent/skills/design-system/rules/color-systems.md +133 -0
- package/.agent/skills/design-system/rules/decision-trees.md +442 -0
- package/.agent/skills/design-system/rules/design-extraction.md +152 -0
- package/.agent/skills/design-system/rules/engineering-spec.md +484 -0
- package/.agent/skills/design-system/rules/motion-design.md +161 -0
- package/.agent/skills/design-system/rules/motion-graphics.md +330 -0
- package/.agent/skills/design-system/rules/spatial-composition.md +184 -0
- package/.agent/skills/design-system/rules/typography-system.md +369 -0
- package/.agent/skills/design-system/rules/typography.md +124 -0
- package/.agent/skills/design-system/rules/ux-psychology.md +565 -0
- package/.agent/skills/design-system/rules/visual-effects.md +407 -0
- package/.agent/skills/design-system/scripts/accessibility_checker.ts +292 -0
- package/.agent/skills/design-system/scripts/ux_audit.ts +356 -0
- package/.agent/skills/doc-templates/AGENTS.md +820 -0
- package/.agent/skills/doc-templates/SKILL.md +260 -0
- package/.agent/skills/doc-templates/rules/_sections.md +20 -0
- package/.agent/skills/doc-templates/rules/_template.md +32 -0
- package/.agent/skills/doc-templates/rules/doc.md +355 -0
- package/.agent/skills/doc-templates/rules/engineering-spec.md +422 -0
- package/.agent/skills/doc-templates/scripts/editor-server.ts +162 -0
- package/.agent/skills/doc-templates/scripts/inject_otel.ts +22 -0
- package/.agent/skills/doc-templates/scripts/kanban-server.ts +171 -0
- package/.agent/skills/doc-templates/scripts/markdown-server.ts +185 -0
- package/.agent/skills/e2e-automation/AGENTS.md +882 -0
- package/.agent/skills/e2e-automation/SKILL.md +175 -0
- package/.agent/skills/e2e-automation/rules/_sections.md +20 -0
- package/.agent/skills/e2e-automation/rules/_template.md +32 -0
- package/.agent/skills/e2e-automation/rules/aria-snapshot.md +185 -0
- package/.agent/skills/e2e-automation/rules/engineering-spec.md +501 -0
- package/.agent/skills/e2e-automation/scripts/playwright_runner.ts +208 -0
- package/.agent/skills/execution-reporter/AGENTS.md +419 -0
- package/.agent/skills/execution-reporter/SKILL.md +152 -0
- package/.agent/skills/execution-reporter/rules/_sections.md +15 -0
- package/.agent/skills/execution-reporter/rules/_template.md +32 -0
- package/.agent/skills/execution-reporter/rules/engineering-spec.md +389 -0
- package/.agent/skills/game-development/2d-games/SKILL.md +140 -0
- package/.agent/skills/game-development/3d-games/SKILL.md +156 -0
- package/.agent/skills/game-development/AGENTS.md +783 -0
- package/.agent/skills/game-development/SKILL.md +178 -0
- package/.agent/skills/game-development/game-art/SKILL.md +207 -0
- package/.agent/skills/game-development/game-audio/SKILL.md +211 -0
- package/.agent/skills/game-development/game-design/SKILL.md +151 -0
- package/.agent/skills/game-development/mobile-games/SKILL.md +130 -0
- package/.agent/skills/game-development/multiplayer/SKILL.md +154 -0
- package/.agent/skills/game-development/pc-games/SKILL.md +167 -0
- package/.agent/skills/game-development/rules/_sections.md +15 -0
- package/.agent/skills/game-development/rules/_template.md +32 -0
- package/.agent/skills/game-development/rules/engineering-spec.md +480 -0
- package/.agent/skills/game-development/vr-ar/SKILL.md +144 -0
- package/.agent/skills/game-development/web-games/SKILL.md +173 -0
- package/.agent/skills/git-workflow/AGENTS.md +554 -0
- package/.agent/skills/git-workflow/SKILL.md +181 -0
- package/.agent/skills/git-workflow/rules/_sections.md +15 -0
- package/.agent/skills/git-workflow/rules/_template.md +32 -0
- package/.agent/skills/git-workflow/rules/engineering-spec.md +518 -0
- package/.agent/skills/gitops/AGENTS.md +921 -0
- package/.agent/skills/gitops/SKILL.md +163 -0
- package/.agent/skills/gitops/rules/_sections.md +25 -0
- package/.agent/skills/gitops/rules/_template.md +32 -0
- package/.agent/skills/gitops/rules/argocd-setup.md +148 -0
- package/.agent/skills/gitops/rules/engineering-spec.md +450 -0
- package/.agent/skills/gitops/rules/sync-policies.md +145 -0
- package/.agent/skills/google-adk-python/AGENTS.md +1054 -0
- package/.agent/skills/google-adk-python/SKILL.md +168 -0
- package/.agent/skills/google-adk-python/rules/_sections.md +25 -0
- package/.agent/skills/google-adk-python/rules/_template.md +32 -0
- package/.agent/skills/google-adk-python/rules/deployment.md +138 -0
- package/.agent/skills/google-adk-python/rules/engineering-spec.md +451 -0
- package/.agent/skills/google-adk-python/rules/multi-agent.md +146 -0
- package/.agent/skills/google-adk-python/rules/tools.md +131 -0
- package/.agent/skills/idea-storm/AGENTS.md +995 -0
- package/.agent/skills/idea-storm/SKILL.md +160 -0
- package/.agent/skills/idea-storm/rules/_sections.md +25 -0
- package/.agent/skills/idea-storm/rules/_template.md +32 -0
- package/.agent/skills/idea-storm/rules/architecture-debate.md +122 -0
- package/.agent/skills/idea-storm/rules/dynamic-questioning.md +374 -0
- package/.agent/skills/idea-storm/rules/engineering-spec.md +466 -0
- package/.agent/skills/knowledge-compiler/SKILL.md +320 -0
- package/.agent/skills/knowledge-graph/AGENTS.md +762 -0
- package/.agent/skills/knowledge-graph/SKILL.md +157 -0
- package/.agent/skills/knowledge-graph/rules/_sections.md +15 -0
- package/.agent/skills/knowledge-graph/rules/_template.md +32 -0
- package/.agent/skills/knowledge-graph/rules/engineering-spec.md +439 -0
- package/.agent/skills/knowledge-linter/SKILL.md +217 -0
- package/.agent/skills/lifecycle-orchestrator/AGENTS.md +989 -0
- package/.agent/skills/lifecycle-orchestrator/SKILL.md +169 -0
- package/.agent/skills/lifecycle-orchestrator/rules/_sections.md +15 -0
- package/.agent/skills/lifecycle-orchestrator/rules/_template.md +32 -0
- package/.agent/skills/lifecycle-orchestrator/rules/engineering-spec.md +525 -0
- package/.agent/skills/lifecycle-orchestrator/scripts/state_manager.ts +189 -0
- package/.agent/skills/mcp-builder/AGENTS.md +1653 -0
- package/.agent/skills/mcp-builder/SKILL.md +166 -0
- package/.agent/skills/mcp-builder/rules/_sections.md +40 -0
- package/.agent/skills/mcp-builder/rules/_template.md +32 -0
- package/.agent/skills/mcp-builder/rules/best-practices.md +157 -0
- package/.agent/skills/mcp-builder/rules/design-principles.md +105 -0
- package/.agent/skills/mcp-builder/rules/engineering-spec.md +473 -0
- package/.agent/skills/mcp-builder/rules/evaluation.md +103 -0
- package/.agent/skills/mcp-builder/rules/python-implementation.md +249 -0
- package/.agent/skills/mcp-builder/rules/quickstart.md +111 -0
- package/.agent/skills/mcp-builder/rules/typescript-implementation.md +280 -0
- package/.agent/skills/mcp-management/AGENTS.md +837 -0
- package/.agent/skills/mcp-management/SKILL.md +164 -0
- package/.agent/skills/mcp-management/rules/_sections.md +25 -0
- package/.agent/skills/mcp-management/rules/_template.md +32 -0
- package/.agent/skills/mcp-management/rules/cli-usage.md +146 -0
- package/.agent/skills/mcp-management/rules/engineering-spec.md +501 -0
- package/.agent/skills/mcp-management/rules/protocol.md +159 -0
- package/.agent/skills/media-processing/AGENTS.md +479 -0
- package/.agent/skills/media-processing/SKILL.md +176 -0
- package/.agent/skills/media-processing/rules/_sections.md +15 -0
- package/.agent/skills/media-processing/rules/_template.md +32 -0
- package/.agent/skills/media-processing/rules/engineering-spec.md +452 -0
- package/.agent/skills/media-processing/scripts/convert-video.ts +155 -0
- package/.agent/skills/media-processing/scripts/optimize-image.ts +127 -0
- package/.agent/skills/mobile-design/AGENTS.md +6531 -0
- package/.agent/skills/mobile-design/SKILL.md +165 -0
- package/.agent/skills/mobile-design/rules/_sections.md +45 -0
- package/.agent/skills/mobile-design/rules/_template.md +32 -0
- package/.agent/skills/mobile-design/rules/decision-trees.md +540 -0
- package/.agent/skills/mobile-design/rules/engineering-spec.md +467 -0
- package/.agent/skills/mobile-design/rules/mobile-backend.md +516 -0
- package/.agent/skills/mobile-design/rules/mobile-color-system.md +436 -0
- package/.agent/skills/mobile-design/rules/mobile-debugging.md +146 -0
- package/.agent/skills/mobile-design/rules/mobile-design-thinking.md +381 -0
- package/.agent/skills/mobile-design/rules/mobile-navigation.md +474 -0
- package/.agent/skills/mobile-design/rules/mobile-performance.md +783 -0
- package/.agent/skills/mobile-design/rules/mobile-testing.md +380 -0
- package/.agent/skills/mobile-design/rules/mobile-typography.md +449 -0
- package/.agent/skills/mobile-design/rules/platform-android.md +682 -0
- package/.agent/skills/mobile-design/rules/platform-ios.md +577 -0
- package/.agent/skills/mobile-design/rules/touch-psychology.md +553 -0
- package/.agent/skills/mobile-design/scripts/mobile_audit.ts +309 -0
- package/.agent/skills/mobile-developer/AGENTS.md +904 -0
- package/.agent/skills/mobile-developer/SKILL.md +194 -0
- package/.agent/skills/mobile-developer/rules/_sections.md +75 -0
- package/.agent/skills/mobile-developer/rules/_template.md +32 -0
- package/.agent/skills/mobile-developer/rules/anti-patterns.md +70 -0
- package/.agent/skills/mobile-developer/rules/app-store-optimization.md +319 -0
- package/.agent/skills/mobile-developer/rules/decision-trees.md +545 -0
- package/.agent/skills/mobile-developer/rules/deep-linking.md +441 -0
- package/.agent/skills/mobile-developer/rules/engineering-spec.md +477 -0
- package/.agent/skills/mobile-developer/rules/flutter.md +475 -0
- package/.agent/skills/mobile-developer/rules/mobile-backend.md +516 -0
- package/.agent/skills/mobile-developer/rules/mobile-color-system.md +444 -0
- package/.agent/skills/mobile-developer/rules/mobile-debugging.md +428 -0
- package/.agent/skills/mobile-developer/rules/mobile-design-thinking.md +367 -0
- package/.agent/skills/mobile-developer/rules/mobile-navigation.md +483 -0
- package/.agent/skills/mobile-developer/rules/mobile-performance.md +778 -0
- package/.agent/skills/mobile-developer/rules/mobile-testing.md +382 -0
- package/.agent/skills/mobile-developer/rules/mobile-typography.md +457 -0
- package/.agent/skills/mobile-developer/rules/native.md +572 -0
- package/.agent/skills/mobile-developer/rules/platform-android.md +676 -0
- package/.agent/skills/mobile-developer/rules/platform-ios.md +571 -0
- package/.agent/skills/mobile-developer/rules/push-notifications.md +599 -0
- package/.agent/skills/mobile-developer/rules/react-native.md +422 -0
- package/.agent/skills/mobile-developer/rules/touch-psychology.md +547 -0
- package/.agent/skills/mobile-developer/scripts/mobile_audit.ts +701 -0
- package/.agent/skills/nextjs-pro/AGENTS.md +3932 -0
- package/.agent/skills/nextjs-pro/SKILL.md +171 -0
- package/.agent/skills/nextjs-pro/rules/_sections.md +50 -0
- package/.agent/skills/nextjs-pro/rules/_template.md +32 -0
- package/.agent/skills/nextjs-pro/rules/advanced-event-handler-refs.md +59 -0
- package/.agent/skills/nextjs-pro/rules/advanced-init-once.md +46 -0
- package/.agent/skills/nextjs-pro/rules/advanced-use-latest.md +43 -0
- package/.agent/skills/nextjs-pro/rules/async-api-routes.md +42 -0
- package/.agent/skills/nextjs-pro/rules/async-defer-await.md +84 -0
- package/.agent/skills/nextjs-pro/rules/async-dependencies.md +55 -0
- package/.agent/skills/nextjs-pro/rules/async-parallel.md +32 -0
- package/.agent/skills/nextjs-pro/rules/async-suspense-boundaries.md +103 -0
- package/.agent/skills/nextjs-pro/rules/bundle-barrel-imports.md +63 -0
- package/.agent/skills/nextjs-pro/rules/bundle-conditional.md +35 -0
- package/.agent/skills/nextjs-pro/rules/bundle-defer-third-party.md +53 -0
- package/.agent/skills/nextjs-pro/rules/bundle-dynamic-imports.md +39 -0
- package/.agent/skills/nextjs-pro/rules/bundle-preload.md +54 -0
- package/.agent/skills/nextjs-pro/rules/client-event-listeners.md +78 -0
- package/.agent/skills/nextjs-pro/rules/client-localstorage-schema.md +75 -0
- package/.agent/skills/nextjs-pro/rules/client-passive-event-listeners.md +52 -0
- package/.agent/skills/nextjs-pro/rules/client-swr-dedup.md +60 -0
- package/.agent/skills/nextjs-pro/rules/engineering-spec.md +440 -0
- package/.agent/skills/nextjs-pro/rules/js-batch-dom-css.md +111 -0
- package/.agent/skills/nextjs-pro/rules/js-cache-function-results.md +84 -0
- package/.agent/skills/nextjs-pro/rules/js-cache-property-access.md +32 -0
- package/.agent/skills/nextjs-pro/rules/js-cache-storage.md +74 -0
- package/.agent/skills/nextjs-pro/rules/js-combine-iterations.md +36 -0
- package/.agent/skills/nextjs-pro/rules/js-early-exit.md +54 -0
- package/.agent/skills/nextjs-pro/rules/js-hoist-regexp.md +49 -0
- package/.agent/skills/nextjs-pro/rules/js-index-maps.md +41 -0
- package/.agent/skills/nextjs-pro/rules/js-length-check-first.md +53 -0
- package/.agent/skills/nextjs-pro/rules/js-min-max-loop.md +86 -0
- package/.agent/skills/nextjs-pro/rules/js-set-map-lookups.md +28 -0
- package/.agent/skills/nextjs-pro/rules/js-tosorted-immutable.md +61 -0
- package/.agent/skills/nextjs-pro/rules/rendering-activity.md +30 -0
- package/.agent/skills/nextjs-pro/rules/rendering-animate-svg-wrapper.md +51 -0
- package/.agent/skills/nextjs-pro/rules/rendering-conditional-render.md +44 -0
- package/.agent/skills/nextjs-pro/rules/rendering-content-visibility.md +42 -0
- package/.agent/skills/nextjs-pro/rules/rendering-hoist-jsx.md +50 -0
- package/.agent/skills/nextjs-pro/rules/rendering-hydration-no-flicker.md +86 -0
- package/.agent/skills/nextjs-pro/rules/rendering-hydration-suppress-warning.md +34 -0
- package/.agent/skills/nextjs-pro/rules/rendering-svg-precision.md +32 -0
- package/.agent/skills/nextjs-pro/rules/rendering-usetransition-loading.md +79 -0
- package/.agent/skills/nextjs-pro/rules/rerender-defer-reads.md +43 -0
- package/.agent/skills/nextjs-pro/rules/rerender-dependencies.md +49 -0
- package/.agent/skills/nextjs-pro/rules/rerender-derived-state-no-effect.md +44 -0
- package/.agent/skills/nextjs-pro/rules/rerender-derived-state.md +33 -0
- package/.agent/skills/nextjs-pro/rules/rerender-functional-setstate.md +78 -0
- package/.agent/skills/nextjs-pro/rules/rerender-lazy-state-init.md +62 -0
- package/.agent/skills/nextjs-pro/rules/rerender-memo-with-default-value.md +42 -0
- package/.agent/skills/nextjs-pro/rules/rerender-memo.md +48 -0
- package/.agent/skills/nextjs-pro/rules/rerender-move-effect-to-event.md +49 -0
- package/.agent/skills/nextjs-pro/rules/rerender-simple-expression-in-memo.md +39 -0
- package/.agent/skills/nextjs-pro/rules/rerender-transitions.md +44 -0
- package/.agent/skills/nextjs-pro/rules/rerender-use-ref-transient-values.md +77 -0
- package/.agent/skills/nextjs-pro/rules/schema.json +34 -0
- package/.agent/skills/nextjs-pro/rules/server-after-nonblocking.md +77 -0
- package/.agent/skills/nextjs-pro/rules/server-auth-actions.md +100 -0
- package/.agent/skills/nextjs-pro/rules/server-cache-lru.md +45 -0
- package/.agent/skills/nextjs-pro/rules/server-cache-react.md +80 -0
- package/.agent/skills/nextjs-pro/rules/server-dedup-props.md +69 -0
- package/.agent/skills/nextjs-pro/rules/server-parallel-fetching.md +87 -0
- package/.agent/skills/nextjs-pro/rules/server-serialization.md +42 -0
- package/.agent/skills/nodejs-pro/AGENTS.md +866 -0
- package/.agent/skills/nodejs-pro/SKILL.md +172 -0
- package/.agent/skills/nodejs-pro/rules/_sections.md +50 -0
- package/.agent/skills/nodejs-pro/rules/_template.md +32 -0
- package/.agent/skills/nodejs-pro/rules/architecture-patterns.md +229 -0
- package/.agent/skills/nodejs-pro/rules/async-patterns.md +246 -0
- package/.agent/skills/nodejs-pro/rules/engineering-spec.md +438 -0
- package/.agent/skills/nodejs-pro/rules/error-handling.md +257 -0
- package/.agent/skills/nodejs-pro/rules/framework-selection.md +220 -0
- package/.agent/skills/nodejs-pro/rules/runtime-modules.md +176 -0
- package/.agent/skills/nodejs-pro/rules/testing-strategy.md +266 -0
- package/.agent/skills/nodejs-pro/rules/validation-security.md +205 -0
- package/.agent/skills/observability/AGENTS.md +607 -0
- package/.agent/skills/observability/SKILL.md +178 -0
- package/.agent/skills/observability/rules/_sections.md +15 -0
- package/.agent/skills/observability/rules/_template.md +32 -0
- package/.agent/skills/observability/rules/engineering-spec.md +440 -0
- package/.agent/skills/offensive-sec/AGENTS.md +849 -0
- package/.agent/skills/offensive-sec/SKILL.md +191 -0
- package/.agent/skills/offensive-sec/rules/_sections.md +15 -0
- package/.agent/skills/offensive-sec/rules/_template.md +32 -0
- package/.agent/skills/offensive-sec/rules/engineering-spec.md +470 -0
- package/.agent/skills/perf-optimizer/AGENTS.md +870 -0
- package/.agent/skills/perf-optimizer/SKILL.md +189 -0
- package/.agent/skills/perf-optimizer/rules/_sections.md +15 -0
- package/.agent/skills/perf-optimizer/rules/_template.md +32 -0
- package/.agent/skills/perf-optimizer/rules/backend-patterns.md +312 -0
- package/.agent/skills/perf-optimizer/rules/engineering-spec.md +428 -0
- package/.agent/skills/perf-optimizer/scripts/lighthouse_audit.ts +201 -0
- package/.agent/skills/problem-checker/AGENTS.md +519 -0
- package/.agent/skills/problem-checker/SKILL.md +189 -0
- package/.agent/skills/problem-checker/rules/_sections.md +15 -0
- package/.agent/skills/problem-checker/rules/_template.md +32 -0
- package/.agent/skills/problem-checker/rules/engineering-spec.md +483 -0
- package/.agent/skills/problem-checker/scripts/check_problems.ts +396 -0
- package/.agent/skills/project-planner/AGENTS.md +2698 -0
- package/.agent/skills/project-planner/SKILL.md +166 -0
- package/.agent/skills/project-planner/rules/_sections.md +15 -0
- package/.agent/skills/project-planner/rules/_template.md +32 -0
- package/.agent/skills/project-planner/rules/engineering-spec.md +420 -0
- package/.agent/skills/python-pro/AGENTS.md +1871 -0
- package/.agent/skills/python-pro/SKILL.md +182 -0
- package/.agent/skills/python-pro/rules/_sections.md +50 -0
- package/.agent/skills/python-pro/rules/_template.md +32 -0
- package/.agent/skills/python-pro/rules/async-patterns.md +168 -0
- package/.agent/skills/python-pro/rules/django-patterns.md +194 -0
- package/.agent/skills/python-pro/rules/engineering-spec.md +442 -0
- package/.agent/skills/python-pro/rules/fastapi-patterns.md +179 -0
- package/.agent/skills/python-pro/rules/framework-selection.md +167 -0
- package/.agent/skills/python-pro/rules/project-structure.md +181 -0
- package/.agent/skills/python-pro/rules/testing-patterns.md +212 -0
- package/.agent/skills/python-pro/rules/type-hints.md +159 -0
- package/.agent/skills/react-pro/AGENTS.md +963 -0
- package/.agent/skills/react-pro/SKILL.md +232 -0
- package/.agent/skills/react-pro/rules/_sections.md +40 -0
- package/.agent/skills/react-pro/rules/_template.md +32 -0
- package/.agent/skills/react-pro/rules/component-patterns.md +145 -0
- package/.agent/skills/react-pro/rules/composition-compound.md +82 -0
- package/.agent/skills/react-pro/rules/data-fetching.md +133 -0
- package/.agent/skills/react-pro/rules/engineering-spec.md +453 -0
- package/.agent/skills/react-pro/rules/error-boundary.md +61 -0
- package/.agent/skills/react-pro/rules/file-organization.md +158 -0
- package/.agent/skills/react-pro/rules/hooks-custom.md +61 -0
- package/.agent/skills/react-pro/rules/mui-styling.md +138 -0
- package/.agent/skills/react-pro/rules/patterns.md +24 -0
- package/.agent/skills/react-pro/rules/performance-optimization.md +65 -0
- package/.agent/skills/react-pro/rules/performance.md +137 -0
- package/.agent/skills/react-pro/rules/react19-hooks.md +85 -0
- package/.agent/skills/react-pro/rules/state-management.md +90 -0
- package/.agent/skills/react-pro/rules/testing-patterns.md +52 -0
- package/.agent/skills/registry.json +1251 -0
- package/.agent/skills/security-scanner/AGENTS.md +851 -0
- package/.agent/skills/security-scanner/SKILL.md +182 -0
- package/.agent/skills/security-scanner/rules/_sections.md +15 -0
- package/.agent/skills/security-scanner/rules/_template.md +32 -0
- package/.agent/skills/security-scanner/rules/auth-patterns.md +281 -0
- package/.agent/skills/security-scanner/rules/checklists.md +186 -0
- package/.agent/skills/security-scanner/rules/engineering-spec.md +440 -0
- package/.agent/skills/security-scanner/scripts/security_scan.ts +513 -0
- package/.agent/skills/seo-optimizer/AGENTS.md +839 -0
- package/.agent/skills/seo-optimizer/SKILL.md +180 -0
- package/.agent/skills/seo-optimizer/rules/_sections.md +15 -0
- package/.agent/skills/seo-optimizer/rules/_template.md +32 -0
- package/.agent/skills/seo-optimizer/rules/engineering-spec.md +433 -0
- package/.agent/skills/seo-optimizer/scripts/geo_checker.ts +109 -0
- package/.agent/skills/seo-optimizer/scripts/seo_checker.ts +308 -0
- package/.agent/skills/server-ops/AGENTS.md +643 -0
- package/.agent/skills/server-ops/SKILL.md +194 -0
- package/.agent/skills/server-ops/rules/_sections.md +15 -0
- package/.agent/skills/server-ops/rules/_template.md +32 -0
- package/.agent/skills/server-ops/rules/engineering-spec.md +450 -0
- package/.agent/skills/shell-script/AGENTS.md +499 -0
- package/.agent/skills/shell-script/SKILL.md +205 -0
- package/.agent/skills/shell-script/rules/_sections.md +15 -0
- package/.agent/skills/shell-script/rules/_template.md +32 -0
- package/.agent/skills/shell-script/rules/engineering-spec.md +463 -0
- package/.agent/skills/skill-generator/SKILL.md +147 -0
- package/.agent/skills/smart-router/SKILL.md +95 -0
- package/.agent/skills/studio/AGENTS.md +636 -0
- package/.agent/skills/studio/SKILL.md +178 -0
- package/.agent/skills/studio/data/charts.csv +26 -0
- package/.agent/skills/studio/data/colors.csv +97 -0
- package/.agent/skills/studio/data/icons.csv +101 -0
- package/.agent/skills/studio/data/landing.csv +31 -0
- package/.agent/skills/studio/data/products.csv +97 -0
- package/.agent/skills/studio/data/prompts.csv +24 -0
- package/.agent/skills/studio/data/react-performance.csv +45 -0
- package/.agent/skills/studio/data/stacks/flutter.csv +52 -0
- package/.agent/skills/studio/data/stacks/html-tailwind.csv +56 -0
- package/.agent/skills/studio/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/skills/studio/data/stacks/nextjs.csv +53 -0
- package/.agent/skills/studio/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/skills/studio/data/stacks/nuxtjs.csv +59 -0
- package/.agent/skills/studio/data/stacks/react-native.csv +52 -0
- package/.agent/skills/studio/data/stacks/react.csv +54 -0
- package/.agent/skills/studio/data/stacks/shadcn.csv +61 -0
- package/.agent/skills/studio/data/stacks/svelte.csv +54 -0
- package/.agent/skills/studio/data/stacks/swiftui.csv +51 -0
- package/.agent/skills/studio/data/stacks/vue.csv +50 -0
- package/.agent/skills/studio/data/styles.csv +59 -0
- package/.agent/skills/studio/data/typography.csv +58 -0
- package/.agent/skills/studio/data/ui-reasoning.csv +101 -0
- package/.agent/skills/studio/data/ux-guidelines.csv +100 -0
- package/.agent/skills/studio/data/web-interface.csv +31 -0
- package/.agent/skills/studio/rules/_sections.md +15 -0
- package/.agent/skills/studio/rules/_template.md +32 -0
- package/.agent/skills/studio/rules/engineering-spec.md +455 -0
- package/.agent/skills/studio/scripts/core.ts +345 -0
- package/.agent/skills/studio/scripts/design_system.ts +953 -0
- package/.agent/skills/studio/scripts/search.ts +197 -0
- package/.agent/skills/studio/scripts/types.ts +147 -0
- package/.agent/skills/studio/scripts/utils/component-specs.ts +154 -0
- package/.agent/skills/studio/scripts/utils/config-loader.ts +165 -0
- package/.agent/skills/studio/scripts/utils/css-templates.ts +169 -0
- package/.agent/skills/studio/scripts/utils/css-validator.ts +95 -0
- package/.agent/skills/studio/scripts/utils/csv-loader.ts +52 -0
- package/.agent/skills/studio/scripts/utils/intelligent-overrides.ts +129 -0
- package/.agent/skills/studio/scripts/utils/page-override-formatter.ts +143 -0
- package/.agent/skills/studio/scripts/utils/page-type-detector.ts +124 -0
- package/.agent/skills/studio/scripts/utils/search-cache.ts +165 -0
- package/.agent/skills/studio/scripts/utils/text-utils.ts +44 -0
- package/.agent/skills/system-design/AGENTS.md +597 -0
- package/.agent/skills/system-design/SKILL.md +153 -0
- package/.agent/skills/system-design/rules/_sections.md +15 -0
- package/.agent/skills/system-design/rules/_template.md +32 -0
- package/.agent/skills/system-design/rules/context-discovery.md +117 -0
- package/.agent/skills/system-design/rules/engineering-spec.md +437 -0
- package/.agent/skills/system-design/rules/examples.md +180 -0
- package/.agent/skills/system-design/rules/pattern-selection.md +130 -0
- package/.agent/skills/system-design/rules/patterns-reference.md +110 -0
- package/.agent/skills/system-design/rules/trade-off-analysis.md +169 -0
- package/.agent/skills/tailwind-kit/AGENTS.md +1135 -0
- package/.agent/skills/tailwind-kit/SKILL.md +171 -0
- package/.agent/skills/tailwind-kit/rules/_sections.md +20 -0
- package/.agent/skills/tailwind-kit/rules/_template.md +32 -0
- package/.agent/skills/tailwind-kit/rules/components.md +232 -0
- package/.agent/skills/tailwind-kit/rules/engineering-spec.md +435 -0
- package/.agent/skills/tailwind-kit/rules/responsive.md +221 -0
- package/.agent/skills/tailwind-kit/rules/v4-config.md +72 -0
- package/.agent/skills/test-architect/AGENTS.md +851 -0
- package/.agent/skills/test-architect/SKILL.md +176 -0
- package/.agent/skills/test-architect/rules/_sections.md +15 -0
- package/.agent/skills/test-architect/rules/_template.md +32 -0
- package/.agent/skills/test-architect/rules/engineering-spec.md +434 -0
- package/.agent/skills/test-architect/scripts/test_runner.ts +265 -0
- package/.agent/skills/typescript-expert/AGENTS.md +1045 -0
- package/.agent/skills/typescript-expert/SKILL.md +200 -0
- package/.agent/skills/typescript-expert/rules/_sections.md +20 -0
- package/.agent/skills/typescript-expert/rules/_template.md +32 -0
- package/.agent/skills/typescript-expert/rules/engineering-spec.md +433 -0
- package/.agent/skills/typescript-expert/rules/tsconfig-strict.json +92 -0
- package/.agent/skills/typescript-expert/rules/typescript-cheatsheet.md +407 -0
- package/.agent/skills/typescript-expert/rules/utility-types.ts +264 -0
- package/.agent/skills/typescript-expert/scripts/ts_diagnostic.ts +321 -0
- package/.agent/skills/vercel-deploy/AGENTS.md +490 -0
- package/.agent/skills/vercel-deploy/SKILL.md +175 -0
- package/.agent/skills/vercel-deploy/rules/_sections.md +15 -0
- package/.agent/skills/vercel-deploy/rules/_template.md +32 -0
- package/.agent/skills/vercel-deploy/rules/engineering-spec.md +463 -0
- package/.agent/skills/vercel-deploy/scripts/deploy.sh +310 -0
- package/.agent/workflows/api.md +377 -0
- package/.agent/workflows/autopilot.md +344 -0
- package/.agent/workflows/build.md +338 -0
- package/.agent/workflows/chronicle.md +279 -0
- package/.agent/workflows/cook.md +217 -0
- package/.agent/workflows/diagnose.md +302 -0
- package/.agent/workflows/fix.md +253 -0
- package/.agent/workflows/game.md +329 -0
- package/.agent/workflows/inspect.md +276 -0
- package/.agent/workflows/knowledge.md +212 -0
- package/.agent/workflows/launch.md +345 -0
- package/.agent/workflows/mobile.md +354 -0
- package/.agent/workflows/monitor.md +239 -0
- package/.agent/workflows/optimize.md +269 -0
- package/.agent/workflows/plan.md +278 -0
- package/.agent/workflows/stage.md +286 -0
- package/.agent/workflows/studio.md +276 -0
- package/.agent/workflows/think.md +262 -0
- package/.agent/workflows/validate.md +289 -0
- package/.agentignore +161 -0
- package/.gitattributes +16 -0
- package/CHANGELOG.md +198 -0
- package/LICENSE +40 -0
- package/README.md +173 -0
- package/docs/SKILL_DESIGN_GUIDE.md +561 -0
- package/docs/The-Complete-Guide-to-Building-Skills-for-Claude.md +1207 -0
- package/docs/WORKFLOW_DESIGN_GUIDE.md +325 -0
- package/package.json +33 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,1871 @@
|
|
|
1
|
+
# python-pro
|
|
2
|
+
|
|
3
|
+
**Version 1.0.0**
|
|
4
|
+
Engineering
|
|
5
|
+
March 2026
|
|
6
|
+
|
|
7
|
+
> **Note:**
|
|
8
|
+
> This document is for agents and LLMs to follow when working on python-pro domain.
|
|
9
|
+
> Optimized for automation and consistency by AI-assisted workflows.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Python Pro — Framework Selection & Architecture
|
|
14
|
+
|
|
15
|
+
> Decision-making principles. Not patterns to copy. Ask, classify, decide.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 5 Must-Ask Questions (Before Decision)
|
|
20
|
+
|
|
21
|
+
| # | Question | Options |
|
|
22
|
+
|---|----------|---------|
|
|
23
|
+
| 1 | Target Scope? | Single Script / API / Full-Stack / Workers |
|
|
24
|
+
| 2 | Project Scale? | MVP / Production / FAANG Scale |
|
|
25
|
+
| 3 | Current Framework? | Green field vs Legacy modification? |
|
|
26
|
+
| 4 | Async Requirements? | High I/O throughput vs CPU heavy? |
|
|
27
|
+
| 5 | DB Dependencies? | Relational vs NoSQL, Async DB drivers? |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## When to Use
|
|
32
|
+
|
|
33
|
+
| Situation | Approach |
|
|
34
|
+
|-----------|----------|
|
|
35
|
+
| Choosing Python framework | Use decision tree |
|
|
36
|
+
| API-first / microservices | Consider FastAPI |
|
|
37
|
+
| Full-stack web / CMS | Consider Django |
|
|
38
|
+
| Async vs sync decision | Check I/O vs CPU classification |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## System Boundaries
|
|
43
|
+
|
|
44
|
+
| Owned by This Skill | NOT Owned |
|
|
45
|
+
|---------------------|-----------|
|
|
46
|
+
| Framework selection (5 branches) | API design (→ api-architect) |
|
|
47
|
+
| Async/sync classification | Testing strategy (→ test-architect) |
|
|
48
|
+
| Type hint rules | Database schema (→ data-modeler) |
|
|
49
|
+
| Architecture layering | Code implementation |
|
|
50
|
+
|
|
51
|
+
**Expert decision skill:** Produces recommendations. Does not write code.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Framework Decision Tree (Deterministic)
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
What are you building?
|
|
59
|
+
│
|
|
60
|
+
├── API-first / Microservices → FastAPI
|
|
61
|
+
├── Full-stack web / CMS → Django
|
|
62
|
+
├── Simple / Script / Learning → Flask
|
|
63
|
+
├── AI/ML API serving → FastAPI
|
|
64
|
+
└── Background workers → Celery + any
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**If user has explicit preference → respect it.** Ask when unclear.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Async vs Sync (Deterministic)
|
|
72
|
+
|
|
73
|
+
| Workload | Decision | Rationale |
|
|
74
|
+
|----------|----------|-----------|
|
|
75
|
+
| I/O-bound (HTTP, DB, file) | `async` | Waiting for external |
|
|
76
|
+
| CPU-bound (compute) | `sync` + multiprocessing | Number crunching |
|
|
77
|
+
| Mixed | Async with sync offload | `run_in_executor` |
|
|
78
|
+
|
|
79
|
+
**Constraints:** Never use sync libraries in async code. Never force async for CPU work.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Architecture Layering (Fixed)
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
Routes (HTTP handlers)
|
|
87
|
+
└→ Services (business logic)
|
|
88
|
+
└→ Repositories (data access)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Rule:** No business logic in routes/views. Routes delegate to services.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Type Hints & Validation (Mandatory)
|
|
96
|
+
|
|
97
|
+
| Rule | Scope |
|
|
98
|
+
|------|-------|
|
|
99
|
+
| Type hints on all public APIs | Functions, methods, return types |
|
|
100
|
+
| Pydantic for validation | All input/output boundaries |
|
|
101
|
+
| No `Any` in public signatures | Use specific types or generics |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Decision Checklist
|
|
106
|
+
|
|
107
|
+
- [ ] Asked user about framework preference?
|
|
108
|
+
- [ ] Chosen framework for THIS context?
|
|
109
|
+
- [ ] Decided async vs sync?
|
|
110
|
+
- [ ] Planned type hint strategy?
|
|
111
|
+
- [ ] Defined project structure?
|
|
112
|
+
- [ ] Considered background tasks (Celery)?
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Error Taxonomy
|
|
117
|
+
|
|
118
|
+
| Code | Recoverable | Trigger |
|
|
119
|
+
|------|-------------|---------|
|
|
120
|
+
| `ERR_INVALID_REQUEST_TYPE` | No | Request type not supported |
|
|
121
|
+
| `ERR_UNKNOWN_PROJECT_TYPE` | Yes | Project type not one of 5 |
|
|
122
|
+
| `ERR_UNKNOWN_FRAMEWORK` | Yes | Framework not fastapi/django/flask |
|
|
123
|
+
|
|
124
|
+
**Zero internal retries.** Same context = same recommendation.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Audit Logging (OpenTelemetry)
|
|
129
|
+
|
|
130
|
+
| Event | Metadata Payload | Severity |
|
|
131
|
+
|-------|------------------|----------|
|
|
132
|
+
| `decision_started` | `{"project_type": "...", "scale": "..."}` | `INFO` |
|
|
133
|
+
| `framework_selected` | `{"framework": "fastapi", "rationale": "..."}` | `INFO` |
|
|
134
|
+
| `async_sync_classified` | `{"mode": "async", "mix_workload": false}` | `WARN` |
|
|
135
|
+
| `arch_recommendation_provided` | `{"layer_complexity": "high"}` | `INFO` |
|
|
136
|
+
|
|
137
|
+
All architectural decision outputs MUST emit a `decision_started` and `arch_recommendation_provided` event.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Anti-Patterns
|
|
142
|
+
|
|
143
|
+
| ❌ Don't | ✅ Do |
|
|
144
|
+
|---------|-------|
|
|
145
|
+
| Default to Django for simple APIs | Choose framework per context |
|
|
146
|
+
| Use sync libraries in async code | Use async-compatible libraries |
|
|
147
|
+
| Skip type hints on public APIs | Annotate all public functions |
|
|
148
|
+
| Put business logic in routes/views | Separate: routes → services → repos |
|
|
149
|
+
| Always pick the same framework | Ask user, evaluate context |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 📑 Content Map
|
|
154
|
+
|
|
155
|
+
| File | Description | When to Read |
|
|
156
|
+
|------|-------------|--------------|
|
|
157
|
+
| [framework-selection.md](rules/framework-selection.md) | Framework comparison | Choosing framework |
|
|
158
|
+
| [async-patterns.md](rules/async-patterns.md) | Async/sync patterns | Concurrency decisions |
|
|
159
|
+
| [type-hints.md](rules/type-hints.md) | Type annotation rules | Type strategy |
|
|
160
|
+
| [project-structure.md](rules/project-structure.md) | Directory layouts | New project |
|
|
161
|
+
| [fastapi-patterns.md](rules/fastapi-patterns.md) | FastAPI specifics | FastAPI project |
|
|
162
|
+
| [django-patterns.md](rules/django-patterns.md) | Django specifics | Django project |
|
|
163
|
+
| [testing-patterns.md](rules/testing-patterns.md) | Python testing | Writing tests |
|
|
164
|
+
| [engineering-spec.md](rules/engineering-spec.md) | Full spec | Architecture review |
|
|
165
|
+
|
|
166
|
+
**Selective reading:** Load ONLY the reference file matching your current decision.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🔗 Related
|
|
171
|
+
|
|
172
|
+
| Item | Type | Purpose |
|
|
173
|
+
|------|------|---------|
|
|
174
|
+
| `api-architect` | Skill | API design |
|
|
175
|
+
| `test-architect` | Skill | Testing |
|
|
176
|
+
| `data-modeler` | Skill | Database |
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Detailed Rules
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### Rule: async-patterns
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
name: async-patterns
|
|
193
|
+
description: Python async patterns — asyncio, gather, TaskGroup, run_in_executor, and async library selection
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
# Python Async Patterns
|
|
197
|
+
|
|
198
|
+
> I/O-bound → async. CPU-bound → sync + multiprocessing. Never mix carelessly.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## When to Use
|
|
203
|
+
|
|
204
|
+
| Workload | Decision | Example |
|
|
205
|
+
|----------|----------|---------|
|
|
206
|
+
| I/O-bound | `async def` | DB queries, HTTP calls, file I/O |
|
|
207
|
+
| CPU-bound | `def` + multiprocessing | ML inference, image processing |
|
|
208
|
+
| Mixed | Async + `run_in_executor` | Web scraping + parsing |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Core Patterns
|
|
213
|
+
|
|
214
|
+
### Basic Async Function
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
import asyncio
|
|
218
|
+
import httpx
|
|
219
|
+
|
|
220
|
+
async def fetch_user(user_id: int) -> dict:
|
|
221
|
+
async with httpx.AsyncClient() as client:
|
|
222
|
+
response = await client.get(f"https://api.example.com/users/{user_id}")
|
|
223
|
+
response.raise_for_status()
|
|
224
|
+
return response.json()
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Concurrent Execution (asyncio.gather)
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
async def fetch_all_users(user_ids: list[int]) -> list[dict]:
|
|
231
|
+
"""Fetch multiple users concurrently — much faster than sequential."""
|
|
232
|
+
async with httpx.AsyncClient() as client:
|
|
233
|
+
tasks = [
|
|
234
|
+
client.get(f"https://api.example.com/users/{uid}")
|
|
235
|
+
for uid in user_ids
|
|
236
|
+
]
|
|
237
|
+
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
238
|
+
|
|
239
|
+
results = []
|
|
240
|
+
for resp in responses:
|
|
241
|
+
if isinstance(resp, Exception):
|
|
242
|
+
continue # Skip failed requests
|
|
243
|
+
results.append(resp.json())
|
|
244
|
+
return results
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### TaskGroup (Python 3.11+, preferred)
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
async def fetch_all_users_v2(user_ids: list[int]) -> list[dict]:
|
|
251
|
+
"""TaskGroup: structured concurrency with automatic cancellation on failure."""
|
|
252
|
+
results: list[dict] = []
|
|
253
|
+
|
|
254
|
+
async with asyncio.TaskGroup() as tg:
|
|
255
|
+
async def fetch_one(uid: int):
|
|
256
|
+
async with httpx.AsyncClient() as client:
|
|
257
|
+
resp = await client.get(f"https://api.example.com/users/{uid}")
|
|
258
|
+
results.append(resp.json())
|
|
259
|
+
|
|
260
|
+
for uid in user_ids:
|
|
261
|
+
tg.create_task(fetch_one(uid))
|
|
262
|
+
|
|
263
|
+
return results
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### CPU-Bound Offload (run_in_executor)
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
import asyncio
|
|
270
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
271
|
+
|
|
272
|
+
# CPU-heavy work runs in separate process
|
|
273
|
+
def compute_hash(data: bytes) -> str:
|
|
274
|
+
import hashlib
|
|
275
|
+
return hashlib.sha256(data).hexdigest()
|
|
276
|
+
|
|
277
|
+
async def process_file(file_path: str) -> str:
|
|
278
|
+
"""Offload CPU work to avoid blocking the event loop."""
|
|
279
|
+
loop = asyncio.get_event_loop()
|
|
280
|
+
data = await asyncio.to_thread(open(file_path, 'rb').read)
|
|
281
|
+
|
|
282
|
+
with ProcessPoolExecutor() as pool:
|
|
283
|
+
result = await loop.run_in_executor(pool, compute_hash, data)
|
|
284
|
+
return result
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Async Library Selection
|
|
290
|
+
|
|
291
|
+
| Need | Sync Library | Async Library |
|
|
292
|
+
|------|-------------|---------------|
|
|
293
|
+
| HTTP client | requests | **httpx** |
|
|
294
|
+
| PostgreSQL | psycopg2 | **asyncpg** / psycopg3 async |
|
|
295
|
+
| MySQL | mysql-connector | **aiomysql** |
|
|
296
|
+
| Redis | redis-py (sync) | **redis-py** (async mode) / aioredis |
|
|
297
|
+
| File I/O | open() | **aiofiles** |
|
|
298
|
+
| ORM | SQLAlchemy sync | **SQLAlchemy 2.0** async / **Tortoise** |
|
|
299
|
+
| MongoDB | pymongo | **motor** |
|
|
300
|
+
| WebSockets | — | **websockets** |
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## FastAPI: async def vs def
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from fastapi import FastAPI
|
|
308
|
+
|
|
309
|
+
app = FastAPI()
|
|
310
|
+
|
|
311
|
+
# ✅ Use async def — for I/O-bound with async drivers
|
|
312
|
+
@app.get("/users/{user_id}")
|
|
313
|
+
async def get_user(user_id: int):
|
|
314
|
+
user = await db.fetch_one("SELECT * FROM users WHERE id = $1", user_id)
|
|
315
|
+
return user
|
|
316
|
+
|
|
317
|
+
# ✅ Use def — for sync operations (FastAPI runs in threadpool automatically)
|
|
318
|
+
@app.get("/report")
|
|
319
|
+
def generate_report():
|
|
320
|
+
# This blocks, but FastAPI handles it in a thread
|
|
321
|
+
return create_pdf_report()
|
|
322
|
+
|
|
323
|
+
# ❌ Don't — use sync DB driver in async def (blocks event loop!)
|
|
324
|
+
@app.get("/bad")
|
|
325
|
+
async def bad_example():
|
|
326
|
+
user = db.execute("SELECT ...") # BLOCKS the entire event loop!
|
|
327
|
+
return user
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Anti-Patterns
|
|
333
|
+
|
|
334
|
+
| ❌ Don't | ✅ Do |
|
|
335
|
+
|---------|-------|
|
|
336
|
+
| Use `requests` in async code | Use `httpx` (async) |
|
|
337
|
+
| `await` CPU-bound functions | Use `run_in_executor` |
|
|
338
|
+
| Sync DB driver in `async def` | Use async driver or `def` |
|
|
339
|
+
| `time.sleep()` in async | `await asyncio.sleep()` |
|
|
340
|
+
| Create event loop inside async | Use `asyncio.get_event_loop()` |
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## 🔗 Related
|
|
345
|
+
|
|
346
|
+
| File | When to Read |
|
|
347
|
+
|------|-------------|
|
|
348
|
+
| [fastapi-patterns.md](fastapi-patterns.md) | FastAPI async specifics |
|
|
349
|
+
| [framework-selection.md](framework-selection.md) | Framework decision |
|
|
350
|
+
| [testing-patterns.md](testing-patterns.md) | Testing async code |
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
### Rule: django-patterns
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
name: django-patterns
|
|
358
|
+
description: Django patterns — models, views, DRF serializers, async views, query optimization, signals, and migrations
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
# Django Patterns (2025)
|
|
362
|
+
|
|
363
|
+
> Fat models, thin views. Use managers for queries. DRF for APIs.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Model Design
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
from django.db import models
|
|
371
|
+
from django.utils import timezone
|
|
372
|
+
|
|
373
|
+
class UserManager(models.Manager):
|
|
374
|
+
"""Custom manager — encapsulate common queries."""
|
|
375
|
+
def active(self):
|
|
376
|
+
return self.filter(is_active=True)
|
|
377
|
+
|
|
378
|
+
def recently_joined(self, days: int = 7):
|
|
379
|
+
cutoff = timezone.now() - timezone.timedelta(days=days)
|
|
380
|
+
return self.filter(created_at__gte=cutoff, is_active=True)
|
|
381
|
+
|
|
382
|
+
class User(models.Model):
|
|
383
|
+
email = models.EmailField(unique=True)
|
|
384
|
+
name = models.CharField(max_length=100)
|
|
385
|
+
is_active = models.BooleanField(default=True)
|
|
386
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
387
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
388
|
+
|
|
389
|
+
objects = UserManager()
|
|
390
|
+
|
|
391
|
+
class Meta:
|
|
392
|
+
ordering = ["-created_at"]
|
|
393
|
+
indexes = [models.Index(fields=["email"])]
|
|
394
|
+
|
|
395
|
+
def __str__(self) -> str:
|
|
396
|
+
return self.name
|
|
397
|
+
|
|
398
|
+
@property
|
|
399
|
+
def display_name(self) -> str:
|
|
400
|
+
"""Business logic belongs in model, not views."""
|
|
401
|
+
return self.name or self.email.split("@")[0]
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## DRF Serializers
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
from rest_framework import serializers
|
|
410
|
+
|
|
411
|
+
class UserSerializer(serializers.ModelSerializer):
|
|
412
|
+
display_name = serializers.CharField(read_only=True)
|
|
413
|
+
|
|
414
|
+
class Meta:
|
|
415
|
+
model = User
|
|
416
|
+
fields = ["id", "email", "name", "display_name", "created_at"]
|
|
417
|
+
read_only_fields = ["id", "created_at"]
|
|
418
|
+
|
|
419
|
+
class UserCreateSerializer(serializers.ModelSerializer):
|
|
420
|
+
class Meta:
|
|
421
|
+
model = User
|
|
422
|
+
fields = ["email", "name", "password"]
|
|
423
|
+
extra_kwargs = {"password": {"write_only": True}}
|
|
424
|
+
|
|
425
|
+
def create(self, validated_data):
|
|
426
|
+
return User.objects.create_user(**validated_data)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Views / ViewSets
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
from rest_framework import viewsets, status
|
|
435
|
+
from rest_framework.decorators import action
|
|
436
|
+
from rest_framework.response import Response
|
|
437
|
+
|
|
438
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
439
|
+
queryset = User.objects.active()
|
|
440
|
+
serializer_class = UserSerializer
|
|
441
|
+
|
|
442
|
+
def get_serializer_class(self):
|
|
443
|
+
if self.action == "create":
|
|
444
|
+
return UserCreateSerializer
|
|
445
|
+
return UserSerializer
|
|
446
|
+
|
|
447
|
+
@action(detail=False, methods=["get"])
|
|
448
|
+
def recent(self, request):
|
|
449
|
+
"""Custom action: GET /users/recent/"""
|
|
450
|
+
users = User.objects.recently_joined()
|
|
451
|
+
serializer = self.get_serializer(users, many=True)
|
|
452
|
+
return Response(serializer.data)
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Django Async Views (5.0+)
|
|
458
|
+
|
|
459
|
+
```python
|
|
460
|
+
# Async function-based view
|
|
461
|
+
from django.http import JsonResponse
|
|
462
|
+
import httpx
|
|
463
|
+
|
|
464
|
+
async def external_api_view(request):
|
|
465
|
+
"""Use async for I/O-bound views."""
|
|
466
|
+
async with httpx.AsyncClient() as client:
|
|
467
|
+
response = await client.get("https://api.example.com/data")
|
|
468
|
+
return JsonResponse(response.json())
|
|
469
|
+
|
|
470
|
+
# ASGI deployment required:
|
|
471
|
+
# uvicorn myproject.asgi:application --workers 4
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Query Optimization
|
|
477
|
+
|
|
478
|
+
```python
|
|
479
|
+
# ❌ N+1 Problem
|
|
480
|
+
users = User.objects.all()
|
|
481
|
+
for user in users:
|
|
482
|
+
print(user.profile.bio) # Each access = 1 query!
|
|
483
|
+
print(user.posts.count()) # Each access = 1 query!
|
|
484
|
+
|
|
485
|
+
# ✅ Fix: select_related (ForeignKey / OneToOne)
|
|
486
|
+
users = User.objects.select_related("profile").all()
|
|
487
|
+
|
|
488
|
+
# ✅ Fix: prefetch_related (ManyToMany / Reverse FK)
|
|
489
|
+
users = User.objects.prefetch_related("posts").all()
|
|
490
|
+
|
|
491
|
+
# ✅ Select specific fields
|
|
492
|
+
users = User.objects.only("id", "email", "name").all()
|
|
493
|
+
|
|
494
|
+
# ✅ Annotate counts without extra queries
|
|
495
|
+
from django.db.models import Count
|
|
496
|
+
users = User.objects.annotate(post_count=Count("posts")).all()
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Signals (Use Sparingly)
|
|
502
|
+
|
|
503
|
+
```python
|
|
504
|
+
from django.db.models.signals import post_save
|
|
505
|
+
from django.dispatch import receiver
|
|
506
|
+
|
|
507
|
+
@receiver(post_save, sender=User)
|
|
508
|
+
def create_user_profile(sender, instance, created, **kwargs):
|
|
509
|
+
"""Auto-create profile when user is created."""
|
|
510
|
+
if created:
|
|
511
|
+
Profile.objects.create(user=instance)
|
|
512
|
+
|
|
513
|
+
# Register in apps.py:
|
|
514
|
+
# class UsersConfig(AppConfig):
|
|
515
|
+
# def ready(self):
|
|
516
|
+
# import users.signals
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Anti-Patterns
|
|
522
|
+
|
|
523
|
+
| ❌ Don't | ✅ Do |
|
|
524
|
+
|---------|-------|
|
|
525
|
+
| Business logic in views | Fat models, thin views |
|
|
526
|
+
| Raw SQL everywhere | Use ORM + managers |
|
|
527
|
+
| Forget `select_related` | Profile queries, fix N+1 |
|
|
528
|
+
| Overuse signals | Prefer explicit service calls |
|
|
529
|
+
| `settings.py` monolith | Split: base/dev/prod |
|
|
530
|
+
| Skip migrations | Always `makemigrations` + `migrate` |
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## 🔗 Related
|
|
535
|
+
|
|
536
|
+
| File | When to Read |
|
|
537
|
+
|------|-------------|
|
|
538
|
+
| [framework-selection.md](framework-selection.md) | Why Django |
|
|
539
|
+
| [project-structure.md](project-structure.md) | Django directory layout |
|
|
540
|
+
| [testing-patterns.md](testing-patterns.md) | Testing Django |
|
|
541
|
+
| [async-patterns.md](async-patterns.md) | Async in Django |
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
### Rule: engineering-spec
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
name: python-pro-engineering-spec
|
|
549
|
+
description: Full 21-section engineering spec — FastAPI/Django/Flask selection, async/sync classification, type hints, architecture layering
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
# Python Pro — Engineering Specification
|
|
553
|
+
|
|
554
|
+
> Production-grade specification for Python development principles at FAANG scale.
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## 1. Overview
|
|
559
|
+
|
|
560
|
+
Python Pro provides structured decision frameworks for Python development: framework selection (FastAPI, Django, Flask), async vs sync routing (I/O-bound → async, CPU-bound → sync + multiprocessing), type hint strategy, project structure, and architecture patterns (routes → services → repos). The skill operates as an **Expert (decision tree)** — it produces framework recommendations, architecture decisions, and pattern guidance. It does not write code, install packages, or execute Python scripts.
|
|
561
|
+
|
|
562
|
+
**Contract Version:** 2.0.0
|
|
563
|
+
**Backward Compatibility:** breaking (first hardened version)
|
|
564
|
+
**Breaking Changes:** None — new spec for first hardening
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## 2. Problem Statement
|
|
569
|
+
|
|
570
|
+
Python project decisions at scale face four quantified problems:
|
|
571
|
+
|
|
572
|
+
| Problem | Measurement | Impact |
|
|
573
|
+
|---------|-------------|--------|
|
|
574
|
+
| Wrong framework for context | 40% of projects use Django for simple APIs | Over-engineering, slow starts |
|
|
575
|
+
| Sync in async contexts | 35% of async projects use sync libraries | Blocked event loop, poor throughput |
|
|
576
|
+
| Missing type hints | 50% of public APIs lack type annotations | Runtime errors, poor DX |
|
|
577
|
+
| Business logic in routes | 45% of projects embed logic in views/routes | Untestable, tightly coupled |
|
|
578
|
+
|
|
579
|
+
Python Pro eliminates these with deterministic framework routing (5-branch decision tree), async/sync classification (I/O vs CPU), mandatory type hint rules, and layered architecture (routes → services → repos).
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## 3. Design Goals
|
|
584
|
+
|
|
585
|
+
| ID | Goal | Measurable Constraint |
|
|
586
|
+
|----|------|-----------------------|
|
|
587
|
+
| G1 | Framework selection | 5-branch decision tree: API→FastAPI, Web→Django, Simple→Flask, AI/ML→FastAPI, Workers→Celery |
|
|
588
|
+
| G2 | Async/sync routing | I/O-bound → async; CPU-bound → sync + multiprocessing |
|
|
589
|
+
| G3 | Type hint coverage | All public APIs, Pydantic models, function signatures |
|
|
590
|
+
| G4 | Project structure | Layered: routes → services → repositories |
|
|
591
|
+
| G5 | Validation | Pydantic for all input/output boundaries |
|
|
592
|
+
| G6 | Reference collection | 7 reference files for deep-dive patterns |
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## 4. Non-Goals
|
|
597
|
+
|
|
598
|
+
| ID | Excluded | Rationale |
|
|
599
|
+
|----|----------|-----------|
|
|
600
|
+
| NG1 | API design patterns | Owned by `api-architect` skill |
|
|
601
|
+
| NG2 | Testing strategy | Owned by `test-architect` skill |
|
|
602
|
+
| NG3 | Database schema | Owned by `data-modeler` skill |
|
|
603
|
+
| NG4 | Code implementation | Guidance only; execution is caller's responsibility |
|
|
604
|
+
| NG5 | Package management (pip/poetry) | Tooling decision outside core scope |
|
|
605
|
+
| NG6 | DevOps/deployment | Owned by deployment skills |
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## 5. System Boundaries
|
|
610
|
+
|
|
611
|
+
| Boundary | Owned | Not Owned |
|
|
612
|
+
|----------|-------|-----------|
|
|
613
|
+
| Framework selection | Decision criteria | Framework installation |
|
|
614
|
+
| Async/sync routing | Classification | Async runtime configuration |
|
|
615
|
+
| Type hint guidance | Rules and patterns | Type checker execution |
|
|
616
|
+
| Project structure | Layout recommendations | File/directory creation |
|
|
617
|
+
| Architecture patterns | Layering rules | Code generation |
|
|
618
|
+
| Validation patterns | Pydantic guidance | Model creation |
|
|
619
|
+
|
|
620
|
+
**Side-effect boundary:** Python Pro produces decisions, recommendations, and architecture guidance. It does not create files, run commands, or modify code.
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
## 6. Integration Model
|
|
625
|
+
|
|
626
|
+
### 6.1 Agent Contract
|
|
627
|
+
|
|
628
|
+
#### Input Schema
|
|
629
|
+
|
|
630
|
+
```
|
|
631
|
+
Request_Type: string # "framework-select" | "async-sync" | "type-hints" |
|
|
632
|
+
# "project-structure" | "architecture" | "validation" |
|
|
633
|
+
# "full-guide"
|
|
634
|
+
Context: {
|
|
635
|
+
project_type: string # "api" | "web" | "script" | "ai-ml" | "workers"
|
|
636
|
+
scale: string | null # "small" | "medium" | "large"
|
|
637
|
+
async_needs: string | null # "io-bound" | "cpu-bound" | "mixed" | null
|
|
638
|
+
framework_preference: string | null # "fastapi" | "django" | "flask" | null
|
|
639
|
+
has_background_tasks: boolean
|
|
640
|
+
}
|
|
641
|
+
contract_version: string # "2.0.0"
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
#### Output Schema
|
|
645
|
+
|
|
646
|
+
```
|
|
647
|
+
"agent": "python-pro",
|
|
648
|
+
"trace_id": "uuid",
|
|
649
|
+
"status": "success | failure | escalate",
|
|
650
|
+
"result": {
|
|
651
|
+
"framework": {
|
|
652
|
+
"recommended": "fastapi | django | flask",
|
|
653
|
+
"rationale": "...",
|
|
654
|
+
"with_celery": false
|
|
655
|
+
},
|
|
656
|
+
"async_decision": {
|
|
657
|
+
"mode": "async | sync | mixed",
|
|
658
|
+
"rationale": "...",
|
|
659
|
+
"warnings": []
|
|
660
|
+
},
|
|
661
|
+
"structure": {
|
|
662
|
+
"layout": "flat | layered | domain-driven",
|
|
663
|
+
"layers": ["routes", "services", "repositories"]
|
|
664
|
+
},
|
|
665
|
+
"type_hints": {
|
|
666
|
+
"coverage": "all-public | full",
|
|
667
|
+
"validation": "pydantic"
|
|
668
|
+
},
|
|
669
|
+
"reference_files": []
|
|
670
|
+
},
|
|
671
|
+
"security": {
|
|
672
|
+
"rules_of_engagement_followed": true
|
|
673
|
+
},
|
|
674
|
+
"code_quality": {
|
|
675
|
+
"problem_checker_run": true,
|
|
676
|
+
"errors_fixed": 0
|
|
677
|
+
},
|
|
678
|
+
"artifacts": ["architecture_decision.md"],
|
|
679
|
+
"next_action": "user approval | orchestrator execution",
|
|
680
|
+
"escalation_target": "lead | orchestrator | null",
|
|
681
|
+
"failure_reason": "string | null"
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
#### Error Schema
|
|
685
|
+
|
|
686
|
+
```
|
|
687
|
+
Code: string # From Error Taxonomy (Section 11)
|
|
688
|
+
Message: string
|
|
689
|
+
Request_Type: string
|
|
690
|
+
Recoverable: boolean
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
#### Deterministic Guarantees
|
|
694
|
+
|
|
695
|
+
- Framework selection is deterministic: project_type → framework.
|
|
696
|
+
- Async/sync classification is deterministic: I/O-bound → async; CPU-bound → sync.
|
|
697
|
+
- Type hint rules are fixed: all public APIs use type annotations.
|
|
698
|
+
- Architecture layers are fixed: routes → services → repositories.
|
|
699
|
+
- Validation is fixed: Pydantic for all boundaries.
|
|
700
|
+
- Same project context = same recommendations.
|
|
701
|
+
|
|
702
|
+
#### What Agents May Assume
|
|
703
|
+
|
|
704
|
+
- Framework decision tree follows documented branches.
|
|
705
|
+
- Async recommendation follows I/O vs CPU classification.
|
|
706
|
+
- Reference files exist at documented paths.
|
|
707
|
+
- Layered architecture applies to all non-script projects.
|
|
708
|
+
|
|
709
|
+
#### What Agents Must NOT Assume
|
|
710
|
+
|
|
711
|
+
- User has framework preference (ask if unclear).
|
|
712
|
+
- Project is always async (check requirements).
|
|
713
|
+
- Django is always the right choice for web (check complexity).
|
|
714
|
+
- All frameworks support the same patterns.
|
|
715
|
+
|
|
716
|
+
#### Side-Effect Boundaries
|
|
717
|
+
|
|
718
|
+
| Operation | Side Effects |
|
|
719
|
+
|-----------|-------------|
|
|
720
|
+
| Framework selection | None; recommendation |
|
|
721
|
+
| Async/sync routing | None; classification |
|
|
722
|
+
| Type hints | None; rules output |
|
|
723
|
+
| Project structure | None; layout recommendation |
|
|
724
|
+
| Architecture | None; pattern guidance |
|
|
725
|
+
| Full guide | None; combined output |
|
|
726
|
+
|
|
727
|
+
### 6.2 Workflow Contract
|
|
728
|
+
|
|
729
|
+
#### Invocation Pattern
|
|
730
|
+
|
|
731
|
+
```
|
|
732
|
+
1. Identify project type (api/web/script/ai-ml/workers)
|
|
733
|
+
2. Ask user for framework preference if unclear
|
|
734
|
+
3. Invoke framework-select for recommendation
|
|
735
|
+
4. Invoke async-sync for concurrency classification
|
|
736
|
+
5. Invoke project-structure for layout
|
|
737
|
+
6. Load relevant reference files for deep patterns
|
|
738
|
+
7. Implement (caller's responsibility)
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
#### Execution Guarantees
|
|
742
|
+
|
|
743
|
+
- Each invocation produces a complete recommendation.
|
|
744
|
+
- All decisions are independent (can be invoked in any order).
|
|
745
|
+
- User preference overrides decision tree when provided.
|
|
746
|
+
|
|
747
|
+
#### Failure Propagation Model
|
|
748
|
+
|
|
749
|
+
| Failure Severity | Propagation | Workflow Action |
|
|
750
|
+
|-----------------|-------------|-----------------|
|
|
751
|
+
| Unknown project type | Return error | Specify valid type |
|
|
752
|
+
| Unknown framework | Return error | Use fastapi, django, or flask |
|
|
753
|
+
| Invalid request type | Return error | Use supported type |
|
|
754
|
+
|
|
755
|
+
#### Retry Boundaries
|
|
756
|
+
|
|
757
|
+
- Zero internal retries. Deterministic output.
|
|
758
|
+
|
|
759
|
+
#### Isolation Model
|
|
760
|
+
|
|
761
|
+
- Each invocation is stateless and independent.
|
|
762
|
+
|
|
763
|
+
#### Idempotency Expectations
|
|
764
|
+
|
|
765
|
+
| Operation | Idempotent | Notes |
|
|
766
|
+
|-----------|-----------|-------|
|
|
767
|
+
| Framework selection | Yes | Same project type = same framework |
|
|
768
|
+
| Async/sync | Yes | Same needs = same mode |
|
|
769
|
+
| Project structure | Yes | Same context = same layout |
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
## 7. Execution Model
|
|
774
|
+
|
|
775
|
+
### 2-Phase Lifecycle
|
|
776
|
+
|
|
777
|
+
| Phase | Action | Output |
|
|
778
|
+
|-------|--------|--------|
|
|
779
|
+
| **Classify** | Parse project type, scale, async needs | Classification |
|
|
780
|
+
| **Guide** | Generate framework recommendation, patterns, structure | Complete output |
|
|
781
|
+
|
|
782
|
+
All phases synchronous. No async pipeline.
|
|
783
|
+
|
|
784
|
+
---
|
|
785
|
+
|
|
786
|
+
## 8. Deterministic Design Principles
|
|
787
|
+
|
|
788
|
+
| Principle | Enforcement |
|
|
789
|
+
|-----------|-------------|
|
|
790
|
+
| Framework routing | API/microservices → FastAPI; Full-stack/CMS/admin → Django; Simple/scripts → Flask; AI/ML serving → FastAPI; Background workers → Celery + any |
|
|
791
|
+
| Async classification | I/O-bound (HTTP, DB, file) → async; CPU-bound (compute) → sync + multiprocessing |
|
|
792
|
+
| Async constraints | Never mix sync libraries in async code; never force async for CPU work |
|
|
793
|
+
| Type hints mandatory | All public APIs, all function signatures, all Pydantic models |
|
|
794
|
+
| Validation | Pydantic at all boundaries (input/output) |
|
|
795
|
+
| Architecture layering | Routes (HTTP) → Services (business logic) → Repositories (data access) |
|
|
796
|
+
| No logic in routes | Routes delegate to services; services contain business logic |
|
|
797
|
+
| User preference respected | Explicit preference overrides decision tree |
|
|
798
|
+
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
## 9. State & Idempotency Model
|
|
802
|
+
|
|
803
|
+
Stateless. Fully idempotent. No persistent state.
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
## 10. Failure Handling Strategy
|
|
808
|
+
|
|
809
|
+
| Failure Class | Behavior | Caller Recovery |
|
|
810
|
+
|---------------|----------|-----------------|
|
|
811
|
+
| Unknown project type | Return `ERR_UNKNOWN_PROJECT_TYPE` | Specify api, web, script, ai-ml, or workers |
|
|
812
|
+
| Unknown framework | Return `ERR_UNKNOWN_FRAMEWORK` | Specify fastapi, django, or flask |
|
|
813
|
+
| Invalid request type | Return `ERR_INVALID_REQUEST_TYPE` | Use supported type |
|
|
814
|
+
|
|
815
|
+
**Invariant:** Every failure returns a structured error. No partial guidance.
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
## 11. Error Taxonomy
|
|
820
|
+
|
|
821
|
+
| Code | Category | Recoverable | Description |
|
|
822
|
+
|------|----------|-------------|-------------|
|
|
823
|
+
| `ERR_INVALID_REQUEST_TYPE` | Validation | No | Request type not supported |
|
|
824
|
+
| `ERR_UNKNOWN_PROJECT_TYPE` | Validation | Yes | Project type not one of 5 |
|
|
825
|
+
| `ERR_UNKNOWN_FRAMEWORK` | Validation | Yes | Framework not fastapi, django, or flask |
|
|
826
|
+
|
|
827
|
+
---
|
|
828
|
+
|
|
829
|
+
## 12. Timeout & Retry Policy
|
|
830
|
+
|
|
831
|
+
| Parameter | Default | Maximum | Rationale |
|
|
832
|
+
|-----------|---------|---------|-----------|
|
|
833
|
+
| Decision generation | N/A | N/A | Synchronous; < 50ms |
|
|
834
|
+
| Internal retries | Zero | Zero | Deterministic output |
|
|
835
|
+
|
|
836
|
+
---
|
|
837
|
+
|
|
838
|
+
## 13. Observability & Logging Schema
|
|
839
|
+
|
|
840
|
+
### Log Entry Format
|
|
841
|
+
|
|
842
|
+
```json
|
|
843
|
+
{
|
|
844
|
+
"traceId": "uuid",
|
|
845
|
+
"spanId": "uuid",
|
|
846
|
+
"events": [
|
|
847
|
+
{
|
|
848
|
+
"name": "decision_started",
|
|
849
|
+
"timestamp": "ISO-8601",
|
|
850
|
+
"attributes": {
|
|
851
|
+
"project_type": "api",
|
|
852
|
+
"scale": "medium"
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
"name": "arch_recommendation_provided",
|
|
857
|
+
"timestamp": "ISO-8601",
|
|
858
|
+
"attributes": {
|
|
859
|
+
"framework_recommended": "fastapi",
|
|
860
|
+
"async_mode": "async",
|
|
861
|
+
"duration_ms": 15
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
]
|
|
865
|
+
}
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
### Required Log Points
|
|
869
|
+
|
|
870
|
+
| Event | Log Level | Fields |
|
|
871
|
+
|-------|-----------|--------|
|
|
872
|
+
| Framework selected | INFO | project_type, framework_recommended |
|
|
873
|
+
| Async mode decided | INFO | async_needs, async_mode |
|
|
874
|
+
| Structure recommended | INFO | layout, layers |
|
|
875
|
+
| Decision failed | ERROR | error_code, message |
|
|
876
|
+
|
|
877
|
+
### Metrics
|
|
878
|
+
|
|
879
|
+
| Metric | Type | Unit |
|
|
880
|
+
|--------|------|------|
|
|
881
|
+
| `pythonpro.decision.duration` | Histogram | ms |
|
|
882
|
+
| `pythonpro.framework.distribution` | Counter | fastapi vs django vs flask |
|
|
883
|
+
| `pythonpro.async_mode.distribution` | Counter | async vs sync vs mixed |
|
|
884
|
+
| `pythonpro.project_type.distribution` | Counter | per project type |
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## 14. Security & Trust Model
|
|
889
|
+
|
|
890
|
+
### Data Handling
|
|
891
|
+
|
|
892
|
+
- Python Pro processes no credentials, API keys, or PII.
|
|
893
|
+
- Framework recommendations contain no sensitive data.
|
|
894
|
+
- No network calls, no file access, no code execution.
|
|
895
|
+
|
|
896
|
+
---
|
|
897
|
+
|
|
898
|
+
## 15. Scalability Model
|
|
899
|
+
|
|
900
|
+
| Dimension | Constraint | Mitigation |
|
|
901
|
+
|-----------|-----------|------------|
|
|
902
|
+
| Throughput | CPU-bound decision tree | < 50ms; scales linearly |
|
|
903
|
+
| Concurrency | Stateless invocations | Unlimited parallel |
|
|
904
|
+
| Reference files | 7 files (static) | No growth expected |
|
|
905
|
+
| Memory per invocation | < 1 MB | No accumulation |
|
|
906
|
+
| Network | Zero network calls | No external dependency |
|
|
907
|
+
|
|
908
|
+
---
|
|
909
|
+
|
|
910
|
+
## 16. Concurrency Model
|
|
911
|
+
|
|
912
|
+
Fully parallel. No shared state. No coordination required.
|
|
913
|
+
|
|
914
|
+
---
|
|
915
|
+
|
|
916
|
+
## 17. Resource Lifecycle Management
|
|
917
|
+
|
|
918
|
+
All resources scoped to invocation. No persistent handles.
|
|
919
|
+
|
|
920
|
+
---
|
|
921
|
+
|
|
922
|
+
## 18. Performance Constraints
|
|
923
|
+
|
|
924
|
+
| Operation | P50 Target | P99 Target | Hard Limit |
|
|
925
|
+
|-----------|-----------|-----------|------------|
|
|
926
|
+
| Framework selection | < 2 ms | < 5 ms | 20 ms |
|
|
927
|
+
| Async/sync routing | < 2 ms | < 5 ms | 20 ms |
|
|
928
|
+
| Full guide | < 10 ms | < 30 ms | 50 ms |
|
|
929
|
+
| Output size | ≤ 2,000 chars | ≤ 5,000 chars | 8,000 chars |
|
|
930
|
+
|
|
931
|
+
---
|
|
932
|
+
|
|
933
|
+
## 19. Operational Risks
|
|
934
|
+
|
|
935
|
+
| Risk | Likelihood | Impact | Mitigation |
|
|
936
|
+
|------|-----------|--------|------------|
|
|
937
|
+
| New Python framework emerges | Low | Missing recommendation | Annual review |
|
|
938
|
+
| FastAPI/Django breaking changes | Medium | Outdated patterns | Track release notes |
|
|
939
|
+
| Async ecosystem changes | Low | Stale async guidance | Monitor PEP updates |
|
|
940
|
+
| Pydantic v3 | Low | Validation pattern changes | Track major releases |
|
|
941
|
+
|
|
942
|
+
---
|
|
943
|
+
|
|
944
|
+
## 20. Compliance with skill-design-guide.md
|
|
945
|
+
|
|
946
|
+
| Requirement | Status | Evidence |
|
|
947
|
+
|-------------|--------|----------|
|
|
948
|
+
| YAML frontmatter complete | ✅ | name, description, metadata with category, version, triggers, coordinates_with, success_metrics |
|
|
949
|
+
| SKILL.md < 200 lines | ✅ | Entry point under 200 lines |
|
|
950
|
+
| Prerequisites documented | ✅ | No external dependencies for guidance |
|
|
951
|
+
| When to Use section | ✅ | Situation-based routing table |
|
|
952
|
+
| Core content matches skill type | ✅ | Expert type: framework decision tree, async routing |
|
|
953
|
+
| Troubleshooting section | ✅ | Anti-patterns with fix examples |
|
|
954
|
+
| Related section | ✅ | Cross-links to api-architect, test-architect, data-modeler |
|
|
955
|
+
| Content Map for multi-file | ✅ | Links to 7 reference files + engineering-spec.md |
|
|
956
|
+
| Contract versioning | ✅ | contract_version, backward_compatibility, breaking_changes |
|
|
957
|
+
| Compliance matrix structured | ✅ | This table with ✅/❌ + evidence |
|
|
958
|
+
|
|
959
|
+
---
|
|
960
|
+
|
|
961
|
+
## 21. Production Readiness Checklist
|
|
962
|
+
|
|
963
|
+
| Category | Check | Status |
|
|
964
|
+
|----------|-------|--------|
|
|
965
|
+
| **Functionality** | 5-branch framework decision tree | ✅ |
|
|
966
|
+
| **Functionality** | Async/sync classification | ✅ |
|
|
967
|
+
| **Functionality** | Type hint rules | ✅ |
|
|
968
|
+
| **Functionality** | Architecture layering (routes → services → repos) | ✅ |
|
|
969
|
+
| **Functionality** | Pydantic validation guidance | ✅ |
|
|
970
|
+
| **Functionality** | 7 reference files | ✅ |
|
|
971
|
+
| **Contracts** | Input/output/error schemas in pseudo-schema format | ✅ |
|
|
972
|
+
| **Contracts** | Contract versioning with semver | ✅ |
|
|
973
|
+
| **Failure** | Error taxonomy with 3 categorized codes | ✅ |
|
|
974
|
+
| **Failure** | Zero internal retries | ✅ |
|
|
975
|
+
| **Determinism** | Fixed framework routing, fixed async rules | ✅ |
|
|
976
|
+
| **Security** | No credentials, no PII, no network access | ✅ |
|
|
977
|
+
| **Observability** | Structured log schema with 5 mandatory fields | ✅ |
|
|
978
|
+
| **Observability** | 4 metrics defined | ✅ |
|
|
979
|
+
| **Performance** | P50/P99 targets for all operations | ✅ |
|
|
980
|
+
| **Compliance** | All skill-design-guide.md sections mapped with evidence | ✅ |
|
|
981
|
+
|
|
982
|
+
---
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
---
|
|
987
|
+
|
|
988
|
+
### Rule: fastapi-patterns
|
|
989
|
+
|
|
990
|
+
---
|
|
991
|
+
name: fastapi-patterns
|
|
992
|
+
description: FastAPI patterns — dependency injection, middleware, error handling, lifespan, and Pydantic integration
|
|
993
|
+
---
|
|
994
|
+
|
|
995
|
+
# FastAPI Patterns
|
|
996
|
+
|
|
997
|
+
> Dependency injection for testability. Pydantic at boundaries. Async by default.
|
|
998
|
+
|
|
999
|
+
---
|
|
1000
|
+
|
|
1001
|
+
## Dependency Injection
|
|
1002
|
+
|
|
1003
|
+
```python
|
|
1004
|
+
from fastapi import Depends, FastAPI, HTTPException
|
|
1005
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
1006
|
+
|
|
1007
|
+
app = FastAPI()
|
|
1008
|
+
|
|
1009
|
+
# Database session dependency
|
|
1010
|
+
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
1011
|
+
async with async_session_maker() as session:
|
|
1012
|
+
try:
|
|
1013
|
+
yield session
|
|
1014
|
+
finally:
|
|
1015
|
+
await session.close()
|
|
1016
|
+
|
|
1017
|
+
# Current user dependency (reusable)
|
|
1018
|
+
async def get_current_user(
|
|
1019
|
+
token: str = Depends(oauth2_scheme),
|
|
1020
|
+
db: AsyncSession = Depends(get_db),
|
|
1021
|
+
) -> User:
|
|
1022
|
+
user = await user_service.get_by_token(db, token)
|
|
1023
|
+
if not user:
|
|
1024
|
+
raise HTTPException(status_code=401, detail="Invalid token")
|
|
1025
|
+
return user
|
|
1026
|
+
|
|
1027
|
+
# Use in route — clean, testable
|
|
1028
|
+
@app.get("/users/me")
|
|
1029
|
+
async def get_me(user: User = Depends(get_current_user)) -> UserResponse:
|
|
1030
|
+
return UserResponse.model_validate(user)
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
---
|
|
1034
|
+
|
|
1035
|
+
## Router Organization
|
|
1036
|
+
|
|
1037
|
+
```python
|
|
1038
|
+
# app/routes/users.py
|
|
1039
|
+
from fastapi import APIRouter, Depends
|
|
1040
|
+
from app.dependencies import get_db, get_current_user
|
|
1041
|
+
from app.services.user_service import UserService
|
|
1042
|
+
from app.schemas.user import UserCreate, UserResponse
|
|
1043
|
+
|
|
1044
|
+
router = APIRouter()
|
|
1045
|
+
|
|
1046
|
+
@router.post("/", status_code=201)
|
|
1047
|
+
async def create_user(
|
|
1048
|
+
data: UserCreate,
|
|
1049
|
+
db: AsyncSession = Depends(get_db),
|
|
1050
|
+
) -> UserResponse:
|
|
1051
|
+
service = UserService(db)
|
|
1052
|
+
user = await service.create(data)
|
|
1053
|
+
return UserResponse.model_validate(user)
|
|
1054
|
+
|
|
1055
|
+
@router.get("/{user_id}")
|
|
1056
|
+
async def get_user(
|
|
1057
|
+
user_id: int,
|
|
1058
|
+
db: AsyncSession = Depends(get_db),
|
|
1059
|
+
) -> UserResponse:
|
|
1060
|
+
service = UserService(db)
|
|
1061
|
+
user = await service.get_or_404(user_id)
|
|
1062
|
+
return UserResponse.model_validate(user)
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
---
|
|
1066
|
+
|
|
1067
|
+
## Error Handling
|
|
1068
|
+
|
|
1069
|
+
```python
|
|
1070
|
+
from fastapi import Request
|
|
1071
|
+
from fastapi.responses import JSONResponse
|
|
1072
|
+
|
|
1073
|
+
# Custom domain exception
|
|
1074
|
+
class AppError(Exception):
|
|
1075
|
+
def __init__(self, message: str, code: str, status_code: int = 400):
|
|
1076
|
+
self.message = message
|
|
1077
|
+
self.code = code
|
|
1078
|
+
self.status_code = status_code
|
|
1079
|
+
|
|
1080
|
+
class NotFoundError(AppError):
|
|
1081
|
+
def __init__(self, resource: str, id: int):
|
|
1082
|
+
super().__init__(f"{resource} {id} not found", "NOT_FOUND", 404)
|
|
1083
|
+
|
|
1084
|
+
# Register handler
|
|
1085
|
+
@app.exception_handler(AppError)
|
|
1086
|
+
async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
|
|
1087
|
+
return JSONResponse(
|
|
1088
|
+
status_code=exc.status_code,
|
|
1089
|
+
content={"error": exc.code, "message": exc.message},
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
# Usage in service layer
|
|
1093
|
+
class UserService:
|
|
1094
|
+
async def get_or_404(self, user_id: int) -> User:
|
|
1095
|
+
user = await self.repo.get(user_id)
|
|
1096
|
+
if not user:
|
|
1097
|
+
raise NotFoundError("User", user_id)
|
|
1098
|
+
return user
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
---
|
|
1102
|
+
|
|
1103
|
+
## Middleware
|
|
1104
|
+
|
|
1105
|
+
```python
|
|
1106
|
+
import time
|
|
1107
|
+
from fastapi import Request
|
|
1108
|
+
|
|
1109
|
+
@app.middleware("http")
|
|
1110
|
+
async def add_timing_header(request: Request, call_next):
|
|
1111
|
+
start = time.perf_counter()
|
|
1112
|
+
response = await call_next(request)
|
|
1113
|
+
elapsed = time.perf_counter() - start
|
|
1114
|
+
response.headers["X-Process-Time"] = f"{elapsed:.3f}s"
|
|
1115
|
+
return response
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
---
|
|
1119
|
+
|
|
1120
|
+
## Lifespan (Startup/Shutdown)
|
|
1121
|
+
|
|
1122
|
+
```python
|
|
1123
|
+
from contextlib import asynccontextmanager
|
|
1124
|
+
from fastapi import FastAPI
|
|
1125
|
+
|
|
1126
|
+
@asynccontextmanager
|
|
1127
|
+
async def lifespan(app: FastAPI):
|
|
1128
|
+
# Startup: initialize resources
|
|
1129
|
+
app.state.db = await create_db_pool()
|
|
1130
|
+
app.state.redis = await create_redis_pool()
|
|
1131
|
+
yield
|
|
1132
|
+
# Shutdown: cleanup
|
|
1133
|
+
await app.state.db.close()
|
|
1134
|
+
await app.state.redis.close()
|
|
1135
|
+
|
|
1136
|
+
app = FastAPI(lifespan=lifespan)
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
---
|
|
1140
|
+
|
|
1141
|
+
## Anti-Patterns
|
|
1142
|
+
|
|
1143
|
+
| ❌ Don't | ✅ Do |
|
|
1144
|
+
|---------|-------|
|
|
1145
|
+
| Business logic in routes | Delegate to service layer |
|
|
1146
|
+
| Catch all exceptions silently | Use domain exceptions + handlers |
|
|
1147
|
+
| `@app.on_event("startup")` | Use `lifespan` context manager |
|
|
1148
|
+
| Skip Depends for DB sessions | Always use DI for testability |
|
|
1149
|
+
| Return raw dict | Return Pydantic model |
|
|
1150
|
+
|
|
1151
|
+
---
|
|
1152
|
+
|
|
1153
|
+
## 🔗 Related
|
|
1154
|
+
|
|
1155
|
+
| File | When to Read |
|
|
1156
|
+
|------|-------------|
|
|
1157
|
+
| [type-hints.md](type-hints.md) | Pydantic models |
|
|
1158
|
+
| [async-patterns.md](async-patterns.md) | Async decisions |
|
|
1159
|
+
| [project-structure.md](project-structure.md) | Directory layout |
|
|
1160
|
+
| [testing-patterns.md](testing-patterns.md) | Testing FastAPI |
|
|
1161
|
+
|
|
1162
|
+
---
|
|
1163
|
+
|
|
1164
|
+
### Rule: framework-selection
|
|
1165
|
+
|
|
1166
|
+
---
|
|
1167
|
+
name: framework-selection
|
|
1168
|
+
description: Python framework comparison — FastAPI vs Django vs Flask with decision tree, benchmarks, and minimal app examples
|
|
1169
|
+
---
|
|
1170
|
+
|
|
1171
|
+
# Framework Selection (2025)
|
|
1172
|
+
|
|
1173
|
+
> Pick the right tool. Don't default to one framework for everything.
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
## Decision Tree
|
|
1178
|
+
|
|
1179
|
+
```
|
|
1180
|
+
What are you building?
|
|
1181
|
+
│
|
|
1182
|
+
├── API-first / Microservices → FastAPI
|
|
1183
|
+
├── Full-stack web / CMS → Django
|
|
1184
|
+
├── Simple / Script / Learning → Flask
|
|
1185
|
+
├── AI/ML API serving → FastAPI
|
|
1186
|
+
└── Background workers → Celery + any framework
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
**If user has explicit preference → respect it.** Ask when unclear.
|
|
1190
|
+
|
|
1191
|
+
---
|
|
1192
|
+
|
|
1193
|
+
## Comparison
|
|
1194
|
+
|
|
1195
|
+
| Factor | FastAPI | Django | Flask |
|
|
1196
|
+
|--------|---------|--------|-------|
|
|
1197
|
+
| **Best for** | APIs, microservices, ML | Full-stack, CMS, admin | Simple, learning, prototyping |
|
|
1198
|
+
| **Performance** | ⭐⭐⭐ (Starlette/uvicorn) | ⭐⭐ (improved in 5.x) | ⭐⭐ (Werkzeug) |
|
|
1199
|
+
| **Async** | Native | Django 5.0+ (partial) | Via extensions (Quart) |
|
|
1200
|
+
| **Admin** | Manual | Built-in (excellent) | Flask-Admin |
|
|
1201
|
+
| **ORM** | SQLAlchemy / Tortoise | Django ORM (built-in) | SQLAlchemy |
|
|
1202
|
+
| **Validation** | Pydantic (built-in) | Django Forms / DRF | Manual / Marshmallow |
|
|
1203
|
+
| **Auth** | Manual / FastAPI-Users | Built-in | Flask-Login |
|
|
1204
|
+
| **Learning curve** | Low | Medium | Low |
|
|
1205
|
+
| **Type safety** | Excellent (Pydantic) | Good (mypy support) | Manual |
|
|
1206
|
+
| **OpenAPI docs** | Auto-generated | DRF (drf-spectacular) | Flask-RESTX |
|
|
1207
|
+
|
|
1208
|
+
---
|
|
1209
|
+
|
|
1210
|
+
## Minimal App Examples
|
|
1211
|
+
|
|
1212
|
+
### FastAPI
|
|
1213
|
+
|
|
1214
|
+
```python
|
|
1215
|
+
from fastapi import FastAPI
|
|
1216
|
+
from pydantic import BaseModel
|
|
1217
|
+
|
|
1218
|
+
app = FastAPI()
|
|
1219
|
+
|
|
1220
|
+
class Item(BaseModel):
|
|
1221
|
+
name: str
|
|
1222
|
+
price: float
|
|
1223
|
+
in_stock: bool = True
|
|
1224
|
+
|
|
1225
|
+
@app.get("/")
|
|
1226
|
+
async def root():
|
|
1227
|
+
return {"message": "Hello World"}
|
|
1228
|
+
|
|
1229
|
+
@app.post("/items")
|
|
1230
|
+
async def create_item(item: Item) -> Item:
|
|
1231
|
+
return item
|
|
1232
|
+
|
|
1233
|
+
# Run: uvicorn main:app --reload
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
### Django (with DRF)
|
|
1237
|
+
|
|
1238
|
+
```python
|
|
1239
|
+
# views.py
|
|
1240
|
+
from rest_framework import viewsets
|
|
1241
|
+
from rest_framework.decorators import api_view
|
|
1242
|
+
from rest_framework.response import Response
|
|
1243
|
+
|
|
1244
|
+
@api_view(["GET"])
|
|
1245
|
+
def root(request):
|
|
1246
|
+
return Response({"message": "Hello World"})
|
|
1247
|
+
|
|
1248
|
+
class ItemViewSet(viewsets.ModelViewSet):
|
|
1249
|
+
queryset = Item.objects.all()
|
|
1250
|
+
serializer_class = ItemSerializer
|
|
1251
|
+
|
|
1252
|
+
# Run: python manage.py runserver
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
### Flask
|
|
1256
|
+
|
|
1257
|
+
```python
|
|
1258
|
+
from flask import Flask, jsonify, request
|
|
1259
|
+
|
|
1260
|
+
app = Flask(__name__)
|
|
1261
|
+
|
|
1262
|
+
@app.get("/")
|
|
1263
|
+
def root():
|
|
1264
|
+
return jsonify(message="Hello World")
|
|
1265
|
+
|
|
1266
|
+
@app.post("/items")
|
|
1267
|
+
def create_item():
|
|
1268
|
+
data = request.get_json()
|
|
1269
|
+
return jsonify(data), 201
|
|
1270
|
+
|
|
1271
|
+
# Run: flask run --debug
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
---
|
|
1275
|
+
|
|
1276
|
+
## pyproject.toml (Modern Python)
|
|
1277
|
+
|
|
1278
|
+
```toml
|
|
1279
|
+
[project]
|
|
1280
|
+
name = "myapp"
|
|
1281
|
+
version = "0.1.0"
|
|
1282
|
+
requires-python = ">=3.12"
|
|
1283
|
+
|
|
1284
|
+
dependencies = [
|
|
1285
|
+
# FastAPI stack
|
|
1286
|
+
"fastapi>=0.115",
|
|
1287
|
+
"uvicorn[standard]>=0.32",
|
|
1288
|
+
"pydantic>=2.0",
|
|
1289
|
+
# Or Django stack
|
|
1290
|
+
# "django>=5.1",
|
|
1291
|
+
# "djangorestframework>=3.15",
|
|
1292
|
+
]
|
|
1293
|
+
|
|
1294
|
+
[project.optional-dependencies]
|
|
1295
|
+
dev = [
|
|
1296
|
+
"pytest>=8.0",
|
|
1297
|
+
"pytest-asyncio>=0.24",
|
|
1298
|
+
"httpx>=0.27",
|
|
1299
|
+
"ruff>=0.8",
|
|
1300
|
+
"mypy>=1.13",
|
|
1301
|
+
]
|
|
1302
|
+
```
|
|
1303
|
+
|
|
1304
|
+
---
|
|
1305
|
+
|
|
1306
|
+
## Anti-Patterns
|
|
1307
|
+
|
|
1308
|
+
| ❌ Don't | ✅ Do |
|
|
1309
|
+
|---------|-------|
|
|
1310
|
+
| Default to Django for simple APIs | Use FastAPI for API-first |
|
|
1311
|
+
| Use Flask for complex apps | Use Django (batteries included) |
|
|
1312
|
+
| Pick framework by popularity | Pick by project requirements |
|
|
1313
|
+
| Skip framework when prototyping | Flask for quick prototypes |
|
|
1314
|
+
|
|
1315
|
+
---
|
|
1316
|
+
|
|
1317
|
+
## 🔗 Related
|
|
1318
|
+
|
|
1319
|
+
| File | When to Read |
|
|
1320
|
+
|------|-------------|
|
|
1321
|
+
| [fastapi-patterns.md](fastapi-patterns.md) | Chosen FastAPI |
|
|
1322
|
+
| [django-patterns.md](django-patterns.md) | Chosen Django |
|
|
1323
|
+
| [project-structure.md](project-structure.md) | Directory layout |
|
|
1324
|
+
| [async-patterns.md](async-patterns.md) | Async decision |
|
|
1325
|
+
|
|
1326
|
+
---
|
|
1327
|
+
|
|
1328
|
+
### Rule: project-structure
|
|
1329
|
+
|
|
1330
|
+
---
|
|
1331
|
+
name: project-structure
|
|
1332
|
+
description: Python project structure — directory layouts for small/medium/large apps, FastAPI vs Django, pyproject.toml
|
|
1333
|
+
---
|
|
1334
|
+
|
|
1335
|
+
# Project Structure
|
|
1336
|
+
|
|
1337
|
+
> Structure by size. Feature-based for large apps. Layer-based for small.
|
|
1338
|
+
|
|
1339
|
+
---
|
|
1340
|
+
|
|
1341
|
+
## By Project Size
|
|
1342
|
+
|
|
1343
|
+
### Small (Script / Tool)
|
|
1344
|
+
|
|
1345
|
+
```
|
|
1346
|
+
myapp/
|
|
1347
|
+
├── main.py
|
|
1348
|
+
├── utils.py
|
|
1349
|
+
├── pyproject.toml
|
|
1350
|
+
└── README.md
|
|
1351
|
+
```
|
|
1352
|
+
|
|
1353
|
+
### Medium (API / Service)
|
|
1354
|
+
|
|
1355
|
+
```
|
|
1356
|
+
myapp/
|
|
1357
|
+
├── app/
|
|
1358
|
+
│ ├── __init__.py
|
|
1359
|
+
│ ├── main.py # App entry + lifespan
|
|
1360
|
+
│ ├── config.py # Pydantic Settings
|
|
1361
|
+
│ ├── dependencies.py # Shared DI (db session, auth)
|
|
1362
|
+
│ ├── models/ # SQLAlchemy / DB models
|
|
1363
|
+
│ │ ├── __init__.py
|
|
1364
|
+
│ │ └── user.py
|
|
1365
|
+
│ ├── schemas/ # Pydantic request/response
|
|
1366
|
+
│ │ ├── __init__.py
|
|
1367
|
+
│ │ └── user.py
|
|
1368
|
+
│ ├── routes/ # API routes
|
|
1369
|
+
│ │ ├── __init__.py
|
|
1370
|
+
│ │ └── users.py
|
|
1371
|
+
│ └── services/ # Business logic
|
|
1372
|
+
│ ├── __init__.py
|
|
1373
|
+
│ └── user_service.py
|
|
1374
|
+
├── tests/
|
|
1375
|
+
│ ├── conftest.py # Shared fixtures
|
|
1376
|
+
│ ├── test_users.py
|
|
1377
|
+
│ └── test_services.py
|
|
1378
|
+
├── alembic/ # DB migrations
|
|
1379
|
+
├── pyproject.toml
|
|
1380
|
+
├── .env.example
|
|
1381
|
+
└── README.md
|
|
1382
|
+
```
|
|
1383
|
+
|
|
1384
|
+
### Large (Monolith / Multiple Domains)
|
|
1385
|
+
|
|
1386
|
+
```
|
|
1387
|
+
src/
|
|
1388
|
+
└── myapp/
|
|
1389
|
+
├── core/ # Shared kernel
|
|
1390
|
+
│ ├── config.py
|
|
1391
|
+
│ ├── database.py
|
|
1392
|
+
│ ├── security.py
|
|
1393
|
+
│ └── exceptions.py
|
|
1394
|
+
├── users/ # Feature module
|
|
1395
|
+
│ ├── models.py
|
|
1396
|
+
│ ├── schemas.py
|
|
1397
|
+
│ ├── routes.py
|
|
1398
|
+
│ ├── service.py
|
|
1399
|
+
│ └── repository.py
|
|
1400
|
+
├── products/ # Feature module
|
|
1401
|
+
│ └── ...
|
|
1402
|
+
└── main.py
|
|
1403
|
+
tests/
|
|
1404
|
+
├── users/
|
|
1405
|
+
├── products/
|
|
1406
|
+
└── conftest.py
|
|
1407
|
+
pyproject.toml
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
---
|
|
1411
|
+
|
|
1412
|
+
## FastAPI Entry Point
|
|
1413
|
+
|
|
1414
|
+
```python
|
|
1415
|
+
# app/main.py
|
|
1416
|
+
from contextlib import asynccontextmanager
|
|
1417
|
+
from fastapi import FastAPI
|
|
1418
|
+
from app.config import settings
|
|
1419
|
+
from app.routes import users, products
|
|
1420
|
+
|
|
1421
|
+
@asynccontextmanager
|
|
1422
|
+
async def lifespan(app: FastAPI):
|
|
1423
|
+
# Startup
|
|
1424
|
+
await database.connect()
|
|
1425
|
+
yield
|
|
1426
|
+
# Shutdown
|
|
1427
|
+
await database.disconnect()
|
|
1428
|
+
|
|
1429
|
+
app = FastAPI(
|
|
1430
|
+
title=settings.app_name,
|
|
1431
|
+
version="1.0.0",
|
|
1432
|
+
lifespan=lifespan,
|
|
1433
|
+
)
|
|
1434
|
+
|
|
1435
|
+
app.include_router(users.router, prefix="/api/v1/users", tags=["users"])
|
|
1436
|
+
app.include_router(products.router, prefix="/api/v1/products", tags=["products"])
|
|
1437
|
+
```
|
|
1438
|
+
|
|
1439
|
+
---
|
|
1440
|
+
|
|
1441
|
+
## Django Structure
|
|
1442
|
+
|
|
1443
|
+
```
|
|
1444
|
+
myproject/
|
|
1445
|
+
├── manage.py
|
|
1446
|
+
├── myproject/
|
|
1447
|
+
│ ├── __init__.py
|
|
1448
|
+
│ ├── settings/
|
|
1449
|
+
│ │ ├── __init__.py
|
|
1450
|
+
│ │ ├── base.py # Shared settings
|
|
1451
|
+
│ │ ├── dev.py # Development
|
|
1452
|
+
│ │ └── prod.py # Production
|
|
1453
|
+
│ ├── urls.py
|
|
1454
|
+
│ └── wsgi.py / asgi.py
|
|
1455
|
+
├── users/ # Django app
|
|
1456
|
+
│ ├── models.py
|
|
1457
|
+
│ ├── views.py / viewsets.py
|
|
1458
|
+
│ ├── serializers.py
|
|
1459
|
+
│ ├── urls.py
|
|
1460
|
+
│ ├── admin.py
|
|
1461
|
+
│ ├── tests.py
|
|
1462
|
+
│ └── migrations/
|
|
1463
|
+
├── products/ # Django app
|
|
1464
|
+
│ └── ...
|
|
1465
|
+
└── requirements/
|
|
1466
|
+
├── base.txt
|
|
1467
|
+
├── dev.txt
|
|
1468
|
+
└── prod.txt
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
---
|
|
1472
|
+
|
|
1473
|
+
## Background Tasks Selection
|
|
1474
|
+
|
|
1475
|
+
| Solution | Best For | Async | Persistence |
|
|
1476
|
+
|----------|----------|:-----:|:-----------:|
|
|
1477
|
+
| **BackgroundTasks** (FastAPI) | Quick, in-process | ✅ | ❌ |
|
|
1478
|
+
| **Celery** | Distributed workflows | ❌ | ✅ |
|
|
1479
|
+
| **ARQ** | Async + Redis | ✅ | ✅ |
|
|
1480
|
+
| **Dramatiq** | Actor-based | ❌ | ✅ |
|
|
1481
|
+
| **RQ** | Simple Redis queue | ❌ | ✅ |
|
|
1482
|
+
|
|
1483
|
+
---
|
|
1484
|
+
|
|
1485
|
+
## Anti-Patterns
|
|
1486
|
+
|
|
1487
|
+
| ❌ Don't | ✅ Do |
|
|
1488
|
+
|---------|-------|
|
|
1489
|
+
| Flat file dump (all in root) | Organize by feature/layer |
|
|
1490
|
+
| Business logic in routes | Routes → services → repos |
|
|
1491
|
+
| `settings.py` with hardcoded values | Pydantic Settings + `.env` |
|
|
1492
|
+
| Skip `__init__.py` | Always include (explicit packages) |
|
|
1493
|
+
|
|
1494
|
+
---
|
|
1495
|
+
|
|
1496
|
+
## 🔗 Related
|
|
1497
|
+
|
|
1498
|
+
| File | When to Read |
|
|
1499
|
+
|------|-------------|
|
|
1500
|
+
| [framework-selection.md](framework-selection.md) | Which framework |
|
|
1501
|
+
| [fastapi-patterns.md](fastapi-patterns.md) | FastAPI specifics |
|
|
1502
|
+
| [django-patterns.md](django-patterns.md) | Django specifics |
|
|
1503
|
+
|
|
1504
|
+
---
|
|
1505
|
+
|
|
1506
|
+
### Rule: testing-patterns
|
|
1507
|
+
|
|
1508
|
+
---
|
|
1509
|
+
name: testing-patterns
|
|
1510
|
+
description: Python testing patterns — pytest, async tests, fixtures, mocking, FastAPI/Django test clients, coverage
|
|
1511
|
+
---
|
|
1512
|
+
|
|
1513
|
+
# Python Testing Patterns
|
|
1514
|
+
|
|
1515
|
+
> pytest for everything. Fixtures for setup. Mock at boundaries. Test behavior, not implementation.
|
|
1516
|
+
|
|
1517
|
+
---
|
|
1518
|
+
|
|
1519
|
+
## Testing Strategy
|
|
1520
|
+
|
|
1521
|
+
| Type | Purpose | Tools | Speed |
|
|
1522
|
+
|------|---------|-------|:-----:|
|
|
1523
|
+
| **Unit** | Business logic / services | pytest | ⚡ Fast |
|
|
1524
|
+
| **Integration** | API endpoints + DB | pytest + httpx/TestClient | 🔄 Medium |
|
|
1525
|
+
| **E2E** | Full workflows | pytest + real DB | 🐢 Slow |
|
|
1526
|
+
|
|
1527
|
+
---
|
|
1528
|
+
|
|
1529
|
+
## FastAPI Testing
|
|
1530
|
+
|
|
1531
|
+
```python
|
|
1532
|
+
import pytest
|
|
1533
|
+
from httpx import AsyncClient, ASGITransport
|
|
1534
|
+
from app.main import app
|
|
1535
|
+
from app.dependencies import get_db
|
|
1536
|
+
|
|
1537
|
+
# Override database dependency for tests
|
|
1538
|
+
async def get_test_db():
|
|
1539
|
+
async with test_session_maker() as session:
|
|
1540
|
+
yield session
|
|
1541
|
+
|
|
1542
|
+
app.dependency_overrides[get_db] = get_test_db
|
|
1543
|
+
|
|
1544
|
+
@pytest.mark.asyncio
|
|
1545
|
+
async def test_create_user():
|
|
1546
|
+
transport = ASGITransport(app=app)
|
|
1547
|
+
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
|
1548
|
+
response = await client.post("/api/v1/users/", json={
|
|
1549
|
+
"email": "test@example.com",
|
|
1550
|
+
"name": "Test User",
|
|
1551
|
+
})
|
|
1552
|
+
assert response.status_code == 201
|
|
1553
|
+
data = response.json()
|
|
1554
|
+
assert data["email"] == "test@example.com"
|
|
1555
|
+
assert "id" in data
|
|
1556
|
+
|
|
1557
|
+
@pytest.mark.asyncio
|
|
1558
|
+
async def test_get_user_not_found():
|
|
1559
|
+
transport = ASGITransport(app=app)
|
|
1560
|
+
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
|
1561
|
+
response = await client.get("/api/v1/users/99999")
|
|
1562
|
+
assert response.status_code == 404
|
|
1563
|
+
assert response.json()["error"] == "NOT_FOUND"
|
|
1564
|
+
```
|
|
1565
|
+
|
|
1566
|
+
---
|
|
1567
|
+
|
|
1568
|
+
## Django Testing
|
|
1569
|
+
|
|
1570
|
+
```python
|
|
1571
|
+
from django.test import TestCase
|
|
1572
|
+
from rest_framework.test import APIClient
|
|
1573
|
+
|
|
1574
|
+
class UserAPITest(TestCase):
|
|
1575
|
+
def setUp(self):
|
|
1576
|
+
self.client = APIClient()
|
|
1577
|
+
self.user = User.objects.create_user(
|
|
1578
|
+
email="test@test.com", password="testpass123"
|
|
1579
|
+
)
|
|
1580
|
+
|
|
1581
|
+
def test_list_users(self):
|
|
1582
|
+
self.client.force_authenticate(user=self.user)
|
|
1583
|
+
response = self.client.get("/api/users/")
|
|
1584
|
+
self.assertEqual(response.status_code, 200)
|
|
1585
|
+
self.assertEqual(len(response.data), 1)
|
|
1586
|
+
|
|
1587
|
+
def test_create_user_unauthenticated(self):
|
|
1588
|
+
response = self.client.post("/api/users/", {"email": "new@test.com"})
|
|
1589
|
+
self.assertEqual(response.status_code, 401)
|
|
1590
|
+
```
|
|
1591
|
+
|
|
1592
|
+
---
|
|
1593
|
+
|
|
1594
|
+
## Fixtures (conftest.py)
|
|
1595
|
+
|
|
1596
|
+
```python
|
|
1597
|
+
# tests/conftest.py
|
|
1598
|
+
import pytest
|
|
1599
|
+
from app.models import User
|
|
1600
|
+
|
|
1601
|
+
@pytest.fixture
|
|
1602
|
+
def sample_user(db) -> User:
|
|
1603
|
+
"""Create a test user — available to all tests."""
|
|
1604
|
+
return User.objects.create(
|
|
1605
|
+
email="fixture@test.com",
|
|
1606
|
+
name="Fixture User",
|
|
1607
|
+
)
|
|
1608
|
+
|
|
1609
|
+
@pytest.fixture
|
|
1610
|
+
def auth_client(sample_user) -> APIClient:
|
|
1611
|
+
"""Authenticated API client."""
|
|
1612
|
+
client = APIClient()
|
|
1613
|
+
client.force_authenticate(user=sample_user)
|
|
1614
|
+
return client
|
|
1615
|
+
|
|
1616
|
+
@pytest.fixture
|
|
1617
|
+
async def async_client() -> AsyncClient:
|
|
1618
|
+
"""Async client for FastAPI."""
|
|
1619
|
+
transport = ASGITransport(app=app)
|
|
1620
|
+
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
|
1621
|
+
yield client
|
|
1622
|
+
```
|
|
1623
|
+
|
|
1624
|
+
---
|
|
1625
|
+
|
|
1626
|
+
## Mocking
|
|
1627
|
+
|
|
1628
|
+
```python
|
|
1629
|
+
from unittest.mock import AsyncMock, patch
|
|
1630
|
+
|
|
1631
|
+
@pytest.mark.asyncio
|
|
1632
|
+
async def test_send_email_on_signup():
|
|
1633
|
+
"""Mock external services at boundaries."""
|
|
1634
|
+
with patch("app.services.email.send_welcome_email", new_callable=AsyncMock) as mock_email:
|
|
1635
|
+
transport = ASGITransport(app=app)
|
|
1636
|
+
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
|
1637
|
+
await client.post("/api/v1/users/", json={
|
|
1638
|
+
"email": "new@test.com",
|
|
1639
|
+
"name": "New User",
|
|
1640
|
+
})
|
|
1641
|
+
|
|
1642
|
+
mock_email.assert_called_once_with("new@test.com", "New User")
|
|
1643
|
+
|
|
1644
|
+
@pytest.mark.asyncio
|
|
1645
|
+
async def test_external_api_failure():
|
|
1646
|
+
"""Test error handling when external service fails."""
|
|
1647
|
+
with patch("app.services.payment.charge", side_effect=Exception("Payment failed")):
|
|
1648
|
+
transport = ASGITransport(app=app)
|
|
1649
|
+
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
|
1650
|
+
response = await client.post("/api/v1/orders/", json={"amount": 100})
|
|
1651
|
+
assert response.status_code == 500
|
|
1652
|
+
```
|
|
1653
|
+
|
|
1654
|
+
---
|
|
1655
|
+
|
|
1656
|
+
## Test Configuration
|
|
1657
|
+
|
|
1658
|
+
```toml
|
|
1659
|
+
# pyproject.toml
|
|
1660
|
+
[tool.pytest.ini_options]
|
|
1661
|
+
asyncio_mode = "auto"
|
|
1662
|
+
testpaths = ["tests"]
|
|
1663
|
+
python_files = "test_*.py"
|
|
1664
|
+
python_functions = "test_*"
|
|
1665
|
+
addopts = "-v --tb=short --strict-markers"
|
|
1666
|
+
markers = [
|
|
1667
|
+
"slow: marks tests as slow",
|
|
1668
|
+
"integration: marks integration tests",
|
|
1669
|
+
]
|
|
1670
|
+
|
|
1671
|
+
[tool.coverage.run]
|
|
1672
|
+
source = ["app"]
|
|
1673
|
+
omit = ["tests/*", "*/migrations/*"]
|
|
1674
|
+
|
|
1675
|
+
[tool.coverage.report]
|
|
1676
|
+
fail_under = 80
|
|
1677
|
+
show_missing = true
|
|
1678
|
+
```
|
|
1679
|
+
|
|
1680
|
+
```bash
|
|
1681
|
+
# Run tests
|
|
1682
|
+
pytest # All tests
|
|
1683
|
+
pytest -x # Stop on first failure
|
|
1684
|
+
pytest -k "test_user" # Filter by name
|
|
1685
|
+
pytest --cov=app --cov-report=html # With coverage
|
|
1686
|
+
pytest -m "not slow" # Skip slow tests
|
|
1687
|
+
```
|
|
1688
|
+
|
|
1689
|
+
---
|
|
1690
|
+
|
|
1691
|
+
## Anti-Patterns
|
|
1692
|
+
|
|
1693
|
+
| ❌ Don't | ✅ Do |
|
|
1694
|
+
|---------|-------|
|
|
1695
|
+
| Test implementation details | Test behavior (inputs → outputs) |
|
|
1696
|
+
| Mock everything | Mock only boundaries (DB, APIs, email) |
|
|
1697
|
+
| Share state between tests | Each test is independent |
|
|
1698
|
+
| Skip error path tests | Test both success AND failure |
|
|
1699
|
+
| Ignore coverage | Aim for ≥80% with `fail_under` |
|
|
1700
|
+
| Use `print()` for debugging | Use `pytest --pdb` or `breakpoint()` |
|
|
1701
|
+
|
|
1702
|
+
---
|
|
1703
|
+
|
|
1704
|
+
## 🔗 Related
|
|
1705
|
+
|
|
1706
|
+
| File | When to Read |
|
|
1707
|
+
|------|-------------|
|
|
1708
|
+
| [fastapi-patterns.md](fastapi-patterns.md) | FastAPI patterns |
|
|
1709
|
+
| [django-patterns.md](django-patterns.md) | Django patterns |
|
|
1710
|
+
| [async-patterns.md](async-patterns.md) | Testing async code |
|
|
1711
|
+
| [type-hints.md](type-hints.md) | Typed test fixtures |
|
|
1712
|
+
|
|
1713
|
+
---
|
|
1714
|
+
|
|
1715
|
+
### Rule: type-hints
|
|
1716
|
+
|
|
1717
|
+
---
|
|
1718
|
+
name: type-hints
|
|
1719
|
+
description: Python type hints — modern syntax, Pydantic v2, generics, TypeVar, and validation patterns
|
|
1720
|
+
---
|
|
1721
|
+
|
|
1722
|
+
# Python Type Hints & Validation
|
|
1723
|
+
|
|
1724
|
+
> Type all public APIs. Use Pydantic at boundaries. No `Any` in public signatures.
|
|
1725
|
+
|
|
1726
|
+
---
|
|
1727
|
+
|
|
1728
|
+
## Modern Type Syntax (Python 3.12+)
|
|
1729
|
+
|
|
1730
|
+
```python
|
|
1731
|
+
# ✅ Modern — use built-in generics (no typing import needed)
|
|
1732
|
+
def get_items() -> list[dict[str, int]]:
|
|
1733
|
+
...
|
|
1734
|
+
|
|
1735
|
+
def find_user(user_id: int) -> User | None: # Union syntax
|
|
1736
|
+
...
|
|
1737
|
+
|
|
1738
|
+
def process(data: str | bytes) -> None:
|
|
1739
|
+
...
|
|
1740
|
+
|
|
1741
|
+
# ❌ Legacy — avoid in new code
|
|
1742
|
+
from typing import Optional, Union, List, Dict
|
|
1743
|
+
def get_items() -> List[Dict[str, int]]: # Old style
|
|
1744
|
+
...
|
|
1745
|
+
```
|
|
1746
|
+
|
|
1747
|
+
---
|
|
1748
|
+
|
|
1749
|
+
## Common Patterns
|
|
1750
|
+
|
|
1751
|
+
```python
|
|
1752
|
+
from typing import TypeVar, Generic, Callable
|
|
1753
|
+
from collections.abc import Sequence, Mapping
|
|
1754
|
+
|
|
1755
|
+
# TypeVar for generics
|
|
1756
|
+
T = TypeVar("T")
|
|
1757
|
+
|
|
1758
|
+
def first(items: Sequence[T]) -> T | None:
|
|
1759
|
+
return items[0] if items else None
|
|
1760
|
+
|
|
1761
|
+
# Callable types
|
|
1762
|
+
def apply(fn: Callable[[int], str], value: int) -> str:
|
|
1763
|
+
return fn(value)
|
|
1764
|
+
|
|
1765
|
+
# Generic class
|
|
1766
|
+
class Repository(Generic[T]):
|
|
1767
|
+
def __init__(self, model: type[T]) -> None:
|
|
1768
|
+
self.model = model
|
|
1769
|
+
|
|
1770
|
+
async def get(self, id: int) -> T | None:
|
|
1771
|
+
...
|
|
1772
|
+
|
|
1773
|
+
async def list(self, limit: int = 20) -> list[T]:
|
|
1774
|
+
...
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
---
|
|
1778
|
+
|
|
1779
|
+
## Pydantic v2 Models
|
|
1780
|
+
|
|
1781
|
+
```python
|
|
1782
|
+
from pydantic import BaseModel, Field, EmailStr, field_validator
|
|
1783
|
+
from datetime import datetime
|
|
1784
|
+
|
|
1785
|
+
class UserCreate(BaseModel):
|
|
1786
|
+
"""Input validation at API boundary."""
|
|
1787
|
+
name: str = Field(min_length=1, max_length=100)
|
|
1788
|
+
email: EmailStr
|
|
1789
|
+
age: int = Field(ge=0, le=150)
|
|
1790
|
+
role: str = "user"
|
|
1791
|
+
|
|
1792
|
+
@field_validator("name")
|
|
1793
|
+
@classmethod
|
|
1794
|
+
def name_must_not_be_empty(cls, v: str) -> str:
|
|
1795
|
+
if not v.strip():
|
|
1796
|
+
raise ValueError("Name cannot be blank")
|
|
1797
|
+
return v.strip()
|
|
1798
|
+
|
|
1799
|
+
class UserResponse(BaseModel):
|
|
1800
|
+
"""Output serialization — never expose internal fields."""
|
|
1801
|
+
id: int
|
|
1802
|
+
name: str
|
|
1803
|
+
email: str
|
|
1804
|
+
created_at: datetime
|
|
1805
|
+
|
|
1806
|
+
model_config = {"from_attributes": True} # Enable ORM mode
|
|
1807
|
+
|
|
1808
|
+
class UserUpdate(BaseModel):
|
|
1809
|
+
"""Partial update — all fields optional."""
|
|
1810
|
+
name: str | None = None
|
|
1811
|
+
email: EmailStr | None = None
|
|
1812
|
+
age: int | None = Field(default=None, ge=0, le=150)
|
|
1813
|
+
```
|
|
1814
|
+
|
|
1815
|
+
### Pydantic Settings (Configuration)
|
|
1816
|
+
|
|
1817
|
+
```python
|
|
1818
|
+
from pydantic_settings import BaseSettings
|
|
1819
|
+
|
|
1820
|
+
class Settings(BaseSettings):
|
|
1821
|
+
"""Load from environment variables automatically."""
|
|
1822
|
+
database_url: str
|
|
1823
|
+
redis_url: str = "redis://localhost:6379"
|
|
1824
|
+
secret_key: str
|
|
1825
|
+
debug: bool = False
|
|
1826
|
+
allowed_origins: list[str] = ["http://localhost:3000"]
|
|
1827
|
+
|
|
1828
|
+
model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
|
|
1829
|
+
|
|
1830
|
+
settings = Settings() # Auto-reads from .env + environment
|
|
1831
|
+
```
|
|
1832
|
+
|
|
1833
|
+
---
|
|
1834
|
+
|
|
1835
|
+
## When to Type
|
|
1836
|
+
|
|
1837
|
+
| Scope | Rule |
|
|
1838
|
+
|-------|------|
|
|
1839
|
+
| Function parameters | Always |
|
|
1840
|
+
| Return types | Always |
|
|
1841
|
+
| Class attributes | Always |
|
|
1842
|
+
| Local variables | Let inference work (skip) |
|
|
1843
|
+
| Tests | Optional (usually skip) |
|
|
1844
|
+
| Scripts | Minimal |
|
|
1845
|
+
|
|
1846
|
+
---
|
|
1847
|
+
|
|
1848
|
+
## Anti-Patterns
|
|
1849
|
+
|
|
1850
|
+
| ❌ Don't | ✅ Do |
|
|
1851
|
+
|---------|-------|
|
|
1852
|
+
| `Any` in public APIs | Use specific types or `TypeVar` |
|
|
1853
|
+
| `typing.Optional[X]` | `X \| None` (Python 3.10+) |
|
|
1854
|
+
| `typing.List`, `typing.Dict` | `list`, `dict` (Python 3.9+) |
|
|
1855
|
+
| Skip return type hints | Always annotate return types |
|
|
1856
|
+
| Validate manually | Use Pydantic at boundaries |
|
|
1857
|
+
| Hardcode config values | Use Pydantic Settings |
|
|
1858
|
+
|
|
1859
|
+
---
|
|
1860
|
+
|
|
1861
|
+
## 🔗 Related
|
|
1862
|
+
|
|
1863
|
+
| File | When to Read |
|
|
1864
|
+
|------|-------------|
|
|
1865
|
+
| [fastapi-patterns.md](fastapi-patterns.md) | Pydantic with FastAPI |
|
|
1866
|
+
| [testing-patterns.md](testing-patterns.md) | Testing typed code |
|
|
1867
|
+
| [project-structure.md](project-structure.md) | Where to put models |
|
|
1868
|
+
|
|
1869
|
+
---
|
|
1870
|
+
|
|
1871
|
+
⚡ PikaKit v3.9.134
|