Architectural sensor for Python codebases
Project description
archy
Architectural sensor for Python codebases - keeps structure honest under AI-assisted development.
Status: v0.4.3. Usable today via:
| Mode | Command |
|---|---|
| Inspection | archy graph, archy cycles |
| CI governance | archy check (reads archy.yaml) |
| One-shot score | archy score |
| Trended score | archy score --record + archy trend |
| MCP server | archy mcp |
Benchmarks against pydantic, fastapi, flask, pytest, and archy-on-archy live in docs/CASE_STUDIES.md. Score design follows sentrux (modularity, acyclicity, depth, equality, geometric mean); see docs/LEARNINGS.md.
Why
AI agents generate code at machine speed. Without a feedback loop on structural health (module coupling, import cycles, layer violations), codebases drift architecturally even when every individual change looks fine in review.
archy watches a Python codebase, builds a live module-dependency graph, and surfaces drift through a single trended score plus a handful of actionable sub-metrics. It's designed to run in CI, in pre-commit, and as an MCP server (archy mcp) so coding agents can read their own architectural impact before committing.
Scope
- Python only. The cross-language story is deliberately someone else's problem.
- Tree-sitter powered. Robust to in-flight edits and partial files; survives syntax errors that would crash
ast. - Score that trends over time. A single number per commit, persisted, plotted. Trend matters more than the absolute value.
- Rules as YAML. "Layer X cannot import Y." No DSL, no plugins (yet).
Non-goals
- Multi-language analysis
- Replacing linters, type checkers, or test runners
- Generating code or auto-fixing violations
Quick start
uv sync
Inspect the graph
uv run archy graph path/to/project --internal-only
uv run archy graph path/to/project --format json > graph.json
uv run archy graph path/to/project --format dot | dot -Tsvg > graph.svg
Find import cycles
Tarjan SCCs of size >= 2. Use --strict in CI to fail on any cycle.
uv run archy cycles path/to/project
uv run archy cycles path/to/project --format json
uv run archy cycles path/to/project --strict
Enforce layer rules
Reads archy.yaml from the repo root. Exits 1 on any violation. See Layer rules below.
uv run archy check path/to/project
uv run archy check path/to/project --format json
uv run archy check path/to/project --config custom.yaml
Compute a quality score
Composite of modularity, acyclicity, depth, and equality (geometric mean).
uv run archy score path/to/project
uv run archy score path/to/project --format json
Track score over time
Persist per-commit scores to .archy/history.jsonl and chart the trend.
uv run archy score path/to/project --record
uv run archy trend path/to/project
uv run archy trend path/to/project --last 30 --format json
Regression gate
Fail if the current score drops more than --strict-tolerance (default 0.02) below the most recent recorded run.
uv run archy score path/to/project --strict
uv run archy score path/to/project --strict --record # check then record
uv run archy score path/to/project --strict --strict-tolerance 0.0
Run as an MCP server
Stdio transport, so AI agents can call archy directly. See MCP server below.
uv run archy mcp
MCP server (archy mcp)
archy mcp exposes five tools to MCP-aware AI agents (Claude Code, the Anthropic API, etc.):
| Tool | Purpose |
|---|---|
archy_score |
Compute the four-metric score; optional record=True and strict=True for the same regression-gate behaviour the CLI offers. |
archy_cycles |
Find import cycles. |
archy_check |
Run layer rules from archy.yaml. |
archy_trend |
Read recent score history. |
archy_record_baseline |
Convenience wrapper for archy_score(record=True); mirrors sentrux's session_start. |
Wire it into Claude Code with this stanza in your config:
{
"mcpServers": {
"archy": { "command": "uv", "args": ["run", "archy", "mcp"] }
}
}
Regression-gate semantics
--strict reads the last row from .archy/history.jsonl and compares the current score against it. Drops beyond the tolerance fail with exit code 1. The default tolerance (0.02) matches the threshold sentrux's gate uses. This gives archy parity with sentrux's regression-gate use case while keeping the long-term JSONL history for archy trend.
CI integration
GitHub Action
archy ships a composite action you can drop into any workflow:
- uses: hslee16/archy@v0.4.3
with:
command: score # score | check | cycles
path: .
strict: "true" # fail on regression (score) or any cycle (cycles)
Inputs (all optional unless noted):
| Input | Default | Notes |
|---|---|---|
command |
score |
score, check, or cycles |
path |
. |
Project root to analyze |
strict |
true |
score/cycles: fail on regression / any cycle |
strict-tolerance |
0.02 |
score --strict tolerance |
record |
false |
score: append result to .archy/history.jsonl |
config |
(auto) | check: path to archy.yaml |
python-version |
3.10 |
Python to install |
Pre-commit hook
Add to .pre-commit-config.yaml:
repos:
- repo: https://github.com/hslee16/archy
rev: v0.4.3
hooks:
- id: archy-check # layer rules from archy.yaml
- id: archy-score-strict # regression gate against last recorded score
- id: archy-cycles # fail on any import cycle
archy-score-strict reads .archy/history.jsonl; commit a baseline first with archy score . --record.
Layer rules (archy check)
Drop an archy.yaml at the repo root declaring layers and forbidden directions:
layers:
domain:
modules:
- "myapp.domain.**"
application:
modules:
- "myapp.application.**"
infra:
modules:
- "myapp.infra.**"
- "myapp.adapters.**"
forbid:
- {from: domain, to: application}
- {from: domain, to: infra}
- {from: application, to: infra}
Pattern syntax. Dotted-name globs: * matches one segment, ** matches zero or more. myapp.domain.** covers the package itself and every descendant. Modules must belong to at most one layer.
Excluding directories. Add an optional exclude: list of directory basenames to skip codegen output, vendored code, etc. Each name is matched anywhere in the project tree (same mechanism as the built-in skips for .venv, node_modules, __pycache__):
exclude:
- baml_client
- generated
exclude: applies to every analysis (graph, cycles, score, check) and the equivalent MCP tools.
Discovery. archy check walks PATH upward to find archy.yaml unless --config is given. Exits 1 on violation.
archy enforces its own architecture this way; see archy.yaml at the repo root and the archy check . step in .github/workflows/ci.yml.
Development
uv sync # install runtime + dev deps from uv.lock
uv run ruff check # lint
uv run ruff format # format
uv run ty check # type check
uv run pytest # tests
Roadmap
Next up:
-
archy_impactMCP tool: blast-radius analysis for a set of changed files -
archy graphMCP tool: expose the dep graph itself for agent-side reasoning - Score deltas (
archy_evolution): per-component diffs vs. the last recorded run - Design Structure Matrix (
archy dsm)
Shipped: tree-sitter import graph, __init__.py re-export resolution, Tarjan cycle detection, YAML layer rules (archy check), composite score (archy score), JSONL history + archy trend, MCP server (archy mcp), GitHub Action + pre-commit hooks.
See docs/FUTURE.md for the longer list and docs/LEARNINGS.md for design notes.
Contributing
See CONTRIBUTING.md for style rules. Notably: no em-dash characters (U+2014) anywhere in the repo.
License
MIT, see LICENSE.
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 archy-0.4.3.tar.gz.
File metadata
- Download URL: archy-0.4.3.tar.gz
- Upload date:
- Size: 126.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81bab7992db1bca41251dd69efeaebfe2c02af50d30c44c371f115a61368992a
|
|
| MD5 |
5f9f825ffb4d5f510b99c9e58652c4fa
|
|
| BLAKE2b-256 |
70284b93c32dc0d2b6fc1c39ad916c84cdd637e13e6ac25141f4f0204b4c942c
|
Provenance
The following attestation bundles were made for archy-0.4.3.tar.gz:
Publisher:
publish.yml on hslee16/archy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
archy-0.4.3.tar.gz -
Subject digest:
81bab7992db1bca41251dd69efeaebfe2c02af50d30c44c371f115a61368992a - Sigstore transparency entry: 1487660396
- Sigstore integration time:
-
Permalink:
hslee16/archy@b2a917514297d0afbb705e4e1d9d6de7a9c68860 -
Branch / Tag:
refs/tags/v0.4.3 - Owner: https://github.com/hslee16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b2a917514297d0afbb705e4e1d9d6de7a9c68860 -
Trigger Event:
release
-
Statement type:
File details
Details for the file archy-0.4.3-py3-none-any.whl.
File metadata
- Download URL: archy-0.4.3-py3-none-any.whl
- Upload date:
- Size: 27.9 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 |
d7f2d7999fa363fa69055db15e326e4e1231fabe3d56ad1fb1031c5afb72eeba
|
|
| MD5 |
d089549dc76ef789c070a118a9645c2f
|
|
| BLAKE2b-256 |
dec96ff65eeb4075200eb63fc7a69814e54115b5dd1008664a5ccc18d66b05d9
|
Provenance
The following attestation bundles were made for archy-0.4.3-py3-none-any.whl:
Publisher:
publish.yml on hslee16/archy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
archy-0.4.3-py3-none-any.whl -
Subject digest:
d7f2d7999fa363fa69055db15e326e4e1231fabe3d56ad1fb1031c5afb72eeba - Sigstore transparency entry: 1487660734
- Sigstore integration time:
-
Permalink:
hslee16/archy@b2a917514297d0afbb705e4e1d9d6de7a9c68860 -
Branch / Tag:
refs/tags/v0.4.3 - Owner: https://github.com/hslee16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b2a917514297d0afbb705e4e1d9d6de7a9c68860 -
Trigger Event:
release
-
Statement type: