---
title: I built a CI tool for the rules ESLint can't express
tags: opensource, rust, react, webdev
---
If you've ever refactored a large codebase you know the pain. You're migrating off an old library or code pattern, but you can't touch everything at once. You chip away sprint by sprint. Then new code shows up following the old pattern -- from a teammate or an AI tool -- and you're back where you started.
I built [Baseline](https://github.com/stewartjarod/baseline) for this. Rust CLI, TOML config, runs in CI.
## Ratcheting
The killer feature. 200 calls to `legacyFetch()`? Set `max_count = 200`. Migrate some next sprint, lower it. CI blocks any PR that adds new ones. The number only goes down.
ESLint is pass/fail. It can't count occurrences across your codebase and enforce a decreasing ceiling.
## Ban patterns where they don't belong
```toml
[[rule]]
id = "no-db-in-pages"
type = "banned-pattern"
pattern = "db."
glob = "app/**/page.tsx"
message = "Use the repository layer"
```
## Ban imports and dependencies
```toml
[[rule]]
id = "no-moment"
type = "banned-import"
packages = ["moment", "moment-timezone"]
message = "Use date-fns or Temporal API"
```
`banned-dependency` parses package.json directly -- catches packages before any source file imports them.
## Tailwind + shadcn
AI writes `bg-white` everywhere. Your design system says `bg-background`. Dark mode breaks.
Two built-in rules: one flags missing `dark:` variants, one bans raw color classes and maps to semantic tokens. 130+ mappings. Understands `cn()`, `clsx()`, `cva()`, `twMerge()`.
## Proximity rules
```toml
[[rule]]
id = "org-scoped-deletes"
type = "window-pattern"
pattern = "DELETE FROM"
condition_pattern = "organizationId"
max_count = 80
message = "DELETE queries must include organizationId within 80 lines"
```
## Presets
```toml
[baseline]
extends = ["ai-codegen", "security", "nextjs"]
```
## CI
```yaml
- uses: stewartjarod/baseline@main
with:
paths: 'src'
```
Annotates inline on PR diffs. Only scans changed files on PRs.
Single binary, no Node runtime, tree-sitter for AST rules, MCP server built in.
```
npx code-baseline scan
```
MIT licensed: [stewartjarod/baseline](https://github.com/stewartjarod/baseline)