Opinionated architecture constraint checker
Project description
chisel
Opinionated architecture constraint checker for FastAPI backends. Designed to catch the patterns agents get wrong — use it as a pre-commit hook to block violations, or run it interactively to steer an agent while it's working.
It pairs with agent skills: the skill teaches the agent the right pattern, the checker enforces it deterministically. Run chisel explain <rule-id> to get fix guidance the agent can consume directly.
Installation
pip install chisel
Quick start
chisel check ./backend # check a project
chisel rules # list all ~55 rules grouped by category
chisel explain structural:print-banned # detailed fix guidance
chisel check . --json # violations with message refs + skill names
chisel check . --no-strict # skip src-layout enforcement
chisel update self # upgrade the installed CLI package
chisel update skills --target codex # overwrite installed skills with bundled copies
Commands
| Command | Description |
|---|---|
chisel check [path] |
Scan a project for architectural violations |
chisel check . --json |
Output violations as structured JSON with deduplicated messages |
chisel check . --strict/--no-strict |
Toggle src-layout and build-config enforcement |
chisel rules |
List all rules, grouped by category |
chisel rules --json |
Machine-readable rule listing |
chisel explain <rule-id> |
Detailed description + fix guidance for a rule |
chisel explain <category> |
All rules in a category (e.g. structural) |
chisel setup [path] |
Install Chisel agent skills into a repo for Codex, Claude Code, or OpenCode |
chisel update self |
Upgrade the installed Chisel package |
chisel update skills [path] |
Refresh installed Chisel skills after confirmation |
Agent skills
Install Chisel's bundled skills into a project so coding agents can load the same architecture guidance that the checker enforces:
chisel setup --target codex # writes .agents/skills/
chisel setup --target claude # writes .claude/skills/
chisel setup --target opencode # writes .opencode/skills/
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.
Refresh previously installed skills after upgrading Chisel:
chisel update skills --target codex
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.
Agent-facing JSON output deduplicates repeated violation messages: each
violation carries message_ref, and the top-level messages array contains
each full message once with its skill_name.
What gets checked
| Category | What it enforces |
|---|---|
| Import Boundaries | Layer-based import restrictions — services can't import SQLAlchemy, routes can't import services, HTTPException only in error_handlers.py, concrete services only in factory.py |
| Structural Invariants | No getattr/setattr, no % string formatting, no f-strings in logger calls, no print() in src/, @dataclass uses slots=True, AppFactory has zero logic, match/case only in error handlers |
| Complexity | Controller method ≤30 LoC, route handler ≤20 LoC, factory cyclomatic complexity = 1, app.py ≤50 LoC |
| Concurrency | asyncio.gather banned unconditionally — use asyncio.TaskGroup |
| Error Flow | AppError subclasses never contain HTTP status codes; status codes decided only in error_handlers.py |
| Project Structure | pyproject.toml only (no setup.py), env vars read at startup only via AppConfig, no .py files at project root |
| Test Structure | Test files only under tests/unit/, tests/integration/, tests/e2e/; one assert per test; no mocking libraries; test names describe invariants; pytest.mark.skip needs a reason; no TestClient outside e2e |
Exceptions
Create a chisel-exceptions.toml at your project root:
[[exceptions]]
files = ["src/legacy/*.py", "src/cli/main.py"]
rules = ["structural:print-banned"]
reason = "CLI requires stdout output"
* matches all rules. A category prefix like structural matches all rules in that category.
Inline suppression per line: # noqa: rule-id — reason.
Severity tiers
| Tier | Behaviour |
|---|---|
| ERROR | Blocks commit, no override. Import boundary violations, asyncio.gather, getattr/setattr, f-string in logger, print() in src/. |
| WARNING | Blocks commit, suppressible with a reason comment. Controller/route LoC thresholds, test coverage gaps. |
| INFO | Logged, non-blocking. |
How it works
models/ Pure data — Violation, Severity, FileInfo, ProjectInfo
services/ One service per rule category, self-describing via describe_rules()
repositories/ File discovery, import graph analysis (grimp)
controllers/ Orchestrates all services, filters exceptions, suppresses via noqa
factory.py Zero-logic DI — wires all services into the controller
cli/main.py Typer-based CLI — check, rules, explain
reporter.py Coloured terminal output via rich
Adding a rule requires one new check method in a service + one describe_rules() entry. The CLI discovers it automatically.
Development
pip install -e ".[dev]"
pytest tests/ -q
The checker is self-validating: chisel check . --strict produces zero violations on its own source.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chisel_checker-0.2.0.tar.gz.
File metadata
- Download URL: chisel_checker-0.2.0.tar.gz
- Upload date:
- Size: 151.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e64ce540402330eb46d50b70799f0da7a0c15585a55e82d6aff4a3e893be55f
|
|
| MD5 |
b9d5d2363cf5b20b39d1a5a5e77f8d97
|
|
| BLAKE2b-256 |
97b173648a1a67cebeca913253be68f8bbc86fa5e59ed809570f513724ac0810
|
Provenance
The following attestation bundles were made for chisel_checker-0.2.0.tar.gz:
Publisher:
ci.yml on ChidiRnweke/chisel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chisel_checker-0.2.0.tar.gz -
Subject digest:
7e64ce540402330eb46d50b70799f0da7a0c15585a55e82d6aff4a3e893be55f - Sigstore transparency entry: 2079317836
- Sigstore integration time:
-
Permalink:
ChidiRnweke/chisel@6304dcf3219438a1f235ad0ccd814dec57cc5434 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/ChidiRnweke
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@6304dcf3219438a1f235ad0ccd814dec57cc5434 -
Trigger Event:
release
-
Statement type:
File details
Details for the file chisel_checker-0.2.0-py3-none-any.whl.
File metadata
- Download URL: chisel_checker-0.2.0-py3-none-any.whl
- Upload date:
- Size: 176.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c71215612b52f894fc672fbd8059a01f3311a9ec7eaf90cebf8f72ece73c7875
|
|
| MD5 |
a70f79967208d99eee421e9d0de7036e
|
|
| BLAKE2b-256 |
841401524d919e333c6684b2bda4a1adb55480a392e24d04632f9dc54766954a
|
Provenance
The following attestation bundles were made for chisel_checker-0.2.0-py3-none-any.whl:
Publisher:
ci.yml on ChidiRnweke/chisel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chisel_checker-0.2.0-py3-none-any.whl -
Subject digest:
c71215612b52f894fc672fbd8059a01f3311a9ec7eaf90cebf8f72ece73c7875 - Sigstore transparency entry: 2079317959
- Sigstore integration time:
-
Permalink:
ChidiRnweke/chisel@6304dcf3219438a1f235ad0ccd814dec57cc5434 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/ChidiRnweke
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@6304dcf3219438a1f235ad0ccd814dec57cc5434 -
Trigger Event:
release
-
Statement type: