chisel-checker 0.1.2__tar.gz → 0.2.0__tar.gz
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.
- chisel_checker-0.2.0/.grimp_cache/af276a573254b22776d0a8d1fe8e976b0694e680.data.json +1 -0
- chisel_checker-0.2.0/.grimp_cache/chisel.meta.json +1 -0
- chisel_checker-0.2.0/.grimp_cache/myapp.meta.json +1 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/PKG-INFO +32 -4
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/README.md +31 -3
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/pyproject.toml +5 -2
- chisel_checker-0.2.0/skills/architecting-fullstack/SKILL.md +345 -0
- chisel_checker-0.2.0/skills/building-python-backend/SKILL.md +147 -0
- chisel_checker-0.2.0/skills/building-python-backend/references/fastapi.md +242 -0
- chisel_checker-0.2.0/skills/building-python-backend/references/layers.md +309 -0
- chisel_checker-0.2.0/skills/building-python-backend/references/patterns-examples.md +185 -0
- chisel_checker-0.2.0/skills/building-python-backend/references/sqlalchemy.md +202 -0
- chisel_checker-0.2.0/skills/building-sveltekit-frontend/SKILL.md +172 -0
- chisel_checker-0.2.0/skills/building-sveltekit-frontend/references/error-handling.md +196 -0
- chisel_checker-0.2.0/skills/building-sveltekit-frontend/references/layers.md +382 -0
- chisel_checker-0.2.0/skills/building-sveltekit-frontend/references/openapi.md +157 -0
- chisel_checker-0.2.0/skills/building-sveltekit-frontend/references/patterns-examples.md +255 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/SKILL.md +373 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/audit.md +133 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-bento-panel-grid.md +89 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-brutalist-neo-brutalist.md +97 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-card-based-modular.md +113 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-claymorphism-soft-3d.md +81 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-editorial-typography-led.md +133 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-flat-design.md +96 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-fluent-design.md +99 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-glassmorphism.md +87 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-gradients-aurora.md +85 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-illustration-first-brand-character-ui.md +91 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-material-design.md +115 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-minimalism-swiss.md +128 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-motion-rich-microinteraction-heavy.md +130 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-neumorphism.md +80 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/style-skeuomorphism-selective.md +117 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/tokens-theming.md +202 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-canvas.md +94 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-conversational.md +112 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-dashboard.md +114 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-document.md +55 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-drill-down.md +72 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-feed.md +134 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-hub-spoke.md +78 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-kanban.md +111 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-launcher.md +111 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-master-detail.md +115 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-matrix.md +91 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-progressive-disclosure.md +93 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-tabs.md +114 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-timeline.md +94 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-wizard.md +115 -0
- chisel_checker-0.2.0/skills/designing-svelte-ui/references/ux-workbench.md +164 -0
- chisel_checker-0.2.0/skills/planning-features/SKILL.md +292 -0
- chisel_checker-0.2.0/skills/qa/SKILL.md +137 -0
- chisel_checker-0.2.0/skills/qa/references/testing-patterns.md +424 -0
- chisel_checker-0.2.0/skills/qa/references/validation.md +187 -0
- chisel_checker-0.2.0/src/chisel/_build_hooks/skills_hook.py +22 -0
- chisel_checker-0.2.0/src/chisel/checker/controllers/skill_setup_controller.py +31 -0
- chisel_checker-0.2.0/src/chisel/checker/controllers/update_controller.py +48 -0
- chisel_checker-0.2.0/src/chisel/checker/models/agent_skill.py +24 -0
- chisel_checker-0.2.0/src/chisel/checker/models/self_update.py +23 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/reporter.py +44 -4
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/repositories/file_discovery.py +1 -1
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/repositories/import_graph.py +1 -0
- chisel_checker-0.2.0/src/chisel/checker/rule_metadata.py +19 -0
- chisel_checker-0.2.0/src/chisel/checker/services/agent_skills.py +179 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/import_boundary.py +14 -9
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/protocols.py +1 -0
- chisel_checker-0.2.0/src/chisel/checker/services/self_update.py +186 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/structural.py +4 -4
- chisel_checker-0.2.0/src/chisel/cli/main.py +343 -0
- chisel_checker-0.2.0/tests/unit/controllers/test_skill_setup_controller.py +36 -0
- chisel_checker-0.2.0/tests/unit/controllers/test_update_controller.py +81 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/repositories/test_file_discovery.py +32 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/repositories/test_import_graph.py +5 -0
- chisel_checker-0.2.0/tests/unit/services/test_agent_skills.py +109 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_import_boundary.py +55 -1
- chisel_checker-0.2.0/tests/unit/services/test_self_update.py +80 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_structural.py +10 -3
- chisel_checker-0.2.0/tests/unit/test_cli.py +221 -0
- chisel_checker-0.2.0/tests/unit/test_factory.py +18 -0
- chisel_checker-0.2.0/tests/unit/test_reporter.py +73 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/uv.lock +108 -2
- chisel_checker-0.1.2/.grimp_cache/af276a573254b22776d0a8d1fe8e976b0694e680.data.json +0 -1
- chisel_checker-0.1.2/.grimp_cache/chisel.meta.json +0 -1
- chisel_checker-0.1.2/.grimp_cache/myapp.meta.json +0 -1
- chisel_checker-0.1.2/src/chisel/cli/main.py +0 -126
- chisel_checker-0.1.2/tests/unit/test_cli.py +0 -68
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/.gitignore +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/.grimp_cache/.gitignore +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/.grimp_cache/84239c1bc60433074e3d05309a9d0d09848ffd65.data.json +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/.grimp_cache/CACHEDIR.TAG +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/config.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/controllers/check_controller.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/errors.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/factory.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/exemption.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/file_info.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/import_edge.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/layer.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/project_info.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/result.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/severity.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/models/violation.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/repositories/exception_registry.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/repositories/file_reader.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/repositories/protocols.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/app_file.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/check_test_structure.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/complexity.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/concurrency.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/config_startup.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/error_flow.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/project_structure.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/session.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/checker/services/suppression.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/src/chisel/py.typed +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/conftest.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/fakes/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/fakes/fake_import_graph.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/controllers/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/controllers/test_check_controller.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/repositories/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/repositories/test_exception_registry.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/repositories/test_file_reader.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/__init__.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_app_file.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_check_test_structure.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_complexity.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_concurrency.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_config_startup.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_error_flow.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_project_structure.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_project_structure_coverage.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_session.py +0 -0
- {chisel_checker-0.1.2 → chisel_checker-0.2.0}/tests/unit/services/test_suppression.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"chisel.checker.models.file_info":[["chisel.checker.models.layer",6,"from chisel.checker.models.layer import Layer"],["ast",2,"import ast"],["pathlib",4,"from pathlib import Path"],["dataclasses",3,"from dataclasses import dataclass, field"]],"chisel.checker.models.exemption":[["dataclasses",1,"from dataclasses import dataclass, field"]],"chisel.checker.config":[["pathlib",3,"from pathlib import Path"],["dataclasses",2,"from dataclasses import dataclass, field"]],"chisel.checker.errors":[],"chisel.checker.models":[["chisel.checker.models.file_info",3,"from chisel.checker.models.file_info import FileInfo"],["chisel.checker.models.violation",9,"from chisel.checker.models.violation import Violation"],["chisel.checker.models.layer",5,"from chisel.checker.models.layer import Layer"],["chisel.checker.models.severity",8,"from chisel.checker.models.severity import Severity"],["chisel.checker.models.import_edge",4,"from chisel.checker.models.import_edge import ImportEdge"],["chisel.checker.models.project_info",6,"from chisel.checker.models.project_info import ProjectInfo"],["chisel.checker.models.exemption",2,"from chisel.checker.models.exemption import Exemption"],["chisel.checker.models.result",7,"from chisel.checker.models.result import CheckResult"]],"chisel.checker.reporter":[["rich",6,"from rich.console import Console"],["json",2,"import json"],["chisel.checker.rule_metadata",13,"from chisel.checker.rule_metadata import skill_name_for_rule"],["dataclasses",3,"from dataclasses import dataclass"],["rich",7,"from rich.markup import escape"],["chisel.checker.models.result",10,"from chisel.checker.models.result import CheckResult"],["rich",8,"from rich.table import Table"],["typing",4,"from typing import Any"],["chisel.checker.models.violation",12,"from chisel.checker.models.violation import Violation"],["chisel.checker.models.severity",11,"from chisel.checker.models.severity import Severity"]],"chisel.checker.factory":[["chisel",4,"from chisel.checker.controllers.check_controller import CheckController"],["dataclasses",2,"from dataclasses import dataclass, field"],["chisel",14,"from chisel.checker.services.protocols import ICheckerService"],["chisel",7,"from chisel.checker.services.app_file import AppFileService"],["chisel",5,"from chisel.checker.repositories.import_graph import ImportGraph"],["chisel",16,"from chisel.checker.services.structural import StructuralService"],["chisel",9,"from chisel.checker.services.concurrency import ConcurrencyService"],["chisel",12,"from chisel.checker.services.import_boundary import ImportBoundaryService"],["chisel",11,"from chisel.checker.services.error_flow import ErrorFlowService"],["chisel",17,"from chisel.checker.services.suppression import SuppressionService"],["chisel",18,"from chisel.checker.services.check_test_structure import CheckTestStructureService"],["chisel",13,"from chisel.checker.services.project_structure import ProjectStructureService"],["chisel",6,"from chisel.checker.repositories.protocols import IImportGraph"],["chisel",15,"from chisel.checker.services.session import SessionService"],["chisel",10,"from chisel.checker.services.config_startup import ConfigStartupService"],["chisel",8,"from chisel.checker.services.complexity import ComplexityService"]],"chisel.checker.models.import_edge":[["dataclasses",2,"from dataclasses import dataclass"]],"chisel.checker.models.self_update":[["dataclasses",1,"from dataclasses import dataclass"],["enum",2,"from enum import Enum"]],"chisel":[["chisel.checker",2,"from chisel.checker import check_project"]],"chisel.checker.rule_metadata":[],"chisel.checker.models.layer":[["enum",2,"from enum import Enum"]],"chisel.checker":[["chisel.checker.factory",2,"from chisel.checker.factory import CheckerFactory"],["chisel.checker.reporter",3,"from chisel.checker.reporter import Reporter"]],"chisel.checker.models.violation":[["chisel.checker.models.severity",4,"from chisel.checker.models.severity import Severity"],["dataclasses",2,"from dataclasses import dataclass"]],"chisel.checker.models.severity":[["enum",2,"from enum import Enum"]],"chisel.checker.models.project_info":[["dataclasses",2,"from dataclasses import dataclass, field"],["chisel.checker.models.file_info",5,"from chisel.checker.models.file_info import FileInfo"],["pathlib",3,"from pathlib import Path"]],"chisel.checker.models.result":[["dataclasses",2,"from dataclasses import dataclass, field"],["chisel.checker.models.violation",4,"from chisel.checker.models.violation import Violation"]],"chisel.checker.models.agent_skill":[["dataclasses",1,"from dataclasses import dataclass"],["enum",2,"from enum import Enum"]]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"chisel.checker.models.import_edge": 1783243922.6667128, "chisel.checker.config": 1783243922.6664708, "chisel.checker.models.violation": 1783243922.6667128, "chisel.checker": 1783243922.6663847, "chisel.checker.models.file_info": 1783243922.6667128, "chisel": 1783243922.6663358, "chisel.checker.models.layer": 1783243922.6667128, "chisel.checker.models.result": 1783243922.6667128, "chisel.checker.models.severity": 1783243922.6667128, "chisel.checker.errors": 1783243922.6665595, "chisel.checker.models.agent_skill": 1783243922.6667128, "chisel.checker.models": 1783243922.6665595, "chisel.checker.models.self_update": 1783243922.6667128, "chisel.checker.models.exemption": 1783243922.6667128, "chisel.checker.models.project_info": 1783243922.6667128, "chisel.checker.rule_metadata": 1783243922.6670513, "chisel.checker.reporter": 1783243922.6667128, "chisel.checker.factory": 1783243922.6665595}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"myapp": 1783243932.1338243}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chisel_checker
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Opinionated architecture constraint checker
|
|
5
5
|
Author-email: ChidiRNweke <chidi125@gmail.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -31,8 +31,10 @@ pip install chisel
|
|
|
31
31
|
chisel check ./backend # check a project
|
|
32
32
|
chisel rules # list all ~55 rules grouped by category
|
|
33
33
|
chisel explain structural:print-banned # detailed fix guidance
|
|
34
|
-
chisel check . --json #
|
|
34
|
+
chisel check . --json # violations with message refs + skill names
|
|
35
35
|
chisel check . --no-strict # skip src-layout enforcement
|
|
36
|
+
chisel update self # upgrade the installed CLI package
|
|
37
|
+
chisel update skills --target codex # overwrite installed skills with bundled copies
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
## Commands
|
|
@@ -40,12 +42,39 @@ chisel check . --no-strict # skip src-layout enforcement
|
|
|
40
42
|
| Command | Description |
|
|
41
43
|
|---|---|
|
|
42
44
|
| `chisel check [path]` | Scan a project for architectural violations |
|
|
43
|
-
| `chisel check . --json` | Output violations as structured JSON |
|
|
45
|
+
| `chisel check . --json` | Output violations as structured JSON with deduplicated messages |
|
|
44
46
|
| `chisel check . --strict/--no-strict` | Toggle src-layout and build-config enforcement |
|
|
45
47
|
| `chisel rules` | List all rules, grouped by category |
|
|
46
48
|
| `chisel rules --json` | Machine-readable rule listing |
|
|
47
49
|
| `chisel explain <rule-id>` | Detailed description + fix guidance for a rule |
|
|
48
50
|
| `chisel explain <category>` | All rules in a category (e.g. `structural`) |
|
|
51
|
+
| `chisel setup [path]` | Install Chisel agent skills into a repo for Codex, Claude Code, or OpenCode |
|
|
52
|
+
| `chisel update self` | Upgrade the installed Chisel package |
|
|
53
|
+
| `chisel update skills [path]` | Refresh installed Chisel skills after confirmation |
|
|
54
|
+
|
|
55
|
+
## Agent skills
|
|
56
|
+
|
|
57
|
+
Install Chisel's bundled skills into a project so coding agents can load the same architecture guidance that the checker enforces:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
chisel setup --target codex # writes .agents/skills/
|
|
61
|
+
chisel setup --target claude # writes .claude/skills/
|
|
62
|
+
chisel setup --target opencode # writes .opencode/skills/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Without `--target`, `chisel setup` asks which agent to configure when running interactively. Use `--skill qa` to install one skill, `--dry-run --json` to preview, and `--overwrite` to replace existing skill directories.
|
|
66
|
+
|
|
67
|
+
Refresh previously installed skills after upgrading Chisel:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
chisel update skills --target codex
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This overwrites local modifications in the selected skill directories, so Chisel asks for confirmation before writing. Use `--yes` in automation and `--dry-run --json` to preview.
|
|
74
|
+
|
|
75
|
+
Agent-facing JSON output deduplicates repeated violation messages: each
|
|
76
|
+
violation carries `message_ref`, and the top-level `messages` array contains
|
|
77
|
+
each full message once with its `skill_name`.
|
|
49
78
|
|
|
50
79
|
## What gets checked
|
|
51
80
|
|
|
@@ -104,4 +133,3 @@ pytest tests/ -q
|
|
|
104
133
|
```
|
|
105
134
|
|
|
106
135
|
The checker is self-validating: `chisel check . --strict` produces zero violations on its own source.
|
|
107
|
-
|
|
@@ -16,8 +16,10 @@ pip install chisel
|
|
|
16
16
|
chisel check ./backend # check a project
|
|
17
17
|
chisel rules # list all ~55 rules grouped by category
|
|
18
18
|
chisel explain structural:print-banned # detailed fix guidance
|
|
19
|
-
chisel check . --json #
|
|
19
|
+
chisel check . --json # violations with message refs + skill names
|
|
20
20
|
chisel check . --no-strict # skip src-layout enforcement
|
|
21
|
+
chisel update self # upgrade the installed CLI package
|
|
22
|
+
chisel update skills --target codex # overwrite installed skills with bundled copies
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
## Commands
|
|
@@ -25,12 +27,39 @@ chisel check . --no-strict # skip src-layout enforcement
|
|
|
25
27
|
| Command | Description |
|
|
26
28
|
|---|---|
|
|
27
29
|
| `chisel check [path]` | Scan a project for architectural violations |
|
|
28
|
-
| `chisel check . --json` | Output violations as structured JSON |
|
|
30
|
+
| `chisel check . --json` | Output violations as structured JSON with deduplicated messages |
|
|
29
31
|
| `chisel check . --strict/--no-strict` | Toggle src-layout and build-config enforcement |
|
|
30
32
|
| `chisel rules` | List all rules, grouped by category |
|
|
31
33
|
| `chisel rules --json` | Machine-readable rule listing |
|
|
32
34
|
| `chisel explain <rule-id>` | Detailed description + fix guidance for a rule |
|
|
33
35
|
| `chisel explain <category>` | All rules in a category (e.g. `structural`) |
|
|
36
|
+
| `chisel setup [path]` | Install Chisel agent skills into a repo for Codex, Claude Code, or OpenCode |
|
|
37
|
+
| `chisel update self` | Upgrade the installed Chisel package |
|
|
38
|
+
| `chisel update skills [path]` | Refresh installed Chisel skills after confirmation |
|
|
39
|
+
|
|
40
|
+
## Agent skills
|
|
41
|
+
|
|
42
|
+
Install Chisel's bundled skills into a project so coding agents can load the same architecture guidance that the checker enforces:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
chisel setup --target codex # writes .agents/skills/
|
|
46
|
+
chisel setup --target claude # writes .claude/skills/
|
|
47
|
+
chisel setup --target opencode # writes .opencode/skills/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Without `--target`, `chisel setup` asks which agent to configure when running interactively. Use `--skill qa` to install one skill, `--dry-run --json` to preview, and `--overwrite` to replace existing skill directories.
|
|
51
|
+
|
|
52
|
+
Refresh previously installed skills after upgrading Chisel:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
chisel update skills --target codex
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This overwrites local modifications in the selected skill directories, so Chisel asks for confirmation before writing. Use `--yes` in automation and `--dry-run --json` to preview.
|
|
59
|
+
|
|
60
|
+
Agent-facing JSON output deduplicates repeated violation messages: each
|
|
61
|
+
violation carries `message_ref`, and the top-level `messages` array contains
|
|
62
|
+
each full message once with its `skill_name`.
|
|
34
63
|
|
|
35
64
|
## What gets checked
|
|
36
65
|
|
|
@@ -89,4 +118,3 @@ pytest tests/ -q
|
|
|
89
118
|
```
|
|
90
119
|
|
|
91
120
|
The checker is self-validating: `chisel check . --strict` produces zero violations on its own source.
|
|
92
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "chisel_checker"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Opinionated architecture constraint checker"
|
|
5
5
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
6
6
|
authors = [
|
|
@@ -29,4 +29,7 @@ build-backend = "hatchling.build"
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
[tool.hatch.build.targets.wheel]
|
|
32
|
-
packages = ["src/chisel"]
|
|
32
|
+
packages = ["src/chisel"]
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.hooks.custom]
|
|
35
|
+
path = "src/chisel/_build_hooks/skills_hook.py"
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architecting-fullstack
|
|
3
|
+
description: Orchestrates full-stack architecture patterns (monorepo, BFF). Triggered when building or reviewing an application spanning frontend and backend, or starting a new project. Decides the pattern and hands off to specialised skills for implementation details.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fullstack SWE Skill
|
|
7
|
+
|
|
8
|
+
This skill decides the architecture and then hands off to the right specialised skills. It does
|
|
9
|
+
not repeat what those skills already cover — read them directly for implementation details.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Blueprint
|
|
14
|
+
|
|
15
|
+
Read this skill to decide the architecture pattern and project structure. Then read the specialised skills (`building-python-backend`, `building-sveltekit-frontend`, `designing-svelte-ui`) for implementation details on each layer.
|
|
16
|
+
|
|
17
|
+
Finally, ask the user if they want you to start coding or explicitly invoke the `planning-features` skill to write a detailed plan.
|
|
18
|
+
|
|
19
|
+
## Monorepo — Always
|
|
20
|
+
|
|
21
|
+
Every project is a monorepo. Even if there's only a frontend today, the structure accommodates a
|
|
22
|
+
backend tomorrow without reorganising. One repo, one agent context, full 360° visibility.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
project-root/
|
|
26
|
+
├── CLAUDE.md # Project-level context — read first every session
|
|
27
|
+
├── AGENTS.md # Design system decisions (populated by designing-svelte-ui skill)
|
|
28
|
+
├── .github/ # CI/CD workflows
|
|
29
|
+
├── docker-compose.yml # Local dev: frontend + backend + postgres
|
|
30
|
+
├── frontend/ # SvelteKit app
|
|
31
|
+
│ ├── package.json # pnpm as package manager
|
|
32
|
+
│ ├── pnpm-lock.yaml
|
|
33
|
+
│ ├── svelte.config.js
|
|
34
|
+
│ ├── tsconfig.json
|
|
35
|
+
│ ├── .env # Frontend env vars only — never shares secrets with backend
|
|
36
|
+
│ └── src/
|
|
37
|
+
│ ├── app.css
|
|
38
|
+
│ ├── app.html
|
|
39
|
+
│ ├── hooks.server.ts
|
|
40
|
+
│ ├── routes/
|
|
41
|
+
│ └── lib/
|
|
42
|
+
│ ├── api/ # openapi-fetch client + generated schema.d.ts
|
|
43
|
+
│ ├── models/
|
|
44
|
+
│ ├── services/
|
|
45
|
+
│ ├── controllers/
|
|
46
|
+
│ ├── factories/
|
|
47
|
+
│ ├── stores/
|
|
48
|
+
│ └── components/
|
|
49
|
+
│ ├── ui/ # shadcn auto-generated
|
|
50
|
+
│ ├── primitives/ # Themed wrappers
|
|
51
|
+
│ ├── layout/ # Page-level structure
|
|
52
|
+
│ └── domain/ # Feature-specific
|
|
53
|
+
└── backend/ # Python FastAPI app — src layout
|
|
54
|
+
├── pyproject.toml # Project metadata, dependencies, tool config
|
|
55
|
+
├── alembic.ini
|
|
56
|
+
├── .env # Backend env vars only
|
|
57
|
+
├── alembic/
|
|
58
|
+
│ ├── env.py
|
|
59
|
+
│ └── versions/
|
|
60
|
+
├── tests/
|
|
61
|
+
│ ├── conftest.py
|
|
62
|
+
│ ├── unit/
|
|
63
|
+
│ └── integration/
|
|
64
|
+
└── src/
|
|
65
|
+
└── myapp/ # Replace "myapp" with actual project name
|
|
66
|
+
├── __init__.py
|
|
67
|
+
├── app.py # FastAPI create_app()
|
|
68
|
+
├── config.py # AppConfig.from_env()
|
|
69
|
+
├── factory.py
|
|
70
|
+
├── errors.py
|
|
71
|
+
├── dependencies.py
|
|
72
|
+
├── models/
|
|
73
|
+
│ └── __init__.py
|
|
74
|
+
├── services/
|
|
75
|
+
│ └── __init__.py
|
|
76
|
+
├── repositories/
|
|
77
|
+
│ ├── __init__.py
|
|
78
|
+
│ └── orm/
|
|
79
|
+
│ ├── __init__.py # Imports all ORM models for Alembic
|
|
80
|
+
│ └── base.py # DeclarativeBase
|
|
81
|
+
├── controllers/
|
|
82
|
+
│ └── __init__.py
|
|
83
|
+
└── routes/
|
|
84
|
+
└── __init__.py
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Why monorepo, always
|
|
88
|
+
|
|
89
|
+
- An agent (Claude Code, Cursor, etc.) can see the full stack in one context window
|
|
90
|
+
- Cross-stack changes (backend endpoint + frontend service + UI) happen in one commit
|
|
91
|
+
- Shared `.github/` CI, shared `docker-compose.yml`, shared `CLAUDE.md`
|
|
92
|
+
- No version drift between frontend expectations and backend reality
|
|
93
|
+
|
|
94
|
+
### Package management
|
|
95
|
+
|
|
96
|
+
- **Frontend**: pnpm. Use `pnpm` for all commands. `pnpm-lock.yaml` committed.
|
|
97
|
+
- **Backend**: `pyproject.toml` with pip or uv. No `setup.py`, no `requirements.txt`.
|
|
98
|
+
Use the src layout (`backend/src/myapp/`) so imports are always `from myapp.x import y`.
|
|
99
|
+
|
|
100
|
+
### Running locally
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# docker-compose.yml
|
|
104
|
+
services:
|
|
105
|
+
db:
|
|
106
|
+
image: postgres
|
|
107
|
+
environment:
|
|
108
|
+
POSTGRES_DB: myapp
|
|
109
|
+
POSTGRES_USER: myapp
|
|
110
|
+
POSTGRES_PASSWORD: myapp
|
|
111
|
+
ports:
|
|
112
|
+
- "5432:5432"
|
|
113
|
+
volumes:
|
|
114
|
+
- pgdata:/var/lib/postgresql/data
|
|
115
|
+
|
|
116
|
+
backend:
|
|
117
|
+
build: ./backend
|
|
118
|
+
env_file: ./backend/.env
|
|
119
|
+
ports:
|
|
120
|
+
- "8000:8000"
|
|
121
|
+
depends_on:
|
|
122
|
+
- db
|
|
123
|
+
volumes:
|
|
124
|
+
- ./backend/src:/app/src # hot reload
|
|
125
|
+
|
|
126
|
+
frontend:
|
|
127
|
+
build: ./frontend
|
|
128
|
+
env_file: ./frontend/.env
|
|
129
|
+
ports:
|
|
130
|
+
- "5173:5173"
|
|
131
|
+
depends_on:
|
|
132
|
+
- backend
|
|
133
|
+
volumes:
|
|
134
|
+
- ./frontend/src:/app/src # hot reload
|
|
135
|
+
|
|
136
|
+
volumes:
|
|
137
|
+
pgdata:
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For local dev without Docker, run backend and frontend in separate terminals:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Terminal 1 — backend
|
|
144
|
+
cd backend && uv run uvicorn src.myapp.app:app --reload --port 8000
|
|
145
|
+
|
|
146
|
+
# Terminal 2 — frontend
|
|
147
|
+
cd frontend && pnpm dev
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Step 1: Decide the Architecture
|
|
153
|
+
|
|
154
|
+
Before writing any code, determine which pattern fits. Both use the monorepo structure above.
|
|
155
|
+
|
|
156
|
+
### Pattern A — TypeScript Monolith
|
|
157
|
+
|
|
158
|
+
**Use when:**
|
|
159
|
+
|
|
160
|
+
- Team is TypeScript-only, no dedicated backend engineers
|
|
161
|
+
- Backend is relatively simple (CRUD, light business logic)
|
|
162
|
+
- Rapid prototyping or early-stage product
|
|
163
|
+
- No existing backend / microservices to integrate with
|
|
164
|
+
|
|
165
|
+
**Stack:** SvelteKit as the full-stack framework. Server routes / API routes handle
|
|
166
|
+
backend logic. Database accessed directly from the server layer (Drizzle or Prisma).
|
|
167
|
+
The `backend/` folder is omitted or used only for shared scripts/migrations.
|
|
168
|
+
|
|
169
|
+
**Structure:** Same monorepo, but `backend/` is either absent or minimal:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
project-root/
|
|
173
|
+
├── CLAUDE.md
|
|
174
|
+
├── AGENTS.md
|
|
175
|
+
├── frontend/ # SvelteKit handles everything
|
|
176
|
+
│ ├── package.json
|
|
177
|
+
│ └── src/
|
|
178
|
+
│ ├── lib/
|
|
179
|
+
│ │ ├── db/ # Drizzle schema + migrations
|
|
180
|
+
│ │ ├── services/ # Business logic (same layering discipline)
|
|
181
|
+
│ │ └── models/
|
|
182
|
+
│ └── routes/
|
|
183
|
+
└── docker-compose.yml # Just postgres for local dev
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Pattern B — BFF (Backend-for-Frontend)
|
|
187
|
+
|
|
188
|
+
**Use when:**
|
|
189
|
+
|
|
190
|
+
- Separate Python backend already exists or is planned
|
|
191
|
+
- Backend has its own OpenAPI spec
|
|
192
|
+
- Teams split across frontend and backend
|
|
193
|
+
- Business logic is complex enough to warrant a dedicated backend
|
|
194
|
+
- Backend needs to run independently (scheduled jobs, other consumers)
|
|
195
|
+
|
|
196
|
+
**Stack:** SvelteKit frontend + Python FastAPI backend. Full monorepo structure as shown above.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Step 2: Hand off to Specialised Skills
|
|
201
|
+
|
|
202
|
+
Once the pattern is decided, the relevant skills take over. Read them fully before writing code.
|
|
203
|
+
|
|
204
|
+
### Pattern A — TypeScript Monolith
|
|
205
|
+
|
|
206
|
+
| What you're building | Read |
|
|
207
|
+
| --------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
208
|
+
| Pages, components, UI | `designing-svelte-ui` skill — design system first, always |
|
|
209
|
+
| Loaders, actions, stores, routing | `building-sveltekit-frontend` skill — BFF patterns apply here too, minus the openapi layer |
|
|
210
|
+
| Database schema + queries | Use Drizzle. Follow the repository pattern from `building-sveltekit-frontend` — keep DB access out of loaders |
|
|
211
|
+
|
|
212
|
+
**Key adaptation for Pattern A:** The `building-sveltekit-frontend` skill is written for BFF with a separate
|
|
213
|
+
backend, but the layering discipline is identical. The difference is your "service" calls Drizzle
|
|
214
|
+
directly instead of an openapi-fetch client. Everything else — no business logic in loaders,
|
|
215
|
+
controllers for multi-service orchestration, stores for client state — applies unchanged.
|
|
216
|
+
|
|
217
|
+
### Pattern B — BFF
|
|
218
|
+
|
|
219
|
+
| What you're building | Read |
|
|
220
|
+
| ------------------------------------------- | -------------------------------------- |
|
|
221
|
+
| Frontend pages, components, UI | `designing-svelte-ui` skill |
|
|
222
|
+
| Frontend loaders, actions, stores | `building-sveltekit-frontend` skill |
|
|
223
|
+
| Backend services, controllers, repositories | `building-python-backend` skill |
|
|
224
|
+
| Typed API client (frontend ↔ backend) | `building-sveltekit-frontend` → `references/openapi.md` |
|
|
225
|
+
|
|
226
|
+
The contract between frontend and backend is the OpenAPI spec. The backend owns it. The
|
|
227
|
+
frontend generates types from it and never hand-writes API types.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Step 3: Project Setup Checklist
|
|
232
|
+
|
|
233
|
+
Regardless of pattern, do these before writing features. Copy this checklist into your initial response scratchpad to track your progress:
|
|
234
|
+
|
|
235
|
+
### Both patterns
|
|
236
|
+
|
|
237
|
+
- [ ] Verify environment dependencies: ensure `pnpm`, `uv` (or `pip`), and `docker` are installed. Fail gracefully with instructions if they are missing.
|
|
238
|
+
- [ ] Monorepo structure created as shown above
|
|
239
|
+
- [ ] `CLAUDE.md` at project root (see below)
|
|
240
|
+
- [ ] `AGENTS.md` at project root with design system decisions (populated by `designing-svelte-ui` skill)
|
|
241
|
+
- [ ] `docker-compose.yml` for local dev (at minimum: postgres)
|
|
242
|
+
- [ ] ESLint + Prettier configured in `frontend/`
|
|
243
|
+
- [ ] TypeScript strict mode on
|
|
244
|
+
- [ ] Environment variable validation at startup (fail fast)
|
|
245
|
+
- [ ] `.gitignore` covers both `frontend/node_modules` and `backend/__pycache__`, `.venv`, etc.
|
|
246
|
+
|
|
247
|
+
### Pattern A additions
|
|
248
|
+
|
|
249
|
+
- [ ] Drizzle configured with Postgres in `frontend/src/lib/db/`
|
|
250
|
+
- [ ] Migration workflow established (`drizzle-kit push` for dev, `migrate` for prod)
|
|
251
|
+
- [ ] Database URL in `frontend/.env`, never hardcoded
|
|
252
|
+
|
|
253
|
+
### Pattern B additions
|
|
254
|
+
|
|
255
|
+
- [ ] Backend uses src layout: `backend/src/myapp/`
|
|
256
|
+
- [ ] `pyproject.toml` configured with dependencies and tool settings
|
|
257
|
+
- [ ] OpenAPI spec location agreed and documented in `CLAUDE.md`
|
|
258
|
+
- [ ] `generate:api` script in `frontend/package.json`
|
|
259
|
+
- [ ] Backend `AppConfig.from_env()` validated at startup
|
|
260
|
+
- [ ] Separate `.env` files: `frontend/.env` and `backend/.env`
|
|
261
|
+
- [ ] Alembic configured and `alembic/env.py` imports all ORM models
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## CLAUDE.md
|
|
266
|
+
|
|
267
|
+
Every project gets a `CLAUDE.md` at the root. It is the first thing Claude reads at the start
|
|
268
|
+
of any session. Populate it during project setup and keep it current.
|
|
269
|
+
|
|
270
|
+
```markdown
|
|
271
|
+
# Project: [Name]
|
|
272
|
+
|
|
273
|
+
## Architecture
|
|
274
|
+
|
|
275
|
+
Monorepo — [Pattern A (TypeScript Monolith) / Pattern B (BFF)]
|
|
276
|
+
|
|
277
|
+
## Stack
|
|
278
|
+
|
|
279
|
+
- Frontend: SvelteKit [version], pnpm
|
|
280
|
+
- Backend: [Python FastAPI / N/A]
|
|
281
|
+
- Database: [Postgres via Drizzle / SQLAlchemy + Alembic]
|
|
282
|
+
- Auth: [describe approach]
|
|
283
|
+
|
|
284
|
+
## Monorepo layout
|
|
285
|
+
|
|
286
|
+
- `frontend/` — SvelteKit app (pnpm)
|
|
287
|
+
- `backend/` — Python FastAPI app, src layout (`backend/src/myapp/`)
|
|
288
|
+
- `docker-compose.yml` — local dev stack
|
|
289
|
+
|
|
290
|
+
## Key conventions
|
|
291
|
+
|
|
292
|
+
- [Any project-specific deviations from the standard skills]
|
|
293
|
+
- [Anything Claude should know that isn't covered by the skills]
|
|
294
|
+
|
|
295
|
+
## Skills active on this project
|
|
296
|
+
|
|
297
|
+
- designing-svelte-ui — UI and design system
|
|
298
|
+
- building-sveltekit-frontend — Frontend architecture
|
|
299
|
+
- building-python-backend — Backend architecture (Pattern B only)
|
|
300
|
+
|
|
301
|
+
@AGENTS.md
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The `@AGENTS.md` reference pulls in the design system decisions from the UI skill.
|
|
305
|
+
`CLAUDE.md` covers architecture; `AGENTS.md` covers visual identity.
|
|
306
|
+
If these do not exist, help the user create them by hand or via the relevant skills.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Cross-cutting Concerns
|
|
311
|
+
|
|
312
|
+
These span both layers and neither skill covers them fully:
|
|
313
|
+
|
|
314
|
+
### Auth
|
|
315
|
+
|
|
316
|
+
- **Pattern A:** Session token in `hooks.server.ts`, `locals.user` set there, guards in loaders
|
|
317
|
+
- **Pattern B:** Frontend hooks set `locals.user` from session. Backend validates its own JWT/token
|
|
318
|
+
on every request independently — never trust the frontend to enforce auth on the backend.
|
|
319
|
+
|
|
320
|
+
### Error handling across the stack
|
|
321
|
+
|
|
322
|
+
- Backend raises domain errors (`AppError` subclasses), maps to HTTP at the FastAPI edge
|
|
323
|
+
- Frontend loader catches HTTP errors, maps to SvelteKit `error()` or `fail()`
|
|
324
|
+
- Never let backend error internals leak to the frontend response body in production
|
|
325
|
+
|
|
326
|
+
### Shared types (Pattern B)
|
|
327
|
+
|
|
328
|
+
- The OpenAPI spec is the source of truth — not hand-written TS interfaces
|
|
329
|
+
- Run `cd frontend && pnpm generate:api` after every backend schema change
|
|
330
|
+
- Treat the generated `schema.d.ts` like a lockfile — commit it, don't hand-edit it
|
|
331
|
+
|
|
332
|
+
### Environment config
|
|
333
|
+
|
|
334
|
+
- `frontend/.env` and `backend/.env` are always separate, even in Pattern A (future-proofing)
|
|
335
|
+
- They should never share secrets
|
|
336
|
+
- Validate all env vars at startup — `AppConfig.from_env()` on backend, startup check on frontend
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Validation Loop
|
|
341
|
+
|
|
342
|
+
Before handing off the project to the user or an executor agent:
|
|
343
|
+
1. Validate the setup by running appropriate package installation (e.g., `pnpm install` or `uv sync`).
|
|
344
|
+
2. Verify structural configuration, such as validating `docker-compose.yml` with `docker compose config`.
|
|
345
|
+
3. If errors occur, autonomously fix them and repeat until the basic scaffolding checks out.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: building-python-backend
|
|
3
|
+
description: Provides architecture and engineering patterns for production async Python backends. Triggered when scaffolding, building, or reviewing Python backend features, FastAPI routes, SQLAlchemy models, or asyncio patterns. Enforces strict layer separation and domain-driven design.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python SWE Skill
|
|
7
|
+
|
|
8
|
+
Opinionated architecture for production async Python backends. Every layer has one job.
|
|
9
|
+
|
|
10
|
+
## Reference files
|
|
11
|
+
|
|
12
|
+
Read these when working in the relevant area:
|
|
13
|
+
|
|
14
|
+
- `references/layers.md` — Full layer guide: services, repositories, controllers, factory, models
|
|
15
|
+
- `references/fastapi.md` — FastAPI route patterns, dependency injection, error mapping
|
|
16
|
+
- `references/sqlalchemy.md` — ORM models, repository pattern, async session handling
|
|
17
|
+
- `references/patterns-examples.md` — Full code examples for all layers
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Blueprint
|
|
22
|
+
|
|
23
|
+
IMPORTANT: ask the user if they want you to start coding or explicitly invoke the `planning-features` skill to write a detailed plan.
|
|
24
|
+
|
|
25
|
+
## Architecture Overview
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
HTTP Layer (FastAPI)
|
|
29
|
+
├── routes/ # Thin handlers. Validate input, call factory, return response.
|
|
30
|
+
├── dependencies.py # FastAPI Depends: session, config, request-scoped deps
|
|
31
|
+
└── error_handlers.py # AppError → HTTP response mapping. Only place HTTP status lives.
|
|
32
|
+
|
|
33
|
+
Domain Layer (no FastAPI imports)
|
|
34
|
+
├── models/ # Dataclasses. Input/output of every service and controller.
|
|
35
|
+
├── errors.py # AppError hierarchy. Domain errors only, no HTTP codes.
|
|
36
|
+
├── services/ # Protocol interface + implementation. One concern each. Never import each other.
|
|
37
|
+
├── repositories/ # SQLAlchemy ORM types stay here. Deserialise to domain models immediately.
|
|
38
|
+
├── controllers/ # Orchestrate services via TaskGroup. No business logic.
|
|
39
|
+
└── factory.py # Assemble everything. Singletons at startup, request-scoped with session.
|
|
40
|
+
|
|
41
|
+
Config
|
|
42
|
+
└── config.py # AppConfig dataclass. Reads from env/secrets at startup. Passed everywhere.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Layer Rules (quick reference)
|
|
48
|
+
|
|
49
|
+
| Layer | Can do | Cannot do |
|
|
50
|
+
| ------------- | -------------------------------------------- | ----------------------------------------------- |
|
|
51
|
+
| Route handler | Parse request, call factory, return response | Business logic, direct DB access, service calls |
|
|
52
|
+
| Controller | Orchestrate services with TaskGroup | Business logic, DB access, HTTP concerns |
|
|
53
|
+
| Service | One domain concern, implement Protocol | Import other services, HTTP concerns, ORM types |
|
|
54
|
+
| Repository | SQLAlchemy queries, ORM↔model mapping | Business logic, calling services |
|
|
55
|
+
| Factory | Assemble singletons + request-scoped objects | Logic of any kind |
|
|
56
|
+
| Model | Pure data, dataclass or Pydantic | Methods with side effects |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Key Conventions
|
|
61
|
+
|
|
62
|
+
### Dataclasses
|
|
63
|
+
|
|
64
|
+
`slots=True` always. `frozen=True` for immutable value objects, omit for mutable aggregates:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from dataclasses import dataclass
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True, slots=True) # value object — immutable
|
|
70
|
+
class UserId:
|
|
71
|
+
value: str
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True, slots=True) # output model — immutable
|
|
74
|
+
class Recipe:
|
|
75
|
+
id: str
|
|
76
|
+
title: str
|
|
77
|
+
cuisine: str
|
|
78
|
+
|
|
79
|
+
@dataclass(slots=True) # input — may be built incrementally
|
|
80
|
+
class CreateRecipeInput:
|
|
81
|
+
title: str
|
|
82
|
+
cuisine: str | None = None
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Protocols for interfaces
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from typing import Protocol, runtime_checkable
|
|
89
|
+
|
|
90
|
+
@runtime_checkable
|
|
91
|
+
class IRecipeService(Protocol):
|
|
92
|
+
async def get_by_user_id(self, user_id: str) -> list[Recipe]: ...
|
|
93
|
+
async def create(self, input: CreateRecipeInput) -> Recipe: ...
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
No ABC, no inheritance. Implementations just match the signature.
|
|
97
|
+
|
|
98
|
+
### Logging
|
|
99
|
+
|
|
100
|
+
Bound logger per module, always:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import structlog
|
|
104
|
+
|
|
105
|
+
logger: structlog.stdlib.BoundLogger = structlog.getLogger(__name__)
|
|
106
|
+
|
|
107
|
+
# Bind context for a request/operation
|
|
108
|
+
log = logger.bind(user_id=user_id, recipe_id=recipe_id)
|
|
109
|
+
await log.ainfo("Creating recipe")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Async — TaskGroup always over gather
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import asyncio
|
|
116
|
+
|
|
117
|
+
async def get_dashboard(user_id: str) -> Dashboard:
|
|
118
|
+
async with asyncio.TaskGroup() as tg:
|
|
119
|
+
recipes_task = tg.create_task(self._recipe_service.get_by_user_id(user_id))
|
|
120
|
+
pantry_task = tg.create_task(self._pantry_service.get_by_user_id(user_id))
|
|
121
|
+
|
|
122
|
+
return Dashboard(recipes=recipes_task.result(), pantry=pantry_task.result())
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Never use `asyncio.gather`. `TaskGroup` propagates exceptions cleanly and cancels siblings on failure.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Code Examples
|
|
130
|
+
|
|
131
|
+
For full code examples of the architecture layers in practice, please read:
|
|
132
|
+
- **`references/patterns-examples.md`** — Examples for Models, Services, Controllers, Factory, AppConfig, and Error hierarchy.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## For detailed patterns, read:
|
|
137
|
+
|
|
138
|
+
- **Repositories, ORM models, session handling** → `references/sqlalchemy.md`
|
|
139
|
+
- **FastAPI routes, dependency injection, error mapping** → `references/fastapi.md`
|
|
140
|
+
- **Full layer guide with anti-patterns** → `references/layers.md`
|
|
141
|
+
|
|
142
|
+
## Validation Checklist
|
|
143
|
+
|
|
144
|
+
Before concluding any implementation task, copy this checklist into your response scratchpad to track your progress:
|
|
145
|
+
- [ ] Run the type-checker (e.g., `pyright .` or `mypy .`) and linter (`ruff check .`).
|
|
146
|
+
- [ ] Run tests (`pytest`).
|
|
147
|
+
- [ ] If errors occur, autonomously fix them and repeat the loop until the checks pass. Do not ask the human to fix your structural or typing errors.
|