Comment-slop policy and mechanical gate — single source of truth for comment rules. Use when checking comment policy, slop comments, before commit, PR review, deslop, stop-ai-slop, writing code comments, documenting a function, adding TODO — multi-line narrative comments, changelog markers (было/стало/instead/fixes) in code, banner divider lines, step-numbered comments, TODO without ticket, markdown inside comments.
Installs into .claude/skills of the current project.
Are you the author of Skill?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/whitebite-skill)
---
name: stop-ai-slop
description: Comment-slop policy and mechanical gate — single source of truth for comment rules. Use when checking comment policy, slop comments, before commit, PR review, deslop, stop-ai-slop, writing code comments, documenting a function, adding TODO — multi-line narrative comments, changelog markers (было/стало/instead/fixes) in code, banner divider lines, step-numbered comments, TODO without ticket, markdown inside comments.
---
# stop-ai-slop — гейт против slop-комментариев
Единый источник правды по политике комментариев: таблица правил и детектор живут в `scripts/scan.mjs` (const `RULES`). OpenCode comment-gate plugin импортирует детекцию отсюда — править правила надо здесь, а не в плагине.
Политика: комментарий — максимум одна строка и только неочевидное внешнее ограничение, инвариант или воркэраунд. Пересказ диффа живёт в коммите, why теста — в имени теста. Исключение: многострочный JSDoc/docstring для публичного контракта класса/функции (ограничения, инварианты, поведение ошибок); пересказ сигнатуры и чейнджлог внутри него запрещены.
## Когда запускать
Перед каждым коммитом. Путь к сканеру — `scripts/scan.mjs` относительно корня скилла; подставьте свой `<SKILL_DIR>` (например, `~/.config/opencode/skills/stop-ai-slop`):
```
node <SKILL_DIR>/scripts/scan.mjs --staged
```
В OpenCode-сессиях write/edit/multiedit дополнительно блокируются на записи плагином comment-gate (error-правила). В Claude Code и вне сессий — через pre-commit hook (`--install` ниже) или вручную.
## Правила
<!-- stop-ai-slop:rules:start -->
| Правило | Severity | Why | Write | Ignore-when |
| --- | --- | --- | --- | --- |
| `multi-line-comment` | error | Многострочный комментарий — почти всегда пересказ кода или диффа. Через год его никто не перечитает, а рассинхрон с кодом не заметит никто. | // сбрасываем здесь, т.к. ниже освобождаем слот | doc-блок (JSDoc/docstring/`///` doc-комментарии) с контрактной документацией; легаси — через baseline |
| `changelog-marker` | error | История изменений живёт в гите. «Было/стало» в коде устаревает в момент коммита и дальше только врёт. Одиночное «вместо/было» — обычная проза; сигнал чейнджлога — пара маркеров в одном блоке комментария или сильный маркер (this fixes, must take over, was…, now…, before this change, the old … kept, we used to; ru: «до этого изменения», «старое правило держало»). | ничего в коде — причину пишем в сообщение коммита | дословная цитата внешней спеки, где формулировка зафиксирована |
| `long-comment` | error | Длинная строка — признак простыни. Ограничение, достойное комментария, формулируется коротко. | // сбрасываем здесь, т.к. ниже освобождаем слот | doc-блок; строка с длинной ссылкой на спеку/issue |
| `vend/step-numbered` | warning | Нумерация дублирует порядок строк кода. После первой правки шаги вставляются между — номера врут. | const normalized = normalize(payload) | протокол из внешнего документа с фиксированной нумерацией шагов |
| `vend/section-divider` | warning | Баннеры — признак файла-простыни. Навигацию даёт структура кода, а не линейки. | ничего — навигацию даёт структура модулей | сгенерированный файл |
| `vend/markdown-in-comment` | warning | Markdown в комментарии — документация, которую никто не читает рядом с кодом; она устаревает. | // сбрасываем здесь, т.к. ниже освобождаем слот | docstring, который реально рендерится генератором доков; одиночный `\|flag\|` в прозе — строка таблицы требует минимум трёх пайпов |
| `vend/this-function-opener` | warning | «This function does X» пересказывает сигнатуру. Ценность только в неочевидном ограничении. | // дедупликация по id, т.к. источник шлёт повторы | публичный API с обязательным JSDoc по внешнему требованию |
| `vend/file-summary-header` | warning | Оглавление файла устаревает при первой же правке. Структуру видно по символам файла. | ничего — файл начинается с кода | лицензионная шапка, требуемая политикой репо |
| `vend/generic-todo` | warning | TODO без тикета — вечный долг: некому искать и некогда чинить. | // TODO KRY-482 снять воркэраунд после фикса upstream | локальный черновик до первого коммита |
| `vend/ticket-ref` | warning | Тикет-ссылка без контекста ротирует вместе с трекером (Jira→GitHub миграции) и протекает чейнджлог в код. Правило неактивно, пока в конфиге не задан ticketPattern. | // TODO KRY-482: снять воркэраунд после апстрима | комментарий документирует сам тикет (трекер-агенты, migration notes) |
| `vend/cross-file-ref` | warning | Указатель на строку чужого файла гниёт при первой же правке там: номер перестаёт совпадать, и ни один инструмент этого не заметит. URL и host:port не флагаются. | // формат фиксирован вендором, см. спеку из тикета | URL с якорем #L12, host:port; требуется разделитель пути или известное кодовое расширение |
| `vend/obvious-comment` | warning | Пересказ строки под ним не добавляет информации: код сам себя описывает, а пересказ рассинхронизируется при первом же рефакторинге. Комментарий с «почему» (т.к., чтобы, иначе, must, должен…) не флагается. | // сбрасываем здесь, т.к. ниже освобождаем слот | комментарий содержит обоснование; только кодовые профили, не проза |
| `vend/ai-plan-narration` | warning | Ссылки на план, ТЗ или промпт и подтверждения «как было запрошено» — метаданные сессии, а не свойство кода: после закрытия задачи референт исчезает, и комментарий превращается в шум. Источник требования — тикет или имя теста. | // таймаут 30 с, т.к. вендор не отвечает быстрее | дословная цитата внешней спеки, где формулировка зафиксирована |
| `vend/ai-vocab-density` | warning | Плотность канонных ИИ-слов — статистическая сигнатура генерации (Juzek & Ward 2025): где есть одно слово, там обычно есть и другие. Одиночное слово бывает и у человека; три разных в комментариях одного файла — уже сигнатура. | обычная человеческая лексика | дословная цитата из внешнего текста, где формулировка зафиксирована |
| `vend/research-citation` | warning | Цитата вида (Cormen et al., 2009) или arXiv-id документирует процесс написания кода, а не его свойство: при смене источников ссылка гниёт, и рассинхрона никто не замечает. Коду нужен инвариант, а не библиография. | инвариант своими словами: // высота дерева <= 2·log(n+1) | порт алгоритма с формулой из статьи, где ссылка дана в README |
| `vend/self-suppression` | warning | Директива в одной правке с кодом, который она глушит, — амнистия без ревизии: никто не проверил обоснование. | // stop-ai-slop-ignore-next-line vend/step-numbered -- нумерация из внешнего протокола | full-scan: директива уже в репо, подавление легитимно |
| `vend/cjk-noise` | warning | Переключение модели на китайский посреди идентификатора или строки не читается и не компилируется осмысленно; склейка иероглифов с латиницей — маркер невычищенной генерации, а не осознанной i18n-строки. | const TAB_LABELS = { features: 'Функции' } | легальные китайские комментарии и строки i18n без смежности с латиницей; prose-файлы (.md/.html/.xml/.rst/.adoc) не проверяются; подавление директивой |
| `vend/zero-width-chars` | error | Невидимые символы — канал инъекций и обфускации (Unicode Instruction Injection, Trojan Source): текст выглядит не так, как исполняется. | const label = 'test' | нет (всегда артефакт или инъекция) |
| `vend/bidi-controls` | error | BiDi-override меняет визуальный порядок кода без изменения логики: ревьюер видит не тот код, что исполняется. Марки U+200E/U+200F порядок не переопределяют, но в коде это артефакт генерации или инъекция; в комментариях и prose-файлах они легальны для RTL-текста, поэтому там не флагуются. | const url = 'example.com' | U+200E/U+200F внутри комментария или prose-файла — легальная RTL-типографика |
<!-- stop-ai-slop:rules:end -->
Error блокирует (exit 1, write-time gate бросает). Warning — учитель: выводится, не блокирует.
## Если гейт заблокировал правку
1) убрать комментарий или сжать до одной строки WHY, 2) перезаписать правку, 3) легитимный случай — критерий ignore-when правила (`--explain <id>`), suppression-директива с причиной или baseline только для легаси; гейт не отключать.
Полное обоснование по правилу: `node <SKILL_DIR>/scripts/scan.mjs --explain <rule-id>` — выводит Why / Instead of / Write / Ignore-it-when из той же таблицы.
## Режимы scan.mjs
- `scan [paths...]` — полное сканирование (160 расширений и 26 имён файлов, таблица в README). В git-репозитории обход = `git ls-files --cached --others --exclude-standard`: gitignored-мусор (кэши, venv, вендор, бандлы, артефакты сборки) невидим; вне репо — обход каталогов. Сгенерированные файлы эксемптся от slop-правил: суффиксы имён экосистем (`*.g.dart`, `*_pb2.py`, `*_pb.go`, `*.min.js`, `zz_generated.*`, …), tool-named шапки первых 10 строк (`@generated`, `Code generated by … DO NOT EDIT`, `<auto-generated`, …) или lax-пара «generat/codegen + do not edit»; голый «DO NOT EDIT» без слова про генерацию не эксемптит. Файлы с NUL в первых 8КБ — бинарные, пропускаются. Нулевые зависимости, Node >= 18, работает на win32, linux и macOS.
- `--staged` — только добавленные строки из `git diff --cached -U0`. Вне git-репозитория: exit 0 с пометкой.
- `--baseline-write` — перезаписать `stop-ai-slop.baseline.txt` текущими находками. Baseline — способ закрыть легаси: записи вычитаются из вывода обоих режимов. Формат v2 хранит на находку пару строк `relpath:line` + `fp:<hash>` (hash правила и текста находки): сдвиг строк от правок выше не воскрешает легаси, а изменённый текст флагается как новый слоп; v1-файлы (`relpath:line`, строки с `#` — комментарии) читаются до следующего `--baseline-write`.
- `--baseline-prune` — удалить из baseline записи без живых находок; амнистирует удалённое легаси, не трогая новый слоп.
- `--bench` / `--bench-write` — FP-регрессионная когорта: 8 пин-репозиториев OSS (SHA до 2025-01-01, таблица `BENCH_COHORT`), счётчики находок по правилам без конфига и baseline; `--bench` сравнивает с `bench-history.json` (рост счётчика = регрессия = exit 1), `--bench-write` перезаписывает эталон. Нужны git и сеть; кэш `~/.cache/stop-ai-slop/bench` (переопределяется `STOP_AI_SLOP_BENCH_CACHE`).
- `--audit [N]` — последние N записей аудит-лога решений write-time плагина (`loaded`/`blocked`/passed с файлом и правилами); путь лога — переменная `STOP_AI_SLOP_LOG`, по умолчанию `~/.config/opencode/logs/comment-gate.jsonl`. Плагин загружается на старте сессии OpenCode: после правок плагина нужен рестарт. Шаг 0 диагностики: если в логе нет новых записей после редактирования — процесс OpenCode не подхватил новую версию плагина.
- `--stdin-path` — читает JSON hook-пейлоад из stdin (`tool_input.file_path`) и сканирует один файл; для PostToolUse-хуков Claude Code/Cursor/Codex (шаблон: `.claude-plugin/stop-ai-slop/hooks/hooks.json`).
- `--self-test` — саботаж-тест на временных фикстурах; exit != 0 при любом расхождении.
- `--install` — в репозитории: добавить npm scripts `stop-ai-slop` / `stop-ai-slop:all` (если есть package.json) и подключить `.git/hooks/pre-commit` с `node .../scan.mjs --staged`. Идемпотентно; существующее тело hook не перезаписывает — дописывает блок с маркером.
- `--install --strict` — то же самое, но hook запускает `--strict`, так что warning тоже блокируют гейт.
- `--install-hooks` — пишет хук-конфиги для Codex CLI, VS Code Copilot и Devin CLI + печатает сниппеты для Gemini CLI/Qwen Code; идемпотентен, чужие хуки не затирает
- `--install-rules` — генерирует rules-файлы из таблицы RULES для Cursor, Windsurf, Aider, Cline, Devin и блок в copilot-instructions; чужой контент без маркера не затирается
- `--diff <ref>` — добавленные строки файлов, отслеживаемых в репо, относительно ref; неотслеживаемые файлы не видны.
- `--fix [paths...]` — применить механические фиксы одним проходом по всей области скана (без агента/LLM): удалить multi-line/шапку-резюме/разделитель/чейнджлог-комментарий, снять префикс «Step N:», вырезать реальные невидимые и BiDi-символы. Затем рескан: выживает то, что чинится только головой (`long-comment`, `this-function-opener`, `generic-todo`, `markdown-in-comment`, `cjk-noise`) — они печатаются и дают exit 1 при error. Идемпотентно. Не трогает: escape-форму невидимых символов (предмет кода, BOM-тест), легальный emoji-ZWJ и ведущий BOM, suppression-директивы и подавленные находки, сгенерированные/бинарные/gitignored файлы.
- `--fix --dry-run` — превью: напечатать unified-diff (контекст 2) всех планируемых правок по файлам и итог `запланировано N правок в M файлах; не чинится автоматически: K`, ничего не записывая, exit 0. Сначала смотреть, потом применять.
- `--strict` — warning тоже блокируют гейт (exit 1).
- `--format <text|json|sarif>` — машиночитаемый вывод вместо текста: json = rdjson (reviewdog), sarif = 2.1.0 (code scanning); exit-коды от формата не зависят, итоговая строка `slop-gate:` печатается только в text.
- Конфиг `.stop-ai-slop.yaml` в корне репо — override severity правил (`off`/`warning`/`error`), `maxCommentLength`, `excludePaths`; читается режимами scan/--staged/--diff/--pre-tool, write-time плагин работает с дефолтами.
- Сгенерированный код: эксемпт только от slop-правил — security-правила (`vend/zero-width-chars`, `vend/bidi-controls`, `vend/cjk-noise`) продолжают флагать (отравленный codegen — supply-chain сигнал). Пользовательские сигналы: `.gitattributes` с `linguist-generated` и `generatedPaths` в конфиге (префиксы как у excludePaths); `scanGenerated: true` снимает эксемпт. Детали — раздел «Generated code» в README.
- `--explain <rule-id>` — полное обоснование правила (Why / Instead of / Write / Ignore it when).
- `--mcp` — MCP-сервер по stdio (JSON-RPC 2.0): три инструмента `slop_scan` / `slop_explain` / `slop_baseline`; согласование версий протокола `2024-11-05` / `2025-11-25` / `2026-07-28`.
- `--pre-tool` — PreToolUse-хук Claude Code: читает stdin JSON `{tool_name, tool_input}`, сканирует предлагаемый дельта-контент (`Write` content или `Edit` new_string минус old_string), при error-находках выводит их в stderr и exit 2 — блокирует запись до исправления. Понимает также `write_file`/`replace` (Gemini CLI, Qwen Code), `apply_patch` с V4A-патчем в `tool_input.command` (Codex CLI, мультифайл) и неизвестные имена по форме payload (VS Code Copilot, Devin CLI); read-only инструменты не гейтятся.
- `--help` — справка по всем режимам и флагам.
Директивы подавления: `// stop-ai-slop-ignore-next-line [rule-id]`, `// stop-ai-slop-ignore-line [rule-id]`, `// stop-ai-slop-ignore-file` (после `--` — причина). Синтаксис комментариев берётся из профиля языка (160 расширений и 26 имён файлов; см. таблицу профилей в README): `#` — комментарий в py/sh/yaml, но препроцессор в C и атрибут в Rust; детектор видит inline-комментарии после кода, блоковые комментарии без маркера на средних строках, doc-блоки, UTF-16 с BOM; zero-width символы игнорируются при матчинге.
## Вывод
```
<relpath>:<line> <rule-id> [<severity>] <сообщение>
instead: <что написать вместо>
```
Коды выхода: 0 — чисто; 1 — сработал гейт (error-находки вне baseline; warnings — при `--strict`); 2 — ошибка использования или git (неверный флаг, несуществующий ref).