Not just structurally sound. Beautiful. An architecture linter for Python projects.
Project description
Gaudi
Not just structurally sound. Beautiful.
Gaudi is an architecture linter for Python projects. It inspects your project's structural design and produces machine-readable error codes that AI coding agents (Claude, Copilot, etc.) can understand and act on.
Style linters catch syntax. Security scanners catch vulnerabilities. Gaudi catches architecture mistakes — the kind that cost you six months of refactoring when you hit 10,000 users.
What It Does
$ gaudi check .
ARCH-001 [ERROR] models.py:14 - Multi-tenant table 'donors' has no tenant isolation column
-> Add a `tenant_id` ForeignKey and enforce row-level filtering on all queries.
IDX-001 [WARN] models.py:28 - Column 'email' is used in filter queries but has no db_index
-> Add db_index=True or create a composite index.
SCHEMA-001 [INFO] models.py:45 - Model 'Donor' has no timestamp fields (created_at, updated_at)
-> Add created_at and updated_at DateTimeField columns for debugging and auditing.
Found 1 error, 1 warning, 1 info across 3 files.
Why Gaudi Exists
AI coding agents are writing more and more of our code. They're great at implementing features. They're terrible at asking, "Should this be built this way?"
Gaudi is the discipline layer. It encodes architectural best practices into structured error codes that any AI agent can parse, understand, and resolve — without ambiguity, without hallucination.
For humans: Catch design mistakes before they become technical debt. For AI agents: Get structured, actionable architecture guidance instead of vague "best practices" prompts.
Installation
pip install gaudi-linter
Quick Start
# Check a project directory
gaudi check .
# Output as JSON (for AI agent consumption)
gaudi check . --format json
# Output as GitHub Actions annotations (inline on PRs)
gaudi check . --format github
# Only errors
gaudi check . --severity error --exit-code
# Check a specific file
gaudi check models.py
# Generate a Markdown report you can paste into a chat with an LLM
gaudi report . --output gaudi-report.md
LLM-collaborative workflow
Gaudi is designed to be the opening move in a developer ↔ LLM conversation, not a list of mechanical autofixes. Most rules are judgment calls — the right thing to do about a finding depends on the surrounding code and the project's priorities.
Two outputs make that conversation cheap to start:
gaudi check --format githubemits GitHub Actions workflow commands so findings render inline on pull requests, in the Files Changed view, exactly where reviewers (and Copilot/Claude PR reviewers) expect them. See docs/llm-workflow.md for a sample workflow.gaudi report .writes a Markdown report grouped by file. Each finding includes a code snippet, the rule's recommendation, and a pre-written "Discuss with LLM" prompt the developer can paste straight into Claude, ChatGPT, or any other assistant.
What It Checks
Gaudi uses deep AST analysis to inspect Django models, SQLAlchemy tables, FastAPI endpoints, Flask apps, Celery tasks, Pandas operations, Pydantic models, pytest fixtures, and DRF views.
Rule Categories
| Prefix | Category | Examples |
|---|---|---|
ARCH |
Architecture | Tenant isolation, god models, nullable FK sprawl |
IDX |
Indexing | Missing indexes on lookup/filter fields |
SCHEMA |
Schema Design | Missing timestamps, column sprawl, type choices |
SEC |
Security | Hardcoded secrets, missing permissions, unsafe defaults |
SCALE |
Scalability | N+1 queries, missing timeouts, iterrows() |
STRUCT |
Structure | Fat files, missing app factories |
Library-Specific Rules
| Prefix | Library | Rules |
|---|---|---|
DJ |
Django | Secret key exposure, DEBUG=True, fat views |
FAPI |
FastAPI | Missing response_model, sync endpoints |
SA |
SQLAlchemy | Session leaks, default lazy loading |
FLASK |
Flask | Module-level app creation |
CELERY |
Celery | Missing retries, missing time limits |
PD |
Pandas | inplace=True, iterrows() |
HTTP |
Requests/HTTPX | Missing timeouts, no retry logic |
PYD |
Pydantic | Mutable default values |
TEST |
pytest | Complex assertions, expensive fixtures |
DRF |
Django REST Framework | Missing permissions, no throttling |
PY314 |
Python 3.14 | Removed APIs, deprecated modules |
Error Code Format
Every finding follows a consistent schema:
{
"code": "ARCH-001",
"severity": "error",
"category": "architecture",
"file": "models.py",
"line": 14,
"message": "Multi-tenant table 'donors' has no tenant isolation column",
"recommendation": "Add a `tenant_id` ForeignKey and enforce row-level filtering."
}
Writing Custom Rules
from gaudi import Rule, Severity, Category
class CheckTenantIsolation(Rule):
code = "ARCH-001"
severity = Severity.ERROR
category = Category.ARCHITECTURE
message_template = "Multi-tenant table '{table}' has no tenant isolation column"
def check(self, context):
for model in context.models:
if not model.has_column("tenant_id"):
yield self.finding(
file=model.source_file,
line=model.source_line,
table=model.name,
)
AI Agent Integration
Gaudi's JSON output is designed to be consumed by AI coding agents. Add it to your Claude Code workflow, your CI pipeline, or your custom agent loop.
# Use in CI
gaudi check . --format json --severity error --exit-code
# Use in a pre-commit hook
gaudi check . --severity error --exit-code
Prompt Fragment for AI Agents
Include this in your system prompt or project instructions:
Before implementing any database schema changes, run `gaudi check .` and resolve
all ERROR-level findings. WARN-level findings should be addressed unless you can
document a specific reason to override them.
Configuration
Create a gaudi.toml in your project root:
[gaudi]
severity = "warn" # minimum severity to report
exclude = ["migrations/"] # paths to skip
[gaudi.rules]
"ARCH-001" = "error" # override severity
"IDX-003" = "off" # disable a rule entirely
[philosophy]
school = "convention" # infer with: gaudi philosophy .
Inline suppression
Suppress findings on a single line with # noqa:
SECRET_KEY = "test-only" # noqa: DJ-SEC-001
urlpatterns = [...] # noqa
# noqa (bare) suppresses all findings on that line. # noqa: CODE1, CODE2 suppresses only the listed rules.
Rule cheat-sheet
Generate a one-line-per-rule markdown file from the live registry, suitable
for @-reference from CLAUDE.md or any other AI agent instructions file:
# Print to stdout
gaudi cheat-sheet
# Write to a file (committed artifact)
gaudi cheat-sheet -o docs/gaudi-rules.md
# CI drift guard: exit 1 if the file is out of date
gaudi cheat-sheet --check -o docs/gaudi-rules.md
The committed artifact at docs/gaudi-rules.md is
generated from rule recommendation_template fields. It cannot drift.
Philosophy inference
Gaudi can recommend which architectural school best matches your project:
gaudi philosophy .
This analyzes your dependencies and project structure to suggest a school (e.g., convention for Django projects, data-oriented for NumPy pipelines).
Known Limitations (v0.1 alpha)
- Some library-specific rules use regex on raw text instead of full AST analysis, which can produce false positives.
These are tracked in the issue tracker.
Philosophy
Gaudi is named after Antoni Gaudi, the architect of La Sagrada Familia. He built hanging chain models — inverted catenary arches — to test structural integrity before laying a single stone. Construction on his masterwork began in 1882 and continues today, still following his structural principles.
This tool embodies that philosophy: validate the architecture before you build. The earlier you catch a structural flaw, the less it costs to fix. And with AI agents writing increasingly large portions of our codebases, we need automated architectural discipline more than ever.
The first principles that govern Gaudi — fourteen numbered claims in three pillars (Truthfulness, Economy, Cost-honesty) — are written down in docs/principles.md. They are intended to be portable: any project can adopt them as the doctrine its design decisions appeal to.
Contributing
Contributions welcome. The highest-impact contributions right now:
- New rules — especially Django, FastAPI, and SQLAlchemy patterns
- Code smell detection — implementing Fowler's 24 code smells programmatically
- CI/CD integration examples — GitHub Actions, GitLab CI, etc.
See CONTRIBUTING.md for details.
License
MIT License. See LICENSE for details.
Built by Nathan Krupa — fundraiser, writer, and reluctant architect.
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 gaudi_linter-0.2.0.tar.gz.
File metadata
- Download URL: gaudi_linter-0.2.0.tar.gz
- Upload date:
- Size: 139.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 |
3d6259141e8e6decb13fe2db86461f073d02ba59d5c4e0a8b141973c10645d08
|
|
| MD5 |
17b41a8b9266cfbf5ccba07001e3d0cc
|
|
| BLAKE2b-256 |
b2611701de718d906f7deb2c21d004ccec591efd439a3202f934252efed4f852
|
Provenance
The following attestation bundles were made for gaudi_linter-0.2.0.tar.gz:
Publisher:
publish.yml on NathanKrupa/gaudi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gaudi_linter-0.2.0.tar.gz -
Subject digest:
3d6259141e8e6decb13fe2db86461f073d02ba59d5c4e0a8b141973c10645d08 - Sigstore transparency entry: 1295162118
- Sigstore integration time:
-
Permalink:
NathanKrupa/gaudi@f6f823cc087c0d26c32b9daa9241da3476112877 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/NathanKrupa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f6f823cc087c0d26c32b9daa9241da3476112877 -
Trigger Event:
release
-
Statement type:
File details
Details for the file gaudi_linter-0.2.0-py3-none-any.whl.
File metadata
- Download URL: gaudi_linter-0.2.0-py3-none-any.whl
- Upload date:
- Size: 156.3 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 |
d6a7e750f8c4b772a9b6f7a83b3dd5be1721a72b07e133f7df8f6d471b39d15d
|
|
| MD5 |
d1619890c03ce852148043cc6398eda1
|
|
| BLAKE2b-256 |
1a570c75bff6370ca584e57caa4f2f4be996ffac76a2c9e812a8acb364ee3d74
|
Provenance
The following attestation bundles were made for gaudi_linter-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on NathanKrupa/gaudi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gaudi_linter-0.2.0-py3-none-any.whl -
Subject digest:
d6a7e750f8c4b772a9b6f7a83b3dd5be1721a72b07e133f7df8f6d471b39d15d - Sigstore transparency entry: 1295162221
- Sigstore integration time:
-
Permalink:
NathanKrupa/gaudi@f6f823cc087c0d26c32b9daa9241da3476112877 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/NathanKrupa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f6f823cc087c0d26c32b9daa9241da3476112877 -
Trigger Event:
release
-
Statement type: