# Quality Automation Guide This document describes the automated quality gates, CI pipeline, and developer tooling for the CleverAgents project. ## Quick Start ```bash # Set up development environment (installs dependencies + pre-commit hooks) bash scripts/setup-dev.sh # Run all quality checks nox # Run specific checks nox -s lint # Ruff linting nox -s format # Ruff formatting nox -s typecheck # Pyright type checking nox -s unit_tests # Behave unit tests nox -s coverage_report # Coverage (must be >=97%) nox -s security_scan # Bandit + Semgrep + Vulture security scanning nox -s dead_code # Vulture dead code detection nox -s complexity # Radon complexity analysis nox -s pre_commit # Run all pre-commit hooks ``` ## Pre-commit Hooks Pre-commit hooks run automatically on every `git commit`. They are configured in `.pre-commit-config.yaml`. ### Installed Hooks | Hook | Tool | Purpose | Auto-fix | | ------------------------- | ---------------- | ---------------------------------------- | -------- | | `no-commit-to-branch` | pre-commit-hooks | Prevents commits to `main` | No | | `check-yaml` | pre-commit-hooks | Validates YAML syntax | No | | `check-toml` | pre-commit-hooks | Validates TOML syntax | No | | `check-json` | pre-commit-hooks | Validates JSON syntax | No | | `check-merge-conflict` | pre-commit-hooks | Detects merge conflict markers | No | | `check-added-large-files` | pre-commit-hooks | Prevents files >500KB | No | | `end-of-file-fixer` | pre-commit-hooks | Ensures files end with newline | Yes | | `trailing-whitespace` | pre-commit-hooks | Removes trailing whitespace | Yes | | `debug-statements` | pre-commit-hooks | Detects `pdb`/`breakpoint` | No | | `ruff-format` | ruff | Code formatting | Yes | | `ruff` | ruff | Linting with safe auto-fixes | Yes | | `bandit` | bandit | Security scanning | No | | `vulture` | vulture | Dead code detection | No | | `pyright` | pyright | Type checking (src/ only) | No | | `semgrep-eval-exec` | semgrep | eval/exec detection (optional) | No | | `commitizen` | commitizen | Commit message format (commit-msg stage) | No | ### Running Hooks Manually ```bash # Run all hooks on all files pre-commit run --all-files # Run a specific hook pre-commit run ruff-format --all-files pre-commit run bandit --all-files pre-commit run pyright --all-files # Skip hooks for emergency (not recommended) git commit --no-verify -m "emergency: fix critical bug" ``` ## CI Pipeline The CI pipeline runs on **Forgejo Actions** (`.forgejo/workflows/ci.yml`). ### CI Jobs | Job | Trigger | Purpose | Failure Impact | | ----------- | ------- | ----------------------------------- | ---------------------- | | `lint` | Push/PR | Ruff format + lint check | Blocks merge | | `typecheck` | Push/PR | Pyright type checking | Blocks merge | | `security` | Push/PR | Bandit + Semgrep + Vulture | Blocks merge | | `quality` | Push/PR | Radon complexity check | Blocks merge (grade F) | | `unit_tests`| Push/PR | Behave BDD tests (Python 3.13) | Blocks merge | | `coverage` | Push/PR | Coverage measurement | Blocks merge (<97%) | | `build` | Push/PR | Wheel build | Blocks release | | `docker` | Push/PR | Docker image build + test | Blocks deployment | | `helm` | Push/PR | Helm chart lint + template | Blocks deployment | ### Nightly Quality A nightly workflow (`.forgejo/workflows/nightly-quality.yml`) runs on all branches at midnight UTC: - Full lint, type check, security scan - Complete Behave test suite with coverage - Complexity and maintainability analysis - Quality trend data generation - Reports uploaded as artifacts (90-day retention) ## Security Scanning ### Bandit Configuration Configured in `pyproject.toml` under `[tool.bandit]`: - Targets: `src/cleveragents` only - Severity threshold: MEDIUM (pre-commit), HIGH (CI fail gate) - Excludes: tests, features, build directories ### Semgrep Rules Custom rules in `.semgrep.yml`: - `no-eval`: Blocks `eval()` usage - `no-exec`: Blocks `exec()` usage - `no-compile-exec`: Blocks `compile()` with exec mode - `no-os-system`: Warns on `os.system()` (use `subprocess.run`) - `no-pickle-loads`: Warns on `pickle.loads()` (use JSON) **Note:** Semgrep is a project dependency (included in `[project.optional-dependencies.dev]`). It runs automatically in the `nox -s security_scan` session and as a pre-commit hook. ## Dead Code Detection Vulture checks `src/cleveragents/` for unused code with 80% confidence threshold. ### False Positive Whitelist Known false positives are listed in `vulture_whitelist.py`: - `exc_tb`: Required by `__exit__` protocol but not used in body - `build_data`: Required by mapping interface in legacy migrator Add new entries when vulture flags intentionally unused code. ## Complexity Monitoring Radon measures cyclomatic complexity: | Grade | Complexity | Status | | ----- | ---------- | --------------------------------- | | A | 1-5 | Excellent | | B | 6-10 | Good | | C | 11-15 | Moderate - review recommended | | D | 16-20 | Complex - refactoring recommended | | E | 21-30 | Very complex - must refactor | | F | 31+ | Extremely complex - CI fails | ## Quality Gate Script `scripts/check-quality-gates.py` aggregates all quality checks: ```bash python scripts/check-quality-gates.py --coverage-min 97 --complexity-max F ``` Gates checked: 1. Coverage >= 97% 2. Pyright: 0 type errors 3. Bandit: 0 high-severity security issues 4. Vulture: 0 dead code findings 5. Radon: No grade-F complexity blocks ## Troubleshooting ### Pre-commit hooks not running ```bash pre-commit install pre-commit install --hook-type commit-msg ``` ### Pyright errors on clean code Check `pyrightconfig.json` for configuration. Pyright uses `strict` mode. ### Bandit false positive If bandit flags intentionally safe code, consider if there's a safer alternative first. Only as a last resort, add to `[tool.bandit]` `skips` in `pyproject.toml`. ### Vulture false positive Add the symbol name to `vulture_whitelist.py` with a comment explaining why. ### Coverage below 97% Run `nox -s coverage_report` to see which lines are uncovered, then add Behave scenarios. Coverage must be >=97% for CI to pass. The threshold is defined by `COVERAGE_THRESHOLD` in `noxfile.py`.