Back to AI guides

Anti-Slop: Oxlint Rules for Safer AI-Generated Code

Stanley Ulili
Updated on August 24, 2026

AI coding agents generate code fast, but they also generate shortcuts: unsafe type assertions, functions with unknown return types, conditional empty object spreads, and other patterns that compile and pass basic tests but introduce subtle risks. These patterns are hard to catch in code review and easy to miss in a diff. Anti-slop is a set of custom Oxlint rules, built by Dillon Mulroy, that catches them automatically.

The project is at github.com/dmmulroy/anti-slop and is meant to be vendored into your repository rather than installed as a fixed dependency. You own the rules, can read them, and can modify them to match your team's standards.

What Oxlint is and why speed matters

Oxlint is part of the oxc (Oxidation Compiler) toolchain, written in Rust. It's benchmarked at 50–100x faster than ESLint.

A slide from the Oxlint website showing its key features.

For a human developer running a lint check manually, the difference between 200ms and 10ms is barely perceptible. For an AI agent running in a tight loop (generate → lint → fix → lint → commit), a slow linter creates real friction. Oxlint's speed keeps it from becoming a bottleneck in automated workflows.

What anti-slop catches

The rules target two overlapping categories: low-evidence patterns (code that makes unverified assumptions) and low-signal patterns (code that's hard for static analysis or other developers to reason about).

A screen showing the extensive list of anti-slop rules.

The key rules:

  • no-chained-type-assertions: Bans as unknown as Type. The creator calls this "type laundering": you're telling the compiler to discard all type evidence and trust you instead. Any runtime shape mismatch becomes an invisible bug.
  • no-unknown-returns: Forces functions to have specific return types rather than unknown or any.
  • no-unknown-parameters: Same enforcement for function parameters.
  • no-conditional-empty-object-spread: Flags ...(condition ? { key: value } : {}). This pattern is idiomatic but has edge cases around property merging that make it risky.
  • no-known-value-widening: Prevents accidentally widening a precise type to a broader one.
  • require-safety-comment-for-type-assertion: If you absolutely need a type assertion, this rule forces a // SAFETY: ... comment explaining why it's safe. This makes intentional assertions visible and auditable.

The concrete problem these rules address is common in AI-generated code. An agent fetching an API response and casting the result directly to an interface:

 
interface Account {
  id: string;
  createdAt: Date;
}

const response = await fetch('/api/account/123');
const body = await response.json();
const account = body as unknown as Account; // anti-slop flags this

This compiles. It works until the API returns createdAt as a string rather than a Date object, at which point you have a runtime error with no type system warning and no clear location to trace it to.

Why the error messages matter for agents

Traditional TypeScript errors can be cryptic. Anti-slop errors are descriptive enough to be actionable instructions.

A comparison of a standard, hard-to-read TypeScript error with the clear feedback from anti-slop.

For no-chained-type-assertions, the error message reads something like: "This assertion chain discards type evidence. Keep the original precise type, or parse untrusted input at its boundary before narrowing it."

When that message gets fed back into an agent's context, it's not just a failure signal. It's a specific instruction: don't cast, parse and validate the data at the boundary. The agent can act on it directly rather than guessing what the error means.

Installation

The fastest path is using the bundled agent skill, which handles copying the plugin, installing dependencies, merging the config, enabling rules, and validating the result:

 
npx skills add dmmulroy/anti-slop --skill install-anti-slop

Then ask your coding agent to install or configure anti-slop in the current repository. The skill is designed to be invoked by the agent rather than run manually.

For manual setup: copy src/ from the repository into your project at tools/oxlint/anti-slop/, install oxlint and @oxlint/plugins, and create the config file.

Configuration

The real oxlint.config.ts structure uses jsPlugins and a specifier path, and importantly ignores all agent configuration directories so the rules don't fire inside .claude/, .codex/, and similar:

oxlint.config.ts
import { defineConfig } from "oxlint";

export default defineConfig({
  ignorePatterns: [
    ".agent/**", ".agents/**", ".claude/**", ".codex/**",
    ".continue/**", ".cursor/**", ".gemini/**", ".opencode/**",
    "tools/oxlint/anti-slop/**",
  ],
  jsPlugins: [
    { name: "anti-slop", specifier: "./tools/oxlint/anti-slop/index.ts" },
  ],
  rules: {
    "anti-slop/no-chained-type-assertions": "error",
    "anti-slop/no-conditional-empty-object-spread": "error",
    "anti-slop/no-known-value-widening": "error",
    "anti-slop/no-module-mocking": "error",
    "anti-slop/no-object-parameters": "error",
    "anti-slop/no-reflect-apply": "error",
    "anti-slop/no-reflect-get": "error",
    "anti-slop/no-runtime-typeof": "error",
    "anti-slop/no-shape-in-symbol-names": "error",
    "anti-slop/no-unknown-parameters": "error",
    "anti-slop/no-unknown-returns": "error",
    "anti-slop/no-unknown-type-aliases": "error",
    // "anti-slop/no-unsafe-type-assertion": "error", // opt-in
  },
});

For repositories using Effect-TS, there's an opt-in Effect rule group that adds additional rules appropriate for that ecosystem.

Add the lint script to package.json:

package.json
{
  "scripts": {
    "lint": "oxlint src"
  }
}

The `package.json` file with the `lint` script configured.

Seeing it in action

With the config in place, running npm run lint on code with a chained type assertion produces a detailed error:

A close-up of the highly descriptive error message provided by an anti-slop rule in the code editor.

The corrected version of the getUser example uses a Zod schema to validate the API response at the boundary rather than asserting the type:

api-client.ts
import { z } from 'zod';

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
});

export type User = z.infer<typeof UserSchema>;

export async function getUser(id: string, token?: string): Promise<User> {
  const headers: Record<string, string> = {};

  if (token) {
    headers['Authorization'] = `Bearer ${token}`;
  }

  const response = await fetch(`/api/users/${id}`, { headers });

  if (!response.ok) {
    throw new Error('Failed to fetch user');
  }

  const data = await response.json();
  return UserSchema.parse(data); // validates instead of asserting
}

This passes all anti-slop rules. The UserSchema.parse() call is high-evidence: it confirms at runtime that the data matches the expected shape and throws with a clear error if it doesn't, rather than silently passing through a mismatched type.

The agent feedback loop

The practical value in agentic workflows is the loop: agent generates code, lint runs, descriptive error output goes back into agent context, agent fixes the specific problem, lint runs again. The error messages are informative enough to close that loop without human intervention in most cases.

For no-chained-type-assertions, the fix is almost always the same: move to runtime validation with a library like Zod or Valibot. The error message tells the agent exactly that. For no-unknown-returns, the fix is to add a specific return type annotation. Again, the error message makes the action clear.

This is different from ESLint errors like "unexpected token" or TypeScript errors that require reading the type system's internal state. Anti-slop errors are written to be acted on, which makes them useful beyond just blocking CI.

The vendoring philosophy reinforces this: you own the rules in your repository, you can read what each one does, and you can adjust severity or disable rules that don't fit your codebase. It's not a black-box dependency; it's a readable set of TypeScript files sitting in tools/oxlint/anti-slop/.

Got an article suggestion? Let us know
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.