SonarQube Automation with AI Agents: From a Debt Number Nobody Owns to a Ranked List of What to Fix
The payments-api gate goes red on Tuesday afternoon. The PR has 312 new issues against it, the coverage condition failed, and the author is sure none of it is theirs. Someone opens the issues tab, scrolls through forty naming-convention smells, finds a null dereference somewhere around issue 60, decides it's probably unreachable, and merges. The debt number on the quarterly slide ticks up again. Nobody connects the two.
There's a difference between a number called "technical debt" and a ranked list of the ten issues actually costing you. The number (340 days, a 12% debt ratio, whatever your dashboard says this quarter) is something everyone glances at and nobody acts on. The list is a decision: these ten, in this order, this sprint, because of this commit and this blast radius. This post is about the machinery that turns the first into the second, continuously, across every project, so the gate stops being noise you learned to override.
Part one mapped the quality-theater patterns: gates that block releases for issues nobody triages, new-code definitions that make the gate judge legacy debt, debt reported quarterly but never trended, gamed coverage thresholds, and security hotspots rotting unreviewed. Part two built the native toolkit: Clean as You Code gates, the new-code period, pipelines that wait for the gate, and the Web API (measures/search_history, issues/search, hotspots/search) for trends and cross-project views.
Part two ended at a ceiling. The gate blocks or passes, portfolios roll debt up, and the API tells you a project has 312 new issues. Nothing in the native stack decides which of those 312 are real bugs, style noise or false-positive candidates, or which project's debt is drifting badly enough to earn a sprint. That triage is the work, and it lands on whoever owns the pipeline. CloudThinker sits on top of SonarQube and does that triage first: it watches, ranks and proposes, and your approval gate stays where it is.
Why rules and thresholds can't do the triage
The obvious move is more configuration: tighten the quality profile, deactivate the noisy rules, add exclusions for generated code. Do that, it helps. It also stops short for structural reasons.
A rule fires on a pattern, not on reachability. java:S2259 (null pointers should not be dereferenced) flags the same shape whether the path is reachable from a public endpoint or only from a test helper. Severity is a property of the rule. Priority is a property of where the code sits and who calls it, and the gate can't see that.
Profiles are global, false positives are local. A rule that fights your DI framework in one service is valuable in the next. Deactivating it in the shared profile loses real findings. Leaving it on generates the same noise on every PR.
Thresholds count, they don't weigh. "Coverage on new code below 80%" fails a PR that skipped tests on a config loader exactly as hard as one that skipped tests on the payment retry path.
History lives in another endpoint. Whether a failure is new code or a misconfigured new-code period, whether a project's debt ratio is flat or climbing, sits in search_history and new_code_periods, not in the gate result. Someone has to go and read it, every time.
Each of those is a judgment call. Configuration can make the pile smaller. It can't sort it.
Connecting: a read-only user token, about five minutes
01Code quality
SonarQube
- Gates and issuesqualitygates/project_status, issues/search
- Debt historymeasures/search_history
- Hotspotshotspots/search
Browse permission, read-only token
02Triage
CloudThinker
- Classify new issuesReal bug, style noise, false-positive candidate
- Trend every projectSlope of the debt ratio, not snapshots
- Propose the workRanked lists with the evidence attached
No plugin installed
03Where the team works
Repo, tracker, chat
GitHub or GitLabThe commit and PR behind a failed gate
- JiraDebt-sprint candidates as tickets
Slack or TeamsFindings and approvals in the thread
Nothing new to watch
The connection is deliberately boring. Create a dedicated SonarQube user with Browse permission on the projects you want covered (or put it in a group that has it), generate a token for that user under My Account → Security → Generate Tokens, and paste the token and your SonarQube URL into CloudThinker. No plugin to install, no database access, no admin rights, no write permissions at connection time.
Browse is enough for everything CloudThinker does by default: reading gate status per project, walking the issues list with all its filters, pulling debt and coverage history from measures/search_history, and reading hotspot review status. The SonarQube connection guide lists the exact permission set, so your security review can see what is granted and what isn't. Write access comes later, per project, and only if you choose it: transitioning issues needs SonarQube's Administer Issues permission, and reviewing hotspots needs Administer Security Hotspots. It works with SonarQube Community Build and the Developer and Enterprise editions, and with SonarQube Cloud (formerly SonarCloud) through the same token flow.
One failed gate, end to end
01Signal
Gate fails
A new analysis lands with the gate in ERROR, or new-issue inflow spikes.
02Context
Read history
Failing conditions, new-code period, the issues behind them, the debt trend.
03Judgment
Classify
Each issue sorted into real bug, style noise or false-positive candidate.
04Cause
Correlate
The commit and pull request that introduced the failing code.
05Output
Ranked list
What to fix first, what to batch, what a person should confirm once.
Back to Tuesday's red gate on payments-api. The gate result says two conditions failed: new-code coverage at 61% against 80%, and new-code reliability rating C. Here's how CloudThinker works it (the project, PR and counts are illustrative).
1. Is this really new code? Three readings are possible. The PR genuinely added untested code. The new-code period is misconfigured and the gate is grading old code. Or a refactor moved old code around and SonarQube now counts it as new. Each predicts something different: a real gap shows uncovered lines concentrated in files the PR touched, a misconfigured period shows a SPECIFIC_ANALYSIS or stale reference branch, and a move shows high new-code line counts with little net change. The first check is cheap:
curl -s -u "$SONAR_TOKEN:" \
"$SONAR_HOST/api/new_code_periods/show?project=payments-api&branch=main" | jq '{type, value}'
It comes back REFERENCE_BRANCH / main. The period is fine, so the failure is about what the branch adds.
2. What does the reliability rating hide? A C rating means at least one major bug in new code. Pull the bugs on the PR, not the whole project:
curl -s -u "$SONAR_TOKEN:" \
"$SONAR_HOST/api/issues/search?componentKeys=payments-api&pullRequest=482&types=BUG&resolved=false&ps=100" \
| jq -r '.issues[] | "\(.severity)\t\(.rule)\t\(.component):\(.line)\t\(.message)"'
Three bugs. Two are java:S2259 null dereferences in a test fixture builder. One is the same rule in RetryPolicy.java, on the path a failed card authorization takes. Same rule, same severity, very different blast radius.
3. Where are the 312 issues coming from? Group before reading:
curl -s -u "$SONAR_TOKEN:" \
"$SONAR_HOST/api/issues/search?componentKeys=payments-api&pullRequest=482&resolved=false&facets=rules&ps=1" \
| jq '.facets[0].values[:5]'
Most of the volume is four maintainability rules, and 47 issues sit under a generated/ directory that should never have been analyzed.
4. Which commit, and is the debt trend related? CloudThinker reads the PR and its commits from GitHub or GitLab, and the project's sqale_debt_ratio history. The uncovered lines all come from one commit that added the retry path. The project's debt ratio has been flat for six months, so this is a one-PR problem, not drift.
5. The finding. Instead of a red badge, the PR thread gets: the gate failed on real new code, one bug that matters (the null path in RetryPolicy.java, with the line and why it's reachable), two bugs in test fixtures that can wait, 47 issues on generated code that need an analysis-scope exclusion rather than fixes, and the coverage gap concentrated in the retry class. Proposed: fix the one bug and cover the retry path in this PR, add **/generated/** to sonar.exclusions in a separate change. Nothing is transitioned or excluded until someone approves it.
The difference from the part-two setup is the branching. The API returned the same data it always did. What changed is that something read it in the order an experienced reviewer would, and wrote down why.
What CloudThinker does with your quality data
That walkthrough runs on every gate result, and three slower passes run on a schedule across every project.
Triage into three buckets. Every new issue gets classified before anyone is asked to care: real bug (a defect with plausible runtime impact, judged by where it sits and what reaches it), style noise (technically correct, practically irrelevant this sprint), or false-positive candidate (likely wrong for your code, with the reasoning attached, so a person confirms it once instead of the team ignoring it forever).
Debt trending by slope. For every project, CloudThinker pulls debt ratio, coverage and duplication history and looks at direction, not snapshots. A project at 9% that's been flat for a year is a different object from one that crossed 6% two months ago and is climbing. Projects get ranked by trajectory, so "which repo needs a debt sprint" gets an evidence-backed answer instead of the loudest team's opinion.
Proposals sized to the evidence. A ranked triage list with real bugs first. Gate or new-code-period changes where a gate is failing on legacy debt, with the history that justifies them. Debt-sprint candidates with the specific hotspots and duplicated blocks that would move the number most. A prioritized hotspot review queue, reachable code first.
Nothing changes that you haven't allowed
CloudThinker acts within Auto Mode, so you decide what runs on its own and what waits for approval.
Every action lands in an evidence trail: the gate result or issue set, the history read, the classification, the proposal, who approved it, what changed in SonarQube and when. A "Won't Fix" stops being an untraceable click in the UI and becomes a reviewable record with its reasoning attached.
What a first pass usually finds
The numbers below are illustrative, a composite of what a first analysis tends to surface across a mid-market estate of around 40 projects with a few years of accumulated debt. Yours will differ.
| Finding | Detail | Proposed action |
|---|---|---|
| Undifferentiated new issues | 312 new issues across 40 projects; ~14% look like real bugs | Ranked triage list, real bugs first |
| Likely false positives | 47 issues on generated and vendored code, same 3 rules | Analysis-scope exclusion, or one batch confirmation |
| Misconfigured new-code period | 4 gates failing on pre-existing debt, not new code | Revised new-code definition per project |
| Drifting debt | 3 projects crossed a 6% debt ratio and are climbing over 60 days | Debt-sprint candidates with top hotspots each |
| Rotting security hotspots | 128 hotspots in To Review for 90+ days across 9 projects | Prioritized review queue, reachable code first |
| Gamed coverage threshold | 2 projects sitting at 80.1% with assertion-free tests | Flag for human review; not an auto-fix |
Most of the volume is noise and misconfiguration, the quality-theater debt from part one, and the bugs that matter are a thin slice buried inside it. Surfacing that slice, ranked, is the point. The table refreshes on every analysis, and each row arrives with a proposed action that waits for approval.
Why this beats the traditional approach
| Native gate and Web API | With CloudThinker on top | |
|---|---|---|
| A red gate | A badge and a list of conditions | The cause, the commit, and which issues matter |
| 300 new issues | Sorted by rule severity | Sorted by real impact, noise batched |
| False positives | Ignored forever, or a profile change that loses real findings | Flagged per codebase, confirmed once |
| Debt | A quarterly number | A per-project slope with an owner and a candidate sprint |
| Hotspots | Old ones invisible to the gate | A review queue, reachable code first |
| Changes | Clicks in the UI, no reasoning kept | Approved proposals with an evidence trail |
The deeper difference is where the judgment lives. In the native setup it lives in whoever happens to open the issues tab, and it leaves when they do. With an agent reading the same API, the judgment is written down, applied to every analysis, and checkable by anyone on the team.
Getting started
Three steps. None of them needs write access.
1. Create the read-only user and token. Browse permission on the projects you want covered, then follow the SonarQube connection guide.
2. Ask about one project, read-only. Nothing changes in SonarQube. From chat:
"The payments-api gate is failing. Show me which commit and PR caused it, and whether it's new code or a new-code-period misconfiguration. Propose only, change nothing."
3. Widen to the portfolio.
"Triage every new issue across all projects from the last two weeks. Classify each as real bug, style noise, or false-positive candidate, and give me the real bugs ranked by blast radius."
"Which projects have a debt ratio above 6% that's trending up over the last 90 days? Rank them and show the top three hotspots in each."
Each answer cites the underlying SonarQube data (the same issues/search, measures/search_history and hotspots/search results you pulled by hand in part two), so you can verify it rather than trust it.
What the agents won't do
"AI with API access to your code quality data" should raise questions. Here are the answers:
- Read-only by default. The token carries Browse only. Transitioning an issue, reviewing a hotspot or editing a gate needs you to grant the SonarQube permission and every write goes through Auto Mode.
- No silent "Won't Fix" sweeps. Every proposed transition carries its reasoning and lands in the evidence trail. Clearing the backlog to make a dashboard green is the gaming pattern this exists to unwind.
- No invisible actions. Every proposal and change is in the trail with its justification and approver.
- No overriding your gate. The quality gate stays the gate. CloudThinker decides which of its findings matter and lines up the work. It doesn't pass a failing gate or block a passing one.
Related reading
- Jenkins build triage with an AI agent
- Connect a repo, get a first PR review
- Auto Mode
- CloudThinker Connections
From a number to a queue
You know which SonarQube signals carry real risk, and how far native gates, webhooks and the Web API take you. What's left is the judgment in the middle: which 40 of 312 issues are worth a developer's afternoon, and which project's debt earns the sprint. Teams that move from a quarterly debt number to continuous, approval-gated triage typically cut the noise developers wade through by well over half within the first months, and that's what makes the gate credible enough to stop overriding.
Try CloudThinker free with 100 premium credits and no card required. Connect SonarQube read-only and see your own first-pass triage table within the hour.
