Metadata-Version: 2.5
Name: axm-audit
Version: 0.12.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: axm-echo
Requires-Dist: axm-ingot
Requires-Dist: complexipy==6.2.0
Requires-Dist: cyclopts>=4.21.0
Requires-Dist: libcst>=1.5.0
Requires-Dist: pydantic>=2.12.5
Requires-Dist: pytest-json-report>=1.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 is available through the unified **`axm` CLI**, the **Python API**, and **MCP** 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 + cognitive complexity (radon + complexipy)
- 🛡️ **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 .

# Filter by category
axm 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 .

# Validate the documentation build
axm doc_gate .
```

### 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`, `EnvCredentialsRule`, `ToolSecretLocationRule` | 7 |
| `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` 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 six rules (private-imports,
pyramid-level, duplicate-tests, tautology, file-naming, no-package-symbol)
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`.

**Params:**

| Param | Type | Purpose |
|---|---|---|
| `categories` | `list[str]` | Categories to audit (default `["lint", "type"]`). Must be a subset of the auditor's valid categories — see below. |
| `working_dir` | `str` | Project root to audit (overridable per-call via a `working_dir` kwarg). |
| `scope` | `str` | Reserved sub-scope selector. |
| `exclude_rules` | `list[str]` | Rule-id **prefixes** to drop from the failure list (e.g. `["QUALITY_DIFF_SIZE"]`). |
| `extra_dirs` | `list[str]` | Extra directories to audit with the same categories (e.g. blast-radius packages). |
| `guidance` | `str \| None` | Extra instructions appended to the failure `how` message. |

**Valid categories = the auditor's, not a private subset.** The witness accepts exactly the categories `audit_project` knows how to run (`architecture`, `complexity`, `deps`, `lint`, `practices`, `security`, `structure`, `test_quality`, `testing`, `tooling`, `type`). A category outside that set is a **hard config error**: the gate returns `WitnessResult.failure(...)` (RED) rather than silently skipping it. An **empty** `categories` list is likewise RED. This is deliberate: a quality gate must never pass green having audited nothing — a mis-configured gate that swallowed unknown categories used to do exactly that.


## 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
