brainstate

Romandredan/1c-quality-gate · source

Snapshot 0 · 2026-11-04T12:47:28Z · JavaScript ·

Content fingerprintbc00cbc7804499f3448e6e9115126000e57f093e0f070a556b14f2426385db92

GitHub description at capture

Контроль качества 1С-разработки для Claude Code: стандарты, принципы ООП и паттерны, XML метаданных, блокирующие гейты и проверяемый след прогона · Claude Code plugin for 1C:Enterprise (BSL) code quality: development standards, SOLID and design patterns, metadata XML, blocking session gates and a machine-readable evidence trail.

README at capture

# 1c-quality-gate

[![CI](https://github.com/Romandredan/1c-quality-gate/actions/workflows/validate.yml/badge.svg?branch=main)](https://github.com/Romandredan/1c-quality-gate/actions/workflows/validate.yml)
[![Release](https://img.shields.io/github/v/release/Romandredan/1c-quality-gate.svg)](https://github.com/Romandredan/1c-quality-gate/releases)
[![Last commit](https://img.shields.io/github/last-commit/Romandredan/1c-quality-gate/main.svg)](https://github.com/Romandredan/1c-quality-gate/commits/main)
[![AGENTS.md](https://img.shields.io/badge/AGENTS.md-compatible-brightgreen.svg)](https://agents.md)

[![Claude Code](https://img.shields.io/badge/Claude%20Code-plugin-d97757.svg)](https://code.claude.com/docs/en/plugins)
[![OpenCode](https://img.shields.io/badge/OpenCode-plugin-000000.svg)](https://opencode.ai/docs/plugins/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Node](https://img.shields.io/badge/node-%E2%89%A518-339933.svg)](https://nodejs.org/)
[![1C:Enterprise](https://img.shields.io/badge/1C%3AEnterprise-8.3-ffd200.svg)](https://v8.1c.ru/)
![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)

**Контроль качества 1С-разработки в паре с языковой моделью.** Правка `.bsl` или XML метаданных
взводит гейт: в Claude Code сессию нельзя завершить, пока проверка не прогнана, в OpenCode
плагин настойчиво возвращает агента к непроверенным правкам.
Глубина считается автоматически: косметическая правка закрывается за секунды, транзакция или
новый общий модуль тянут за собой полный разбор. Результат — отчёт с находками и машиночитаемый
след, по которому видно, что проверено, что пропущено и почему.

> **English.** A Claude Code and OpenCode plugin that enforces code-quality checks for
> 1C:Enterprise (BSL) development. Editing a BSL module or a metadata XML arms a session gate:
> in Claude Code a `Stop` hook refuses to end the session until the quality run has happened;
> OpenCode has no such mechanism, so the plugin keeps returning the agent to the unchecked
> edits instead. Depth is computed per change across
> three axes — volume, code archetypes, complexity — so a comment fix costs seconds while a
> transaction or a new common module triggers the full review. Four review loops cover static
> analysis and 1C development standards, architecture (SOLID and GRASP as applied to 1C),
> metadata XML integrity, and file hygiene. Every run emits a machine-readable evidence trail:
> what was checked, what was skipped, and why. Skills, reports and docs are in Russian.

Значение слов «гейт», «контур», «класс», «след» — в разделе [Термины](#термины).

<details>
<summary><b>Содержание</b></summary>

- [Как выглядит прогон](#как-выглядит-прогон)
- [Зачем это нужно](#зачем-это-нужно)
- [Что проверяется](#что-проверяется)
- [Быстрый старт](#быстрый-старт)
- [Рекомендуемый харнесс](#рекомендуемый-харнесс)
- [Устройство](#устройство)
- [Состав пакета](#состав-пакета)
  - [Оркестратор](#оркестратор)
  - [Контуры](#контуры)
  - [Команды, субагенты и хуки](#команды-субагенты-и-хуки)
- [Термины](#термины)
- [Расчёт глубины](#расчёт-глубины)
  - [Ось 1: объём](#ось-1-объём)
  - [Ось 2: архетипы кода](#ось-2-архетипы-кода)
  - [Ось 3: сложность](#ось-3-сложность)
  - [Сложение осей](#сложение-осей)
- [Порядок прогона](#порядок-прогона)
- [След проверок](#след-проверок)
- [Меры против ложных срабатываний](#меры-против-ложных-срабатываний)
- [Файлы в проекте пользователя](#файлы-в-проекте-пользователя)
- [Настройка под проект](#настройка-под-проект)
- [Скрипты](#скрипты)
- [Структура репозитория](#структура-репозитория)
- [Версии и вклад](#версии-и-вклад)
- [Лицензия](#лицензия)
- [Благодарности и переиспользованное](#благодарности-и-переиспользованное)

</details>

---

## Как выглядит прогон

Модель правит модуль и собирается завершить работу. `Stop`-хук её не отпускает:

```text
[ГЕЙТ КАЧЕСТВА 1С — ЗАВЕРШЕНИЕ ЗАБЛОКИРОВАНО]

В этой работе изменены файлы 1С (2), но Skill: quality-gate не прогонялся.

BSL (2):
  - src/cf/CommonModules/РасчётСкидок/Ext/Module.bsl
  - src/cf/Documents/ЗаказПокупателя/Ext/ObjectModule.bsl

Прогони Skill: quality-gate. Он определит глубину сам — по объёму правки,
архетипам кода и сложности — и запустит только нужные контуры.
Косметическая правка закрывается за секунды: класс C0 требует лишь гигиены файлов.
```

Прогон даёт отчёт: находки для человека, ниже — след для машины.

```text
🔴 Critical  src/cf/CommonModules/РасчётСкидок/Ext/Module.bsl::ПересчитатьСтроки:142
   Запрос внутри цикла по строкам табличной части — одно обращение к БД на строку.
   Правило: #std436, bslls:QueryInLoop · измерено: 1 запрос × N строк при пороге 0
   Исправление: вынести запрос из цикла, получить строки одним пакетом и разложить
   результат в соответствие по ключу.

🟠 Major     src/cf/CommonModules/РасчётСкидок/Ext/Module.bsl::ПересчитатьСтроки:96
   ARCH-A2: ветвление вместо диспетчеризации — цепочка из 6 «ИначеЕсли» по виду скидки
   при пороге ≥4. Каждый новый вид скидки правит этот же метод.
   Целевая структура: соответствие «вид скидки → имя обработчика», обработчики —
   экспортные методы модуля; цепочка сворачивается в один вызов по имени.
   Контр-сигнал не сработал: ветки нетривиальны (12–20 строк) и растут вместе с видами.

## quality evidence

[qg scope: volume=C2, files=2, loc=+64/-11, archetypes=[query,object-event], complexity=[nesting:5], driver=archetype:query, resolved=code:L1+L2|arch:2|xml:n/a|hygiene:full, config=default]
[qg sentinel: target=v8std, id=std454, status=found]
[qg sentinel: target=bslls, id=CommonModuleInvalidType, status=found, engine=bsl-analyzer@0.2.73]
[qg applied: layer=hygiene, scope=file-encoding, ids=[qg:HYG-BOM,qg:HYG-DASH], verdict=clean]
[qg applied: layer=code, scope=query-in-loop, ids=[std436,bslls:QueryInLoop], verdict=violation:std436]
[qg applied: layer=code, scope=query-top-order, ids=[qg:QRY-TOP-WITHOUT-ORDER], verdict=clean]
[qg applied: layer=code, scope=transaction-nesting, ids=[qg:BSL-TXN-IN-HANDLER], verdict=clean]
[qg applied: layer=arch, scope=branching-dispatch, ids=[qg:ARCH-A2], verdict=violation:qg:ARCH-A2]
[qg skipped: layer=xml, reason=not_applicable]
[qg not_verified: dimension=compilation, reason=no_platform]
[qg not_verified: dimension=query-execution, reason=no_platform]
```

У каждой находки ключ локации `путь::Метод:строка`, правило (`#stdNNN`, код диагностики или
идентификатор эвристики), **измеренное значение против порога** и способ исправления; у
архитектурной — ещё целевая структура и проверка контр-сигнала. Уровней три: 🔴 Critical и
🟠 Major блокируют вердикт «чисто», 🟡 Minor не блокирует.

След показывает не только найденное: почему контур XML неприменим, что компилируемость тел
модулей осталась непроверенной и что запрос ни разу не выполнялся. Недоступный анализатор и
отсутствующий индекс кода фиксируются такими же явными записями. Гейт снимается только по корректному следу — его разбирает
`evidence-validator.mjs`, поэтому вердикт проверяем независимо от того, что утверждает отчёт.

---

## Зачем это нужно

Контроль качества в паре с языковой моделью отказывает не из-за незнания стандартов. Отказы
дают пять механизмов.

| Механизм отказа | Последствие | Ответ плагина |
|---|---|---|
| Инструкция «сверяйся со стандартами» не оставляет следа: выполнена она или нет, снаружи неотличимо | непроверенные правки уходят в коммит, отметка «проверено» перестаёт быть информативной | правка файла 1С взводит гейт, снятие возможно только по предъявленному следу |
| Отчёт «чисто» умалчивает о том, что часть проверок была недоступна | синтаксическая ошибка в теле процедуры проявляется при инициализации модуля в базе, а «Неоднозначное поле» в запросе — при первом выполнении | вердикт «чисто» без заявленной компилируемости валидатор отклоняет; сработавший архетип запроса обязан отчитаться, выполнялся ли запрос |
| Инструмент отказал или потерял контекст конфигурации, но продолжает выдавать правдоподобный отчёт | целый класс диагностик гаснет, остальные замечания продолжают поступать | часовой по каждому источнику: подтверждённый `v8std` не заменяет подтверждения анализатора |
| Высокая доля ложных находок | проверку отключают целиком | автоопределение состава проекта, контр-сигнал у каждого архитектурного признака, отдельная обработка неразобранных файлов |
| Полный прогон на правке комментария | гейт начинают обходить | глубина считается по трём осям, на классе `C0` прогон занимает секунды |

Гейт снимается только по корректному следу. Для классов `C0` и `C1` допустимо снятие с
письменной причиной без прогона, для `C2` и `C3` требуется полный прогон.

**Кому адресован:** разработчику 1С, работающему в паре с Claude Code или OpenCode над выгрузкой
конфигурации или расширения в файлы. Плагин задаёт процедуру и критерии приёмки прогона,
содержательный разбор выполняет модель по чеклистам контуров.

**Когда не применяется:** в проектах вне 1С хуки молчат по построению; при чтении чужого кода
гейт не взводится, поскольку реагирует только на правку; при работе в конфигураторе без
выгрузки исходников в файлы проверять нечего.

---

## Что проверяется

| Контур | Предмет | Когда работает |
|---|---|---|
| `code` | диагностики статического анализатора, сверка кода со справочником платформы (члены типов, системные перечисления, конструкторы), антипаттерны производительности и механики платформы, стандарты разработки `#stdNNN`, именование, сверка сигнатур API и существования общих модулей; лексические проверки текстов запросов и транзакций в обработчиках событий | с класса `C1` |
| `arch` | распределение ответственности, границы и контракты, связанность модулей, ветвление вместо диспетчеризации, дублирование, переусложнение — SOLID и GRASP в их штатной для 1С реализации | с класса `C2` либо по архетипу |
| `xml` | структура файлов метаданных, форм, СКД, ролей и макетов; дубли структурных узлов; регистрация объекта в составе конфигурации; сверка «диск ↔ состав» в обе стороны; уникальность UUID объектов; права ролей расширения | по факту правки XML |
| `hygiene` | кодировка, BOM, управляющие символы, тире вместо ASCII-дефиса, согласованность переводов строк | всегда, включая `C0` |

Граница между `code` и `arch` проходит по радиусу исправления: умещается внутри тела метода —
код; требует нового шва (выделение метода, перенос в другой модуль, новый экспорт, изменение
«кто кого вызывает») — архитектура.

---

## Быстрый старт

### Установка

**Claude Code:**

```text
/plugin marketplace add Romandredan/1c-quality-gate
/plugin install 1c-quality-gate
```

**OpenCode** — строка в `opencode.json`:

```json
{
  "plugin": ["1c-quality-gate@git+https://github.com/Romandredan/1c-quality-gate.git#v3.20.0"]
}
```

Оба харнесса ставят один и тот же тег одного репозитория: Claude Code — через
`marketplace.json`, OpenCode — через эту строку. Закрепляйте тег: без него OpenCode тянет
текущее состояние `main`, и версии разъезжаются.

После установки перезапустите сессию: навыки материализуются при старте. Проверка
работоспособности: `/gate-status` отвечает «Гейт не взведён». Строка выше подходит OpenCode
1.x и 2.x; в 2.x добавьте ещё `"experimental": { "subagent_depth": 2 }` — иначе исполнитель
гейта не запустит субагентов контуров. Отличия поведения в OpenCode — в
[docs/OPENCODE.md](docs/OPENCODE.md).

Обязательная зависимость одна: MCP-сервис стандартов `v8std`, объявленный в самом плагине и
работающий по HTTP без настройки. Статический анализатор устанавливается автоматически при
первом прогоне: закреплённый релиз скачивается, сверяется по SHA-256 и размещается в каталоге
данных плагина. Остальное необязательно и расширяет покрытие — см.
[Рекомендуемый харнесс](#рекомендуемый-харнесс) и [docs/INSTALL.md](docs/INSTALL.md).

### Первый вызов

| Задача | Вызов |
|---|---|
| Обычная работа | не требуется: правка взводит гейт, оркестратор запускается перед завершением сессии |
| Прогнать проверку и снять гейт | `/gate` |
| Показать охват взведённого гейта и ожидаемый класс правки | `/gate-status` |
| Поднять глубину против расчётной | `/gate --deep` |
| Минимальный прогон, допустим только для `C0` и `C1` | `/gate --quick` |
| Применить безопасные исправления: именование, форматирование, quick-fix | `/gate --fix` |
| Запустить один контур вне гейта | обращение к навыку контура напрямую |
| Механический чеклист после правки BSL | субагент `bsl-verifier` |
| Снять гейт без прогона, когда правка проверки не требует | `node "$QG/tools/gate.mjs" release --class C0 --reason "<почему>"` |
| Снять с сессии файл, который записала не её команда | `node "$QG/tools/gate.mjs" disown --reason "<кто записал>" <файл или каталог>` |

---

## Рекомендуемый харнесс

Плагин связан с внешними инструментами **по роли, а не по имени продукта**: любой источник,
закрывающий роль, подходит. Ни один из них, кроме `v8std`, не обязателен — недоступный
инструмент даёт запись `skipped` с точной причиной, и прогон продолжается. Мощность падает,
честность — нет.

| Компонент | Роль в прогоне | Без него | Установка |
|---|---|---|---|
| MCP `v8std` — [zeegin/v8std](https://github.com/zeegin/v8std) | тексты стандартов `#stdNNN`, расшифровка диагностик, страницы принципов и паттернов | прогон недостоверен: часовой не подтверждён, гейт не снимается | ничего не нужно: объявлен в [.mcp.json](.mcp.json) плагина, работает по HTTP |
| [bsl-analyzer](https://github.com/itrous/bsl-analyzer) — статический анализ BSL | диагностики контура `code`, метрики для оси сложности | `skipped: reason=analyzer_unavailable` — контур кода остаётся набором инструкций без воспроизводимого вердикта | ставится сам при первом прогоне: закреплённая версия, сверка SHA-256 |
| Python 3.8+ и `lxml` | валидаторы XML метаданных | контур `xml` теряет валидацию файлов; сверка «диск ↔ состав» на Node остаётся в строю | `pip install lxml` |
| Индекс кода 1С — MCP [rlm-tools-bsl](https://github.com/Dach-Coin/rlm-tools-bsl) или аналог | граф вызовов: вызывающие, экспорты модуля, использования символа, состав метаданных | признаки `ARCH-A1`, `ARCH-A7`, `ARCH-A9`, `ARCH-A11` уходят в `skipped: reason=rlm_unavailable`, а не в зелёный вердикт | по инструкции проекта-источника |
| Справочник API платформы — MCP [mcp-bsl-platform-context](https://github.com/alkoleft/mcp-bsl-platform-context) или аналог | сигнатуры методов и типов платформы при верификации вызовов | `skipped: reason=platform_unavailable`, сверка API выполняется частично | по инструкции проекта-источника |
| Платформа 1С:Предприятие 8.3 | проверка конфигурации (`/CheckConfig`) — единственный способ узнать, компилируются ли тела модулей | `not_verified: dimension=compilation, reason=no_platform` в каждом чистом вердикте | локальная установка платформы |

Минимальная рабочая связка — Claude Code и плагин: гигиена, стандарты и архитектурные признаки
без графа вызовов работают сразу. Рекомендуемая — плюс анализатор (ставится сам), `lxml` и
индекс кода: она закрывает контур `code` воспроизводимым вердиктом и включает четыре
архитектурных признака, которые иначе честно пропускаются.

---

## Устройство

Четыре слоя, каждый отвечает на свой вопрос.

| Слой | Вопрос | Триггер | Состав |
|---|---|---|---|
| 0. Механика гейта | будет ли проверка прогнана | правка файла 1С инструментом правки или командой оболочки (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`), попытка завершить сессию (`Stop`) | хуки гейта, `tools/gate.mjs` |
| 1. Профиль изменения | на какой глубине проверять эту правку | вызов `quality-gate` | оркестратор: три оси, матрица глубин, `driver` |
| 2. Контуры | что не так с кодом, архитектурой, метаданными, байтами | глубина, назначенная слоем 1 | навыки контуров, анализатор, валидаторы XML, MCP `v8std` |
| 3. След и снятие | что проверено, что пропущено и почему | завершение прогона | формат следа, `evidence-validator.mjs`, `gate.mjs release` |

Два инварианта.

**Профиль считается один раз**, до запуска контуров. Контуры получают его готовым и не
пересчитывают: иначе оси расходятся, и обоснование выбранной глубины перестаёт быть
проверяемым задним числом.

**Пропуск фиксируется.** Контур возвращает либо `applied`, либо `skipped` с причиной. Слой, не
оставивший записи, неотличим от выполненного, и это обесценивает весь механизм.

### Взвод по правкам из оболочки

У команды оболочки нет аргумента «файл», поэтому модуль, записанный генератором, `Set-Content`
или `git apply`, и XML, изменённый скриптом навыка правки метаданных, находятся по снимку до и
после команды. До команды хук запоминает время и для каждого затронутого git-дерева — HEAD и
изменённые файлы. После команды кандидатами становятся изменённые и новые файлы по
`git status`, файлы, вернувшиеся к HEAD, и файлы коммитов, созданных во время команды; вне git
каталог обходится с потолком. Взводится кандидат, у которого время изменения или смены
метаданных не раньше старта команды, а файл ли это 1С, решает та же классификация, что и при
правке инструментом. Затронутыми считаются рабочий каталог, корень проекта и пути из текста
команды.

Два намеренных исключения защищают от ложного взвода. Игнорируемые файлы не смотрятся:
выгрузка конфигурации во временный каталог — десятки тысяч модулей, которых никто не писал, а
в репозиторий игнорируемое не попадает. Коммиты, существовавшие до команды, тоже не берутся:
переход на ветку, `pull` и `reset` переписывают модули, но это чужой или уже проверенный код.

Цена — снимок `git status` до и после каждой команды: на дереве около 14 тыс. модулей и 36 тыс.
XML замер дал 0,2 с до команды и 0,5 с после, на небольшом репозитории — около 0,06 с. Не видно
файла, путь к которому вычислен внутри скрипта и лежит в дереве, не названном в команде, и
содержимого вложенных репозиториев. Не видно и записей фоновой команды: хук «после» срабатывает в
момент её запуска, поэтому модель получает предупреждение запустить команду, пишущую файлы 1С,
не в фоне. Касание без изменения содержимого взводит гейт — ошибка в громкую сторону. Если git
не ответил или обход упёрся в потолок, пользователь получает сообщение «не смог посмотреть», а
не тишину.

**Чья правка.** Время изменения говорит, что файл записан, пока шла команда, но не говорит
кем: в одном рабочем каталоге одновременно работают несколько сессий, пользователь и выгрузка
из конфигуратора. Поэтому у каждой записи состояния есть источник и отпечаток файла — размер и
время изменения на момент взвода.

| Кто записал файл в окно команды | Что делает гейт | Почему |
|---|---|---|
| инструмент правки другой сессии | не взводит; пользователь видит сообщение с идентификатором владельца | у владельца есть запись с тем же отпечатком — свидетельство точное |
| инструмент правки другой сессии, хук которой ещё не отработал | взводит, затем взвод владельца забирает файл обратно | отпечатки совпали — команда видела именно эту правку |
| команда этой сессии | взводит с источником `shell` | обычный случай, ради которого взвод из оболочки введён |
| команда другой сессии, пользователь, внешний процесс | взводит с источником `shell` | по времени их не отличить от предыдущей строки |

Последняя строка — граница метода. Молчаливый пропуск оставил бы без проверки и то, что
команда записала сама, поэтому файл взводится, а выход даётся явный:
`gate.mjs disown --reason "<кто записал>" <файл или каталог>` снимает его с сессии и сохраняет
причину в журнале снятий. Отказаться можно только от файла с источником `shell`: файл, который
сессия хотя бы раз правила инструментом, остаётся её файлом. Какие файлы взведены по времени,
показывают `gate.mjs status` и сообщение блокировки.

### Маршрутизация знания

До v3.6.0 контур кода на каждом прогоне читал два справочника с пометкой «прогоняется
всегда» (131 КБ), не оставлявшие следа ни разу за 25 боевых прогонов — разбор в
[docs/superpowers/specs/2026-09-07-context-routing-analysis.md](docs/superpowers/specs/2026-09-07-context-routing-analysis.md).
С этой версии обнаружение отделено от разбора: `references/catalog/INDEX.md` (генерируется
из карточек) несёт только триггеры и читается всегда, полная карточка — по попаданию;
проход по каталогу выполняет изолированный субагент `antipattern-reader` и аттестует
`tools/catalog.mjs`, оставляя записи `ai-antipatterns`/`platform-antipatterns` в следе.

Байт обязательного чтения до первой строки кода (метод и файлы — в разделе «Результат»
спецификации):

| Класс | До v3.6.0 | С v3.6.0 |
|---|---|---|
| `C1`, контур кода, слой 1 | 249 КБ | 95 КБ |
| `C1` + архетип «Запрос» | 289 КБ | 135 КБ |
| `C2` + архитектура | 363 КБ | 209 КБ |

Порог `C2` ≤ 190 КБ не достигнут (≈209 КБ): справочники контура архитектуры (SKILL.md,
`signs-map.json`, `checklist-architecture.md`, `ai-antipatterns-arch.md`,
`patterns-in-1c.md` — 76 КБ) по-прежнему читаются целиком при любом прогоне архитектуры.
Их маршрутизация по карточкам (тот же приём, что применён к контуру кода) в эту версию не
входила — контур `arch` план не задействован, задача явно не переносила его на карточки.
Следующий кандидат на ту же перестройку.

---

## Состав пакета

### Оркестратор

| Навык | Назначение | Входы | Выходы |
|---|---|---|---|
| [`quality-gate`](skills/quality-gate/SKILL.md) | Единственная точка входа. `gate.mjs plan` (`tools/profile.mjs`) печатает профиль изменения, строку `scope`, команды инструментов и справочники под архетип; навык прогоняет план, запускает контуры на назначенной им глубине, проверяет часового, собирает отчёт и снимает гейт. Контуры напрямую не вызываются. | `git diff` по рабочему дереву либо явно названные файлы; `gate.mjs status` (охват и отметки проверенного); `.1c-quality-gate.json` (пороги проекта) | план прогона, отчёт с находками, секция `## quality evidence`, снятый гейт, запись в журнале `qg-done.json` |

### Контуры

| Контур | Навык | Условие запуска | Предмет проверки | Инструменты |
|---|---|---|---|---|
| `hygiene` | [`file-hygiene`](skills/file-hygiene/SKILL.md) | всегда, включая класс `C0` | валидность UTF-8, BOM у модулей и XML, управляющие символы, тире вместо ASCII-дефиса вне строковых литералов, смешанные переводы строк | `tools/hygiene-check.mjs` по явному списку файлов |
| `code` | [`bsl-code-review`](skills/bsl-code-review/SKILL.md) | с `C1` слой 1; на `C2` дополнительно слой 2; на `C3` те же слои плюс предложение слоя 3 | слой 1а: диагностики статического анализатора и сверка со справочником платформы. Слой 1б: антипаттерны производительности и механики платформы, [антипаттерны кода языковой модели](skills/bsl-code-review/references/catalog/INDEX.md), затенение колонки псевдонимом источника, служебное слово языка запросов в псевдониме и `ПЕРВЫЕ N` без `УПОРЯДОЧИТЬ ПО` в тексте запроса, собственная транзакция внутри неявной транзакции обработчика, строковая колонка без квалификатора длины у таблицы-параметра запроса, перенос полей поштучно в изменённых методах, `ВыполнитьПакет()` ради одного результата, менеджер записи регистра сведений только для чтения, реквизиты объекта, найденного запросом, отдельным чтением, проверка состава структуры списком полей, ссылки на задачи в комментариях и слова механики в именах (список слов — настройка проекта `bslLint`), стандарты под архетип, именование, сверка сигнатур API и существования общих модулей. Слой 2: ревью логики моделью | `tools/analyzer-run.mjs`, `tools/platform-context-run.mjs`, `tools/query-lint.mjs`, `tools/bsl-lint.mjs`, `tools/rename-check.mjs`, `tools/catalog.mjs` (индекс триггеров и аттестация читателя), MCP `v8std`; при наличии индекс кода |
| `arch` | [`bsl-architecture-review`](skills/bsl-architecture-review/SKILL.md) | с `C2` уровни 1-2, на `C3` уровень 3; архетип поднимает уровень независимо от класса | признаки `ARCH-A1…A11`: модуль-комбайн, ветвление вместо полиморфизма, дубль-алгоритм, параллельная коллекция, перекладка переменных, инлайн-конструирование контракта, дублирующая валидация, инфраструктура в прикладном коде, экспорт без потребителей, переусложнение, проверки по обе стороны вопроса пользователю | [`signs-map.json`](skills/bsl-architecture-review/references/signs-map.md) с сигналом, порогом и контр-сигналом, MCP `v8std`, индекс кода |
| `xml` | [`xml-structure-review`](skills/xml-structure-review/SKILL.md) | по факту правки XML метаданных, независимо от объёма | сверка «диск ↔ состав» в обе стороны; уникальность UUID объектов метаданных; валидация файлов объектов, форм, СКД, ролей, подсистем, макетов, командного интерфейса; права на объекты в ролях расширения с [контр-сигналами модели прав](skills/xml-structure-review/references/role-rights-model.md); [различение собственного и заимствованного](skills/xml-structure-review/references/cfe-object-belonging.md) в расширении; дефекты, проходящие валидацию | `tools/xml/orphan-check.mjs`, `tools/xml/uuid-unique.mjs`, валидаторы Python в `tools/xml/` |

Правила дедупликации находок по ключу локации и шкала severity приведены в
[shared/routing-contract.md](shared/routing-contract.md).

### Команды, субагенты и хуки

| Компонент | Тип | Назначение |
|---|---|---|
| [`/gate`](commands/gate.md) | команда | прогон `quality-gate` целиком и снятие гейта; принимает `--deep`, `--quick`, `--fix` |
| [`/gate-status`](commands/gate-status.md) | команда | охват взведённого гейта и ожидаемый класс правки; гейт не снимает |
| [`bsl-verifier`](agents/bsl-verifier.md) | субагент | механический чеклист после правки `.bsl`: сигнатуры платформы, существование и экспортность общих модулей, состав метаданных, диагностики, именование. Логику и архитектуру не проверяет |
| [`antipattern-reader`](agents/antipattern-reader.md) | субагент | изолированный проход по каталогу антипаттернов (`references/catalog/`) для контура кода: получает индекс триггеров и изменённые файлы, возвращает кандидатов с цитатами; результат аттестует `tools/catalog.mjs`. Модель уровня контура, не дешёвая — семантика антипаттернов языковой модели не механизируется до haiku |
| [`gate-runner`](agents/gate-runner.md) | субагент | исполнитель гейта в свежем контексте. Сообщение блокировки велит основной сессии не гнать гейт самой, а запустить его и передать описание работы по семи пунктам: задача, решения и отвергнутые варианты, инварианты, что за рамками, что проверено вживую, сомнения, пути к спецификации. Сессию гейта и каталог плагина подставляет хук. Исполнитель проводит прогон целиком, сверяет описание с кодом как утверждение автора, холодному читателю его не передаёт, гейт не снимает — возвращает вердикт и путь к отчёту |
| [`cold-reader`](agents/cold-reader.md) | субагент | холодный читатель слоя 2 контура кода: получает только сравнение версий и пути изменённых файлов, не знает задачи и отвечает, что код делает как написан, на чём ломается и какое ожидаемое поведение из него не следует. Работает в любом харнессе, поэтому заменяет `advisor()` там, где того нет. Модель не ниже `opus`; свежий контекст — условие метода и заодно главная экономия: читатель не перечитывает историю сессии |
| [`bsl-scout`](agents/bsl-scout.md) | субагент | факты из индекса кода для контура архитектуры: вызывающие, экспорты модуля, триггеры экспорта в XML. Вердиктов не выносит |
| [`xml-runner`](agents/xml-runner.md) | субагент | прогон сверки «диск ↔ состав» и валидаторов структуры по изменённым файлам, разбор их вывода в короткий вердикт |
| [`gate-arm.mjs`](hooks/gate-arm.mjs) | хук `PostToolUse` | взводит гейт на правках `.bsl`, `.os` и XML метаданных, снимает с файла отметки проверенного; в не-1С проектах молчит |
| [`gate-shell.mjs`](hooks/gate-shell.mjs) | хуки `PreToolUse`, `PostToolUse`, `PostToolUseFailure` на `Bash` и `PowerShell` | взводит гейт на файлах 1С, изменённых командой оболочки, по снимку до и после команды; упавшая команда тоже проверяется — запись до сбоя остаётся правкой |
| [`gate-check.mjs`](hooks/gate-check.mjs) | хук `Stop` | блокирует завершение своей сессии, пока гейт не снят; после снятия один раз показывает пользователю остаток находок; при внутренней ошибке завершается кодом 0 без сообщения |
| [`gate-prompt.mjs`](hooks/gate-prompt.mjs) | хук `UserPromptSubmit` | сообщение пользователя начинает счёт проходов цикла заново; в проекте без состояния гейта молчит |
| `v8std` | MCP | тексты стандартов `#stdNNN` и расшифровка диагностик во время прогона; объявлен в [.mcp.json](.mcp.json) |

---

## Термины

| Термин | Значение |
|---|---|
| Гейт | Блокировка завершения сессии. Правка файла 1С взводит гейт; пока прогон не выполнен и след не предъявлен, сессия не закрывается. |
| Контур | Одна из четырёх проверок: `code`, `arch`, `xml`, `hygiene`. |
| Класс | Оценка объёма правки: `C0` косметика, `C1` точечная, `C2` модульная, `C3` структурная. |
| Архетип | Метка вида кода в правке: запрос, транзакция, права, интеграция и другие. У каждой метки объявлен свой минимум глубины, поэтому три строки внутри транзакции проверяются глубже, чем триста строк переименований. |
| Профиль изменения | Результат расчёта по трём осям: класс объёма, набор сработавших архетипов, оценка сложности. Определяет, какие контуры запускаются и на какой глубине. |
| След (evidence) | Секция `## quality evidence` в конце отчёта, по строке на каждую проверку: что выполнено, что пропущено и по какой причине. Без корректного следа гейт не снимается. |
| Часовой (sentinel) | Проверка живости источника, на который опирается вердикт. Для `v8std` запрашивается заведомо существующий стандарт, для анализатора прогоняется фикстура с заведомой ошибкой. Неподтверждённый источник делает прогон недостоверным. |

Грамматика следа и полный перечень причин пропуска приведены в
[evidence-format.md](skills/quality-gate/references/evidence-format.md).

---

## Расчёт глубины

Три оси, считаются один раз до запуска контуров. Источник истины — `tools/profile.mjs`
(`ARCHETYPES`, `computeProfile`): таблицы ниже описывают то же самое прозой, но профиль
печатает и сверяет `gate.mjs plan`, а не память модели о таблицах README.

### Ось 1: объём

| Класс | Признаки |
|---|---|
| `C0` косметика | комментарии, форматирование, переименование без смены семантики; тела методов не менялись |
| `C1` точечная | 1 файл, не более 40 изменённых строк, правка внутри существующих методов; нет новых экспортов, изменённых сигнатур, новых модулей и объектов метаданных |
| `C2` модульная | более одного метода, либо новый экспортный метод, либо изменённая сигнатура, либо более 40 строк, либо более одного файла, в пределах существующих модулей |
| `C3` структурная | новый модуль, объект метаданных или форма, либо изменение проведения и бизнес-логики, либо новая интеграция, либо затронуто 4 и более модулей разных подсистем |

### Ось 2: архетипы кода

Архетипы образуют набор меток, а не шкалу. Расставляются механически по маркерам в диффе и
путям файлов; одна правка может нести несколько меток одновременно: запрос, транзакция и
блокировки, запись наборов записей, обработчик события объекта, интеграция и HTTP, права и
RLS, CFE-перехват, регламентное и фоновое задание, клиент-сервер, модуль формы, асинхронный
клиент, новый общий модуль, новый объект метаданных.

У каждой метки объявлен собственный минимум для контуров `code` и `arch`. Транзакция требует
минимум `code` L2, но `arch` не поднимает вовсе; новый общий модуль наоборот оставляет `code`
на L1, а `arch` поднимает до уровня 2. Полная таблица меток с порогами и маркерами приведена
в [`profile-axes.md`](skills/quality-gate/references/profile-axes.md); данные, которые
считает инструмент, — в `tools/profile.mjs` (`ARCHETYPES`).

### Ось 3: сложность

Ось закрывает случай «строк мало, но код тяжёлый», когда архетип может не сработать вовсе.
Срабатывает при вложенности от 4, длине метода свыше 120 строк, 7 и более параметрах, цепочке
ветвлений от 4, рекурсии. Метрики берутся из отчёта анализатора.

### Сложение осей

Базовая матрица по объёму:

| Контур | `C0` | `C1` | `C2` | `C3` |
|---|---|---|---|---|
| `hygiene` | полный | полный | полный | полный |
| `code` | пропуск | L1 | L1 + L2 | L1 + L2, предложить аудит |
| `arch` | пропуск | пропуск | ур. 1-2 | ур. 3 |
| `xml` | пропуск | не применим, если XML не менялся | изменённые объекты плюс регистрация | полный: валидация, сироты в обе стороны, права ролей |
| компилируемость | не требуется | если платформа доступна | да | да |

> глубина контура = max(по объёму, максимум минимумов по сработавшим архетипам, по сложности)

Вторая ось введена из-за асимметрии: три строки внутри транзакции опаснее трёхсот строк
переименований, но транзакции нужен глубокий разбор *кода*, а новому общему модулю на тридцать
строк нужен разбор *архитектуры*. Одномерным «множителем риска» это не выражается.

Отдельно фиксируется `driver`: что именно подняло глубину, объём, конкретный архетип или
сложность. Без него вердикт виден, а его логика нет, и первое же «почему так долго на трёх
строках» превращается в спор.

Понижающий модификатор называется эталонной правкой. Глубина опускается до `C1` независимо от объёма при
одновременном выполнении трёх условий: все фрагменты являются кальками типового эталона того же
механизма с точечной адаптацией имён и полей; они уже прошли предметный верификатор с нулём
ошибок; они не затрагивают транзакции, блокировки и права.

---

## Порядок прогона

```mermaid
flowchart TD
    EDIT([правка .bsl или XML метаданных:<br/>инструментом правки или командой оболочки]) --> ARM["gate-arm.mjs / gate-shell.mjs:<br/>взвести гейт сессии,<br/>снять отметки проверенного"]
    ARM --> WORK[работа продолжается]
    WORK --> STOP{{"gate-check.mjs (Stop):<br/>гейт взведён?"}}
    STOP -->|нет| DONE([сессия завершается])
    STOP -->|да| HAND["основная сессия:<br/>запускает субагента gate-runner,<br/>передаёт описание работы по семи пунктам"]
    HAND --> PLAN{{"gate-runner, свежий контекст —<br/>gate.mjs run:<br/>профиль по трём осям, строка scope,<br/>инструменты плана одним вызовом, черновик следа"}}
    PLAN --> ORCH{{"quality-gate:<br/>прогоняет план, глубину поднять можно, понизить нет"}}
    ORCH --> HYG["hygiene: всегда"]
    ORCH --> CODE["code: с C1"]
    ORCH --> ARCH["arch: с C2"]
    ORCH --> XML["xml: если менялся XML"]
    ORCH --> SENT["часовой: v8std и анализатор живы?"]
    HYG --> EV[["отчёт и секция quality evidence"]]
    CODE --> EV
    ARCH --> EV
    XML --> EV
    SENT --> EV
    EV --> VAL{{"evidence-validator.mjs --gate"}}
    VAL -->|"след дефектен или часовой мёртв"| ORCH
    VAL -->|след полон| BACK["вердикт и путь к отчёту —<br/>основной сессии"]
    BACK --> REL["gate.mjs release --evidence"]
    REL --> DONE
```

Диаграмма иллюстративна; нормативный порядок шагов приведён в
[навыке `quality-gate`](skills/quality-gate/SKILL.md).

Последовательность одного прогона:

1. Собрать охват: `gate.mjs status` и `git diff`. Слои, уже отработавшие по этому содержимому
   файла, пропускаются с причиной `verified_earlier`. Отметку снимает любая следующая правка,
   поэтому переиспользование устаревшего доказательства исключено по построению.
2. `gate.mjs run` считает профиль по трём осям, печатает готовую строку `scope` (с
   `config=...`) и сам исполняет инструменты плана одним вызовом: сводка по строке на
   инструмент, вывод только тех, у кого находки или сбой, черновик следа из строк, напечатанных
   инструментами. Полный вывод, сравнение версий и индекс каталога антипаттернов уходят в
   каталог состояния, а не в контекст сессии. `gate.mjs plan` печатает те же команды без
   исполнения. Модель вправе поднять глубину против расчётной с записанной причиной, понизить —
   нет; `driver` фиксирует, что именно её подняло.
3. Прогнать контуры на назначенной планом глубине. Недоступный инструмент даёт `skipped` с
   точной причиной: `analyzer_unavailable`, `rlm_unavailable`, `platform_unavailable` и другими.
4. Проверить часового по каждому источнику, на который опирается вердикт `clean`: заведомо
   существующий стандарт через `v8std`, заведомо неверную фикстуру через анализатор.
5. Собрать отчёт: находки для человека, ниже секция `## quality evidence`.
6. Проверить след валидатором и снять гейт командой `gate.mjs release --evidence`.

Слой 3, состязательный аудит, автоматически не запускается. Контуры предлагают его в отчёте
при классе `C3` с находками 🔴 или 🟠; запуск только после явного согласия. Методология
приведена в [adversarial-audit.md](skills/quality-gate/references/adversarial-audit.md).

### Цикл проходов

Цикл — от взвода гейта до снятия. Первый проход полный; после него сессия исправляет все
открытые находки в правке, с которыми согласна, и запускает следующий проход. Проходов на цикл
— три, четвёртый `gate.mjs run` не запускает без `--decision "<кто решил и что>"`; решение
остаётся в журнале снятий. Сообщение пользователя начинает счёт заново.

Находки делятся по границе правки, которую печатает `run` (`## Задето правкой`): в правке —
методы BSL и диапазоны строк XML, изменённые относительно HEAD; вне правки — остальное. Находки
вне правки гейт не держат на любом уровне и в этом цикле не исправляются: они уходят
пользователю как техдолг. 🔴 вне правки и находки, которые субагент не может оценить, попадают
в раздел «Нужно решение пользователя». Раздел находки валидатор сверяет с той же границей по
адресу «файл:строка»: 🔴, положенная вне правки, хотя её строка в правке, — ошибка, гейт так не
снимается; находка без адреса не сверяется.

Второй и третий проход — проходы по исправлению. На старте прохода `run` записывает снимок
файлов сессии, а валидатор, приняв отчёт (`--gate --session <id>`), отмечает файлы
проверенными. Следующий проход считает объём и глубину от этого снимка: модельные слои читают
разницу с ним (`fix.diff`) и прошлый отчёт, а не правку целиком, поэтому старый код заново не
читается и случайных находок в нём не появляется. Граница правки при этом по-прежнему от HEAD.
Исправление значительнее правки одного метода проверяется не ниже глубины прошлого прохода, а
ревью логики и контур архитектуры читают и всю правку: такое исправление может нарушить общую
логику, и архитектуру после него нужно проверить заново.
Каждая находка прошлого отчёта переносится в новый: в раздел «Закрыто», если закрытие проверено
по коду, либо остаётся в своём разделе; пропавшую находку с идентификатором валидатор
отклоняет. Файл без отметки (добавлен позже, изменён во время прохода) и файл, чей снимок убрала
сборка мусора git, проверяются от HEAD полностью. Разбор —
[skills/quality-gate/references/fix-pass.md](skills/quality-gate/references/fix-pass.md).

При снятии `release` переносит остаток — открытое в правке, вне правки, требующее решения — в
журнал снятий и печатает его; первое завершение после снятия показывает пользователю служебное
сообщение со счётом по уровням и путём к отчёту. Почему так — разбор журналов рабочего проекта в
[docs/superpowers/specs/2026-10-01-gate-passes-convergence-design.md](docs/superpowers/specs/2026-10-01-gate-passes-convergence-design.md).

---

## След проверок

Пример полного следа приведён выше, в разделе [Как выглядит прогон](#как-выглядит-прогон):
он показывает не только найденное, но и причину, по которой контур XML неприменим, и то, что
компилируемость осталась непроверенной.

| Тип записи | Смысл | Обязательные поля |
|---|---|---|
| `scope` | профиль изменения, ровно одна запись за прогон | `volume`, `files`, `archetypes`, `driver`, `resolved`, `config` |
| `applied` | проверка выполнена | `layer`, `scope`, `ids`, `verdict` |
| `skipped` | слой можно было прогнать, но он не требовался либо инструмент недоступен | `layer`, `reason` |
| `not_verified` | измерение непроверяемо доступными средствами | `dimension`, `reason` |
| `sentinel` | источник подтвердил в этом прогоне, что жив | `target`, `status` |

Поле `config` в записи `scope` — настройка проекта, применённая к прогону: `default` либо
`custom:<секция>` с перечнем переопределённых. Строку печатает `config.mjs show`, и без неё
гейт не снимается: «C1» в проекте с задранными порогами означает не то же, что «C1» рядом, а
прогон, не заглянувший в настройку, иначе неотличим от прогона, который её учёл. Валидатор
**сверяет** отметку с фактической настройкой — приписанное по памяти значение не пройдёт.

Проверка следа:

```bash
node "$QG/tools/evidence-validator.mjs" <файл отчёта>          # lint: только оформление
node "$QG/tools/evidence-validator.mjs" <файл отчёта> --gate --session <id>   # строгий, для снятия гейта
```

Строгий режим требует ровно одну запись `scope`, хотя бы одну `applied` или `skipped`,
подтверждённого часового по каждому источнику, на который опирается вердикт `clean`, заявленную
компилируемость при полностью чистом вердикте и — если сработал архетип запроса — заявленное
исполнение запроса. Грамматика и перечень причин приведены в
[evidence-format.md](skills/quality-gate/references/evidence-format.md).

**Проверка с инструментом заявляется только по прогону.** Строку следа пишет в отчёт модель, и
написанная по прочтении кода она неотличима от полученной прогоном. Поэтому инструменты
печатают свою строку сами и отмечаются в журнале `.claude/.state/qg-runs.jsonl`, а валидатор
сверяет с ним каждую запись `applied`, у чьей проверки инструмент есть: `static-analysis`,
`platform-api`, `query-alias-shadowing`, `query-alias-vs-nested-table`, `query-top-order`,
`query-alias-reserved-word`,
`transaction-nesting`, `enum-string-assign`, `unbounded-string-column`, `attribute-access`,
`form-attribute-shadowing`, `dispatch-fallback`, `db-read-in-loop`, `field-transfer`,
`batch-single-result`, `record-manager-read`, `queried-object-attributes`,
`own-structure-check`, `comment-reference`, `mechanic-word`, `stale-local-calls`,
`file-encoding`, `registration-check`,
`uuid-uniqueness`, `structure-validation`, `form-binding`, `ai-antipatterns`,
`platform-antipatterns`, `ai-antipatterns-diff` (все три — `tools/catalog.mjs`, аттестация
результата читателя; последний — признаки `needs: [diff]`, когда `--diff` недоступен). Отметка должна быть не старше
последней правки файлов своей сессии — прогон до правки описывает состояние, которого уже нет.

**Сверяется и покрытие.** В журнале лежат пути файлов, которые инструмент видел; они
сопоставляются с составом правки, и прогон по одному файлу из десяти заявление обо всех десяти
не закрывает. Прогоны складываются — гонять инструмент по частям законно. Непокрытый файл
блокирует там, где инструмент применим к любому файлу своего расширения, и предупреждает для
проверки структуры: часть служебных XML выгрузки не проверяет ни один валидатор.

Запись `skipped` с причиной `not_applicable` проверяется наравне с `applied` — это тоже
утверждение о работе инструмента («посмотрел файлы, правило к ним не относится»). Причины
вида `analyzer_unavailable` отметки не требуют: ставить её некому.

Что сверка **не** делает: она не защищает от записи, дописанной в журнал вручную. В отличие от
поля `config`, где истина заново выводится с диска, независимого источника здесь нет — это
обнаружение молчания, и не больше.

Имена проверок берутся из закрытого словаря (`tools/evidence-scopes.mjs`). Свободное имя
проходило бы формат, но не закрывало бы ни одного требования — а выглядело бы проверкой.

Оба требования об одном и том же. Компилируемость тел BSL не проверяют ни загрузка конфигурации
из файлов, ни выгрузка, ни валидаторы XML: они разбирают структуру, но тела не компилируют.
Синтаксическая ошибка внутри процедуры проходит их все и проявляется только при инициализации
модуля в базе. Текст запроса не проверяет и это: для анализатора, сборки и валидаторов он
остаётся строковым литералом, а «Неоднозначное поле» или «Поле не найдено» всплывают при первом
выполнении. Вердикт «чисто», умалчивающий об этом, закрывает вопрос фальшивой зеленью.

---

## Меры против ложных срабатываний

Ложная находка обходится дороже пропущенной: она провоцирует переделку работающего кода, и
после двух-трёх таких случаев проверку отключают целиком. Отсюда шесть конструктивных решений.

**Состав проекта определяется автоматически:** основная конфигурация подключается к анализу
вместе с расширениями. Расширение, разобранное в одиночку, не видит библиотек, и каждое
обращение к ним становится «неразрешённым вызовом». На боевых расширениях это давало треть всех
находок.

**Неразобранные файлы называются неразобранными**, а не источником трёхсот проблем. Замечания
по ним получены на обрывке синтаксического дерева и снимаются целиком.

**Диагностики с высокой долей ложных на идиоматичном коде 1С отключены** в гейтовом конфиге, а
типографика и форматирование вынесены в отдельный дешёвый контур.

**Пороги проекта и пороги гейта разделены.** Анализатор запускается с конфигом из состава
плагина: иначе отключённая в проекте диагностика делала бы гейт тише, и об этом никто бы не
узнал.

**Контр-сигнал обязателен** для каждого архитектурного признака: это законная форма, в которой
признак не является дефектом. Новая проверка без контр-сигнала не принимается.

**Отсутствие данных не превращается в «чисто».** Признаки `ARCH-A1`, `ARCH-A7`, `ARCH-A9` и `ARCH-A11`
опираются на граф вызовов, поэтому без индекса кода уходят в `skipped`, а не в зелёный вердикт.

Замеры на боевом коде: [docs/false-positives-cfe.md](docs/false-positives-cfe.md). Обоснование
выбора анализатора и его ограничения:
[docs/analyzer-integration.md](docs/analyzer-integration.md).

---

## Файлы в проекте пользователя

```text
<корень проекта>/
├── .1c-quality-gate.json           настройка: пороги осей, движок анализатора, архетипы, часовой
└── .claude/.state/
    ├── qg-pending.json             взведённые гейты по сессиям: источник и отпечаток файла, отметки проверенного (файл × слой), цикл — проходы и решения
    ├── qg-done.json                журнал снятий: класс, причина, охват, предупреждения валидатора, отказы от файлов, остаток находок по разделам
    ├── qg-reports/                 архив принятых отчётов: копию кладёт `gate.mjs release`
    ├── qg-runs.jsonl               журнал прогонов инструментов: чем и когда проверено на самом деле
    ├── qg-config-init.json         отметка, что настройка уже создавалась
    ├── qg-state.lock               замок записи состояния; существует, пока идёт запись
    └── qg-analyzer.toml            сгенерированный конфиг анализатора с найденным составом проекта
```

Отметки старта команд оболочки живут во временном каталоге системы (`qg-shell/`), а не в
проекте: хук срабатывает на каждую команду, и в проекте вне 1С каталог состояния был бы мусором.

Корень проекта инструменты определяют одинаково: переменная `CLAUDE_PROJECT_DIR`, иначе подъём
до `.1c-quality-gate.json`, иначе до `.git`. Пока каждый инструмент брал рабочий каталог,
запуск из подкаталога давал «настройки нет» и «гейт не взведён» — оба ответа неотличимы от
честных. `status` и отказы печатают найденный корень со способом опознания; `QG_PROJECT_DIR`
задаёт его явно. Файлы вне корня (правки в соседнем репозитории) адресуются в состоянии и
журнале одним каноническим ключом — абсолютным путём, поэтому гейт по ним снимается обычным
следом.

Состояние гейта разделено по сессиям: завершение блокируется только за файлы текущей сессии,
правки соседней выводятся информационной строкой. Снятие чужого гейта недопустимо, поскольку
объявляет проверенной работу, которая не просматривалась, — поэтому при нескольких сессиях
`verify` и `release` без `--session` отказывают, а не выбирают самую свежую.

В `.gitignore` проекта добавляются `.claude/.state/` (рабочее состояние сессии) и
`.qg-analyzer/` (временный каталог отчёта запасного движка).

---

## Настройка под проект

**Файл заводит плагин, а не пользователь.** `.1c-quality-gate.json` создаётся при первом
взводе гейта — раньше неизвестно, что проект вообще на 1С. Секции в нём пустые, с описанием
ключей внутри: проставленное умолчание закрепилось бы навсегда и пережило бы обновление
плагина, поменявшее это умолчание. Удалённый файл повторно не создаётся — удаление читается
как отказ.

Что действует прямо сейчас — с указанием источника каждого значения (умолчание, файл,
переменная окружения):

```bash
node "$QG/tools/config.mjs" show
```

Эту же команду выполняет оркестратор до расчёта осей: пороги берутся из вывода, а не по
памяти.

```json
{
  "analyzer": { "engine": "bsl-analyzer", "version": "0.4.0", "required": false },
  "volume": { "c1MaxLines": 40, "c1MaxFiles": 1 },
  "complexity": { "maxNesting": 4, "maxMethodLines": 120, "maxParams": 7 },
  "archetypes": {
    "custom": [
      { "name": "exchange", "markers": ["ПланОбмена", "ОбменДанными.Загрузка"], "minCode": "L2" }
    ]
  },
  "sentinel": { "id": "std454" }
}
```

| Параметр | Где задаётся |
|---|---|
| Движок анализатора, закрепление версии, обязательность, автоустановка | `analyzer.*` либо переменные окружения `QG_ANALYZER_*` |
| Пороги трёх осей и собственные архетипы проекта | `volume`, `complexity`, `archetypes.custom` |
| Пути автотестов, которые гейт не взводит и не проверяет | `tests.paths` |
| Номер стандарта для часового по `v8std` | `sentinel.id` — если исчезнет именно эта страница, часовой упадёт разом во всех проектах |
| Разовое отклонение от расчётной глубины | флаги `/gate --deep` и `/gate --quick` |

Приоритет: переменная окружения → файл → умолчание. Неизвестный ключ не применяется, но и не
замалчивается: `show` называет его отдельной строкой — опечатка в имени иначе неотличима от
настройки, которой нет. Полный справочник — [docs/CONFIG.md](docs/CONFIG.md).

При `analyzer.required: true` гейт без анализатора не снимается. По умолчанию установлено
`false`: плагин публичный, у стороннего пользователя анализатора может не быть, и контур в этом
случае пишет `skipped`, а прогон продолжается. В собственных проектах значение следует
переключить на `true`. Установка, обновление и диагностика разобраны в
[docs/INSTALL.md](docs/INSTALL.md).

---

## Скрипты

Команды используют `$QG`, каталог установленного плагина. Переменная `CLAUDE_PLUGIN_ROOT`
доступна хукам, но не оболочке; способ разрешить путь первой командой прогона описан в разделе
«Путь к инструментам плагина» навыка [`quality-gate`](skills/quality-gate/SKILL.md).

Механика гейта и следа:

```bash
node "$QG/tools/gate.mjs" status                                   # охват, сессии, отметки проверенного
node "$QG/tools/gate.mjs" verify --layer code <файл> [...]         # отметить слой проверенным
node "$QG/tools/gate.mjs" disown --reason "<кто записал>" <файл или каталог> [...]   # снять с сессии файл, взведённый по времени и записанный не её командой
node "$QG/tools/gate.mjs" release --evidence <файл отчёта>         # снять гейт по следу
node "$QG/tools/gate.mjs" release --evidence <файл отчёта> --critical-decision "<кто решил и что>"   # то же при находке 🔴: без записанного решения гейт не снимается
node "$QG/tools/gate.mjs" release --class C0 --reason "<почему>"   # снять без прогона (только C0 и C1)
node "$QG/tools/gate.mjs" run [--files <f> ...] [--only <имя,...>]  # профиль, scope и инструменты плана одним вызовом; черновик следа
node "$QG/tools/gate.mjs" run --session <id> --decision "<кто решил и что>"   # четвёртый проход цикла — только с записанным решением
node "$QG/tools/gate.mjs" handoff [--session <id>]                   # текст передачи проверки субагенту gate-runner — тот же, что печатает хук
node "$QG/tools/gate.mjs" plan --files <f> [<f> ...]               # то же без исполнения: профиль, scope, команды инструментов
node "$QG/tools/evidence-validator.mjs" <файл отчёта> --gate --session <id>   # проверить след и принять проход
node "$QG/tools/config.mjs" show                                  # действующие пороги и их источник
```

Проверки контуров:

```bash
node "$QG/tools/analyzer-run.mjs" --changed <файл> [...]   # статический анализ, часовой, записи следа
node "$QG/tools/analyzer-run.mjs" --sentinel               # только проверка живости анализатора
node "$QG/tools/analyzer-bootstrap.mjs" [--verify]         # установка анализатора и сверка SHA-256
node "$QG/tools/hygiene-check.mjs" <файл> [...]            # байтовая гигиена
node "$QG/tools/query-lint.mjs" <файл.bsl|.xml> [...]      # тексты запросов (включая <query> СКД и динсписки): затенение колонки, служебное слово в псевдониме, ПЕРВЫЕ N без порядка
node "$QG/tools/bsl-lint.mjs" <файл.bsl> [...]             # транзакция в обработчике; примитив в поле ссылочного типа; строковая колонка без квалификатора длины; перенос полей поштучно; пакет ради одного результата; менеджер записи для чтения; реквизиты объекта, найденного запросом; проверка состава структуры; метки задач в комментариях; слова механики из списка проекта
node "$QG/tools/rename-check.mjs" <файл.bsl> [...]         # вызов метода, объявление которого исчезло из модуля в этой правке
node "$QG/tools/catalog.mjs" index [--archetypes a,b]      # индекс триггеров каталога антипаттернов
node "$QG/tools/catalog.mjs" card qg:AI-07                 # полная карточка признака: как чинить, законные формы
node "$QG/tools/catalog.mjs" attest --result <файл.json> --files <f> [...]   # аттестация результата читателя, печатает след
node "$QG/tools/xml/orphan-check.mjs" <каталог выгрузки>   # сверка «диск ↔ состав» в обе стороны
node "$QG/tools/xml/uuid-unique.mjs" <каталог выгрузки>    # дубли UUID объектов метаданных
python "$QG/tools/xml/meta-validate.py" -Path "<путь>"     # валидаторы XML; -Path принимают все
```

Коды выхода: `0` чисто, `1` предупреждения или «не найдено», `2` блокирующая ошибка.

Работа над самим плагином, то же выполняет CI на каждый push:

```bash
node tests/run-tests.mjs             # тесты программных проверок
node tools/validate-package.mjs      # целостность пакета, ссылки, утечки проектных данных
node tools/gen-signs-map-md.mjs      # перегенерация signs-map.md, если менялся signs-map.json
node tools/gen-catalog-index.mjs --check   # проверить, что references/catalog/INDEX.md не устарел
node tests/recall.mjs                # полнота срабатывания читателя каталога на контрольных примерах
```

---

## Структура репозитория

| Путь | Содержимое |
|---|---|
| [skills/quality-gate/](skills/quality-gate/SKILL.md) | оркестратор: инварианты прогона, план (`gate.mjs plan`), формат следа, состязательный аудит; три оси и архетипы — данные в `tools/profile.mjs` |
| [skills/bsl-code-review/](skills/bsl-code-review/SKILL.md) | контур кода и справочники: стандарты, оптимизация запросов, сверка API, [`references/catalog/`](skills/bsl-code-review/references/catalog/INDEX.md) — карточки антипаттернов с генерируемым индексом |
| [skills/bsl-architecture-review/](skills/bsl-architecture-review/SKILL.md) | контур архитектуры, карта признаков `signs-map.json`, паттерны в 1С |
| [skills/xml-structure-review/](skills/xml-structure-review/SKILL.md) | контур XML метаданных, модель прав ролей, семантика заимствования в расширении |
| [skills/file-hygiene/](skills/file-hygiene/SKILL.md) | контур гигиены файлов |
| [agents/](agents/bsl-verifier.md), [commands/](commands/gate.md) | субагенты (`gate-runner`, `bsl-verifier`, `antipattern-reader`, `cold-reader`, `bsl-scout`, `xml-runner`), слэш-команды |
| [hooks/](hooks/hooks.json) | взвод гейта на правке, блокировка завершения сессии |
| [tools/](tools/gate.mjs) | исполняемые проверки: Node.js без зависимостей, валидаторы XML на Python |
| [shared/](shared/routing-contract.md) | знание, общее для нескольких навыков: контракт маршрутизации |
| [assets/analyzer/](assets/analyzer/runtime-manifest.json) | гейтовые конфиги движков, закреплённая версия с суммами, фикстура часового |
| [tests/](tests/run-tests.mjs) | самодостаточный раннер; фикстуры с байтовой семантикой генерируются, а не хранятся; [`tests/recall/`](tests/recall/cases/) — контрольные примеры (`tests/recall.mjs` — замер полноты читателя каталога), результаты в `.gitignore` |
| [docs/](docs/INSTALL.md) | установка, интеграция анализатора, замеры ложных срабатываний, порядок выпуска релизов |
| [.github/workflows/](.github/workflows/) | CI: проверка на каждый push, выпуск релиза по тегу, еженедельный сдвиг закрепления движков через PR |

---

## Версии и вклад

Версия хранится в одном месте, [.claude-plugin/plugin.json](.claude-plugin/plugin.json), и
держится синхронно с `marketplace.json`; расхождение обнаруживает `validate-package.mjs`.

Релизы помечаются аннотированными тегами `vX.Y.Z`, и запись плагина в маркетплейсе пиннится на
тег полем `source.ref`: `main` остаётся рабочей веткой, а устанавливается только помеченное
дерево. Описание релиза живёт в сообщении тега, отдельного CHANGELOG в репозитории нет.

```bash
git tag -l                             # список релизов
git tag -n99 vX.Y.Z                    # описание релиза
git log --oneline vX.Y.Z..vA.B.C       # что вошло между релизами
```

Порядок выпуска, нумерация и шаблон описания приведены в [docs/RELEASING.md](docs/RELEASING.md).
Номер версии отвечает на вопрос «придётся ли что-то делать руками»: MAJOR — да (настройка,
расположение состояния, имена инструментов и проверок, флаги CLI), MINOR — гейт стал строже
или умнее в прежнем контракте. Новое блокирующее требование выходит в два шага: сначала
предупреждением, ошибкой — следующим релизом, чтобы обновление не красило чужой рабочий
процесс без предупреждения.

Правила добавления проверок и требования к PR приведены в [CONTRIBUTING.md](CONTRIBUTING.md),
инструкции для агента, работающего над самим плагином, в [CLAUDE.md](CLAUDE.md) и
[AGENTS.md](AGENTS.md). Основное требование: новая проверка сопровождается двумя тестами, один
подтверждает обнаружение дефекта, второй молчание на заведомо корректном коде. Для
архитектурного признака дополнительно обязателен контр-сигнал. Перед PR выполняется локальный
прогон `node tests/run-tests.mjs` и `node tools/validate-package.mjs`, CI должен быть зелёным.

Язык пакета русский: тела навыков, справочники и отчёты пишутся по-русски, код, команды,
идентификаторы и номера стандартов остаются в оригинале.

---

## Лицензия

Код, скрипты, чеклисты, карты соответствий и эвристики этого репозитория распространяются по
MIT, см. [LICENSE](LICENSE).

Тексты стандартов разработки 1С не воспроизводятся. Плагин публикует только ссылки и номера
вида `#stdNNN`, а сами тексты получает во время работы через MCP-сервис `v8std`. Правила
изложены авторами своими словами со ссылкой на номер стандарта.

---

## Благодарности и переиспользованное

Плагин опирается на результаты других проектов. Ничто из перечисленного в пакет не входит:
анализаторы скачиваются с релизов своих авторов и запускаются как внешние процессы, тексты
стандартов запрашиваются по сети во время прогона. Лицензии относятся к самим этим компонентам,
а не к коду плагина.

| Проект | Автор | Лицензия | Что взято и как используется |
|---|---|---|---|
| [cc-1c-skills](https://github.com/Nikolay-Shirokov/cc-1c-skills) | Nick Shirokov | MIT | десять валидаторов XML метаданных в `tools/xml/*.py`, портированы с адаптацией под контур `xml`; ссылка на источник и уведомление об авторских правах сохранены в шапке каждого файла |
| [bsl-analyzer](https://github.com/itrous/bsl-analyzer) | itrous | LGPL-3.0-or-later | основной движок статического анализа: закреплённый релиз скачивается при первом прогоне, сверяется по SHA-256 и вызывается консольно |
| [bsl-language-server](https://github.com/1c-syntax/bsl-language-server) | 1c-syntax | LGPL-3.0 | запасной движок анализа, устанавливается пользователем, вызывается консольно |
| [v8std](https://github.com/zeegin/v8std) · [v8std.ru](https://v8std.ru/) | zeegin | CC0 1.0 | MCP-сервис `ai.v8std.ru`: тексты стандартов по номеру, расшифровка диагностик, страницы принципов SOLID, GRASP и паттернов, на которые ссылается карта архитектурных признаков |
| [ai_rules_1c](https://github.com/comol/ai_rules_1c) | Oleg Philippov | не указана | идеи трёх проверок: уникальность UUID объектов метаданных, собственная транзакция внутри неявной, `ПЕРВЫЕ N` без `УПОРЯДОЧИТЬ ПО`. Правила изложены своими словами и сверены со стандартами; тексты и примеры не переносились |
| Стандарты разработки 1С:Предприятие, ИТС | 1С | правообладатель | номера `#stdNNN` и ссылки; тексты в репозитории не воспроизводятся |
| Claude Code | Anthropic | нет | среда выполнения: механика плагинов, навыков, субагентов и хуков, на которой держится гейт |

Совместимые внешние источники фактов — индекс кода
[rlm-tools-bsl](https://github.com/Dach-Coin/rlm-tools-bsl) и справочник API платформы
[mcp-bsl-platform-context](https://github.com/alkoleft/mcp-bsl-platform-context) — в пакет не
входят и подключаются пользователем как отдельные MCP-серверы; связь с ними идёт по роли, а не
по имени продукта.

Авторам обоих анализаторов отдельная признательность: без них контур кода остался бы набором
инструкций для модели без воспроизводимого вердикта. Плагин не зеркалит их сборки и не изменяет
условий их лицензий, он закрепляет версию и сверяет контрольную сумму, чтобы вердикт не менялся
между прогонами.