Interpret Results
AuditResult Object
Every audit returns an AuditResult with these properties:
| Property | Type | Description |
|---|---|---|
project_path |
str \| None |
Path of the audited project |
checks |
list[CheckResult] |
Individual check results |
success |
bool |
True if all checks passed |
total |
int |
Total number of checks |
failed |
int |
Number of failed checks |
quality_score |
float \| None |
Composite score 0–100 (9-category weighted). None when no numeric scored category exists |
grade |
str \| None |
Letter grade A–F. None when quality_score is None |
CheckResult Object
Each individual check returns:
| Property | Type | Description |
|---|---|---|
rule_id |
str |
Unique identifier (QUALITY_LINT, DEPS_AUDIT, etc.) |
passed |
bool |
Whether the check passed |
message |
str |
Human-readable description |
severity |
Severity |
error, warning, or info |
details |
dict \| None |
Tool-specific structured data (counts, lists) |
text |
str \| None |
Pre-rendered detail text for display (bullet lines) |
fix_hint |
str \| None |
Suggested fix |
score |
int \| None |
Numeric score in [0, 100] for scored categories |
Formatters
Human-Readable Report
JSON Output
from axm_audit.formatters import format_json
import json
data = format_json(result)
print(json.dumps(data, indent=2))
# format_json raises ScoreIncalculableError when result has no numeric score.
Agent Output
format_agent minimizes tokens for AI agent consumption. Passed checks are compact strings — unless they carry actionable detail (e.g. missing docstrings, complexity top offenders), in which case they become dicts. For failed checks, both text and details are retained when present. Non-empty rule-specific metadata is also retained:
For a compact text representation optimised for token count, pass the
agent dict through format_agent_text:
from axm_audit.formatters import format_agent_text
text = format_agent_text(data)
print(text)
# Illustrative multi-category output: audit | B 85 | 3 pass · 1 fail
# ✓ QUALITY_LINT QUALITY_TYPES QUALITY_SECURITY
# ✗ QUALITY_COMPLEXITY 3 functions exceed CC threshold
# src/mod.py:10 func_a CC=15
# fix: Extract helpers to reduce CC
Example agent dict structure:
{
"score": 85.0,
"grade": "B",
"passed": [
"QUALITY_LINT: Lint score: 100/100 (0 issues)",
{
"rule_id": "PRACTICE_DOCSTRING",
"message": "Docstring coverage: 95% (19/20)",
"details": {"coverage": 0.95, "missing": ["module.py:func"]},
"fix_hint": "Add docstrings to public functions"
}
],
"failed": [
{
"rule_id": "QUALITY_TYPE",
"message": "Type score: 70/100 (6 errors)",
"text": "\u2022 mod.py:10: Incompatible types in assignment",
"fix_hint": "Run: mypy src/"
}
]
}
Test Report Text
format_audit_test_text renders a TestReport as compact text for LLM consumption (~27 tokens green, ~240 tokens with failures):
from axm_audit.tools.audit_test_text import format_audit_test_text
from axm_audit.core.test_runner import TestReport
text = format_audit_test_text(report)
print(text)
# audit_test | ✅ 42 passed | 1.2s | cov 95.0%
The output includes:
| Section | When shown | Format |
|---|---|---|
| Header | Always | audit_test \| {icon} {counts} \| {duration}s [\| cov {pct}%] |
| Non-test cause | non_test_cause is set |
cause {code}: {summary} then the classifier's bounded excerpt |
| Failure blocks | failures is non-empty |
✗ {test} ({file}:{line}) + error + traceback |
| Coverage | Files below 95% exist | cov< {basename} {pct}% |
When coverage is None (targeted/files runs), the coverage section is omitted entirely.
A red that no test failure explains (coverage threshold, pytest usage error,
interrupted run) carries a non_test_cause. Its machine-readable code and its
one-line human summary are rendered immediately after the pytest exit N
header line, followed by the excerpt of the captured output backing the verdict:
audit_test | ❌ 1 passed · 1 collected | 2.1s | pytest exit 1
cause coverage_threshold: required test coverage of 100% not reached
FAIL Required test coverage of 100% not reached. Total coverage: 62.50%
That excerpt is the one the classifier already bounded — the renderer never truncates it a second time. When the report carries no cause the block is omitted entirely and the output is byte-for-byte what it was before.
Scoring
The quality_score is computed from 9 weighted categories:
| Category | Weight |
|---|---|
| Linting (Ruff) | 15% |
| Type Safety (mypy) | 15% |
| Complexity (radon) | 15% |
| Security (Bandit) | 10% |
| Dependencies (pip-audit + deptry) | 10% |
| Testing (pytest-cov) | 10% |
| Test Quality (AST analysis) | 10% |
| Architecture (AST analysis) | 10% |
| Practices (AST analysis) | 5% |
The structure and tooling categories emit findings but are not scored.
For details, see Scoring & Grades.
Severity Levels
| Severity | Effect | Example |
|---|---|---|
error |
Impact classification; inspect passed |
Missing pyproject.toml |
warning |
May accompany a failed check | High complexity function |
info |
Does not itself determine pass/fail | Docstring coverage stats |
CheckResult also carries category and a metadata dictionary.
AuditResult.crashed_rules lists rules that raised. A formatted detail list
may be a bounded summary; it is not always every raw tool diagnostic.
Tool success is independent of quality: inspect audit failed, test
verdict, or documentation count as described in the
tool reference.