Metadata-Version: 2.4
Name: axm-audit
Version: 0.10.0
Summary: Code auditing and quality rules for AXM
Project-URL: Homepage, https://github.com/axm-protocols/axm-audit
Project-URL: Documentation, https://axm-protocols.github.io/axm-audit/
Project-URL: Repository, https://github.com/axm-protocols/axm-audit
Project-URL: Issues, https://github.com/axm-protocols/axm-audit/issues
Author-email: AXM Protocols <gabriel@axm-protocols.io>
License: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: axm
Requires-Dist: axm-anvil
Requires-Dist: axm-ast
Requires-Dist: complexipy>=5.4.0
Requires-Dist: cyclopts>=3.0
Requires-Dist: libcst>=1.5.0
Requires-Dist: pydantic>=2.12.5
Requires-Dist: radon>=6.0.1
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://raw.githubusercontent.com/axm-protocols/axm-forge/main/assets/logo.png" alt="AXM Logo" width="180" />
</p>

<p align="center">
  <strong>axm-audit — Code auditing and quality rules for Python projects</strong>
</p>


<p align="center">
  <a href="https://github.com/axm-protocols/axm-forge/actions/workflows/ci.yml"><img src="https://github.com/axm-protocols/axm-forge/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://forge.axm-protocols.io/audit/"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/axm-protocols/axm-forge/gh-pages/badges/axm-audit/axm-audit.json" alt="axm-audit"></a>
  <a href="https://forge.axm-protocols.io/init/"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/axm-protocols/axm-forge/gh-pages/badges/axm-audit/axm-init.json" alt="axm-init"></a>
  <a href="https://github.com/axm-protocols/axm-forge/actions/workflows/axm-quality.yml"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/axm-protocols/axm-forge/gh-pages/badges/axm-audit/coverage.json" alt="Coverage"></a>
  <a href="https://pypi.org/project/axm-audit/"><img src="https://img.shields.io/pypi/v/axm-audit" alt="PyPI"></a>
  <img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="Python 3.12+">
  <a href="https://forge.axm-protocols.io/audit/"><img src="https://img.shields.io/badge/docs-live-brightgreen" alt="Docs"></a>
</p>

---

`axm-audit` audits Python project quality across **9 scored categories** (plus `structure` and `tooling`, which emit findings but are not scored), producing a composite **0–100 score** with an **A–F grade**. It works as a **CLI**, **Python API**, and **MCP tool** for AI agents.

📖 **[Full documentation](https://forge.axm-protocols.io/audit/)**

## Features

- 🔍 **Linting** — Ruff analysis (800+ rules)
- 🔒 **Type Checking** — Strict mypy (per-project `pyproject.toml` config)
- 📊 **Complexity** — Cyclomatic complexity via radon (Python API with subprocess fallback)
- 🛡️ **Security** — Bandit integration + hardcoded secrets detection
- 📦 **Dependencies** — Vulnerability scanning (pip-audit) + hygiene (deptry) with false-positive filtering for entry-point and optional-dependency packages, uv workspace support (auto-aggregation across members), and dual-format text output (`• pkg ver→fix CVE-id` with `+N` suffix for multiple CVEs)
- 🧪 **Testing** — Coverage enforcement via pytest-cov
- 🏗️ **Architecture** — Circular imports, god classes, coupling metrics, duplication detection
- 📐 **Practices** — Docstring coverage (with cross-file abstract override detection), bare except detection, hardcoded secrets, blocking I/O, test mirroring (unit 1:1 with src) and scenario naming (integration/e2e)
- 🔧 **Tooling** — CLI tool availability checks
- 📈 **Composite Scoring** — Weighted 9-category 0–100 score with A–F grade

## Installation

```bash
uv add axm-audit
```

## Quick Start

### CLI

```bash
# Full audit
axm-audit audit .

# JSON output
axm-audit audit . --json

# Agent-optimized output (compact, actionable)
axm-audit audit . --agent

# Filter by category
axm-audit audit . --category lint

# Deterministically reorganise the test suite (dry-run by default)
axm-audit fix .
axm-audit fix . --apply

# Run tests with structured output
axm-audit test .

# Agent-optimized test output (compact, actionable)
axm-audit test . --agent
```

### Python API

```python
from pathlib import Path
from axm_audit import audit_project

result = audit_project(Path("."))

print(f"Grade: {result.grade} ({result.quality_score:.1f}/100)")
print(f"Checks: {result.total - result.failed}/{result.total} passed")

for check in result.checks:
    if not check.passed:
        print(f"  ❌ {check.rule_id}: {check.message}")
        if check.fix_hint:
            print(f"     Fix: {check.fix_hint}")
```

### MCP (AI Agent)

`axm-audit` is available as an MCP tool via [`axm-mcp`](https://github.com/axm-protocols/axm-forge/tree/main/packages/axm-mcp). AI agents can call `audit(path)` or `verify(path)` directly:

```python
# Agent-optimized output: passed checks as compact strings,
# failed checks as dicts with rule_id, message, details, fix_hint
from axm_audit.formatters import format_agent

data = format_agent(result)
# data["score"], data["grade"], data["passed"], data["failed"]
```

See the [MCP how-to guide](https://forge.axm-protocols.io/audit/howto/mcp/) for details.

## Scoring Model

9-category weighted composite on a 100-point scale:

| Category | Weight | Tool |
|---|---|---|
| Linting | **15%** | Ruff |
| Type Safety | **15%** | mypy |
| Complexity | **15%** | radon + complexipy |
| Security | **10%** | Bandit |
| Dependencies | **10%** | pip-audit + deptry |
| Testing | **10%** | pytest-cov |
| Test Quality | **10%** | AST analysis |
| Architecture | **10%** | AST analysis |
| Practices | **5%** | AST analysis |

Categories `structure` and `tooling` emit findings but are not scored.

## Categories

| Category | Rules | Count |
|---|---|---|
| `lint` | `LintingRule`, `FormattingRule`, `DiffSizeRule`, `DeadCodeRule` | 4 |
| `type` | `TypeCheckRule` | 1 |
| `complexity` | `ComplexityRule` | 1 |
| `security` | `SecurityRule` (Bandit), `SecurityPatternRule` | 2 |
| `deps` | `DependencyAuditRule`, `DependencyHygieneRule` | 2 |
| `testing` | `TestCoverageRule` | 1 |
| `test_quality` | `DuplicateTestsRule`, `FileNamingRule`, `NoPackageSymbolRule`, `PrivateImportsRule`, `PyramidLevelRule`, `TautologyRule` | 6 |
| `architecture` | `CircularImportRule`, `GodClassRule`, `CouplingMetricRule`, `DuplicationRule` | 4 |
| `practices` | `MirrorRule`, `AntiMirrorRule`, `BareExceptRule`, `BlockingIORule`, `DocstringCoverageRule` | 5 |
| `structure` | `PyprojectCompletenessRule`, `TestsPyramidRule` | 2 |
| `tooling` | `ToolAvailabilityRule` | 1 |

## Configuration

### Coupling Thresholds

The `CouplingMetricRule` reads thresholds from `pyproject.toml`:

```toml
[tool.axm-audit.coupling]
fan_out_threshold = 15          # default: 10
severity_error_multiplier = 2   # default: 2, minimum: 1

[tool.axm-audit.coupling.overrides]
"my_package.hub" = 20           # allow higher fan-out for hub modules
"registry" = 25                 # matches any module ending with .registry
```

- **`fan_out_threshold`** — global fan-out limit (modules above this are flagged)
- **`overrides`** — per-module thresholds; keys match by exact name or suffix
- **`severity_error_multiplier`** — tiered severity: modules with fan-out above the effective threshold but within `threshold × multiplier` get a **warning** (−3 pts); beyond that they get an **error** (−5 pts). Only errors cause the check to fail; warnings alone still pass.

When no configuration is present, the default threshold of 10 and multiplier of 2 are used.

### Mirror Exemptions

`MirrorRule` (alias `TestMirrorRule`) reads optional exemptions for both
mirror directions from `pyproject.toml`:

```toml
[tool.axm-audit.mirror]
exempt_paths = ["commands/*.py", "schemas/*.py", "**/_facade.py"]
exempt_tests = ["conformance/**", "contracts/*.py"]
```

`exempt_paths` covers the **forward** direction: globs anchored at
`src/<top_pkg>/`; exempted modules do not require a matching
`tests/unit/test_*.py` and surface in `details["exempt"]`.

`exempt_tests` covers the **reverse** direction: globs anchored at
`tests/unit/`; cross-cutting test files with no source mirror (e.g.
conformance suites that exercise every dispatcher) are whitelisted out
of the orphan list and surface in `details["exempt_tests"]`.

For both keys, `*` and `?` never cross `/` and `**` matches zero or more
path segments. The two keys are independent: `exempt_paths` never clears
an orphan and `exempt_tests` never clears a missing source module.
Invalid TOML or a wrong `exempt_paths` / `exempt_tests` type fails the
rule with a `fix_hint` instead of raising.

### UV Workspace Support

`DependencyHygieneRule` automatically detects uv workspaces via `[tool.uv.workspace].members` in the root `pyproject.toml`. When a workspace is detected, deptry runs on each member package independently and results are aggregated into a single `CheckResult` with per-member attribution in `top_issues`.

## Test Quality

The `test_quality` category ships four rules (private-imports,
pyramid-level, duplicate-tests, tautology) plus the v6 pyramid stack and
the v4 tautology triage ladder. See [docs/test_quality.md](docs/test_quality.md)
for the full guide, including the 5 pyramid scoping rules, the 3 + 4
duplicate signals/rescues, and the 22-step triage ladder.

## Witness Rules

`axm-audit` ships a witness rule for use with the `axm.witnesses` entry point group:

| Rule | Entry point key | Default categories |
|---|---|---|
| `AuditQualityRule` | `audit_quality` | `lint`, `type` |

`AuditQualityRule` runs `audit_project` for each configured category independently (a lint failure does not prevent type checking) and returns structured agent-friendly feedback via `format_agent`, with a compact `text` summary via `format_agent_text`.

## Hooks

`axm-audit` ships hooks for use with the `axm.hooks` entry point group:

| Hook | Entry point key | Description |
|---|---|---|
| `AutofixHook` | `audit:autofix` | Run `ruff check --fix` + `ruff format` |
| `QualityCheckHook` | `audit:quality-check` | Run audit categories and report violations |

`QualityCheckHook` accepts `working_dir` (str) and `categories` (list, default `["lint", "type"]`) via params. It returns `HookResult.ok(has_violations=bool, violations=list[dict], summary=str)` for injection into protocol session context. Each violation dict includes a `snippet` field with ±5 lines of source around the violation line (line-numbered, with `>` marker), or `None` when the file/line is unresolvable. When `working_dir` points at a multi-package workspace (`packages/*/src/`), each package is audited independently and violations are reported per-package.

## Development

This package is part of the [**axm-forge**](https://github.com/axm-protocols/axm-forge) workspace.

```bash
git clone https://github.com/axm-protocols/axm-forge.git
cd axm-forge
uv sync --all-groups
uv run --package axm-audit --directory packages/axm-audit pytest -x -q
```

## License

Apache-2.0 — © 2026 axm-protocols
