Skip to main content
New aislop v0.9.3: rule-precision release. Fewer false positives across docs, imports, eval, wrappers, JSON-LD, and bundled files. Read more →
← Blog
Release · 5 min read · reads

aislop v0.9.3. We measured the noise. Then we cut it by 38%.

A circulating comment suggested aislop produced too much noise on real-world code. The specific findings it cited didn't actually reproduce on our codebase — but the underlying critique was fair: a quality gate that cries wolf gets ignored. So we put numbers on it. 70 popular open-source repos, seven languages, eight iteration rounds, 35 new regression tests, and a 38% reduction in cohort-wide findings — without disabling a single rule.

For most of aislop's history we tuned rules against our own codebases and a handful of trending projects. That's fine for shipping early, but it leaves blind spots: every language has documentation conventions, intentional code patterns, and ecosystem-level injections that look exactly like the AI slop our rules were built to catch. When the rule can't tell them apart, you get noise. Enough noise and developers learn to scroll past your output.

v0.9.3 is the receipts release. We ran the numbers. We fixed what was wrong. We kept the regression tests.

The experiment

We scanned 70 popular open-source repositories — 10 each across TypeScript/JavaScript, Python, Go, Rust, Ruby, PHP, and Java. Names you know: pytest, pydantic, mypy, vue/core, tailwindcss, guava, rubocop, faker, phpunit, regex, ripgrep, composer, laravel/framework, and 57 more.

Baseline cohort: 32,840 findings.

For every rule that fired heavily, we sampled the actual code being flagged and asked one question: is this AI slop, or is it the language's documentation convention? An interface implementation? A bundler-injected global? A test-helper pattern?

What we found

A handful of false-positive classes kept showing up:

  • security/eval matched $obj->eval(...) in PHP — Predis Lua-script calls, not PHP's eval() language construct. 28 of 29 PHP hits were this pattern.
  • ai-slop/thin-wrapper matched public function valid(): bool { return isset(...); }. The JS function-shape regex didn't require an export, so PHP interface implementations (Countable::count, Iterator::valid) and Java methods got swept in. 325 hits across the cohort.
  • ai-slop/hallucinated-import flagged from _pytest.runner import x 835 times in pytest alone. Pytest's own internal src/_pytest/ package wasn't declared in pyproject.toml, so the rule treated it as a missing dependency.
  • ai-slop/narrative-comment flagged every Javadoc, PHPDoc, and YARD block above a declaration. 1,230 hits in Guava alone — all on standard Javadoc.
  • eslint/no-undef flagged DEV / BROWSER / VERSION in Vue 777 times. These are conventional define injections every modern bundler does.
  • ai-slop/rust-non-test-unwrap flagged .unwrap() inside /*! ... */ doc comments in the regex crate. Those are ```rust example blocks — teaching shorthand, not production code.

These aren't quirks. They're the language conventions developers expect their linter to understand.

Precision, not suppression

The lazy fix when you find a noisy rule is to turn it off. We didn't.

Every change in v0.9.3 tightens detection. The slop-signal paths still fire — // Matches X, // This function does N things, // First it ... then it ... finally it are all still caught, because those are the patterns the rule was designed for. What changed is that the rule now distinguishes those AI-style restatements from the genuine documentation around them.

PatternBeforeAfter
Javadoc above a class, no slop signalflaggedexempt
Javadoc starting with Represents X / Wraps Yflaggedstill flagged
catch (Exception ignored)flaggedexempt
catch (Exception e) { } (empty)flaggedstill flagged
from X import Y as Y (PEP 484 re-export)flaggedexempt
from X import Y where Y is never usedflaggedstill flagged
$obj->eval(...) (Redis Lua)flaggedexempt
Top-level eval($code)flaggedstill flagged

How we got here

This wasn't one diff. We iterated eight rounds — each round sampled the new top-of-list false-positive class, traced it back to the specific regex or heuristic that was misfiring, and tightened the rule. After every round we re-scanned all 70 repos to confirm the change moved the needle without breaking the cases the rule was supposed to catch.

Every fix landed with regression tests. The suite grew from 800 → 835 cases, all green.

The numbers

Same 70 repos, same commits, before and after the full v0.9.3 change set:

RuleBeforeAfterΔ
ai-slop/narrative-comment13,8176,250−55%
ai-slop/hallucinated-import2,161900−58%
eslint/no-undef926314−66%
ai-slop/thin-wrapper569244−57%
ai-slop/trivial-comment4,0353,190−21%
ai-slop/swallowed-exception694497−28%
security/eval (Ruby)8726−70%
security/eval (PHP)2912−59%
Total cohort findings32,84020,366−38%

Several popular projects moved from unranked (score 0) to honest scores. A few highlights:

faker-ruby/faker
0 36
sinatra/sinatra
3 31
rspec/rspec-mocks
3 30
phpunit/phpunit
9 25
doctrine/dbal
14 24
Seldaek/monolog
32 44
jekyll/jekyll
1 18
rubocop/rubocop
0 2

And four more popular projects moved from score 0 to a positive score for the first time: vuejs/core, pytest, tailwindcss, and rust-lang/regex.

What ships

  • Comment rules recognize Javadoc, PHPDoc, JSDoc, YARD, RDoc, and Go struct-field docs as documentation. The catch-all "long narrative" rule no longer fires above declarations; explicit slop signals (^This function/method/class, ^It does/handles/..., ^First/Then/Finally it) replace the length-only heuristic.
  • Hallucinated-import discovers local Python packages via __init__.py scanning and honors PEP 484 re-exports.
  • Security/eval has a proper lookbehind for method-call forms.
  • Thin-wrapper patterns are language-gated to JS/TS and Python.
  • Swallowed-exception allows tolerated/ignored/expected catch parameters.
  • Rust non-test-unwrap skips /*! ... */ doc-comment example blocks; xxx_test.rs (singular) recognized as test file.
  • Oxlint registers conventional bundler globals (__DEV__ and friends) for every project, plus discovers ambient .d.ts declarations and SST globals automatically.
  • Source-file walker filters *.min.js, *.bundle.js, honors Biome's files.includes, and ignores .pnpm-store/.
  • Dangerous innerHTML respects aislop-ignore / biome-ignore / eslint-disable directives and JSON-LD structured data.

What's next

A 38% drop is meaningful, but it isn't the finish line. The remaining findings include real slop, real complexity issues, and a long tail of cases where the rule is right but the call is debatable. We'll keep iterating — and we'll keep posting the receipts when we do.

The whole point of a quality gate is that it tells you the truth. v0.9.3 makes aislop a little more honest.

Install

$ npx aislop@0.9.3 scan .

Full changelog on GitHub. Star the repo to get the next release in your feed.