Benchmarks
argot flags what a linter structurally can't: code that is valid, typed and lint-clean but doesn't fit this repo — the kind of thing an AI agent drags in. It has five distinct detectors: four get their own scorecard below, each with its own catch metric and its own false-alarm metric; the fifth catches an AI agent gaming a test to force a failing suite green, benchmarked separately (154/164 (93.9%) — detector-specific fixture catch; controls and accepted-history results are separate; accepted-history controls are reported separately below).
A dependency, API or runtime the repo has never used. The statistical voice model, learned from your git history.
A function that reimplements one you already have. The local code-embedding index.
The right code filed in the wrong package — its nearest peers live elsewhere.
A module edge foreign to the repo's own layering — a reversed dependency, a cycle, or an edge out of a sink.
What the headline metric covers. All these detectors fire as ordinary rules — error severity by default (the check fails; test-weakened and the migration-awaresuperseded ship warn), each one configurable to warn oroff in [rules]. The headline catch/over-fire numbers argot is judged on cover detectors 1 & 4; detectors 2 & 3 are measured separately below, and the test-integrity group has its own separate benchmark — none of the three fold into the headline. Every catch number is held-out; every false-alarm number is a leak-free temporal holdout (fit at an old commit, replay only unseen commits). Higher catch, lower false-alarm.
1 · Foreign-pattern detection · the gate
The base voice model, learned entirely from your git history. It fires on two gated classes of 0-usage foreign pattern: a foreign import / dep(a package the repo never imports — the import stage catches it by construction; a foreign concurrency lib is just a dep and folds in here) and aforeign API / unfamiliar callee (a call into a foreign library that isn't an explicit import — the harder call-receiver stage). Catch isvisible (620/637 (97.3%) — detector-specific fixture recall; not a product-wide accuracy claim, the foreign symbol is in the diff) vs masked (a name colliding with one of your own, or a dynamic import() — a documented statistical limit). Numbers are per-corpus, fed from CI so they can't drift from what ships.
How to read the false-alarm numbers
A fire is not automatically a false alarm. We split every one two ways:
- Over-fire — argot fired on code that introducednothing new to the repo. That's the true false alarm, and it's the number shown in each column. This is what "will it nag me?" means.
- Detection (shown muted, “+x% detections”)— argot fired on code that genuinely introduced a 0-usage symbol: a real new dependency or API. That's argot doing its job, not crying wolf, so it never counts against the score.
And we split by where the change lands. Existing filesis the "does it stay quiet on my stable code?" number — the one that matters most. New files introduce new dependencies far more often, so most new-file fires are detections; the new-file over-fire column (true false alarms) stays near zero.
Commit-level bootstrap 95% CIs. Numbers published before July 2026 were measured train-on-test and were materially optimistic — seeissue #92. Fed from CI, so the numbers can't drift from what ships.
Public claim provenance: foreign 36 corpora / 12 languages / visible foreign-import and foreign-api fixture symbols after excluded-host purge · d1007f50; generated_at=2026-07-28; integrity 23 corpora / 12 languages; authored test-gaming fixtures, production fit→check · 1b0de32d; just integrity-verify, 2026-07-28. The combined brief has no approved aggregate public claim.
catch by difficulty across 756 graded fixtures / 34 corpora: easy 378/383 (99%) · medium 242/254 (95%) · masked 25/119 (21%) — name collisions & dynamic imports, documented limit
run d1007f50 · 2026-07-28 · ⚠️ marks corpora under the 300-hunk sample bar (excluded from the worst-FP tiles) · false-alarm = over-fire on the repo's own code; novel-pattern detections shown muted
Of code that introduces a 0-usage foreign pattern, the share argot flags. This is the gated number; the two tiles below split it by capability. Masked (hard) tier: 21% (25/119) — the documented name-collision / dynamic-import limit.
Catch is split by whether the foreign symbol is visible in the diff (easy/medium — an explicit import, FQN call, or distinct API name — the pattern an agent introduces in the open) or masked (hard — a foreign call whose name collides with one of the repo's own, an attested root namespace, or a dynamic import()). Masked foreign is the documented statistical limit of a name-based guardrail. In-vocabulary misuse of a library the repo already uses is a separate, deliberately-ungated class (below), not counted here.
Read the two columns honestly. Foreign import / depvisible catch is near-100% on nearly every repo — an explicit foreign import is caught by construction, so it is the easy, reliable half (foreign concurrency libs are folded in here: they are deps, caught the same way, and gave no distinct signal as their own column). The half that actually varies isforeign API (70–100%): catching a foreign callee that isn't an explicit import is the harder job. Each cell's second line (“N/M hard”) is the masked tier — where the real difficulty hides (import/dep ~22%, API ~20% on the hard cases) — surfaced so no column reads as a clean 100% while its hard cases quietly miss.
| Corpus | Language | Import / dep catch | API catch | Over-fire · existing | Over-fire · new-file |
|---|---|---|---|---|---|
| bat | Rust | 100% (13/13) 0/1 hard | 100% (6/6) 0/2 hard | 0.30% +6.80% detections | 0.00% |
| castle-engine | pascal | 100% (7/7) | 100% (4/4) | 0.00% +0.30% detections | 0.00% |
| commander | JavaScript | 100% (11/11) 1/1 hard | 100% (5/5) 0/1 hard | 0.00% +5.09% detections | 0.00% |
| composer | PHP | 100% (12/12) 0/2 hard | 100% (7/7) 0/1 hard | 0.00% +0.21% detections | 0.00% |
| curl | C | 92% (12/13) 0/1 hard | 100% (6/6) 0/2 hard | 0.00% | 0.00% |
| dagster | Python | 100% (6/6) | 100% (3/3) 0/2 hard | 0.00% | 0.00% |
| eslint ⚠️ | JavaScript | 100% (11/11) 0/1 hard | 100% (5/5) 1/1 hard | 0.00% | 0.00% |
| excalidraw | TypeScript | 100% (12/12) 1/1 hard | 91% (10/11) 0/3 hard | 0.09% +1.04% detections | 0.00% |
| express ⚠️ | JavaScript | 100% (11/11) 0/1 hard | 100% (5/5) 0/1 hard | 0.00% +15.38% detections | 0.00% |
| faker ⚠️ | Python | 100% (11/11) 0/1 hard | 92% (11/12) 3/6 hard | 0.92% | 0.00% |
| faker-js | TypeScript | 100% (10/10) 0/1 hard | 100% (8/8) 1/3 hard | 0.00% | 0.00% |
| fastapi | Python | 100% (11/11) | 100% (9/9) 1/5 hard | 1.75% +0.87% detections | 0.00% |
| fmt ⚠️ | C++ | 100% (13/13) 0/1 hard | 86% (6/7) 0/2 hard | 0.00% +3.12% detections | 0.00% |
| gh-cli | Go | 100% (10/10) 0/1 hard | 89% (8/9) 0/3 hard | 0.00% | 0.00% |
| guava | Java | 100% (12/12) 0/2 hard | 71% (5/7) 1/3 hard | 0.17% +0.17% detections | 0.00% |
| homebrew | Ruby | 100% (11/11) 2/2 hard | 100% (8/8) 3/3 hard | 0.00% | 0.00% |
| hono | TypeScript | 92% (11/12) 0/2 hard | 100% (11/11) 1/5 hard | 0.00% | 0.00% |
| hugo | Go | 100% (12/12) 1/1 hard | 100% (7/7) 0/2 hard | 1.18% +0.39% detections | 0.00% |
| ideu | pascal | — | — | 0.55% +0.17% detections | 0.00% |
| ink | TypeScript | 100% (11/11) 0/1 hard | 100% (9/9) 2/2 hard | 0.00% +6.08% detections | 0.00% |
| jellyfin | C# | 100% (12/12) 0/1 hard | 88% (7/8) 0/2 hard | 0.00% +0.45% detections | 0.00% |
| junit5 | Java | 92% (11/12) 0/2 hard | 100% (7/7) 0/1 hard | 0.27% +0.53% detections | 0.00% |
| laravel | PHP | 100% (12/12) | 100% (10/10) 1/4 hard | 0.00% +0.63% detections | 0.00% |
| mormot2 | pascal | 100% (6/6) | 100% (5/5) | 0.00% +0.75% detections | 0.00% |
| mseide-msegui | pascal | 100% (5/5) | 60% (3/5) | 0.61% +0.79% detections | 0.00% |
| outline | TypeScript | 100% (11/11) | 80% (8/10) 0/6 hard | 0.11% +0.32% detections | 0.00% |
| powershell | C# | 100% (13/13) 1/2 hard | 89% (8/9) 1/2 hard | 0.06% +0.22% detections | 0.00% |
| redis | C | 100% (11/11) 2/3 hard | 100% (8/8) 0/1 hard | 0.18% +0.18% detections | 0.00% |
| rich | Python | 100% (12/12) | 100% (8/8) 1/4 hard | 0.00% +0.51% detections | 0.00% |
| ripgrep | Rust | 100% (11/11) 0/1 hard | 100% (8/8) 0/2 hard | 0.00% +0.63% detections | 0.00% |
| rocksdb | C++ | 100% (9/9) 0/2 hard | 100% (12/12) 0/3 hard | 0.18% +1.00% detections | 0.00% |
| rubocop | Ruby | 100% (13/13) 0/1 hard | 83% (5/6) 0/2 hard | 0.63% +0.51% detections | 0.00% |
| saleor | Python | 100% (13/13) 0/1 hard | 90% (9/10) 0/2 hard | 0.00% | 0.00% |
| scrapy | Python | 100% (12/12) 0/3 hard | 100% (6/6) 0/3 hard | 0.00% +0.68% detections | 0.00% |
| uos | pascal | — | — | 0.00% +0.29% detections | 0.00% |
| wagtail | Python | 100% (13/13) 1/1 hard | 100% (12/12) 0/3 hard | 0.00% | 0.00% |
Novel-pattern catch aggregates the two gated foreign classes (foreign import / dep — incl. concurrency libs — and foreign API) — this is the metric argot gates on.False alarm is over-fire: a fire on the repo's own existing code (0 new symbols) — the true false alarm, held near zero.Detections (muted, “+x%”) are fires on a symbol that is 0-usage at fit — a real new dependency or API argot correctly flags for review — so they never count against the score. Reinvention and misplacement (below) are measured separately and are not folded into these numbers. ⚠️ marks corpora under the 300-hunk sample bar.
2 · Reinvention · rule redundant
A local code-embedding index — built with a 15.6 MB model compiled into the binary, nothing to download — finds a new function that reimplements one the repo already has.Catch is high — parallel agents write faithful reimplementations of a repo's own functions (renamed, restructured); planted as new code, argot flags them redundant at ≥ 72% on every corpus (median 94%). False-fire is calibrated away, not hidden. Real repos hold real parallel code, and a naïve embedding sense fires on all of it (5–14% of real-commit hunks). argot filters the shapes that aren't reinventions — thin wrappers, interface / family methods (a linter'son_send across 271 cops), dense sibling clusters, co-located protocol-variant families (curl's cf_h3_*next to cf_h2_*) — and atfit it measures itself against your own history: a mini-replay of the repo's recently added functions estimates its false-fire rate, and repos that practice systematic parallel implementation (saleor's checkout/order mirrors) automatically get a stricter mode. On the leak-free clean-commit replay this lands at amedian 0.4%/hunk, ≤ 2% on 30 of 31 corpora — jellyfin the lone exception at 4.5% (its C# tree at the pinned commit both over-fires and under-catches; the twin-rate guard holds every other repo).redundant is measured separately — never folded into the headline catch/over-fire numbers: argot names the nearest existing function and lets you decide. (Like every rule it fails the check by default; set it to warn oroff in [rules], or mute the hit.)
| Corpus | Language | Catch (recall) | False-fire / hunk |
|---|---|---|---|
| bat | Rust | 100% (18/18) | 0.89% (2/225) |
| commander | JavaScript | 100% (18/18) | 1.49% (6/404) |
| composer | PHP | 100% (18/18) | 2.04% (14/686) |
| curl | C | 89% (16/18) | 0.47% (2/423) |
| dagster | Python | 95% (19/20) | 2.03% (20/987) |
| eslint | JavaScript | 89% (16/18) | 0.00% (0/210) |
| excalidraw | TypeScript | 75% (15/20) | 1.94% (39/2006) |
| express | JavaScript | 94% (17/18) | 0.00% (0/39) |
| faker | Python | 100% (20/20) | 0.00% (0/163) |
| faker-js | TypeScript | 90% (18/20) | 0.14% (2/1420) |
| fastapi | Python | 90% (18/20) | 0.00% (0/66) |
| fmt | C++ | 89% (16/18) | 0.00% (0/23) |
| gh-cli | Go | 94% (17/18) | 3.49% (81/2320) |
| guava | Java | 89% (16/18) | 1.84% (56/3041) |
| homebrew | Ruby | 94% (17/18) | 3.20% (19/594) |
| hono | TypeScript | 100% (20/20) | 0.99% (6/604) |
| hugo | Go | 89% (16/18) | 2.59% (18/694) |
| ink | TypeScript | 100% (22/22) | 0.61% (3/493) |
| jellyfin | C# | 72% (13/18) | 4.74% (35/739) |
| junit5 | Java | 100% (18/18) | 3.50% (7/200) |
| laravel | PHP | 89% (16/18) | 3.92% (23/587) |
| outline | TypeScript | 100% (20/20) | 0.18% (2/1088) |
| powershell | C# | 100% (18/18) | 0.00% (0/109) |
| redis | C | 89% (16/18) | 1.04% (9/863) |
| rich | Python | 100% (20/20) | 0.42% (3/707) |
| ripgrep | Rust | 94% (17/18) | 1.23% (5/405) |
| rocksdb | C++ | 89% (16/18) | 2.66% (51/1915) |
| rubocop | Ruby | 100% (18/18) | 1.29% (5/388) |
| saleor | Python | 90% (18/20) | 0.78% (9/1161) |
| scrapy | Python | 95% (19/20) | 1.93% (17/882) |
| wagtail | Python | 95% (19/20) | 0.31% (1/324) |
Catch = recall over planted faithful reimplementations (caught/total). False-fire / hunk = share of replayed-commit hunks that fired redundant (fires / hunks scanned); the muted line is the judged-true rate after three independent reviewers removed the genuine reinventions (majority vote, default false-alarm). Shown for every corpus; window 150, leak-free holdout. Measured separately — never folded into the headline catch/over-fire metric.
3 · Misplacement · rule misplaced
The same index judges where a function belongs: if its nearest peers nearly all live in a different package than the one it's filed under, it's probably misplaced. The "package" isn't a fixed directory depth — atfit, argot walks each repo's tree to its real package granularity (descending through containers likesrc/ or src/Illuminate/),merges packages that are semantically entangled (a header-only library's src/ and include/are one thing, not two), and calibrates the vote against itself: a transplant simulation on the repo's own functions picks the thresholds, and when no configuration reaches usable recall the sense is disabled for that repo— a flat single-package library gets silence, not noise (marked “—” below).Catch = transplant recall: a real function re-filed into a foreign but existing package fires misplaced at 85–99% on every judging repo (median 96%)— up from a median of 66% (and literally 0% on composer, whose whole tree sat in one fixed-depth area) before the self-calibration. False-fire = the leak-free clean-commit holdout: ≤ 1.2% of hunks on every repo. The former outliers are gone the honest way — fmt (20%: a header-only layout with no separable architecture) now abstains, and junit5 (5.5%: near-identicaljunit-platform-* siblings) merges them and lands at 0.
| Corpus | Language | Catch (transplant recall) | False-fire / hunk |
|---|---|---|---|
| bat | Rust | — (n=0) | 0.00% (0/225) |
| commander | JavaScript | — (n=0) | 0.00% (0/404) |
| composer | PHP | 94% (n=602) | 0.15% (1/686) |
| curl | C | 97% (n=536) | 0.00% (0/423) |
| dagster | Python | 96% (n=1129) | 1.01% (10/987) |
| eslint | JavaScript | 98% (n=541) | 0.00% (0/210) |
| excalidraw | TypeScript | 88% (n=584) | 0.50% (10/2006) |
| express | JavaScript | — (n=0) | 0.00% (0/39) |
| faker | Python | 95% (n=720) | 0.00% (0/163) |
| faker-js | TypeScript | — (n=0) | 0.00% (0/1420) |
| fastapi | Python | 96% (n=650) | 0.00% (0/66) |
| fmt | C++ | — (n=0) | 0.00% (0/23) |
| gh-cli | Go | 99% (n=544) | 0.00% (0/2320) |
| guava | Java | 97% (n=504) | 0.07% (2/3041) |
| homebrew | Ruby | 98% (n=508) | 0.00% (0/594) |
| hono | TypeScript | — (n=0) | 0.00% (0/604) |
| hugo | Go | 97% (n=517) | 0.86% (6/694) |
| ink | TypeScript | — (n=0) | 0.00% (0/493) |
| jellyfin | C# | — (n=0) | 0.00% (0/739) |
| junit5 | Java | 99% (n=519) | 0.00% (0/200) |
| laravel | PHP | 95% (n=517) | 0.17% (1/587) |
| outline | TypeScript | 94% (n=521) | 0.28% (3/1088) |
| powershell | C# | 96% (n=501) | 0.00% (0/109) |
| redis | C | 99% (n=516) | 0.00% (0/863) |
| rich | Python | — (n=0) | 0.00% (0/707) |
| ripgrep | Rust | 92% (n=529) | 0.00% (0/405) |
| rocksdb | C++ | 95% (n=503) | 0.37% (7/1915) |
| rubocop | Ruby | 95% (n=507) | 0.00% (0/388) |
| saleor | Python | 97% (n=542) | 1.03% (12/1161) |
| scrapy | Python | 94% (n=716) | 0.00% (0/882) |
| wagtail | Python | 97% (n=1250) | 0.00% (0/324) |
Catch = transplant recall (n = functions sampled); “—” marks a repo where the fit-time self-calibration disabled placement — flat single-package or semantically-fused layouts (bat, commander, express, ink, rich, fmt) with no separable architecture to judge; their false-fire is 0 by construction (the sense abstains). False-fire / hunk = replayed-commit hunks that firedmisplaced (fires / hunks scanned), window 150. Measured separately — never folded into the headline catch/over-fire metric.
4 · Architecture · gated
argot builds a directed graph of the repo's own internal module dependencies at fit time — which layer is allowed to import which — and learns the layering the repo actually follows. At check time it flags a module-dependency edgethat breaks it: a reversed dependency (a low-level module reaching back up into a caller), a cycle the edge closes, or an edge out of a sink layer (a models/ file importing views/, a utility reaching into the app layer). It's the relational analog of Foreign-pattern detection above — Foreign catches a foreign import; this catches a foreign edge between the repo's own modules, the shape an agent produces when it wires code together without noticing how the repo is layered. Pure-Rust, no added runtime dependency, and it gates at the same mildest (unusual) tier as Foreign above.
0% false positives on held-out clean edges (0/148 controls fired) · 0.37% mean over-fire on replayed history (worst 2.7%) ·25 corpora across 12 languages.
| Corpus | Language | Real recall | Control-FP | Over-fire |
|---|---|---|---|---|
| saleor | Python | 100% (12/12) | 0/6 | 2.67% |
| scrapy | Python | 100% (12/12) | 0/6 | 0.00% |
| wagtail | Python | 100% (12/12) | 0/5 | 0.00% |
| fastapi | Python | 100% (10/10) | 0/6 | 0.17% |
| faker | Python | 100% (10/10) | 0/5 | 0.00% |
| dagster | Python | 100% (12/12) | 0/8 | 0.67% |
| composer | PHP | 92% (11/12) | 0/8 | 1.75% |
| laravel | PHP | 92% (11/12) | 0/8 | 0.00% |
| ripgrep | Rust | 90% (9/10) | 0/5 | 0.00% |
| bat | Rust | 100% (12/12) | 0/8 | 0.00% |
| guava | Java | 100% (12/12) | 0/8 | 0.00% |
| junit5 | Java | 100% (12/12) | 0/8 | 0.33% |
| powershell | C# | 90% (9/10) | 0/5 | 0.00% |
| jellyfin | C# | 92% (11/12) | 0/8 | 0.00% |
| rubocop | Ruby | 100% (7/7) | 0/2 | 0.00% |
| gh-cli | Go | 88% (7/8) | 0/5 | 1.06% |
| hugo | Go | 92% (12/13) | 0/6 | 0.00% |
| hono | TypeScript | 100% (10/10) | 0/6 | 0.00% |
| eslint | JavaScript | 100% (8/8) | 0/4 | 0.00% |
| excalidraw | TypeScript | 100% (12/12) | 0/8 | 2.00% |
| faker-js | TypeScript | 100% (12/12) | 0/6 | 0.00% |
| curl | C | 90% (9/10) | 0/1 | 0.00% |
| rocksdb | C++ | 100% (12/12) | 0/8 | 0.67% |
| mormot2 | Pascal | 100% (10/10) | 0/4 | 0.00% |
| castle-engine | Pascal | 100% (10/10) | 0/4 | 0.00% |
Real recall = planted foreign layering-edge fixtures caught / total (caught/total). Control-FP = planted clean, in-layering edges that should not fire, fires / total —0/148 across every corpus. Over-fire = false-alarm rate on the repo's own replayed history (leak-free holdout, same protocol as Foreign above). Fed from CI so the numbers can't drift from what ships; gated at the mildest (unusual) tier.
What argot deliberately does not gate on
Two more classes are reported for interest but never gated — they are a documented local limit, and not the danger an LLM poses (an agent drags in a whole foreign pattern; it doesn't subtly misuse your own vocabulary):naming-shape breaks (camelCase in a snake_case repo) andin-vocabulary misuse — a wrong choice among constructs the repo already uses: a bare ValueError where the repo raisesHTTPException, die vs throw, or araw-builtin concurrency antipattern (a barepthread/Thread where the repo has its own thread wrapper, a blocking call inside async code, a busy-wait poll). The line is the same everywhere: a foreign concurrency library — an async runtime or thread pool the repo never depends on — is a foreign dep, gated under import/dep above; misusing the concurrency builtins the repo already hasis this ungated class, caught only about half the time — the same proven local limit as the other misuse breaks. Seewhat it catches.
History
| Date | Commit | Catch (gated) | Worst false alarm · existing | Worst false alarm · new-file |
|---|---|---|---|---|
| 2026-07-05 | 5e533bcc | 86% | 0.98% | 0.00% |
| 2026-07-07 | 9276d52e | 85% | 1.17% | 0.00% |
| 2026-07-14 | 7be8b1eb | 86% | 1.46% | 0.00% |
| 2026-07-28 | d1007f50 | 85% | 1.75% | 0.00% |