ADR-0022: Cognitive Complexity Gate via Pre-commit and SonarQube
Status: Accepted Deciders: elcubonegro, Claude (Sonnet 4.6) Date: 2026-03-27
Context and Problem Statement
Cornerstone generates projects for DeAcero's legacy modernisation workflow. The primary risk when rewriting legacy SQL/COBOL/VB6 logic into Python is translating high-complexity legacy code into equally high-complexity modern code — reproducing the problem rather than solving it.
Cognitive Complexity (Sonar's metric, not cyclomatic) measures how hard a function is to understand: nested control flow, boolean operators in conditions, and recursive structures each add to the score in a human-readable way. A function with score > 15 is statistically correlated with defect introduction and reviewer fatigue.
Without an automated gate, complexity creep is invisible until it becomes a maintenance crisis.
Decision Drivers
- Legacy modernisation is the primary workflow. New code must not replicate legacy complexity.
- Two enforcement layers are better than one: catch it locally before CI pays the cost.
- SonarQube is already planned for the DeAcero toolchain (telemetry, observability).
- The gate must not block PRs for existing legacy code — only for new code introduced after adoption.
Considered Options
- Option A: Pre-commit only (
flake8-cognitive-complexity) — fast, local, no server dependency. - Option B: SonarQube Quality Gate only — catches it in CI, requires server.
- Option C: Both layers — pre-commit for developer feedback, SonarQube as the merge gate.
Decision Outcome
Chosen option: Option C — both layers.
| Layer | Tool | When | Scope | Blocks |
|---|---|---|---|---|
| Pre-commit | flake8-cognitive-complexity |
At every git commit |
All modified Python files | The local commit |
| CI | SonarQube Quality Gate | On every PR / push to main | New code only | The PR merge |
Why new code only in SonarQube?
DeAcero projects will contain legacy-ported code that may arrive with pre-existing complexity. Enforcing the gate on overall code would immediately block every PR that touches a legacy module. Enforcing on new code allows the team to port legacy logic, raise findings, and refactor incrementally — without being blocked on every commit.
The pre-commit hook catches new violations immediately, before CI is even involved.
Threshold
Default: 15 (SonarQube's standard). This is a template variable (cognitive_complexity_threshold) in cookiecutter.json so teams can tighten it (10 for greenfield, 20 for aggressive legacy ports).
SonarQube job is opt-in
The CI job runs only if SONAR_TOKEN is set as a GitHub secret. If the secret is absent the job is skipped — allowing teams to adopt Cornerstone before provisioning a SonarQube instance.
Exclusions
Both layers exclude generated artefacts (consistent with AGENTS.md § 2b):
.venv/, venv/, __pycache__/, *.egg-info/, dist/, build/
Positive Consequences
- Complexity violations caught at commit time — zero CI wait.
- SonarQube provides trend dashboards, historical complexity reports, and per-file drill-down.
- Threshold is configurable per project via
cookiecutter.json. - Teams without SonarQube can still enforce locally via pre-commit.
Negative Consequences
- Teams need a SonarQube instance and
SONAR_TOKEN/SONAR_HOST_URLsecrets to unlock the CI gate. flake8added as a dev dependency alongsideruff(different tools: ruff does not enforce cognitive complexity).- Pre-commit hook adds ~1s per commit for Python files changed.
Implementation
| File | Change |
|---|---|
{{cookiecutter.project_slug}}/.pre-commit-config.yaml |
Add flake8 + flake8-cognitive-complexity hook |
{{cookiecutter.project_slug}}/.github/workflows/ci.yml |
Add sonarqube job after test |
{{cookiecutter.project_slug}}/sonar-project.properties |
New file — project config for sonar-scanner |
{{cookiecutter.project_slug}}/pyproject.toml |
Add flake8, flake8-cognitive-complexity to [dev] |
cookiecutter.json |
Add cognitive_complexity_threshold: 15 |
Links
- Related: ADR-0007 (OWASP CI/CD security posture)
- Related: ADR-0014 (atomic commit policy — same pre-commit layer)
- Related: ADR-0020 (agentic semver — same commit-msg layer)
- SonarQube Cognitive Complexity whitepaper: https://www.sonarsource.com/docs/CognitiveComplexity.pdf