ADR-014: Code Quality and Security Tooling¶
Status¶
- Proposed by: Claude on 2026-01-08
- Accepted on: 2026-01-08
Context¶
As holoconf grows with multiple language bindings (Rust core, Python, future Node.js/Go), we need consistent code quality and security standards across all languages. Without standardized tooling:
- Code style varies between contributors
- Security vulnerabilities may go undetected
- Unused dependencies accumulate
- License compliance is not verified
- Code complexity grows unchecked
Alternatives Considered¶
Alternative 1: Manual Code Review Only¶
- Pros: No tooling overhead, flexible
- Cons: Inconsistent, error-prone, doesn't scale, security issues missed
Alternative 2: Language-Specific Tools (No Standardization)¶
- Pros: Each language uses its best-of-breed tools
- Cons: Inconsistent CI configuration, harder to maintain, no unified quality gate
Alternative 3: Unified Quality Pipeline with Language-Specific Tools¶
- Pros: Best tools for each language, unified CI structure, comprehensive coverage
- Cons: More CI configuration, multiple tools to learn
Decision¶
Adopt Alternative 3: Unified quality pipeline with best-of-breed tools for each language.
Design¶
Language Version Requirements¶
| Language | Minimum Version | Defined In |
|---|---|---|
| Rust | 1.88+ | Cargo.toml (rust-version), rust-toolchain.toml |
| Python | 3.9+ | pyproject.toml (requires-python) |
To update Rust: rustup update stable
Rust Tooling¶
| Tool | Purpose | Configuration | When |
|---|---|---|---|
| rustfmt | Code formatting | rustfmt.toml |
CI |
| clippy | Linting, complexity | clippy.toml |
CI |
| cargo-deny | License & advisory audit | deny.toml |
CI |
| cargo-audit | CVE vulnerability scan | - | CI |
| cargo-machete | Unused dependency detection | - | CI |
| cargo-geiger | Audit unsafe code usage | - | Manual |
| cargo-semver-checks | Detect breaking API changes | - | Pre-release |
| cargo-cyclonedx | Generate SBOM (CycloneDX format) | - | Release |
Clippy Configuration (clippy.toml)¶
Cargo Deny Configuration (deny.toml)¶
- Deny known vulnerabilities
- Warn on unmaintained crates
- Allow only approved licenses (MIT, Apache-2.0, BSD, ISC, etc.)
- Warn on multiple versions of same crate
Python Tooling¶
| Tool | Purpose | Configuration | When |
|---|---|---|---|
| ruff | Linting + formatting | pyproject.toml |
CI |
| pip-audit | Dependency security scan | - | CI |
| cyclonedx-bom | Generate SBOM (CycloneDX format) | - | Release |
Ruff Configuration¶
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"S", # flake8-bandit (security)
"SIM", # flake8-simplify
"RUF", # Ruff-specific rules
]
CI Pipeline Structure¶
quality.yml
├── rust-lint (rustfmt, clippy)
├── rust-security (cargo-deny, cargo-audit)
├── rust-unused (cargo-machete)
├── python-lint (ruff check, ruff format)
├── python-security (pip-audit)
└── complexity (tokei - informational)
Local Development Commands¶
A Makefile provides unified commands across all languages:
make help # Show all available commands
make lint # Run all linters (Rust + Python)
make format # Format all code
make security # Run all security checks
make test # Run all tests
make check # Run everything (lint + security + test)
make audit-unsafe # Report unsafe code usage
make semver-check # Check for breaking API changes (pre-release)
make sbom # Generate SBOMs for all packages
Individual language commands are also available:
# Rust
make lint-rust # clippy + fmt check
make format-rust # cargo fmt
make security-rust # cargo-deny + cargo-audit
make test-rust # cargo test
# Python
make lint-python # ruff check + format check
make format-python # ruff format + fix
make security-python # pip-audit
make test-python # pytest
Or run tools directly:
# Rust
cargo fmt --all # Format
cargo clippy --all-targets # Lint
cargo deny check # License/security
cargo audit # CVE scan
# Python (from packages/python/holoconf/)
ruff check src/ tests/ # Lint
ruff format src/ tests/ # Format
Rationale¶
-
Rust-focused complexity checks - Most logic is in Rust, so clippy's cognitive complexity and function length limits are the primary complexity gates
-
Security at dependency level - Both cargo-audit/cargo-deny (Rust) and pip-audit (Python) catch CVEs in dependencies before they ship
-
Ruff over Black/flake8 - Ruff is significantly faster and combines formatting + linting in one tool
-
Separate CI jobs - Allows parallel execution and clear failure identification
-
CycloneDX for SBOMs - Widely adopted format (OWASP, GitHub), better tooling support than SPDX, more concise
SBOM Generation¶
Software Bill of Materials (SBOM) generation is available for supply chain transparency:
This generates CycloneDX-format SBOMs in the sbom/ directory:
| File | Contents |
|---|---|
holoconf-rust.cdx.json |
All Rust crate dependencies |
holoconf-python.cdx.json |
All Python package dependencies |
SBOMs should be generated and published with each release to provide downstream users visibility into the dependency tree.
Trade-offs Accepted¶
- More CI time in exchange for comprehensive quality gates
- Learning multiple tools in exchange for best-of-breed per language
- Stricter defaults in exchange for cleaner codebase (can allow specific lints when justified)
Migration¶
N/A - This ADR establishes tooling for an existing codebase. Initial cleanup was performed: - Fixed clippy warnings (PI constant in tests, wildcard patterns) - Fixed ruff issues (import sorting, deprecated typing imports)
Consequences¶
- Positive:
- Consistent code style across all contributors
- Security vulnerabilities caught before merge
- License compliance verified automatically
- Code complexity stays manageable
- Unused dependencies removed
-
SBOM generation enables supply chain transparency
-
Negative:
- CI runs take longer (~2-3 min additional)
- Contributors must install tools locally for pre-commit checks
-
Some false positives may require
#[allow(...)]annotations -
Neutral:
- Existing code required minor cleanup to pass new lints