Metrics
Every number on the dashboard comes from your repo’s git history and its code, measured in your CI. Nothing is estimated by a model, and nothing about AI is guessed.
- Code health
- How hard the code is to maintain, per 1,000 linesCognitive complexityDuplicationHotspots
- AI signals
- Which commits and PRs agents wrote, from the records the tools leaveCoverageWhen agents took over
- PRs
- PRs with AI signals against the rest, over 6 monthsSizeComplexity addedFixed within 30 daysReview
- What it points out
- Where the numbers say to look, and what to tryWhere to look firstWhat to tryWorst changesJumps
Snapshots and trends
The first run measures the repo at one commit a week for the last 12 months. After that, every push to the default branch adds a snapshot of the latest commit. coderot keeps one a day for the last 90 days and about one a week before that.
Trends are per 1,000 lines of code, so a growing codebase doesn’t look worse just for growing. Test files are measured but kept apart, so these numbers are about code (what counts as a test). Duplication is measured on the latest commit and once a month.
Commits, here and everywhere on the dashboard, are the default branch’s own: a merged PR counts once, however it was merged.
Code health
Cognitive complexity
The headline metric: how hard code is to read. A file’s complexity is the sum over its functions, and the repo’s is the sum over its code files.
It follows SonarSource’s public specification and adds 1 for each break in the straight flow of code: if, else if, else, ?:, switch, loops, catch, jumps to a label, each run of like && / || operators, and recursion. Nested structures pay 1 more for each level they’re nested in. ?., ??, try and finally are free.
Two languages score differently by design, so compare a Go or Rust repo with repos in its own language:
- Go:
if err != nilis aniflike any other. Go checks errors after most calls, so its code scores higher per line than other languages’. - Rust: the other way round.
?returns an error early, as athrowdoes, and the specification counts neither, so the error handling that costs Go 1 per call is free in Rust. Amatchpays once, as aswitchdoes, andlet … elsepays as theifit stands for.
Checked against other tools on open-source repos, counting the functions both measure:
| Language | Checked against | Repos | Same score |
|---|---|---|---|
| TypeScript, JavaScript | Sonar’s analyzers | 11 | 96% |
| C# | Sonar’s analyzers | 11 | 99% |
| Python | complexipy | 8 | 98% |
| Go | gocognit | 8 | 99.9% |
| Rust | rust-code-analysis | 8 | 93% |
The rest:
- Sonar: places where Sonar departs from its own specification.
- complexipy: places it doesn’t look, such as keyword arguments and f-strings.
- gocognit: recursion through other functions, which gocognit counts and coderot doesn’t.
- rust-code-analysis: places it reads differently, such as
loopand let chains, which it doesn’t count.
Cyclomatic complexity
How many paths run through a function, shown per function on a file’s page. Cognitive complexity weighs how hard those paths are to follow; this one only counts them.
It’s 1 plus each decision point: if, else if, case, loops, catch, &&, ||, ?? and ?:. Also:
- C#:
when, andandandorpatterns. - Python:
elif,except,and,or, conditional expressions, and eachforandifin a comprehension. - Rust:
?,loop,let … else, match guards, and each match arm but_.
Duplication
Copies of code within the repo, shown as duplicated lines per 1,000 lines of code, code files only.
A copy is the same run of at least 50 tokens and 5 lines in two or more places. Comments don’t count, so a copy with a comment of its own still matches. C# using lists are left out.
Hotspots
Complex code that keeps changing, where maintenance costs the most. A hotspot is a code file changed by 10 or more commits in the last 90 days, with a cognitive complexity of 100 or more. The hotspots table ranks files by how often they changed times how complex they are.
Nesting, parameters and length
Per function: its deepest stack of if, switch, try and loops, its parameters, and its lines of code (a nested function’s lines count toward its parent too).
What counts as a line and a function
Lines of code are non-blank lines that aren’t only a comment. A line with code and a comment after it counts.
Functions are methods, constructors, accessors, operators and local functions. Members without a body aren’t functions: interface and abstract members, and TypeScript overload signatures. A lambda counts toward the function it’s in, unless that function has no control flow of its own: then the lambda is measured as its own function.
By language:
- C#: lines in inactive
#ifbranches don’t count. - Python: docstrings don’t count as lines: they’re comments in all but syntax. A function whose body is only
...isn’t a function. - Go: a method is named after its type, as
Tree.Insert. Functions implemented in assembly aren’t functions. - Rust:
- Doc comments are comments.
- A method is named after its type, as
Tree.insert. In a trait’simplit’s<Tree as Display>.fmtor<Error as From<io::Error>>.from, as Rust writes it, and a trait’s default method isShape.describe. - Macros are read as written, not expanded: code inside a macro call (
tokio::select! { … }) counts as lines but not as complexity, and functions amacro_rules!writes aren’t seen. - Unit tests in a code file (an item under
#[cfg(test)]and a#[test]function) are tests: they don’t count toward the file’s lines or functions, and they aren’t measured anywhere, since only files are split into code and tests.
Which files and syntax versions each language covers is on Languages.
AI signals
coderot counts a commit as AI when the tools left a record of it, and never guesses from how the code looks. The records, most precise first:
- Git AI notes: with Git AI installed, the exact lines each agent wrote (configuration).
- Co-author trailers:
Co-Authored-By: Claude <[email protected]>and the ones Copilot, Cursor, Codex, Devin, Gemini and others add. - Made-with and Assisted-by trailers: Cursor’s
Made-with: Cursor, Trae’sMade-with: Trae/GPT-6,Assisted-by: Claude:claude-opus-5as Fedora, LLVM and, until October 2026, the Linux kernel ask, andGenerated-by: OpenAI Codex. The tool’s name counts, never the model:Cursor:claude-opus-5is Cursor’s.Generated-byandMade-withcount only for a known agent, as code generators useGenerated-bytoo. - Bot authors: commits by an agent’s account, such as
copilot-swe-agent[bot]. - Branch prefixes: PRs from branches such as
claude/,copilot/orcursor/. - Your own agents in
.coderot.yml(configuration).
What each tool adds by default, and the setting that turns it off: Should you keep “Co-authored-by: Claude”?
A PR counts as AI when one of its commits has a signal, and as its first agent’s. Without Git AI, all of its lines count as AI, which overstates it.
Attribution coverage
No signal doesn’t mean human. Many agents leave no trace unless asked to, so a PR without a signal is unknown. That’s why attribution coverage, the share of the last 3 months’ merged PRs with an AI signal or Git AI notes, stands next to every AI number. Below 50%, the dashboard says how much is unknown and holds back the suggestions that rest on it.
When agents took over
The month the share of merged PRs with an AI signal passed 50% and stayed above it. It’s the marker on every chart. Where signals are too few to tell, you can set the day your team started with agents yourself.
PRs
These cover PRs merged in the last 6 months, PRs with AI signals against the rest. A share needs at least 10 PRs in a group.
- Size: lines added and deleted, without generated and vendored files or lockfiles. The median shows the typical PR, and the share over 400 lines, where reviews commonly lose their grip, shows the big ones.
- Complexity added per 100 lines: each code file’s rise in complexity between two snapshots is split over the PRs that changed it in between, by lines added. Per 100 lines of code a group’s PRs changed, it shows which kind of PR leaves the code more complex.
- Fixed within 30 days: a PR whose code lines a fix changed or deleted within 30 days. A fix is a PR, or a commit pushed without one, whose title says fix, bug, hotfix or revert. The run traces each fix’s lines back to the PR that added them with
git blame, in your CI. Counted over the PRs merged at least 30 days ago, so each had its full 30 days. - No other human review: merged without a review from anyone but its author.
- Approved, no human comment: approved, with no comment from a person other than its author, inline or in the conversation. Bots’ comments, Copilot’s reviewer among them, don’t count.
- Median time to merge: from when a PR was ready for review (opened, if it was never a draft) to its merge. Time as a draft doesn’t count.
The last two are facts, not verdicts: a PR talked over in a call or written in a pair leaves no comments. A PR merged by rebase has no PR boundary in git, so it’s left out.
What drove it splits the same PRs three ways: by who wrote them (each agent, and people or unknown), by size, and by whether they were fixed later.
What the dashboard points out
Where to look first
Up to 3 folders whose complexity rose beyond their own rate, from the month agents took over (or over the whole year without one) to now.
- Qualifies: its complexity rose, and its complexity per 1,000 lines rose 5% or more.
- Ranked: by the complexity added beyond what the new lines would have brought at the folder’s old rate, so the folders that just grew most don’t win by size alone.
- Named file: when one file holds at least half of a folder’s rise, and wasn’t copied or moved there.
- Folders: the repo split, biggest first, until none holds more than a sixth of the code, keeping the 15 largest.
Each card ends with one thing to consider, never a verdict.
What to try
Suggestions for how the team works with agents, each with its numbers:
- A size limit: when PRs over 400 lines added at least 1.2 times the complexity per 100 lines that smaller ones did, with 30 PRs or more on each side.
- A second reviewer for an agent’s PRs: the same test, one agent’s PRs against those without AI signals.
- A line in
AGENTS.mdorCLAUDE.md: for files that agents keep growing, changed by 5 or more PRs with AI signals, half or more of the PRs that changed them.
The ones that rest on AI signals show only from 50% coverage. Mark the day you try one with We tried this, and it shows as a line on the charts.
Worst changes this month
The 10 code files whose complexity rose most between the snapshot 30 days ago and now, each with the PRs that changed them since and their agents. A new file counts with all its complexity, unless git sees it as renamed, it’s mostly a copy of an existing file, or its code moved from a file that shrank in the same PRs: then it’s labeled so.
What drove the jumps
The 3 biggest rises in complexity per 1,000 lines between two snapshots in a row, and the 2 biggest in duplication between two monthly ones, each at least 1%. Each names the 3 files that rose most and the PRs that changed them. Deleting simple code also raises a ratio, so each jump shows the raw change and lines deleted next to it.