AGENTS.md · diff

git:20260811.5a54ffc to git:20260812.f693a70

8 added, 4 removed. Audit A to A.

# AGENTS.md
Инструкции для агента, работающего **над самим плагином** (не для тех, кто его использует).
Дополняет `CLAUDE.md`, а не заменяет его: принципы конструкции и правила, нарушение которых
ломает плагин, описаны там.
## Что это за проект
`1c-quality-gate` — плагин Claude Code для контроля качества разработки на 1С:Предприятие
(язык BSL, XML-выгрузки конфигураций и расширений). Версия — в
`.claude-plugin/plugin.json`, в документации не дублируется.
Плагин делает четыре вещи:
1. **Блокирующий гейт.** Правка файла 1С (через `PostToolUse`-хук на `Write|Edit|MultiEdit`)
взводит гейт; `Stop`-хук не даёт завершить сессию, пока гейт не снят. Снятие возможно
только с машиночитаемым следом проверок (evidence), который проверяется валидатором.
2. **Четыре контура проверки:** код BSL (статический анализатор + антипаттерны), архитектура
(SOLID/GRASP/паттерны для 1С), XML-структуры метаданных, гигиена файлов.
3. **Адаптивная глубина прогона** по трём осям: объём правки (C0…C3), архетипы кода
(множество меток), сложность. Итог — `max` по осям.
4. **Честность про непроверенное.** Недоступный инструмент — не тишина, а запись `skipped`
- с причиной; полностью зелёный отчёт без записи `not_verified` валидатор отклоняет.
+ с причиной; полностью зелёный отчёт без заявленной компилируемости валидатор отклоняет, а
+ сработавший архетип запроса обязан отчитаться, выполнялся ли запрос.
Это **не линтер**: плагин запускает внешние статические анализаторы (`itrous/bsl-analyzer`
по умолчанию, `1c-syntax/bsl-language-server` запасной), а не заменяет их.
## Язык
Отвечать и писать файлы **по-русски**. Код, команды, имена файлов, идентификаторы и
технические термины — в оригинале. Комментарии в коде — тоже по-русски.
## Раскладка репозитория
```
.claude-plugin/plugin.json манифест (только метаданные; version обязателен)
.claude-plugin/marketplace.json маркетплейс-манифест (version держать синхронно с plugin.json)
.mcp.json MCP-серверы ПЛОСКИМ объектом, без обёртки mcpServers
skills/<имя>/SKILL.md навыки; имя в frontmatter = имя каталога
skills/<имя>/references/ справочники навыков (чеклисты, антипаттерны, карты)
agents/<имя>.md субагенты (bsl-verifier)
commands/<имя>.md слэш-команды (/gate, /gate-status)
hooks/hooks.json хуки; пути только через ${CLAUDE_PLUGIN_ROOT}
hooks/_shared.mjs общая механика хуков (чтение payload, корень проекта)
tools/*.mjs исполняемые проверки (Node.js, без зависимостей)
tools/xml/*.py валидаторы XML метаданных (Python 3.8+)
shared/ знание, общее для нескольких навыков (routing-contract.md)
assets/analyzer/ конфиги анализаторов, runtime-manifest.json, sentinel-фикстура
tests/run-tests.mjs тесты программных проверок
tests/fixtures/ фикстуры (байтовые — генерируются, не хранятся)
docs/ INSTALL.md, analyzer-integration.md, false-positives-cfe.md
```
Компоненты **не перечисляются** в манифесте — Claude Code подхватывает их по соглашению об
именах каталогов.
## Технологический стек
- **Node.js 18+** (ES-модули, `node:`-импорты) — все хуки и инструменты в `tools/`.
**Зависимостей нет** и быть не должно: `node_modules/` и `package-lock.json` в `.gitignore`
как защита от случайного `npm i`. `package.json` в проекте нет — скрипты запускаются
напрямую: `node tools/<имя>.mjs`.
- **Python 3.8+** — валидаторы XML в `tools/xml/` (стандартная библиотека, `-Path` —
универсальное имя параметра у всех валидаторов).
- **Внешние бинарники:** статический анализатор BSL плагин скачивает сам при первом прогоне
(`tools/analyzer-bootstrap.mjs`): закреплённый релиз из `assets/analyzer/runtime-manifest.json`,
проверка SHA-256 и размера до запуска, установка в каталог данных плагина.
- **MCP-сервис v8std** (`https://ai.v8std.ru/mcp`, объявлен в `.mcp.json`) — обязательная
зависимость, тексты стандартов #stdNNN запрашиваются через него во время работы.
## Команды перед коммитом
```bash
node tests/run-tests.mjs # тесты программных проверок (самодостаточный раннер)
node tools/validate-package.mjs # целостность пакета, ссылки, утечки проектных данных
node tools/gen-signs-map-md.mjs # перегенерация signs-map.md, если менялся signs-map.json
```
То же гоняет CI (`.github/workflows/validate.yml`, Node 20 + Python 3.12 на ubuntu-latest)
плюс компиляция Python-валидаторов (`python -m compileall tools/xml`) и сверка производной
`signs-map.md` с источником через `git diff --exit-code`.
## Как устроена механика гейта
- Состояние — в проекте пользователя: `.claude/.state/qg-pending.json` (взведённые гейты) и
`qg-done.json` (журнал снятий). **Именуется по сессии** (`session_id` из payload хука) —
общий на проект маркер запирает сессию, которая правила совсем другие файлы.
- `hooks/gate-arm.mjs` (PostToolUse) — взводит гейт на правках `.bsl`/`.os` и XML метаданных
(классификация — по маркерам выгрузки 1С: `Configuration.xml`, `cf/`, `cfe/`, типовые
каталоги объектов). В не-1С проектах плагин обязан молчать. Вывод — JSON с
`hookSpecificOutput.additionalContext`, иначе до модели не доходит.
- `hooks/gate-check.mjs` (Stop) — блокирует завершение своей сессии (exit 2), пока гейт не
снят. Любая внутренняя ошибка хуков — молча exit 0: хук качества не имеет права ломать
работу.
- `tools/gate.mjs` — единственный законный путь снятия: `release --evidence <файл>` либо
`release --class C0|C1 --reason "<почему>"` (для C2/C3 только след; заявленный класс
сверяется с реальным охватом). Команда `verify --layer <code|arch|xml|hygiene>` отмечает
файлы проверенными; отметку снимает следующая правка.
- Формат следа описан в `skills/quality-gate/references/evidence-format.md`; валидируется
`tools/evidence-validator.mjs` (режим `--gate` для снятия гейта). Поле `config` записи
`scope` не принимается на слово: валидатор пересчитывает его сам через `config.mjs` и
отвергает расхождение — иначе отметка о применённых порогах была бы подписью под тем, чего
никто не проверял.
- `tools/config.mjs` — единственный читатель `.1c-quality-gate.json`: умолчания → файл →
переменные окружения. Новый настраиваемый параметр заводится ТОЛЬКО через `DEFAULTS` этого
модуля: ключ, которого там нет, объявляется неизвестным, а не применяется молча. Сам файл
создаёт `gate-arm` при первом взводе — это единственное место, где уже известно, что проект
на 1С; повторно после удаления не создаётся (маркер `.claude/.state/qg-config-init.json`).
## Правила, нарушение которых ломает плагин
- **Пути только через переменные.** `${CLAUDE_PLUGIN_ROOT}` внутри плагина,
`${CLAUDE_PROJECT_DIR}` для проекта пользователя. Абсолютный путь работает только у автора.
- **Никаких проектных данных.** Плагин публичный: имена систем, модулей, полей внешних
контрактов, пути машин автора в артефакт не попадают. Проверяется автоматически —
`node tools/validate-package.mjs`.
- **Тексты стандартов не воспроизводятся.** Публикуются только ссылки и номера вида
`#stdNNN`; тексты запрашиваются через MCP `v8std` во время работы. Это лицензионное
ограничение, а не стиль (подробности — в `LICENSE`).
- **Frontmatter навыков — только поддерживаемые поля.** `inclusion`, `fileMatchPattern` и
подобные из других сред молча не работают: навык просто не активируется. Триггеры по типам
файлов живут в хуках.
- **Frontmatter `name` навыка = имя каталога**, иначе навык не подхватится.
## Тестирование
- Раннер — `tests/run-tests.mjs` (`node tests/run-tests.mjs [--verbose]`), самодостаточный,
без тест-фреймворка; коды выхода 0/1. Рабочий каталог — во временной папке ОС.
- - Покрывается только то, что является **кодом**: гигиена файлов, сверка «диск ↔ состав»
- (`tools/xml/orphan-check.mjs`), валидатор следа, механика гейта, нормализация вывода
- анализаторов, манифест установки.
+ - Покрывается только то, что является **кодом**: гигиена файлов, лексическая проверка текстов
+ запросов (`tools/query-lint.mjs`), сверка «диск ↔ состав» (`tools/xml/orphan-check.mjs`),
+ валидатор следа, механика гейта, нормализация вывода анализаторов, манифест установки.
- Контуры `code` и `arch` — инструкции для модели, программно их прогнать нельзя. Для них
тесты проверяют только **полноту правил** (что строка про конкретный антипаттерн не
исчезла). Это ловит регрессию удаления, но не качество применения.
- **Фикстуры с байтовой семантикой генерируются, а не хранятся** (git нормализует переводы
строк и может снять BOM). Невидимые символы задаются кодом (`String.fromCharCode`), а не
литералом. Хранимые фикстуры с значимыми байтами помечены `-text` в `.gitattributes`.
- **Новая проверка приходит с двумя тестами:** что находит дефект, и что молчит на заведомо
корректном коде. Тесты проверяются мутациями: сломай проверку намеренно — тест обязан
упасть. Тест, который не падает при поломке, хуже отсутствующего.
## Принципы конструкции (держать в голове при любой правке)
- **Пропуск проверки обязан оставлять след.** Невыполненная проверка неотличима от
выполненной, если после неё ничего не остаётся. Недоступный инструмент → запись `skipped`
с причиной, а не тишина.
- **Вердикт «чисто» обязан признавать непроверяемое.** Компилируемость тел BSL-модулей не
проверяется ничем, кроме платформы; полностью зелёный отчёт без `not_verified` отклоняется.
+ То же у текста запроса: для анализатора, сборки и валидаторов XML он остаётся строковым
+ литералом, поэтому измерение `query-execution` закрывается либо выполнением, либо записью.
+ Требование адресное — по имени измерения: иначе каждое новое имя ослабляло бы предыдущее.
- **Ложная находка вреднее пропущенной.** Она провоцирует переделку рабочего кода, и после
двух-трёх таких проверку отключают. Поэтому у каждого архитектурного признака обязателен
**контр-сигнал** — законная форма, в которой это не дефект; новая проверка без
контр-сигнала не принимается. Неразобранные файлы называются неразобранными, а не
источником сотен проблем.
- **Часовой против ложной зелени.** Вместе с рабочими файлами прогоняется фикстура с
заведомым нарушением (`assets/analyzer/sentinel-fixture/`, требует диагностики по
метаданным). Часовой проверяется **по целям**, а не «хотя бы один живой»: живой v8std не
маскирует отсутствие часового по анализатору. Фикстура обязана оставаться невалидной.
- **Градация вместо «всё или ничего».** Три строки внутри транзакции опаснее трёхсот строк
переименований — глубина считается по трём осям, для мелкой правки прогон занимает секунды.
- **Приближение лучше пропуска, но должно быть заявлено.** Где разбор неточен, находка не
блокирует, и это сказано прямо.
## Синхронизация производных файлов
- `skills/bsl-architecture-review/references/signs-map.json` — **источник истины** по
архитектурным признакам; `signs-map.md` — производный, перегенерируется только скриптом
`node tools/gen-signs-map-md.mjs`. Ручная правка производного рассинхронизирует его с
источником — CI это поймает.
- Версия в `.claude-plugin/plugin.json` и `.claude-plugin/marketplace.json` держится
синхронно (проверяется `validate-package.mjs`).
- Обновление версии анализатора: поменять `version` и суммы в
`assets/analyzer/runtime-manifest.json`, взяв их из GitHub API релиза (поле `digest`
у каждого asset).
## Безопасность и лицензии
- Код, скрипты, чеклисты, карты и эвристики — MIT.
- Статические анализаторы в пакет не входят: скачиваются с релизов автора, распространяются
по своим лицензиям, запускаются как внешние программы; SHA-256 сверяется до запуска.
- Тексты стандартов 1С не воспроизводятся (см. выше) — PR со скопированными текстами
принят не будет.
- Секретов в проекте нет и быть не должно; `validate-package.mjs` ловит следы проектных
данных автоматически.
## Стиль коммитов
Conventional Commits на русском: `<тип>(<область>): описание`. Типы: `feat`, `fix`, `docs`,
`refactor`, `chore`, `test`. Область — имя контура или компонента (`контур-кода`, `гейт`,
`контур-архитектуры`). Описание — в повелительном наклонении, без точки. В теле полезно
объяснить **почему**, а не только что: правила этого плагина часто неочевидны.
## Стиль кода
- Node: ES-модули, только `node:`-импорты, без внешних зависимостей; файлы `.mjs` с shebang
`#!/usr/bin/env node`. CLI-коды выхода: 0 — успех, 1 — мягкая проблема/не найдено,
2 — блокирующая ошибка.
- Каждый инструмент начинается с docstring-комментария: зачем существует, какой дефект
ловит, формат использования. Комментарии объясняют **почему** код такой, а не что он делает.
- Общая логика выносится в один модуль (`hooks/_shared.mjs`), а не копируется: скопированная
в два места механика разъезжается при первой же правке.