Case studies · benchmarked, not asserted

Numbers we’ll stand behind, losses included.

18 benchmarks against real production codebases: blind adjudication by an independent judge, the same corpus for both tools, and a separate audit against detector tampering. 12 are wins, 3 are losses today, and 3 have no competitor to measure against yet. More than once a number we had already published went stale and was replaced with the worse, verified one. All of it is in the table, published as measured.

01 · BLIND JUDGE

An independent judge model rules each finding correct or wrong, not self-reported by either tool.

02 · SAME CODEBASE

One real, shared codebase for both tools per language, no cherry-picked test fixtures.

03 · AUDITED

A documented audit specifically checks that no detector was hollowed out or removed to flatter the number.

Head to head

18 benchmarks. 12 wins, 3 losses.

Wrong-match rate is the number that matters: of everything a tool reports, how much of it is factually wrong. Lower is better. Every row below went through independent blind adjudication, and every row names the real codebase it was measured on. Rows we lose are sorted into the same table rather than kept off it, and rows where the competitor was not re-run against the fresh pool say so on the row.

TypeScript / Reactvs Deno lint · next.js source
CONFIRMED · SAME SESSION
TRUEMEND
2.1%
DENO LINT
5.0%

Needed no remediation of its own. The TypeScript fixes from the Biome campaign applied to the whole TS surface, not one competitor’s ruleset, so they carried over. Worth-fixing came back tied, and we are not claiming an edge there.

Needed no remediation of its own.

read the write-up →

TypeScript / Reactvs Biome · next.js source
CONFIRMED · 12-JUDGE PANEL
TRUEMEND
3.0%
BIOME
8.4%

Started as a near-tie with us narrowly behind at 16.8%. Took three remediation rounds. A collector bug that had Biome scoring 0% correct was caught and fixed before publishing, not after.

Started as a near-tie with us narrowly behind at 16.8%.

read the write-up →

TypeScript / Reactvs ESLint · next.js source
CONFIRMED · JUDGED TWICE
TRUEMEND
3.5%
ESLINT
5.0%

An earlier round had us behind at 13.6%, which we published. ESLint’s raw number came in at 11.3%, but 60% of its wrong findings sat on vendored build output we skip, so we recomputed on the shared file scope instead of taking the win.

An earlier round had us behind at 13.6%, which we published.

read the write-up →

Pythonvs Ruff · real unmodified source
CONFIRMED · GOODHART AUDITED
TRUEMEND
4.5%
RUFF
6.1%

The first benchmark through full independent adjudication, against the fastest and most widely adopted Python linter there is. A separate audit checked that no detector had been hollowed out to flatter the number.

The first benchmark through full independent adjudication, against the fastest and most widely adopted Python linter there is.

read the write-up →

Rustvs Clippy · tokio, 376 files
CONFIRMED · TWO JUDGES
TRUEMEND
4.8%
CLIPPY
18.2%

Two independent adjudications, neither judge seeing the other. Clippy sampled only 11 findings, so read its number as small-sample variance. The honest comparison is our own arc: 9.3% to 4.8%.

Two independent adjudications, neither judge seeing the other.

read the write-up →

Govs golangci-lint · Gin source
CONFIRMED · RIVAL NOT RE-RUN
TRUEMEND
5.9%
GOLANGCI (ROUND 1)
21.4%

One Go/Gin campaign. Round 1 judged every finding on a production Gin API rather than sampling, 91 of 91: TrueMend 0.0% wrong-match against Semgrep’s 50.0%, the latter dominated by an 18-instance cluster where an HTML-escaping rule fired on Gin’s own render package without checking content type. Rounds 2 and 3 ran a second, harder corpus. Round 2 published a 40.4% loss, four times our own bar. Round 3 found the cause: an engine bug had the per-function scan path indexing matches against the wrong baseline, so every negative-context check on it suppressed nothing. That plus three fixes and five quarantines reached 5.9%, and 865 false positives left the corpus. golangci-lint’s 21.4% is still round 1, so this is a win on our own side of a stale comparison.

Round 1 judged every finding on a production Gin API rather than sampling, 91 of 91: TrueMend 0.0% wrong-match against Semgrep’s 50.0%, the latter dominated by an 18-instance cluster where an HTML-escaping rule fired on Gin’s own render package without checking content type.

read the write-up →

C# / .NETvs Roslynator · Polly
CONFIRMED · REGRESSION CAUGHT
TRUEMEND
9.1%
ROSLYNATOR
42.0%

The published 7.7% went stale: a same-repo reverify caught a real 12.5% regression, four Microsoft.Extensions.AI detectors shipped without the type gate their own neighbors already had. Fixed same day. 9.1% is a recompute on the same judged sample; a fresh blind campaign is still pending.

The published 7.7% went stale: a same-repo reverify caught a real 12.5% regression, four Microsoft.Extensions.AI detectors shipped without the type gate their own neighbors already had.

read the write-up →

JavaScript / Expressvs Semgrep · Habitica
CONFIRMED · CERTIFIED ROUND 3
TRUEMEND
9.9%
SEMGREP
62.7%

Certified on the full post-fix pool: 596 findings judged, 90.1% correct, by the same judge as every earlier round. A merged 9.98% went stale when an unrelated engine fix changed the finding pool underneath it, so we re-collected and re-proved the number rather than re-publish the old one.

Certified on the full post-fix pool: 596 findings judged, 90.1% correct, by the same judge as every earlier round.

read the write-up →

Python / FastAPIvs Semgrep · Netflix Dispatch
CONFIRMED · CERTIFIED ROUND 7
TRUEMEND
10.9%
SEMGREP
17.1%

Started as a loss and flipped twice on fresh certifications before rounds 6 and 7 closed it: a harness fix (function-aware adjudication windows), ten shared-detector fixes, and three quarantines. 10.9% is at our own <10% bar, not under it, and we say so. Same-judge trajectory: 21.9% → 12.9% → 10.9%; the round-5-to-6 delta includes a judge change, disclosed. Real-rate stays open: 27.7%, with Semgrep at 39.0% on the same judge.

Same-judge trajectory: 21.9% → 12.9% → 10.9%; the round-5-to-6 delta includes a judge change, disclosed.

read the write-up →

TypeScript / Reactvs ESLint + react plugins · Excalidraw
CONFIRMED · ROUND 3
TRUEMEND
10.6%
ESLINT+REACT
19.3%

Round 2 was a clear loss, 30.3% against 8.0%, judged completely. Round 3 quarantined nine whole-construct detectors a line-scan provably cannot scope, fixed a loader bug that let quarantined detectors keep firing, and re-adjudicated: 10.6% against 19.3%, same judge, same corpus. Round 3 used a different judge than round 2, and the numbers across that change are not directly comparable; a same-judge re-measure is not yet run.

Round 2 was a clear loss, 30.3% against 8.0%, judged completely.

read the write-up →

Javavs SpotBugs + Checkstyle + PMD · real Java corpora
CONFIRMED · RIVAL NOT RE-RUN
TRUEMEND
18.6%
BUNDLE (ROUND 1)
60.8%

The bundle’s 60.8% is its round-1 number, not re-measured against the fresh 485-finding sample. Wrong-match held roughly flat while real-rate nearly quadrupled, 5.5% to 22.9%: far more of what fires is worth an engineer’s time. 18.6% is still above our own 10% bar.

The bundle’s 60.8% is its round-1 number, not re-measured against the fresh 485-finding sample.

read the write-up →

GitHub Actionsvs actionlint · 165 real workflows
CONFIRMED · RIVAL NOT RE-RUN
TRUEMEND
19.5%
ACTIONLINT (ROUND 1)
40.0%

Round 1’s 11.6% moved to 19.5% on a fresh, differently-composed pool; the round-1 file-type-gate fixes were re-verified and hold. Real-rate more than doubled, 17.0% to 40.3%. actionlint’s 40.0% is its round-1 number, and 19.5% is above our own bar. Both disclosed.

Round 1’s 11.6% moved to 19.5% on a fresh, differently-composed pool; the round-1 file-type-gate fixes were re-verified and hold.

read the write-up →

Python / Djangovs Bandit + Pylint + Ruff · NetBox
CONFIRMED · BUNDLE WINS
TRUEMEND
25.0%
BUNDLE
23.0%

Five rounds in, we still trail the bundle’s 23.0% weighted average, and Ruff alone sits at 14.4%. Round 4’s six fixes held on re-certification; they cover 6 of well over 100 wrong categories firing on this corpus. The gap is breadth, not methodology.

Five rounds in, we still trail the bundle’s 23.0% weighted average, and Ruff alone sits at 14.4%.

read the write-up →

Bashvs ShellCheck · nvm
CONFIRMED · RIVAL NOT RE-RUN
TRUEMEND
33.2%
SHELLCHECK (ROUND 1)
23.1%

Round 1 had us winning at 18.5%. A fresh sample five times larger came in at 33.2%, worse than ShellCheck’s last known number. The five targeted fixes verifiably held; the regression is breadth, over 100 untouched categories, and it is published as a loss.

Round 1 had us winning at 18.5%.

read the write-up →

SCSS / CSSvs stylelint · real SCSS/CSS
CONFIRMED · STYLELINT WINS
TRUEMEND
45.4%
STYLELINT
9.6%

Our clearest current loss, both tools re-measured in the same round. stylelint’s surface is almost purely syntactic and it shows. Four of five fixed categories now sample at zero raw findings; roughly 110 untouched categories drag the number, and our real-rate sits at 3.1%.

Our clearest current loss, both tools re-measured in the same round.

read the write-up →

C# / ASP.NET Corevs no competitor wired in · Ombi
CONFIRMED · TRUEMEND ONLY
TRUEMEND
4.5%
NO COMPETITOR

No ASP.NET-aware competitor is in the harness, so this is TrueMend judged blind on its own. An audit caught the prior number stale at 19.4%; six regex fixes reached 11.6%, and three detectors withdrawn as disclosed coverage cuts reach 4.5%. A recompute, not yet a fresh campaign.

No ASP.NET-aware competitor is in the harness, so this is TrueMend judged blind on its own.

read the write-up →

Kotlin / Ktorvs no competitor exists · two production Ktor apps
BASELINE · NO FIXES YET
TRUEMEND
65.1%
NO COMPETITOR

First real baseline, published before any remediation: 418 findings judged, 14.8% real-rate. Prior evidence had tested a SQL library with no Ktor code in it by mistake; that result is retired, and this is the first measurement that exercises the Ktor detectors at all.

First real baseline, published before any remediation: 418 findings judged, 14.8% real-rate.

read the write-up →

Swift / SwiftUIvs SwiftLint not wired in · IceCubesApp
BASELINE · NO FIXES YET
TRUEMEND
57.4%
NO COMPETITOR

First real baseline, published before any remediation: 646 findings judged on a shipping App Store app, 14.2% real-rate. SwiftLint exists but is not in the harness yet. Prior evidence had tested a networking library with zero UI code; retired the same way as Ktor’s.

First real baseline, published before any remediation: 646 findings judged on a shipping App Store app, 14.2% real-rate.

read the write-up →

method: blind two-axis adjudication · independent judge, blind to which tool produced each finding · same codebase and commit for both · separate audit against detector tampering
The part nobody publishes

Most of these started as a loss.

We publish the arc, not the endpoint. A precision number you can only see at its best tells you nothing about whether it will hold. One of these arcs is a correction to a number we had already shipped, and three have not turned yet: Django, Bash and SCSS are losses today, sitting in the table above rather than waiting offstage for a better round.

GITHUB ACTIONS
55.7%19.5%

A confirmed loss to actionlint on its home turf, fixed by file-type gates that silenced 496 false positives and reached 11.6%. A fresh round-2 pool moved it back up to 19.5%; the fixes held, and the higher number is the one published.

RUST
34.5%4.8%

A correctly-scoped run found a real regression: the detector surface had moved and nobody re-checked it. Eight structural defects, then ten more mined from the judge’s own reasoning on a tie.

TYPESCRIPT / BIOME
16.8%3.0%

Behind on the first blind panel, still tied after round 2, and only won on round 3 by fixing category by category instead of applying one blanket rule.

TYPESCRIPT / ESLINT
13.6%3.5%

One meta-pattern explained 35 of 41 wrong categories: detectors matching a keyword shape without confirming the AST construct they claimed to flag. 142 regression tests.

C# / .NET
15.6%9.1%

Detectors carrying a premise that does not hold for a resilience library, then a regression caught by a same-repo reverify: four detectors shipped without the type gate their own neighbors already proved necessary. Fixed same day, recomputed to 9.1%.

JAVASCRIPT / EXPRESS
36.9%9.9%

Three rounds against a real production app, plus one caught regression: an unrelated engine fix silently tripled the finding pool under an already-merged win. The re-collect exposed two real bugs, Vue files getting JSX rules and an unanchored console.log check, both fixed before the number was re-proved.

TYPESCRIPT / REACT
30.3%10.6%

A published, completely-judged loss to ESLint+react. Nine detectors checking whole-construct properties a single-line regex cannot scope were quarantined with a recorded reason each, after a constructed recall test proved no window size was recall-safe. A shared loader bug letting quarantined detectors keep firing was found mid-round and fixed.

PYTHON / FASTAPI
24.3%10.9%

Seven rounds, two flips, one measurement artifact: a flat ±35-line excerpt was making judges mark correct whole-function findings wrong, fixed in the harness for every future campaign. Six shared-detector bugs, three quarantines, and one judged-wrong finding left unchanged because SQLAlchemy’s own docs say it is correct.

GO / GIN
40.4%5.9%

The generalization check the 0.0% result was missing: a second Gin corpus broke the surface at 40.4% on first contact. Three fixes, five quarantines, and one engine bug underneath them all: the per-function scan path computed match positions from the wrong baseline, so every negative-context check on that path silently suppressed nothing. One detector went from 66 raw findings to 1 the moment it landed, with no change to its own rule.

WHAT WE ARE NOT CLAIMING

On FastAPI we now beat Semgrep on wrong-match, 10.9% against 17.1%, same corpus, same judge, and 10.9% is at our own <10% bar rather than under it: we are not rounding it down. Real-rate is still the open gap, 27.7% against a 70% target, and Semgrep is not clean on it either at 39.0%. The round-5-to-6 improvement includes a judge change alongside the fixes, disclosed in the write-up; what is not judge-dependent is the raw count, 391 false positives removed from the same corpus across rounds 6 and 7.

On GitHub Actions we lead only against actionlint’s round-1 number, our own 19.5% is above our 10% bar, and actionlint still finds more genuinely worth-fixing correctness bugs than we do: its expression and action rules catch invalid property references and deprecated versions that need a full workflow-expression AST. The same gap shape appears in C#, where two XML-doc cases need signature-to-doc-order correlation a line scan cannot do. All of it is disclosed rather than folded into the headline.

Four comparisons carry a competitor number that was not re-run against the same fresh pool: Java, GitHub Actions, Bash and Go show the rival’s round-1 figure next to a fresh TrueMend measurement. Each of those rows says so. Go is the sharpest case: our own side moved from 40.4% to 5.9% while golangci-lint’s number stayed where round 1 left it, so the row is a win against a figure nobody has re-measured. And several of our own numbers sit above the 10% bar we hold ourselves to even where we lead, Java and GitHub Actions among them.

Express’s 9.9% is judge-dependent, and we say so. Round 3’s first pass used a stricter judge model and read the identical 596-finding sample at 23.3% wrong; running 407 findings through both judges showed they draw the line between “literally present” and “worth fixing” differently, agreeing 85% of the time. The certified number uses the same judge as every earlier round, so it is directly comparable, and the stricter reading stays on record. React’s round-3 win carries the same caveat in the other direction: rounds 2 and 3 used different judges, so only the same-judge round-3 gap is claimed, its 22.1% real-rate is still far under the 70% bar, and a same-judge re-measure has not been run yet.

Detectors get switched off rather than narrowed when no honest version of the rule survives scrutiny. A Promise.all fail-fast check went, after a same-file minimal pair proved the difference was architectural judgment with no regex-visible signal. So did a C# null-check whose premise was wrong for type patterns, a design-pattern heuristic that was a matter of taste, a production-logging rule that misread intentional framework logging, and four GitHub Actions checks whose premise cannot be verified from a trigger event at all.

One went the other way. cq-deeply-nested-callbacks was judged wrong on every sampled instance and we kept it anyway, because disabling it would have cost real callback-pyramid recall. That is a trade-off, not a fix, and it stays disclosed as one.

Everywhere else

Nine are still unmeasured.

Twenty-two languages ship. Eleven have a head-to-head number against that language’s default linter or scanner, and two more, Kotlin and Swift, carry a first framework baseline with no competitor wired in yet. The rest go through the same blind-judge, same-codebase, tamper-audited process before they appear here, not before.

Ruby, Dart, PHP, C, C++, SQL, PowerShell, Terraform, Dockerfile: no head-to-head number yet
See it on your own code.

The free tier runs the full 5,520-pattern analysis across 22 languages: open-core, no card, no account.

· hello@truemend.ai · code, made true