doc-ai-slopcheck
Deterministic scanner for AI-style code failures. 75 rules. No LLM. No backend.
Digitalist fork of ai-slopcheck by Euraika, which is no longer published.
Catches the mistakes AI coding assistants leave behind — stub functions, silent error handling, copy-pasted code, plan/ticket IDs in comments, hallucinated placeholders, hardcoded secrets, and more across Python, JS/TS, PHP (incl. Drupal), Go, C/C++, SQL, and Markdown.
Install
From PyPI (Python 3.12+):
pip install doc-ai-slopcheck
# or as an isolated command-line tool
pipx install doc-ai-slopcheck
Quick Start
# Scan a project (findings go to findings.json by default)
doc-ai-slopcheck scan . --output findings.json --fail-on warning
# Read the results
doc-ai-slopcheck summary findings.json # Markdown summary
doc-ai-slopcheck html findings.json > report.html # shareable HTML report
# GitHub Security tab (SARIF)
doc-ai-slopcheck sarif findings.json > results.sarif
# Only scan changed files (CI)
doc-ai-slopcheck scan . --changed-files git --fail-on warning
Scanning a directory other than the current one works as expected: when you
pass exactly one directory and no --repo-root, that directory becomes the
repository root (paths and .slopcheck.yaml are resolved from there). Paths
outside the repository root are rejected with an error instead of being
silently skipped.
What It Catches
| Category | Rules | Examples |
|---|---|---|
| AI Detection | 16 | Stub functions, instruction comments, plan/task IDs in comments, conversational bleed, identity refusals |
| Security | 7 | Hardcoded secrets, SQL injection, XSS, weak hashing, obfuscated code |
| JavaScript / Node | 11 | await-in-loop, unguarded JSON.parse, loose equality, React antipatterns |
| Go | 3 | Ignored errors, missing defer, wrong error wrapping |
| PHP / Drupal | 2 | Stub functions, raw $_GET/$_POST into eval/shell/SQL/echo; plus PHP support in debug (incl. devel dpm), SQL, obfuscation, empty catch, large functions, duplication |
| Python | 1 | Mutable default arguments |
| Cross-Language | 17 | Debug code, unreachable code, deep inheritance, dangerous shell in markdown |
| Data-Flow | 4 | Contradictory null checks, lock safety, IDOR risk, thread-unsafe globals |
| Quality | 9 | Large functions (>150 lines), copy-pasted code across files, plus opt-in deep nesting |
| API Contract | 1 | Removed routes, deprecated endpoints |
| Repo-Specific | 1 | Import boundary violations |
| Meta | 1 | Unused suppression directives |
Features
- 75 deterministic rules — no LLM, no network, no randomness
- 7 languages — Python, JS/TS, PHP (incl. Drupal .module/.inc/.install/.theme), Go, C/C++, SQL, Markdown
- Cross-file duplication — finds copy-pasted blocks within and across files, one finding per file pair
- Code-aware — rules about code skip comments and docstrings; rules about comments (TODOs, plan IDs, chat bleed) read them
- Test code weighs less — findings in tests, mocks and fixtures drop one severity level
- HTML report — standalone, shareable page that opens with a "Where to start" ranking of the files with the most kinds of problem
- Inline suppression —
# slopcheck: ignore[rule_id], or# slopcheck: ignore-next[duplicate_code]above a copied block - SARIF output — integrates with GitHub Security tab
- Diff-only mode — scan only changed files (
--changed-files git) - Baselines — suppress existing findings, fail only on new ones
- API snapshots — detect removed API routes
- Threaded — multi-core scanning (
--jobs N) - Confidence filtering —
--min-confidence mediumfor high-signal-only mode
Severity
Findings are error, warning or note. --fail-on (default error) sets
the level that makes scan exit with status 1.
Findings in test and mock code (tests/, mocks/, fixtures/, test_*.py,
*.test.ts, *Test.php, …) are lowered one level — error to warning, warning
to note — and tagged test-code. Set test_paths: [] in the config to treat
test code like production code.
SQL built from strings is an error when a value is spliced in, and a
warning when only a table or column name is (FROM $table … WHERE id = ?),
since names can't be bound as parameters.
PHP and Drupal
.php files and Drupal's .module, .inc, .install, .theme, .profile
and .engine files are scanned. Only the project's own code is scanned by
default: vendor/, Drupal core (web/core, docroot/core, html/core),
contrib modules/themes/profiles, libraries/, sites/*/files, scaffold copies
(default.settings.php, .ht.router.php, web/index.php, …) and generated
files (.phpstorm.meta.php, sites/simpletest) are ignored.
PHP comments, docblocks, #[Attributes], heredocs and inline HTML outside
<?php ?> are told apart, so rules don't fire on documentation or templates.
GitHub Actions
name: slopcheck
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: '3.12' }
- run: pip install doc-ai-slopcheck
- run: doc-ai-slopcheck scan . --output findings.json --fail-on warning
- if: always()
run: |
doc-ai-slopcheck github-annotations findings.json
doc-ai-slopcheck sarif findings.json > results.sarif
doc-ai-slopcheck html findings.json > slopcheck-report.html
- if: always()
uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: results.sarif }
- if: always()
uses: actions/upload-artifact@v4
with: { name: slopcheck-report, path: slopcheck-report.html }
Baselines (Incremental Adoption)
# First run: baseline existing findings
doc-ai-slopcheck scan . --output findings.json --fail-on none
doc-ai-slopcheck create-baseline findings.json
# CI: only fail on NEW findings
doc-ai-slopcheck scan . --baseline .slopcheck/baseline.json --fail-on warning
CLI Reference
| Command | Purpose |
|---|---|
doc-ai-slopcheck scan [paths] |
Scan files, write findings JSON |
doc-ai-slopcheck summary <file> |
Markdown summary |
doc-ai-slopcheck github-annotations <file> |
GitHub workflow annotations |
doc-ai-slopcheck sarif <file> |
SARIF v2.1.0 output |
doc-ai-slopcheck html <file> > report.html |
Standalone HTML report |
doc-ai-slopcheck create-baseline <file> |
Create fingerprint baseline |
doc-ai-slopcheck api-snapshot |
Snapshot API routes for contract checks |
Key scan Options
| Option | Default | Description |
|---|---|---|
--output |
findings.json |
Findings JSON file; - for stdout |
--repo-root |
scanned dir, else . |
Root for relative paths and config lookup |
--config |
.slopcheck/config.yaml, .slopcheck.yaml or .slopcheck.yml |
Config file |
--fail-on |
error |
Exit 1 at severity: none / note / warning / error |
--min-confidence |
low |
Filter: low / medium / high |
--baseline |
— | Suppress fingerprints from baseline file |
--changed-files |
— | git or @file.txt for diff-only mode |
--jobs |
auto | Thread count (0=auto, 1=sequential) |
--api-baseline |
— | API snapshot for contract comparison |
Configuration
Create .slopcheck/config.yaml (or .slopcheck.yaml) in the repository root:
# Replaces the default list, so keep the defaults you still want
# (vendor, node_modules, Drupal core/contrib, ...).
ignored_paths:
- "vendor/**"
- "**/node_modules/**"
- "services/legacy-api/**"
# Paths treated as test code (findings drop one severity level); [] disables.
test_paths: ["*/tests/*", "*/mocks/*", "*Test.php", "test_*.py"]
rules:
# Disable a rule
js_loose_equality:
enabled: false
# Tune thresholds
large_function:
max_lines: 150
duplicate_code:
min_lines: 10 # normalised lines (blank and comment lines excluded)
# Enable an opt-in rule with custom threshold
deep_nesting:
enabled: true
max_depth: 5
# Plan/ticket IDs in comments: add your own ID formats, e.g. decision records
plan_reference_comment:
patterns:
- '\b(?:Task|Phase|Milestone|Sprint)\s+#?\d+\b'
- '\bD-\d+\b'
# One class per file is enforced for JS/TS; opt in for Python
multiple_classes_per_file:
python: true
hallucinated_placeholder:
allowed_domains: ["example.com", "localhost"]
Project Layout
doc_ai_slopcheck/
├── cli.py — 7 CLI commands
├── config.py — Pydantic config models
├── models.py — Finding, ScanResult (stable contract)
├── engine/
│ ├── scanner.py — Threaded orchestrator, comment and test-code filters
│ ├── duplication.py — Cross-file copy-paste detector
│ ├── context_filter.py — Comment/docstring/string detection (Python, C-style, PHP)
│ ├── repo_files.py — File discovery and ignore patterns
│ ├── suppression.py — Inline ignore parser
│ └── api_routes.py — Route extraction for API snapshots
├── rules/
│ ├── generic/ — 73 cross-repo rules
│ └── repo/ — 1 architecture rule
├── output/
│ ├── html_report.py — Standalone HTML report with hotspot ranking
│ ├── sarif.py — SARIF v2.1.0
│ ├── annotations.py — GitHub annotations
│ └── markdown_summary.py
└── state/
└── store.py — Baseline persistence
Development
Requires Python 3.12+ and Poetry 2.x.
poetry install
poetry run pytest
poetry run ruff check .
poetry build
Tests and ruff run on GitHub Actions for every push and pull request, on
Python 3.12 and 3.13 (.github/workflows/tests.yml).
License
MIT — Euraika 2026. Fork maintained by Digitalist.
Metadata
Release files for doc-ai-slopcheck 1.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| doc_ai_slopcheck-1.2.3.tar.gz | 97.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| doc_ai_slopcheck-1.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 257.3 kB
Release files / doc_ai_slopcheck-1.2.3.tar.gz
| Download URL | doc_ai_slopcheck-1.2.3.tar.gz |
|---|---|
| Size | 97.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2d4adde90e45af165f43ea348116d20a49e22e0d517a0e5193f7d57d52109796
|
|
BLAKE2b-256 checksum How to use checksums |
9d84c32bcc004aec9004f15a8d259383426533711ead53ecd6426413751b644c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.4 CPython/3.13.4 Darwin/25.5.0
|
Release files / doc_ai_slopcheck-1.2.3-py3-none-any.whl
| Download URL | doc_ai_slopcheck-1.2.3-py3-none-any.whl |
|---|---|
| Size | 160.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
79ce560cf14d5a9e1484f4f2accff17aa459051c9ce03f4b2dda314b563d0e96
|
|
BLAKE2b-256 checksum How to use checksums |
03718e39595a6e138dcb801a7d4f5c8bac4f4f46d73624c9e68504f819c01047
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.4 CPython/3.13.4 Darwin/25.5.0
|