Claude Skill

quality-gate

Оркестратор контроля качества 1С-разработки. Определяет профиль изменения по трём осям (объём правки, архетипы кода, сложность), выбирает глубину каждого контура, запускает проверки, формирует отчёт с машиночитаемым следом и снимает блокирующий гейт. Вызывать после правок BSL или

LLM Mart · 0 points · 2 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download Romandredan-1c-quality-gate-skills_quality-gate-661b111.zip · 35 KB
Part of romandredan/1c-quality-gate — 5 skills

Install

skills CLI npx skills add https://github.com/Romandredan/1c-quality-gate/tree/main/skills/quality-gate
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install romandredan-1c-quality-gate@llmmart
Git git clone https://github.com/Romandredan/1c-quality-gate.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole romandredan/1c-quality-gate collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

quality-gate — оркестратор контроля качества 1С

Единственная точка входа плагина. Контуры (code, arch, xml, hygiene) не вызываются напрямую: сначала считается профиль изменения, он решает глубину.

Главное правило. Полный прогон на правке комментария — налог, из-за которого гейт начинают обходить; пропуск без следа — ложная зелень. Глубина адаптивная, но любой пропуск фиксируется явной записью с причиной.

<ЖЁСТКИЙ-ШЛЮЗ> По умолчанию — только проверка и отчёт. НЕ переписывай бизнес-логику и НЕ меняй метаданные по своей инициативе. Правки — только в режиме --fix, из безопасных категорий. Critical и любые изменения логики, проведения, запросов, прав — никогда без явного подтверждения пользователя. </ЖЁСТКИЙ-ШЛЮЗ>


Инварианты прогона

Девять утверждений: усвоен только этот блок — прогон ещё имеет смысл; нарушено любое — уже нет.

  1. Профиль считается один раз, до контуров; контуры его не пересчитывают.
  2. Глубина — по профилю, не по привычке: не гонять архитектуру на опечатке, не ограничиваться гигиеной на новом модуле проведения.
  3. Контур исполняется вызовом навыка, а не по памяти.
  4. Строку следа печатает инструмент, где он есть — не модель по смыслу.
  5. Любой пропуск — запись skipped с причиной. Молчание неотличимо от выполнения.
  6. Вердикт «чисто» признаёт непроверяемое — записью not_verified.
  7. Находка — с номером стандарта, кодом диагностики или эвристикой и значением против порога; 🔴/🟠 блокируют «Чисто», вне --fix — только отчёт.
  8. Гейт снимается утилитой gate.mjs release, а не удалением файла состояния.
  9. План печатает gate.mjs plan; модель вправе поднять глубину с причиной, понизить — нет.

Шаг 1. План

Путь к инструментам плагина ($QG)

Все команды ниже используют $QG — каталог плагина. Под OpenCode он готов в QG_ROOT; в Claude Code CLAUDE_PLUGIN_ROOT оболочке не видна — путь схлопнулся бы в /tools/....

Разреши путь первой командой прогона, дальше подставляй значение буквально. Кандидат — только после test -d "$QG/tools": переменная сама по себе не гарантирует актуальный плагин.

QG="${QG_ROOT:-}"
[ ! -d "$QG/tools" ] && QG="${CLAUDE_PLUGIN_ROOT:-}"
[ ! -d "$QG/tools" ] && QG="$(node -e "const p=require(require('node:os').homedir()+'/.claude/plugins/installed_plugins.json').plugins;const k=Object.keys(p).find(n=>n.startsWith('1c-quality-gate@'));if(k&&p[k][0])process.stdout.write(p[k][0].installPath)" 2>/dev/null)"
[ ! -d "$QG/tools" ] && QG="$(ls -d ~/.claude/plugins/cache/*/1c-quality-gate/*/ 2>/dev/null | sort -V | tail -1)" && QG="${QG%/}"
test -d "$QG/tools" && echo "$QG" || { echo "Плагин не найден ни в одном харнессе" >&2; exit 1; }

sort -V обязателен: без него берётся устаревшая версия плагина (references/run-environment.md).

Профиль и план прогона

node "$QG/tools/gate.mjs" plan --files <f> [<f> ...] [--no-analyzer]

Без --files — состав из взведённой сессии (gate.mjs status). Печатает профиль по трём осям, строку scope (с config=...), инструменты по порядку, что закрыть в следе. --no-analyzer — когда анализатор недоступен или падает; ось сложности тогда not_computed, driver не бывает complexity:*.

Строку [qg scope: ...] перенеси в отчёт дословно — валидатор пересчитывает профиль сам. Расхождение — сверь порог: node "$QG/tools/config.mjs" show. Профиль можно поднять, нельзя понизить: увидел то, чего план не видит (правка тише архетипа, но трогает деньги) — подними, добавь «глубина поднята: <причина>»; обратное валидатор не пропустит.

Почему оси устроены так — references/profile-axes.md; данные — tools/profile.mjs, tools/gate.mjs.


Шаг 2. Инструменты

Выполни команды раздела «## Инструменты» плана по порядку — файлы уже подставлены. У части есть исполняемый инструмент: он печатает строку следа ([qg applied: ...] / [qg skipped: ...]) сам — переноси дословно, не сочиняй.

Прогон отмечается в журнале qg-runs.jsonl: валидатор сверяет по нему каждую запись applied — инструмент видел весь состав правки, а не файл из десяти; skipped ... reason=not_applicable — тоже утверждение о работе. Прогоны по частям складываются. platform-context-run.mjs, когда включён планом, обязателен: анализатор знает имена конфигурации, но не платформы. query-lint берёт и изменённые XML, не только .bsl.

Перед повторным прогоном слоя проверь gate.mjs status: отработавший по этому содержимому слой пропускается с причиной verified_earlier (references/evidence-format.md).


Шаг 3. Контуры

Запускай контуры и глубины из resolved: code=... arch=... xml=... hygiene=... плана. Каждый контур обязан вернуть applied либо skipped — молчание не допускается.

Контур Навык
code bsl-code-review
arch bsl-architecture-review
xml xml-structure-review
hygiene file-hygiene

Передавай контуру профиль целиком: класс, архетипы, файлы.

Контур исполняется вызовом навыка, а не по памяти

Таблица называет навыки, а не проверки: чем проверяется признак — в SKILL.md контура. Прогон без открытия навыка — чеклист для чтения глазами, не проверка (references/run-environment.md).

Субагенты в составе прогона

Четыре штатных, только читающие: возвращают факты, запись формирует контур. Три дешёвые; antipattern-reader — на модели уровня контура: семантика LLM-антипаттернов не механизируется до haiku.

Субагент Вызывает Факты Спавнов
bsl-verifier code сигнатуры платформы, экспортность модулей, метаданные на список файлов
antipattern-reader code кандидаты с цитатами; аттестует tools/catalog.mjs на контур
bsl-scout arch вызывающие, экспорты, триггеры XML по вопросу
xml-runner xml «диск ↔ состав», валидаторы структуры на контур

Недоступность субагента, контура или инструмента проверку не отменяет — она проходит сама либо получает skipped с причиной (references/run-environment.md).


Слой 3: состязательный аудит

Самая дорогая проверка, никогда не запускается сама: контуры предлагают её при классе C3 с находками 🔴/🟠, запуск — только по явному согласию пользователя (references/adversarial-audit.md).


Шаг 4. Sentinel — проверка живости источника стандартов

Один раз за прогон запроси через MCP v8std стандарт, чей номер — sentinel.id настройки (умолчание std454): v8std_get_page("<id>"), ожидание — страница найдена.

Без неё «нарушений не найдено» неотличимо от «сервис недоступен»: неподтверждённый часовой делает прогон недостоверным (references/run-environment.md).


Шаг 5. Отчёт и след

Отчёт для человека — находки по важности (🔴 Critical / 🟠 Major / 🟡 Minor). Ниже, в ## quality evidence, — машиночитаемый след: строка на проверку (references/evidence-format.md).

Каждая запись not_verified повторяется в человекочитаемой части фразой: «Ошибок: 0» рядом с невидимым not_verified читается как «проверено».

Минимум следа: scope (Шаг 1), sentinel (Шаг 4), запись от каждого контура и всё из плана «Закрыть в следе» — включая not_verified: dimension=compilation, если платформа не запускалась (её не проверяет ничто другое).

Сработал архетип «Запрос» — выполни запрос до вердикта (консоль запросов, тестовые параметры): текст остаётся строковым литералом, «Неоднозначное поле» доживает до продуктива. Выполнить негде — законный исход, но записанный:

[qg applied: layer=code, scope=query-execution, ids=[qg:QRY-EXECUTED], verdict=clean]
[qg not_verified: dimension=query-execution, reason=no_platform]

Файл, до которого не добрался анализатор, не проверен — analyzer-run.mjs печатает запись сам, переноси дословно.

node "$QG/tools/evidence-validator.mjs" <файл отчёта> --gate

Шаг 6. Снятие гейта

Гейт снимается только утилитой, не удалением файла состояния:

node "$QG/tools/gate.mjs" release --evidence <файл отчёта>        # по результатам прогона
node "$QG/tools/gate.mjs" release --class C0 --reason "<почему>"  # C0/C1 без прогона

Отчёт — во временный каталог, не в проект: release копирует его в архив вне git; ссылайся на путь Копия отчёта: (references/run-environment.md).

НЕ снимай гейт, если прогон прерван на полпути и отчёт не сформирован: он должен остаться для повторной проверки.

Чужие сессии не трогай. Гейт разделён по сессиям: несколько в gate.mjs status — verify/release без --session <id> отказывают, «самая свежая» может быть чужой. Идентификатор — в подсказке при взводе и в сообщении о блокировке (references/run-environment.md).

Files (1c-quality-gate)
  • references
    • adversarial-audit.md 9.5 KB
      # Состязательный аудит (Слой 3)
      
      Самая дорогая проверка плагина и единственная, которая **никогда не запускается сама**.
      Контуры лишь предлагают её в отчёте; запуск — только после явного согласия пользователя.
      
      Общий ресурс для контуров `bsl-code-review` и `bsl-architecture-review`: методология одна,
      различаются лишь измерения веера.
      
      ## Зачем нужен отдельный слой
      
      Обычное ревью ищет подтверждения: модель формулирует находку и на ней останавливается.
      Состязательный аудит устроен наоборот — каждую находку **пытаются опровергнуть**, и она
      проходит, только если опровергнуть не удалось.
      
      Это прямое продолжение принципа контр-сигнала: ложная находка провоцирует переделку рабочего
      кода. На крупном изменении, где находок десятки, доля ложных растёт, а цена каждой — чужое
      время и подорванное доверие к инструменту.
      
      Второе назначение — покрытие. Один проход смотрит на код одним взглядом; веер по измерениям
      находит то, что при последовательном чтении сливается в фон.
      
      ## Когда предлагать
      
      Предлагать в отчёте, если выполнено **хотя бы одно**:
      
      - класс изменения C3 и найдена хотя бы одна находка уровня 🔴 либо 🟠;
      - затронуты проведение документов, транзакции, права или интеграция — и объём выше C1;
      - пользователь явно просил «глубокий аудит», «будь исчерпывающим», «проверь тщательно»;
      - изменение уходит в продуктивную среду и откат дорог.
      
      **Не предлагать** при C0/C1, при чистом вердикте без находок, при эталонной правке
      (калька типового механизма, прошедшая предметный верификатор).
      
      Формулировка предложения в отчёте — одной строкой, с честной ценой:
      
      ```
      Рекомендую состязательный аудит: класс C3, найдено 🔴 2 / 🟠 5.
      Это N параллельных ревьюеров и по 3 проверяющих на находку — заметно дороже обычного
      прогона. Запускать?
      ```
      
      ## Схема прогона
      
      Три стадии. Вторая и третья — то, чего нет в обычном ревью.
      
      **1. Веер по измерениям.** Независимые ревьюеры, каждый смотрит на изменение под своим углом
      и не видит выводов остальных. Изоляция принципиальна: общий контекст порождает согласие, а
      нужны независимые мнения.
      
      Измерения для контура кода: корректность механики платформы · запросы и производительность ·
      транзакции и блокировки · обработчики событий объектов · клиент-серверное взаимодействие ·
      безопасность и права · антипаттерны кода, порождаемого моделью.
      
      Измерения для контура архитектуры: распределение ответственности · границы и контракты ·
      связанность модулей · дублирование и ветвление вместо диспетчера · переусложнение (обратная сторона).
      
      **2. Состязательная проверка каждой находки.** По каждой находке — несколько независимых
      проверяющих, которым поставлена задача **опровергнуть** её, а не подтвердить. Формулировка
      задания принципиальна: «попробуй опровергнуть, при сомнении считай опровергнутой».
      
      Лучше давать проверяющим **разные линзы**, а не копии одного вопроса: воспроизводится ли
      дефект на конкретных входных данных · нарушает ли это правило платформы, а не вкус ·
      существует ли законная форма, в которой такой код корректен (контр-сигнал).
      
      **3. Синтез.** Собрать выжившие находки, снять дубликаты по ключу локации, отсортировать по
      важность. Опровергнутые не выбрасываются молча — они попадают в отчёт отдельным списком с
      причиной опровержения: это защита от повторного «открытия» той же находки на следующем прогоне.
      
      ## Пороги
      
      | Параметр | Значение по умолчанию | Когда менять |
      |---|---|---|
      | Ревьюеров в веере | по одному на измерение | «быстро» — только измерения с находками из Слоя 1 |
      | Проверяющих на находку | 3 | «исчерпывающе» — 5 |
      | Порог прохождения | большинство не опровергло | 🔴 требуют единогласия для отклонения |
      | Максимум находок на проверку | 20 самых тяжёлых | больше — сначала синтез, потом второй круг |
      | Одновременных задач | 8 | среда с жёстким лимитом на конкурентные задачи — 4 |
      
      **Потолок параллелизма объявлен явно** по той же причине, что и бюджет обращений к индексу
      кода в контуре архитектуры. Без него вторая стадия на двадцати находках попытается развернуть
      шестьдесят проверяющих разом: часть упрётся в лимит среды, часть оборвётся на середине, а
      аудит отчитается по неполным данным и будет выглядеть выполненным. Веер запускается волнами по
      потолку, пока не пройдены все находки. Недобранная волна — это не «проверено», а причина
      остановиться и сказать об этом записью `skipped`.
      
      **Асимметрия для 🔴 намеренная.** Находку уровня Critical отклоняют, только если **все**
      проверяющие сочли её ложной. Пропустить настоящий Critical дороже, чем потратить время на
      разбор спорного.
      
      ## Если механизм параллельного запуска недоступен
      
      Оркестрация субагентов есть не в каждой среде и требует явного разрешения пользователя.
      Без неё аудит выполняется **последовательно и в сокращённом виде**: измерения с находками из
      предыдущих слоёв прогоняются по очереди, состязательная проверка делается для находок 🔴 и 🟠.
      
      Это дольше по времени, но методологически то же самое. Чего делать нельзя — молча
      пропустить слой, о котором заявлено в отчёте: тогда пользователь считает аудит выполненным.
      Пропуск фиксируется записью:
      
      ```
      [qg skipped: layer=code, scope=adversarial-audit, reason=orchestration_unavailable]
      ```
      
      ## Записи следа
      
      ```
      [qg applied: layer=code, scope=adversarial-audit, ids=[std436,std783], verdict=violation:std436]
      [qg applied: layer=arch, scope=adversarial-audit, ids=[qg:ARCH-A1], verdict=clean]
      ```
      
      В отчёте дополнительно указывается, сколько находок не пережило проверку — это показатель
      качества первых двух слоёв. Если опровергается больше половины, проблема не в аудите, а в
      порогах контуров: они выдают шум, и чинить нужно их.
      
    • evidence-format.md 42.7 KB
      # Формат следа проверок (evidence)
      
      Слова, которые плагин употребляет как свои — контур, архетип, признак, эвристика, часовой,
      след прогона, — объяснены в `glossary.md`.
      
      Машиночитаемый след прогона. Живёт в отчёте, в секции `## quality evidence`, по одной
      строке на проверку. Проверяется `tools/evidence-validator.mjs`.
      
      ## Зачем он нужен
      
      Невыполненная проверка неотличима от выполненной, если после неё ничего не остаётся.
      Отчёт «нарушений не найдено» одинаково выглядит и когда всё проверено и чисто, и когда
      инструмент не запустился, и когда слой просто забыли. След делает разницу видимой:
      каждая проверка оставляет запись, **включая обоснованный пропуск**.
      
      Валидатор отвергает записи, которые лишь выглядят заполненными: пустое обязательное поле
      или пустой список идентификаторов — это не «проверил ничего», а отсутствие проверки.
      
      ## Грамматика
      
      ```
      [qg <тип>: <ключ>=<значение>, <ключ>=[<элемент>,<элемент>], ...]
      ```
      
      Значение — либо скаляр без запятых и скобок, либо список в квадратных скобках.
      
      ## Типы записей
      
      ### `scope` — профиль изменения (ровно одна за прогон)
      
      ```
      [qg scope: volume=C2, files=3, loc=+87/-12, archetypes=[query,transaction],
                 complexity=[nesting:4], driver=archetype:transaction,
                 resolved=code:L2|arch:skip|xml:n/a|hygiene:full,
                 config=custom:volume+sentinel]
      ```
      
      Обязательные поля: `volume`, `files`, `archetypes`, `driver`, `resolved`, `config`.
      
      `volume` — один из `C0`, `C1`, `C2`, `C3`. `archetypes` — список сработавших меток, либо
      `[none]`, если ни одна не сработала (пустой список запрещён — он неотличим от «не считали»).
      `driver` — что подняло глубину: `volume`, `archetype:<имя>` или `complexity:<метрика>`.
      
      **Метки берутся из колонки «Метка в следе»** таблицы архетипов в `../SKILL.md`; проектные —
      из `archetypes.custom` настройки. Список закрытый, и это не педантизм: на метках завязаны
      требования валидатора, а поле пишет модель — инструмент его не печатает. `queries` вместо
      `query` не «почти то же самое», а молчаливое неприменение правила, поэтому незнакомую метку
      валидатор называет и снятие гейта не пропускает.
      
      `config` — настройка проекта, применённая к этому прогону: `default`, если все пороги
      умолчаний, либо `custom:<секция>[+<секция>]` с перечнем переопределённых секций
      (`analyzer`, `volume`, `complexity`, `archetypes`, `sentinel`). **Строку печатает
      `node "$QG/tools/config.mjs" show`** — её переносят в запись, а не сочиняют: сочинённая
      отметка ничего не доказывает.
      
      Без неё «C1» из одного отчёта не означает того же, что «C1» из другого, а прогон, не
      заглянувший в настройку проекта, неотличим от прогона, который её учёл. Снятие гейта такую
      запись не пропустит; при обычном линте отсутствие поля — предупреждение, чтобы отчёты,
      собранные до его появления, оставались читаемыми.
      
      **Поле сверяется с фактической настройкой проекта, а не принимается на слово.** Приписать
      `config=default` там, где пороги задраны, не сложнее, чем забыть посмотреть настройку, — и
      последствия те же. Расхождение блокирует снятие гейта и называет оба значения. Отсюда же
      следует: если настройка изменилась после прогона, след устарел вместе с профилем и прогон
      надо повторить.
      
      **`volume` и `archetypes` тоже сверяются, но с расчётным профилем** — тем, что по файлам
      сессии вернул бы `computeProfile` (`tools/profile.mjs`). Правило асимметрично: модель вправе
      поднять глубину, но не понизить — заявленный `volume` ниже расчётного или отсутствие в
      `archetypes` сработавшей метки блокируют снятие гейта. Строка `scope` из `gate.mjs plan`
      проходит всегда: план считает профиль той же функцией по тем же файлам. Расхождение значит,
      что после плана файлы сессии изменились, — план печатается заново.
      
      ### `applied` — проверка выполнена
      
      ```
      [qg applied: layer=code, scope=query-in-loop, ids=[std436,bslls:QueryInLoop], verdict=clean]
      [qg applied: layer=arch, scope=module-responsibility, ids=[qg:ARCH-A1], verdict=violation:qg:ARCH-A1]
      ```
      
      Обязательные поля: `layer`, `scope`, `ids`, `verdict`.
      
      `layer` — `code`, `arch`, `xml` или `hygiene`. `scope` — что именно проверялось, **из
      закрытого словаря** `tools/evidence-scopes.mjs`. `ids` — непустой список идентификаторов:
      `stdNNN`, `bslls:<Код>`, `acc:NNN`, `v8cs:<код>`, `qg:<ЭВРИСТИКА>`, `patterns:<путь>`.
      `verdict` — `clean` либо `violation:<id>`.
      
      **Пространство `qg:` — тоже закрытый список**, реестр `QG_IDS` в том же
      `tools/evidence-scopes.mjs`. Пока идентификатор проверялся только по форме, проходило любое
      правдоподобное имя: в живой сессии больше половины `qg:*` в отчётах не существовало в плагине
      (`qg:XML-VALID` вместо `qg:XML-STRUCT`, выдуманный `qg:SKD-VALID`), и отчёт с вымышленными
      признаками выглядел строже настоящего. Неизвестный `qg:*` — ошибка, а не предупреждение.
      Чужие пространства (`std`, `bslls`, `acc`, `v8cs`) по-прежнему проверяются формой: их реестры
      не наши.
      
      **Почему словарь закрытый.** Пока проверялся только формат имени, проходило любое похожее
      слово — и этим пользовалась не злая воля, а документация: одну проверку называли
      `static-diagnostics`, `lsp-diagnostics` и `static-analysis` в трёх разных местах. Запись со
      свободным именем выглядит проверкой, но не закрывает ни одного требования: имена измерений и
      имена проверок сведены в одно пространство, и требование ищется по точному совпадению.
      Валидатор называет замену для каждого прежнего имени.
      
      **Проверка с инструментом заявляется только по прогону.** Строку `[qg applied: ...]` пишет в
      отчёт модель, и написанная по прочтении кода она неотличима от полученной прогоном —
      наблюдалось четыре раза подряд в одной живой сессии, причём вердикты случайно оказались
      верны. Поэтому инструменты печатают свою строку сами и отмечаются в журнале
      `.claude/.state/qg-runs.jsonl`, а валидатор в строгом режиме сверяет с ним каждую запись
      `applied`, чей `scope` есть в списке проверок с инструментом — полный словарь имён —
      `tools/evidence-scopes.mjs` (поле `tool` у записи `SCOPES`); тот же список печатает
      `gate.mjs plan` для конкретной правки.
      
      Отметка должна быть **не старше последней правки файлов** своей сессии: прогон, сделанный до
      правки, описывает состояние, которого уже нет. Это то же правило, по которому гейт снимает
      отметки проверенного при изменении файла.
      
      **Сверяется и покрытие.** В журнале лежат пути файлов, которые инструмент видел, и они
      сопоставляются с составом правки из состояния гейта: прогон по одному файлу из десяти больше
      не закрывает заявление обо всех десяти. Складываются все прогоны — гонять инструмент по
      частям законно, требование к результату, а не к способу. Файл засчитывается и тогда, когда в
      журнале записан содержащий его каталог: валидаторам XML путь дают то файлом, то каталогом
      объекта.
      
      Непокрытый файл — ошибка там, где инструмент применим к каждому файлу своего расширения
      (гигиена, проверки `.bsl`), и предупреждение для проверки структуры: часть служебных XML
      выгрузки не проверяет ни один валидатор, и отказ на них был бы находкой за отсутствующий
      инструмент.
      
      **`skipped` с причиной `not_applicable` тоже требует отметки.** Это утверждение о работе
      инструмента — «посмотрел файлы, правило к ним не относится», — и без требования оно было бы
      дырой шире исходной: такой записью закрывается любая проверка. То же относится к
      `no_queries_found` (query-lint смотрел файлы и запросов не нашёл), `no_metadata_resolved`
      (bsl-lint не нашёл XML объекта рядом с модулем) и `unreadable` (`catalog.mjs attest`
      самостоятельно проверил файлы и часть не читается — не принял заявление читателя на слово).
      Инструменты ставят отметку и на этих исходах. Прочие причины (`analyzer_unavailable`,
      `contour_not_installed`, `reader_unavailable`) отметки не требуют: ставить её некому — читатель
      или инструмент не запускался вовсе.
      
      **Проходы по каталогу антипаттернов заявляются всегда, когда запущен контур кода.** Записи
      `scope=ai-antipatterns` и `scope=platform-antipatterns` печатает режим `attest` инструмента
      `catalog.mjs` по результату субагента-читателя: он сверяет `examined`, `files`, каждую находку
      и каждый заявленный `unreadable` файл (не существует или не читается по факту, а не потому что
      так написал читатель) с каталогом и с содержимым файлов, и только тогда пишет журнал и
      печатает строку следа. Без записи об этих проходах строгий режим отказывает, обычный линт
      предупреждает. Причина: проходы делает модель, и без записи их пропуск неотличим от
      выполнения.
      
      Чего сверка не делает: она не защищает от записи, дописанной в журнал вручную. В отличие от
      поля `config`, где истина заново выводится с диска, независимого источника здесь нет. Это
      обнаружение молчания — и сказано об этом прямо, чтобы никто не принял его за большее.
      
      **Признак `needs: [diff]` (сегодня — `qg:AI-11`) не заявляется без сравнения версий.** Он
      виден только в дифе, а не в теле метода как он есть сейчас — читателю без оболочки его взять
      негде. Основание, которое `attest` признаёт, — только то, что сам вернул `git diff HEAD --`
      по файлам прогона: подстрочная сверка переданного файла с текстом, «упоминающим имя файла»,
      пропускала обычный текст, дифф без единого hunk и дифф ЧУЖОГО файла с именем целевого,
      дописанным в комментарий (ревью round 1, task-22) — три фикстуры, каждая с `ok: true`.
      `--diff <файл>` остаётся входом для читателя (путь к `git diff HEAD --`, сохранённому
      оркестратором до делегирования), но attest ему не верит на слово: если файл передан, его
      заголовки `diff --git` и `@@` сверяются с тем, что вернул git по тем же файлам, — расхождение
      называет, чего не хватает или что лишнее. Без непустого git diff карточка с `needs: [diff]`
      в `examined` не бывает независимо от того, передан ли `--diff`.
      
      Сравнивать легитимно нечем — новый файл без истории, репозиторий без git — тогда единственный
      путь: `attest --no-diff-available` печатает честную запись вместо молчания:
      
      ```
      [qg skipped: layer=code, scope=ai-antipatterns-diff, planned=[qg:AI-11], reason=no_diff]
      ```
      
      ### `logic-review` — слой 2 контура кода не оставляет иного следа
      
      `advisor()` и, при высокой цене ошибки, холодный читатель (`../../bsl-code-review/SKILL.md`,
      слой 2) не печатают ничего, кроме того, что впишет в отчёт модель — в отличие от инструментальных
      проверок, здесь нет журнала, который отличил бы прогон от его пропуска. A/B-прогон на живой
      правке класса C3 показал: пропуск слоя 2 не заметил никто, потому что заметить было нечему.
      
      ```
      [qg applied: layer=code, scope=logic-review, ids=[qg:...], verdict=clean]
      [qg skipped: layer=code, scope=logic-review, reason=advisor_unavailable]
      ```
      
      Требование адресное: контур `code` дошёл до `L2` (поле `resolved` записи `scope`) — запись
      обязана быть одной из двух форм. Отсутствие записи на `L2` — ошибка строгого режима.
      
      **Находка слоя закрывается записью `violation`, как и любая другая.** Каждая находка 🔴 и 🟠
      в тексте отчёта обязана иметь в следе запись `verdict=violation:<id>` хотя бы по одному
      идентификатору, который в ней назван. Дефект логики, у которого нет своего признака, пишется
      одним из двух:
      
      - `qg:LOGIC-CONTRACT` — код расходится с заявленным поведением: справкой, комментарием,
        описанием метода, подписью в интерфейсе;
      - `qg:LOGIC-CASE-LOSS` — часть допустимых входов молча теряется или обрабатывается не так:
        неполный разбор значений, отсечение соединением, пустой результат вместо ошибки.
      
      ```
      [qg applied: layer=code, scope=logic-review, ids=[qg:LOGIC-CONTRACT], verdict=violation:qg:LOGIC-CONTRACT]
      [qg applied: layer=code, scope=logic-review, ids=[qg:LOGIC-CASE-LOSS], verdict=violation:qg:LOGIC-CASE-LOSS]
      ```
      
      Одна запись — одно нарушение. Инструментальный признак (`qg:BSL-DISPATCH-NO-FALLBACK` и
      подобные) руками не заявляется и как нарушение: если дефект того же рода найден там, куда
      инструмент не достаёт (текст запроса внутри XML схемы компоновки), это `qg:LOGIC-CASE-LOSS`.
      
      Почему это правило. В третьем A/B блокирующая находка слоя 2 и ещё одна существенная стояли
      только в тексте отчёта: признака под них не было, модель записала `qg:LOGIC` прозой, а след
      остался без единого нарушения по ним — и валидатор принял отчёт с нулём предупреждений. Теперь
      валидатор разбирает текст отчёта и называет каждую находку 🔴/🟠 без покрывающей записи.
      Разбор прозы приближённый (важность берётся из заголовка, отклонённые и непроверенные разделы
      пропускаются), поэтому это предупреждение, а не ошибка, и ошибкой оно не станет.
      
      ### `skipped` — проверка не выполнялась, и это заявлено
      
      ```
      [qg skipped: layer=arch, reason=volume_below_threshold]
      [qg skipped: layer=code, scope=static-analysis, planned=[bslls:*], reason=analyzer_unavailable]
      ```
      
      Обязательные поля: `layer`, `reason`.
      
      Типовые причины: `volume_below_threshold`, `not_applicable`, `no_queries_found`,
      `no_metadata_resolved`, `unreadable`, `contour_not_installed`, `analyzer_unavailable`,
      `reader_unavailable`, `rlm_unavailable`, `platform_unavailable`, `stale_or_unavailable_index`,
      `verified_earlier`.
      
      Отметки в журнале требуют причины, утверждающие, что инструмент СМОТРЕЛ файлы:
      `not_applicable`, `no_queries_found`, `no_metadata_resolved`, `unreadable`. `reason=unreadable`
      печатает `catalog.mjs attest`, когда среди переданных файлов есть настоящие нечитаемые —
      `reader_unavailable`, напротив, отметки не требует: это значит, что читатель вообще не
      запускался, ставить отметку некому.
      
      `no_queries_found` и `not_applicable` — разные утверждения: первое значит «инструмент файлы
      ЧИТАЛ и запросов не нашёл», второе — «правило к файлам этого вида не относится». Файл, чьи
      запросы инструмент не умеет читать, прятался бы за вторым — поэтому query-lint его больше
      не пишет.
      
      ### `not_verified` — измерение непроверяемо доступными средствами
      
      ```
      [qg not_verified: dimension=compilation, reason=no_platform]
      [qg not_verified: dimension=query-execution, reason=no_platform]
      ```
      
      Обязательные поля: `dimension`, `reason`.
      
      Отличие от `skipped`: `skipped` — «слой можно было прогнать, но не требовалось или инструмент
      лежал»; `not_verified` — «этого в принципе нельзя проверить тем, что есть».
      
      Измерения: `compilation`, `query-execution`, `static-analysis`, `cross-config-resolution`,
      `artifact-freshness`. Последнее печатает `gate.mjs release`, когда собранный артефакт старше
      своих исходников (пары «исходники → артефакт» — секция `artifacts` настройки проекта);
      `static-analysis` и `cross-config-resolution` печатает `analyzer-run.mjs` сам:
      
      ```
      [qg not_verified: dimension=static-analysis, reason=parse_failed, files=3]
      [qg not_verified: dimension=static-analysis, reason=not_in_analyzer_report, files=2]
      ```
      
      `parse_failed` — файл не разобрался, и остальные находки по нему получены на обрывке дерева.
      `not_in_analyzer_report` — изменённый файл движок не видел вовсе: отсечён фильтром подсистем,
      не передан. Пока этой записи не было, такой файл давал результат, неотличимый от чистого.
      Число непроверенных файлов уходит в журнал прогонов, и молчание о них блокирует снятие гейта.
      
      Выгрузки внешних обработок и отчётов под эту запись больше не попадают: у них нет
      `Configuration.xml`, поэтому корень ищется по дескриптору `X.xml` рядом с каталогом `X`, и
      такая выгрузка разбирается вторым проходом от своего корня. Состава конфигурации в ней нет
      по устройству формата — об этом печатается отдельная оговорка:
      
      ```
      [qg not_verified: dimension=cross-config-resolution, reason=standalone_artifact_without_configuration, roots=1]
      ```
      
      Имя причины своё, а не общее с расширением: у расширения основную конфигурацию можно доложить
      в проект и разрешение восстановится, у внешней обработки — нет.
      
      Список измерений закрытый: `compilation`, `query-execution`, а также `static-analysis` и
      `cross-config-resolution` — последние два печатает `analyzer-run.mjs`, когда файл не разобран
      или основной конфигурации в проекте нет. Опечатка вроде `query_execution` оставила бы запись,
      которая выглядит заполненной, но требуемое измерение не закрывает; валидатор такое имя
      называет.
      
      **`compilation` — компилируемость тел модулей.** Ни загрузка конфигурации из файлов, ни
      выгрузка, ни валидаторы XML не компилируют тела: они разбирают структуру. Синтаксическая
      ошибка внутри процедуры проходит их все и всплывает лишь при инициализации модуля в базе.
      Ловит только проверка конфигурации платформой или реальный запуск.
      
      **`query-execution` — выполнимость запроса.** Близнец предыдущего. Текст запроса — строковый
      литерал: для анализатора это строка, для сборки бинарника — строка, для валидатора XML —
      строка. Проверки по тексту кода (`qg:QRY-ALIAS-SHADOWS-FIELD`) снимают один класс дефектов, но
      «Поле не найдено», несовместимость типов в `ОБЪЕДИНИТЬ` и опечатка в имени параметра
      виртуальной таблицы обнаруживаются только выполнением.
      
      Измерение считается закрытым двумя способами: оно проверено — тогда есть запись `applied` с
      тем же именем в `scope`; либо признано непроверяемым — тогда есть `not_verified`.
      
      Запись `not_verified` обязана прозвучать и в человекочитаемой части отчёта — одной фразой на
      запись. Валидатор этого не проверяет и проверить не может, но читатель отчёта видит «Ошибок: 0»
      и не видит след: измерено на живом разборе, что такая пара дважды создала ложную уверенность
      в проверенности запроса. Формулировка называет и вывод, и его границу: «текст запроса
      статически чист; выполнимость не проверялась — платформы нет».
      
      ```
      [qg applied: layer=code, scope=query-execution, ids=[qg:QRY-EXECUTED], verdict=clean]
      ```
      
      Что требует строгий режим:
      
      | Условие прогона | Требование |
      |---|---|
      | Все проверки `clean` | закрыт `compilation` |
      | В `archetypes` есть `query` | закрыт `query-execution` |
      
      Первое правило именно по измерению, а не «хотя бы одна запись `not_verified`»: иначе каждое
      новое измерение ослабляло бы проверку — прогон заявляет непроверенным что-нибудь одно, о
      компилируемости молчит, и полностью зелёный отчёт снова проходит.
      
      **Обе записи — заявление, а не доказательство.** Валидатор не умеет проверить, что запрос
      действительно выполнялся или что конфигурация действительно компилировалась: он требует
      сознательного выбора между «проверил» и «проверить было нечем». Это слабее сверки поля
      `config`, которую валидатор пересчитывает сам, и сильнее молчания, которое ничего не значит.
      
      ### `sentinel` — источник жив
      
      ```
      [qg sentinel: target=v8std, id=std454, status=found]
      [qg sentinel: target=bslls, id=CommonModuleInvalidType, status=found, engine=bsl-analyzer@0.2.73]
      ```
      
      Обязательные поля: `target`, `status` (`found` либо `not_found`).
      
      Без этой записи «нарушений не найдено» неотличимо от «источник недоступен». Прогон с
      `status=not_found` считается недостоверным.
      
      **Часовой проверяется по целям, а не «хотя бы один живой».** Иначе подтверждённый `v8std`
      маскирует мёртвый анализатор: в следе стоит `bslls:…` с вердиктом `clean`, рядом живой
      часовой по `v8std` — формально правило выполнено, а про анализатор не известно ничего.
      
      Правило: каждый вердикт `clean` опирается на источник, доказавший в этом прогоне, что он жив.
      
      | Идентификаторы в `ids` | Цель часового |
      |---|---|
      | `stdNNN`, `acc:NNN`, `v8cs:<код>` | `v8std` |
      | `bslls:<Код>`, `bslls:*` | `bslls` |
      | `qg:<ЭВРИСТИКА>`, `patterns:<путь>` | не требуется: это наши эвристики, внешнего источника у них нет |
      
      Часовой по `bslls` устроен как **фикстура**: мини-конфигурация из состава плагина с заведомо
      неверным сочетанием флагов общего модуля прогоняется тем же вызовом и тем же конфигом, что и
      рабочие файлы. Ожидается `CommonModuleInvalidType` — диагностика, читающая **метаданные**.
      Выбор не случаен: при потере контекста конфигурации гаснет именно этот класс проверок, а
      обычные замечания продолжают приходить, и отчёт выглядит рабочим. Часовой, доказывающий лишь
      «хоть что-то сработало», такое ухудшение пропустит.
      
      Записи `sentinel` и `applied` по слою `code` формирует `tools/analyzer-run.mjs` — вручную их
      писать не нужно.
      
      `bslls:*` — законная форма для чистого прогона: перечислять полторы сотни проверенных кодов
      бессмысленно. Нарушения, наоборот, выводятся по записи на код.
      
      ## Отклонённая находка: обоснование через последствие
      
      Формат требует обоснования от находки — сигнал против измеренного порога — и до сих пор ничего
      не требовал от обратного решения, «здесь это не важно». А пишет его тот же, кто писал код, и
      звучит оно короче любой находки: «пустая ветка смысла не несёт», «другого признака всё равно
      нет». Так самое слабое звено отчёта оказывается единственным местом без правил.
      
      Порядок обратный привычному: сначала назови **последствие** — что произойдёт, если инструмент
      прав, — и лишь потом объясняй, почему оно невозможно либо приемлемо.
      
      ```
      🟡 bslls:<Код> — <где>: <почему принято>
         Последствие, если находка верна: <что сломается и как это проявится>
      ```
      
      Разница видна сразу. «Ветвление по типу документа без `Иначе`» отклонить легко, пока не
      написано последствие: документ неизвестного вида уходит во внешнюю систему, не пройдя ни одной
      проверки. «Поиск справочного значения по коду» — то же самое: после переименования элемента
      механизм перестаёт находить документы молча, без ошибки, и обнаружится это расхождением с
      внешней системой.
      
      Правило дешёвое и работает на самом частом сценарии: отчёт пишет тот, кто заинтересован в
      зелёном вердикте. Отклонение без названного последствия — не разбор, а согласие с самим собой.
      
      ### Довод, снимающий находку: источник или прогон
      
      Последствие описывает, что сломается, если находка верна, но ничего не говорит о том, откуда
      взят сам довод «здесь это не так». «Обработчик реально может сработать лишь на повреждённых
      данных — он защитный, а не маскирующий штатный путь» звучит как разбор, а по факту то же
      согласие с собой на один шаг дальше: утверждение о поведении платформы, ничем не подкреплённое.
      
      Правило: **довод, который снимает находку или понижает её важность, обязан опираться на
      прочитанный источник — файл и строку, страницу справочника платформы, пункт стандарта — либо
      на выполненный прогон — вывод инструмента, результат исполненной проверки.** Довода без того
      и другого не бывает — есть только предположение того, кто писал код, а находка существует
      ровно на случай, если это предположение ошибочно.
      
      Пример разрыва. Правка убрала проверку `Ответ.ТелоСтрокой = Неопределено` — кандидат в
      `qg:AI-11` (пропавшая проверка редкого случая при упрощении кода, разбор —
      `../../bsl-code-review/references/catalog/AI-11.md`). Один прогон отклонил находку доводом
      «обработчик исключения реально может сработать лишь на повреждённом gzip или неверном имени
      кодировки — он защитный, а не маскирующий штатный путь», не сославшись ни на строку кода, ни
      на прогон. `Тело` заполняется из `Ответ.ПолучитьТелоКакДвоичныеДанные()`; справочник платформы
      называет типом результата «ДвоичныеДанные, Неопределено», но не называет условие, при котором
      возвращается `Неопределено`, — прочитанный источник вопрос не закрывает. Более ранний прогон
      того же гейта на этом же коде эту неопределённость признал прямо: «Уверенность: средняя. Как
      проверить до правки: … Если … отдаёт `ДвоичныеДанные` нулевой длины, а не `Неопределено`, —
      находка снимается». Довод, отбросивший её позже, не прочитал страницу справочника до конца и
      не выполнил тот прогон — то есть не имел ни источника, ни прогона.
      
      Пока источника или прогона нет, находка **не снимается** — остаётся с понижением уверенности
      и называет проверку, которая её закроет:
      
      ```
      🟡 qg:AI-11 — <где>: обработчик <условие> не задаёт причину возврата `Неопределено`
      Уверенность: средняя. Как проверить: <один прогон или чтение, который решает вопрос>
      ```
      
      **Когда так делать не нужно** — снимать находку без прогона можно, если прочитанный источник
      решает вопрос сам, а не просто упоминает возможность:
      
      - источник закрывает вопрос целиком: страница справочника платформы прямо называет условие
        («возвращает `Неопределено`, если тело ответа пусто») — находка снимается со ссылкой на
        страницу, прогон не требуется;
      - источник допускает исход, но не называет условие (как в примере выше) — вопрос остаётся
        открытым, и снять находку может только прогон; до него она держится с понижением
        уверенности.
      
      Проверяется это чтением текста находки, а не `evidence-validator.mjs`: инструмент видит
      формат записи следа, а не то, на чём держится довод внутри неё.
      
      ## Переиспользование доказательств
      
      Гейт — требование к **текущему состоянию артефакта**, а не просьба ещё раз позвать тот же
      инструмент. Если слой уже отработал по этому содержимому файла, повторный прогон ничего не
      добавляет, кроме расхода времени.
      
      Отметить проверенное:
      
      ```bash
      node "$QG/tools/gate.mjs" verify --layer code <файл> [<файл> ...]
      ```
      
      Отметка хранится в состоянии гейта по паре файл × слой. Перед повторным прогоном слоя
      загляни в `gate.mjs status`: файлы с уже проставленной отметкой можно пропустить, записав
      `skipped` с причиной `verified_earlier`.
      
      **Инвалидация автоматическая.** Любая правка файла снимает все его отметки — это делает хук
      взвода. Устаревшее доказательство переиспользовано быть не может по построению: оно относится
      к другому содержимому.
      
      Это единственный случай, когда пропуск слоя не требует отдельного обоснования: причина
      объективна и проверяема по состоянию.
      
      ## Режимы валидатора
      
      ```bash
      node tools/evidence-validator.mjs <файл>          # lint: только оформление
      node tools/evidence-validator.mjs <файл> --gate   # строгий, для снятия гейта
      ```
      
      Строгий режим дополнительно требует: ровно одну запись `scope`, хотя бы одну
      `applied`/`skipped`, подтверждённый `sentinel` **по каждой цели, на которую опирается вердикт
      `clean`**, закрытое измерение `compilation` при полностью чистом вердикте и закрытое
      `query-execution`, если сработал архетип `query`.
      
      Коды выхода: `0` — чисто, `1` — предупреждения, `2` — блокирующие нарушения.
      
      ## Пример полного следа
      
      ```markdown
      ## quality evidence
      
      [qg scope: volume=C1, files=1, loc=+18/-3, archetypes=[query], complexity=[none], driver=archetype:query, resolved=code:L2|arch:skip|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=static-analysis, ids=[bslls:MissingCodeTryCatchEx], verdict=violation:bslls:MissingCodeTryCatchEx]
      [qg applied: layer=code, scope=query-in-loop, ids=[std436,bslls:QueryInLoop], verdict=clean]
      [qg applied: layer=code, scope=query-alias-shadowing, ids=[qg:QRY-ALIAS-SHADOWS-FIELD], verdict=clean]
      [qg applied: layer=code, scope=attribute-access, ids=[qg:BSL-REF-DOT-ACCESS,std437], verdict=violation:qg:BSL-REF-DOT-ACCESS]
      [qg skipped: layer=arch, reason=volume_below_threshold]
      [qg skipped: layer=xml, reason=not_applicable]
      [qg not_verified: dimension=compilation, reason=no_platform]
      [qg not_verified: dimension=query-execution, reason=no_platform]
      ```
      
      Здесь видно не только что нашли, но и почему архитектурный контур не гонялся, почему XML
      неприменим, что компилируемость осталась непроверенной и что запрос ни разу не выполнялся.
      Последнее — не придирка: правка текста запроса прошла проверку по тексту, но «Поле не
      найдено» без выполнения не обнаружить, и отчёт говорит об этом прямо. Обратите внимание на
      двух часовых: вердикт `clean` в строке про `query-in-loop` опирается сразу на два источника —
      сервис стандартов и анализатор, — и оба обязаны подтвердить, что живы. Именно это отличает
      след от подписи «проверено».
      
    • glossary.md 6.6 KB
      # Словарь: слова, которые плагин употребляет как свои
      
      Здесь объяснены слова, которых нет в языке разработчика 1С и которые придуманы или заимствованы
      внутри этого плагина. Всё остальное — термины платформы и стандартов — объясняется по месту
      употребления либо ссылкой на номер стандарта.
      
      Файл читается один раз: дальше по документам эти слова используются без повторного объяснения.
      
      ## Устройство проверки
      
      **Гейт** — блокировка, которая не даёт объявить работу законченной, пока проверки не прогнаны.
      Взводится при изменении файлов 1С, снимается отчётом о прогоне.
      
      **Контур** — набор проверок одного рода со своим навыком и своими инструментами. Контуров
      четыре: код, архитектура, структура XML метаданных, гигиена файлов. Контур запускается целиком,
      а не выборочными пунктами.
      
      **Оркестратор** — навык `quality-gate`, который смотрит на состав правки, выбирает нужные контуры
      и глубину проверки, запускает их и собирает общий отчёт. Сам проверок не выполняет.
      
      **Профиль изменения** — три ответа о правке, по которым оркестратор выбирает глубину: сколько
      изменено, какого рода этот код, насколько он сложен.
      
      **Архетип кода** — род изменённого кода, опознаваемый по коду и путям файлов: запрос,
      транзакция, интеграция, форма, роль. Одна правка может относиться сразу к нескольким. От архетипа
      зависит, какие проверки обязательны.
      
      ## Проверки и находки
      
      **Признак** — то, по чему находка опознаётся: `qg:BSL-REF-DOT-ACCESS`, `qg:ARCH-A4`, `qg:AI-17`.
      Идентификатор со `qg:` есть в реестре плагина; чужие пространства — `std`, `bslls`, `acc`,
      `v8cs` — принадлежат стандартам разработки и внешним анализаторам.
      
      **Эвристика** — правило распознавания, которое срабатывает по совпадению формы кода, без
      доказательства. Оно даёт находки-кандидаты: часть из них при проверке окажется законной формой.
      Поэтому у эвристики всегда есть контр-сигнал — описание того, когда совпадение не является
      дефектом.
      
      **Контр-сигнал** — форма, в которой найденное законно. Проверка без него рано или поздно
      сработает на нормальном коде, и тогда перестают применять всю проверку целиком.
      
      **Проверка по тексту кода** — разбор исходного текста без выполнения и без обращения к
      метаданным. Так работают быстрые проверки плагина: они видят написанное, но не знают ни типов
      переменных, ни того, что вернёт вызванный метод.
      
      **Часовой** — заведомо дефектный образец кода из состава плагина, который прогоняется вместе с
      проверяемыми файлами. Если анализатор молчит на нём, значит он не работает вовсе, и «чисто» по
      проекту ничего не значит.
      
      **Холодный читатель** — второй просмотр кода моделью, которой не показывают ни задачу, ни
      переписку: только сам код. Он видит программу такой, какой её увидит сопровождение через год.
      
      **Контракт** — договорённость о том, какие данные метод принимает и что возвращает, записанная
      в его описании по #std453: состав полей структуры, колонки таблицы, тип и смысл каждого параметра.
      «Нарушить контракт» значит вернуть не то, что обещано описанием; «доверять контракту» — не
      перепроверять у себя то, что вызванный метод уже обязался обеспечить.
      
      ## Отчёт
      
      **След прогона** — машиночитаемая часть отчёта: по одной строке на каждую выполненную проверку, с
      признаком, областью и вердиктом. Проверяется отдельным инструментом, чтобы вердикт нельзя было
      написать, не запуская проверку.
      
      **Инварианты прогона** — несколько утверждений, без которых результат прогона недействителен.
      Если из всего навыка усвоено только это, прогон ещё имеет смысл.
      
      **Уровень «требует нового шва»** — граница между контуром кода и контуром архитектуры.
      Исправление, которое умещается в замену строк внутри метода, относится к коду. Исправление,
      требующее новой границы между частями программы — выделить метод, перенести его в другой модуль,
      изменить, кто кого вызывает, — относится к архитектуре.
      
    • profile-axes.md 20.2 KB
      # Профиль изменения: оси, матрица глубин и почему они устроены так
      
      Оркестратор считает профиль по трём осям и по нему выбирает глубину контуров. С Task 14 это
      делает инструмент: таблицы и пороги из этого файла — данные `tools/profile.mjs`
      (`ARCHETYPES`, функция `computeProfile`) и `tools/gate.mjs` (`TOOL_ORDER`, построение плана).
      `node "$QG/tools/gate.mjs" plan` печатает результат для конкретной правки — считать оси в
      голове по таблицам не нужно.
      
      Здесь — то же самое для чтения: расшифровка таблиц из кода и причины, почему ось устроена
      именно так, что она закрывает и какой дефект появился бы без неё. Читать, когда правишь
      правила профиля, добавляешь архетип, разбираешь спорный результат плана или объясняешь
      пользователю, почему на трёх строках прогон получился глубоким.
      
      ---
      
      ## Ось 1: объём (скаляр)
      
      Что тронуто и пересекает ли правка границы. Считает `computeProfile` по `git diff --numstat`
      и `git diff -U0` (или по всем строкам файла, если он «без истории» в HEAD).
      
      | Класс | Признаки |
      |---|---|
      | **C0** Косметика | комментарии, форматирование, переименование без изменения смысла; тела методов не менялись |
      | **C1** Точечная | файлов ≤ `volume.c1MaxFiles` (умолчание 1), изменённых строк ≤ `volume.c1MaxLines` (умолчание 40), правка внутри существующих методов; нет новых экспортов, изменённых сигнатур, новых модулей и объектов метаданных |
      | **C2** Модульная | >1 метода, ИЛИ новый экспортный метод, ИЛИ изменена сигнатура, ИЛИ превышен порог строк либо файлов — в пределах существующих модулей |
      | **C3** Структурная | новый модуль / объект метаданных / форма, ИЛИ изменение проведения и бизнес-логики, ИЛИ новая интеграция, ИЛИ затронуто ≥4 модуля разных подсистем |
      
      Новый общий модуль или объект метаданных выводит правку из C1 **безусловно**, раньше проверки
      числа строк: однострочный новый общий модуль — всё равно C3, а не C1 по объёму. Иначе
      определение C1 («нет... новых модулей и объектов метаданных») просто не выполнялось бы для
      такой правки, и класс разошёлся бы с собственным же условием.
      
      «>1 метода, ИЛИ новый экспортный метод, ИЛИ изменена сигнатура» — не оценка на глаз, а разбор
      границ `Процедура|Функция ... КонецПроцедуры|КонецФункции` (`methodRanges` в
      `tools/profile.mjs`, кириллица и латиница ключевых слов — обе формы). Для файла, у которого
      есть версия в HEAD, инструмент строит границы методов дважды — для рабочего дерева и для
      HEAD — и по изменённым строкам диффа (не суммарному числу, а конкретным номерам) решает: (a)
      сколько РАЗНЫХ методов правка задела — больше одного значит C2 независимо от суммарного числа
      строк; (b) есть ли в рабочем дереве метод, которого не было в HEAD под тем же именем — новый,
      экспортный или нет (переименование сюда тоже попадает: старое имя пропало); (c) совпадает ли
      строка сигнатуры (список параметров, `Знач`, `Экспорт`) существующего метода с версией в HEAD.
      Причина уходит в поле `volumeReason` (`methods:2`, `new-method:Имя`, `signature:Имя`, `lines`,
      `files`) и печатается `gate.mjs plan` отдельной строкой «объём: C2 (…)».
      
      Для файла БЕЗ версии в HEAD (только что созданный) сравнивать методы не с чем — правила
      (a)-(c) на него не распространяются, объём такого файла решают архетипы `new-common-module`
      / `new-metadata-object` (весь новый модуль/объект метаданных — C3) и обычный порог по размеру.
      
      ## Ось 2: архетипы кода (множество меток)
      
      **Объёма недостаточно: три строки внутри транзакции опаснее трёхсот строк переименований.**
      Архетипы не упорядочены и комбинируются — одна правка может быть одновременно «запросом»,
      «транзакцией» и «интеграцией». Определяются механически по маркерам и путям файлов.
      
      Маркер ищется не только в добавленных строках диффа, но и в ПОЛНЫХ ТЕЛАХ методов, которых
      правка коснулась (working tree, те же границы `Процедура|Функция`, что и на оси 1). Иначе
      правка внутри уже архетипичного метода могла остаться незамеченной: если строка `Новый
      HTTPСоединение` в методе уже была, а diff тронул другую строку того же метода, маркер не
      попал бы в добавленные строки — но метод как был работой с HTTP-соединением, так ей и
      остался.
      
      Источник истины — `ARCHETYPES` в `tools/profile.mjs`; таблица здесь — для чтения, при правке
      архетипа менять нужно код, а не эту копию.
      
      | Архетип | Метка в следе | Маркер в изменениях | Мин. `code` | Мин. `arch` |
      |---|---|---|---|---|
      | Запрос | `query` | `Новый Запрос`, `ВЫБРАТЬ` | L2 | — |
      | Транзакция, блокировки | `transaction` | `НачатьТранзакцию`, `Заблокировать`, `БлокировкаДанных` | L2 | — |
      | Запись наборов записей | `record-set` | `Записать(Истина)`, `СоздатьНаборЗаписей` | L2 | — |
      | Обработчик события объекта | `object-event` | `ПередЗаписью`, `ПриЗаписи`, `ОбработкаПроведения`, `ОбработкаУдаленияПроведения`, `ПередУдалением` | L2 | ур. 1 |
      | Интеграция, HTTP | `integration` | `HTTPСоединение`, `WSПрокси`, `Новый COMОбъект` | L2 | ур. 1 |
      | Права, RLS | `rights` | XML ролей (`Roles/*/Ext/Rights.xml`), `УстановитьПривилегированныйРежим` | L2 | ур. 2 |
      | CFE-перехват | `cfe-patch` | `&Перед`, `&После`, `&Вместо`, `&ИзменениеИКонтроль` | L2 | ур. 1 |
      | Регламентное, фоновое | `scheduled-job` | путь `ScheduledJobs/`, `ФоновыеЗадания.`, `РегламентныеЗадания.` | L2 | — |
      | Клиент-сервер | `client-server` | директивы `&НаСервере...`, `&НаКлиенте...` | L1 | **ур. 1** |
      | Диалог посреди логики | `user-dialog` | `ПоказатьВопрос`, `ВопросАсинх`, `ОповещениеОЗавершении` | L1 | **ур. 1** |
      | Модуль формы | `form-module` | путь `Forms/*/Module.bsl` | L1 | ур. 1 при `loc > 400` |
      | Асинхронный клиент | `async-client` | `Асинх`, `Ждать`, `Обещание` | L1 | — |
      | Новый общий модуль | `new-common-module` | новый `CommonModules/<Имя>/Ext/Module.bsl` + декларация объекта | L1 | **ур. 2** |
      | Новый объект метаданных | `new-metadata-object` | новый XML/MDO в `src/`, кроме декларации общего модуля | L1 | **ур. 3** |
      
      **Колонка «Метка в следе» — ровно то, что пишется в поле `archetypes` записи `scope`.** Метка
      не переводится и не сокращается: `queries` вместо `query` означает не «почти то же самое», а
      «правило не сработало», и снятие гейта валидатор не пропустит. Свои архетипы проект заводит
      в секции `archetypes.custom` (имя, маркеры, `minCode`, `minArch`), их имена — тоже законные
      метки.
      
      ### Почему минимумы по контурам асимметричны
      
      У архетипа два минимума — по контуру `code` и по контуру `arch`, — и они намеренно не
      совпадают. В этом весь смысл оси.
      
      Правка внутри транзакции требует глубокого разбора **кода**: порядок `Попытка` и
      `НачатьТранзакцию`, вложенность, отмена. Проверять там SOLID нечего — метода два, границы
      модулей не двигались. Новый общий модуль на тридцать строк — зеркально: транзакций нет,
      антипаттернов производительности нет, а вопросы размещения, границ ответственности и флагов
      модуля критичны, и цена ошибки — годы жизни с неправильным швом.
      
      Одна шкала «глубины вообще» усреднила бы эти два случая и в обоих дала бы не тот результат.
      
      ### Архетипы проекта участвуют наравне со встроенными
      
      Секция `archetypes.custom` проектной настройки описывает свои архетипы: имя, маркеры в
      изменённых файлах, минимальные `minCode` и `minArch`. Они входят в правило разрешения так же,
      как встроенные, и их имя — законная метка следа: валидатор читает настройку.
      
      Без этого механизм, которого нет во встроенной таблице, не поднял бы глубину никогда, и знал
      бы об этом только автор проекта — остальные видели бы прогон, который «по правилам» проверил
      обмен как обычную правку.
      
      Запись `archetypes.custom` может вместо `name` задать `extends: "<встроенная метка>"` —
      расширить встроенный архетип своими маркерами, не заводя новую метку. Так учат плагин видеть
      проектную обёртку платформенного вызова (например, общий модуль-обёртка HTTP расширяет
      `integration`): находка идёт под встроенной меткой, справочники и чеклист архетипа не меняются,
      а `minCode`/`minArch` могут только поднять встроенный минимум. Разбор — `docs/CONFIG.md`.
      
      ## Ось 3: сложность (скаляр)
      
      Закрывает случай «мало строк, но код тяжёлый», когда архетипа может не быть вовсе. Считается
      по изменённым методам: вложенность ≥ `complexity.maxNesting` (4), длина метода
      > `complexity.maxMethodLines` (120), параметров ≥ `complexity.maxParams` (7), цепочка
      ветвлений ≥4, рекурсия. Срабатывание поднимает `code` до L2 и `arch` до уровня 1.
      
      Метрики — из отчёта `tools/analyzer-run.mjs --json` (`functions`, `complexity`,
      `cognitive_complexity`); `gate.mjs plan` запускает его сам, если не передан `--no-analyzer`.
      
      Важно, чем именно запущен движок. Проектные конфигурации (`.bsl-language-server.json`,
      `bsl-analyzer.toml`) могут отключать диагностики или ограничивать анализ подсистемами через
      `subsystemsFilter`. С таким конфигом изменённые прикладные файлы в анализ не попадают вовсе,
      а отчёт остаётся правдоподобным: находок нет, метрик нет, вердикт «чисто». Гейт запускает
      движок со своим конфигом из состава плагина — проектный правит IDE, гейтовый правит гейтом.
      
      ## Матрица глубин по объёму (базовый уровень)
      
      | Контур | C0 | C1 | C2 | C3 |
      |---|---|---|---|---|
      | `hygiene` | полный | полный | полный | полный |
      | `code` | пропуск | L1 | L1 + L2 | L1 + L2, предложить аудит |
      | `arch` | пропуск | пропуск | ур. 1–2 | ур. 3 |
      | `xml` | пропуск | не применим, если XML не менялся | изменённые объекты + регистрация | полный: валидация + сироты в обе стороны + права ролей |
      | компилируемость | — | если платформа доступна | да | да |
      
      `hygiene` гоняется всегда: стоимость околонулевая, а ловит она то, что проявляется позже
      всего и объясняется хуже всего.
      
      У `arch`, как и у `code`, объём даёт пол сам по себе: C2 — не ниже уровня 1, C3 — уровень 3,
      безусловно. Живой след, показывавший `arch:skip` при `volume=C2` без сработавших архетипов
      (до Task 17, раунд 2), был отступлением модели от этой же таблицы при заполнении evidence, а
      не альтернативным прочтением правила. Конфликта с `minArch: 2` архетипа `new-common-module`
      нет: итог берёт максимум пола объёма и минимумов архетипов (`max(3, 2) = 3`), архетип пол
      не понижает.
      
      ## Правило разрешения и `driver`
      
      > **глубина контура = max(по объёму, максимум минимумов по сработавшим архетипам, по сложности)**
      
      Отдельно фиксируется **`driver`** — что именно подняло глубину: объём, конкретный архетип или
      сложность. Без него вердикт виден, а логика нет, и первое же «почему так долго на трёх
      строках?» превращается в спор, в котором ни одна сторона не может сослаться на запись, — а
      следующим шагом глубину начинают занижать вручную, потому что объяснить её нечем.
      
      `driver` считается отдельно по каждой оси (`code`, `arch`) через контрфактическое сравнение —
      во что превратился бы итог без вклада сложности и без вклада архетипов, — а не только по
      `code`: иначе прогон, где `arch` подняла ИСКЛЮЧИТЕЛЬНО правка архетипа (а по объёму `arch`
      остался бы `skip`), печатал бы `driver=volume` и врал о причине.
      
      ## Понижающий модификатор: эталонная правка
      
      Понижает глубину до C1 независимо от объёма, если выполнены **три условия сразу**:
      
      1. все фрагменты — кальки типового эталона того же механизма с точечной адаптацией имён и
         полей;
      2. они уже прошли предметный верификатор механизма с нулём ошибок;
      3. они не трогают транзакции, блокировки и права.
      
      Невыполнение любого условия отменяет модификатор целиком. Два из трёх — не «почти эталонная
      правка», а обычная: именно в оставшейся трети и живёт отличие, ради которого правку писали
      руками.
      
      ---
      
      ## Пороги читаются из проекта, а не по памяти
      
      Значения в таблицах выше — умолчания. Действующие печатает `tools/config.mjs show` вместе с
      источником каждого: умолчание, проектный файл `.1c-quality-gate.json` или переменная
      окружения. Файл плагин заводит сам при первом взводе гейта — пользователю создавать его не
      нужно, и пустые секции в нём означают «берём умолчание», а не «настройка забыта».
      
      Расхождение вывода команды с таблицей разрешается в пользу команды. Иначе настройка, которую
      никто не читает, неотличима от правила, которое не сработало: проект переопределил
      `volume.c1MaxFiles`, прогон посчитал по умолчанию, и оба выглядят одинаково правдоподобно.
      
      ## Отметка `config=` переносится дословно
      
      Строка `[qg scope: ...]`, которую печатает `gate.mjs plan`, уже несёт готовое поле
      `config=...`. Валидатор пересчитывает его сам и сверяет с заявленным, поэтому написанное по
      памяти не пройдёт.
      
      Причина не формальная. Прогон, не заглянувший в настройку, оставлял бы запись, неотличимую от
      прогона, который её учёл. При этом «C1» в проекте с поднятыми порогами и «C1» в соседнем
      проекте — разные утверждения: без отметки читатель отчёта не может узнать, какое из них перед
      ним.
      
    • run-environment.md 10.7 KB
      # Среда прогона: путь к инструментам, часовой, субагенты
      
      Механика, от которой зависит достоверность прогона, но которая не участвует в решении о
      глубине. Команды и правила — в `SKILL.md`; здесь то, почему они выглядят именно так, и что
      ломается, если сделать иначе.
      
      ---
      
      ## Почему `$QG` разрешается командой
      
      `CLAUDE_PLUGIN_ROOT` доступна хукам, но **не оболочке**: в шелле она пуста, и путь вида
      `"${CLAUDE_PLUGIN_ROOT}/tools/..."` схлопывается в `/tools/...`. Проверено эмпирически.
      Отсюда правило: путь разрешается первой командой прогона, дальше подставляется буквально —
      состояние оболочки между вызовами не сохраняется.
      
      В OpenCode угадывать не приходится: у него есть хук `shell.env`, которым плагин кладёт
      корень пакета в переменную `QG_ROOT` при каждом запуске оболочки. Поэтому под OpenCode
      перебор не выполняется вовсе — первый же кандидат верен по построению.
      
      Почему это важнее, чем кажется. Пакет OpenCode ставит из репозитория и держит в своём
      кэше, обновляя при смене закреплённого тега. Прежняя редакция вместо этого раскладывала
      копию пакета в проект установочным скриптом и искала её по `QG_PROJECT_DIR`. Копия не
      обновлялась никогда, а переменная — публичная и не помечена как «только OpenCode»: сессия
      Claude Code, где её выставили руками, молча уходила работать в устаревшую копию. Ни
      ошибки, ни кода возврата — тот же класс отказа, против которого написан `sort -V` ниже.
      
      ## Порядок источников и `sort -V`
      
      Сначала `QG_ROOT` — прямой ответ от плагина OpenCode. Затем `installed_plugins.json`,
      указатель на **активную** версию, который ведёт сам Claude Code. Перебор каталогов кэша —
      только запасной путь, и обязательно через `sort -V`.
      
      В кэше лежат все когда-либо установленные версии, а лексикографический порядок ставит
      `0.10.0` раньше `0.9.0`. Сессия, взявшая последний по алфавиту каталог, работает
      инструментами устаревшей версии и честно отчитывается, что проверок «не существует» —
      диагноз, который выглядит как отсутствие функциональности, а не как неверный путь.
      Инструменты печатают свою версию в выводе: если поведение кажется устаревшим, сверь её.
      
      ## Где лежит отчёт
      
      Место файла отчёта раньше выбирала модель, и отчёты оседали в двух местах. Одно — временный
      каталог сессии: его чистят, и ссылка в журнале снятий потом ведёт в пустоту. Другое —
      документация проекта, рядом с кодом и под версионным контролем.
      
      Теперь принятый отчёт раскладывает утилита: `gate.mjs release` копирует его в `qg-reports/`
      каталога состояния под датой снятия и пишет путь копии в журнал снятий (`evidenceArchive`).
      Место одно при любом выборе модели, а каталог состояния вне git — архив не засоряет
      репозиторий. Черновик пишется во временный каталог, чтобы в проекте не остался второй
      экземпляр; отчёт, написанный прямо в архив, не копируется. Черновик в каталогах проекта
      снятию не мешает, но `release` называет его предупреждением: иначе оригинал рядом с кодом
      остался бы незамеченным.
      
      Отклонённый след в архив не попадает, снятие без отчёта (C0/C1 с причиной) архива не
      заводит. Не удалась запись копии — гейт всё равно снят: прогон принят, копия лишь удобство;
      вывод говорит об этом прямо, а поле в журнале остаётся пустым.
      
      ## Часовой: почему номер задан настройкой
      
      Номер стандарта для проверки живости берётся из `sentinel.id` (умолчание `std454`).
      
      Будь он зашит в навык, исчезновение именно этой страницы уронило бы часового во всех
      проектах разом, и отличить «страницы больше нет» от «сервис стандартов недоступен» было бы
      нечем. Настройка даёт проекту сменить цель, не трогая плагин.
      
      Смысл самой проверки: без неё «нарушений стандартов не найдено» неотличимо от «источник
      стандартов не отвечал». Неподтверждённый часовой делает прогон недостоверным, и валидатор
      следа отклоняет снятие гейта.
      
      ## Субагенты: границы применения
      
      Штатные субагенты только читают и никогда не пишут записи следа сами: агент возвращает
      факты, запись формирует контур. Иначе нельзя проверить, кто что утверждал, — а отчёт, в
      котором вердикт написан тем же, кто собирал факты, теряет единственную точку сверки.
      
      Недоступность субагента проверку не отменяет: контур выполняет её сам либо пишет `skipped`
      с причиной.
      
      Веер из многих агентов существует только в состязательном аудите (слой 3) и запускается
      исключительно после явного согласия пользователя — методология в `adversarial-audit.md`.
      
      ## Деградация записывается, а не замалчивается
      
      Контур не установлен (плагин ставится по частям) или его инструменты недоступны — пишется
      `skipped` с точной причиной: `contour_not_installed`, `lsp_unavailable`, `rlm_unavailable`,
      `platform_unavailable`. Полный список причин и что каждая означает — в `evidence-format.md`.
      
      Деградация допустима всегда. Недопустимо одно: молчание, при котором непрогнанная проверка
      выглядит как прогнанная и чистая.
      
      ## Чужие сессии
      
      Состояние гейта разделено по сессиям: блокирует только то, что правила текущая сессия. Если
      `gate.mjs status` показывает несколько сессий, `verify` и `release` без `--session <id>`
      отказывают с перечнем — утилита не угадывает свою по свежести, потому что в параллельной
      работе самая свежая сессия почти всегда чужая. Идентификатор печатается в подсказке при
      взводе гейта и в сообщении о блокировке; при одной сессии `--session` не нужен.
      
      Общий на проект маркер запирал бы сессию, которая правила совсем другие файлы. Обратная
      ошибка дороже: снятие чужого гейта объявляет проверенной работу, которую ты не видел, и
      перехватывает чужую сессию.
      
      ## Файлы вне корня и сторонние каталоги
      
      Файл вне корня проекта взводит гейт как обычный, ключом служит абсолютный путь — в журнале
      прогонов та же форма, сверка покрытия сходится. Путезависимые проверки (bsl-lint,
      query-lint, гигиена, валидаторы XML) работают по нему штатно; привязанные к проекту —
      статический анализатор по конфигурации, индекс кода — его не увидят и закрываются записью
      `not_verified` с точной причиной: заявленное приближение, а не пропуск.
      
      Корень проекта команды определяют сами: переменная окружения, иначе подъём до
      `.1c-quality-gate.json`, иначе до `.git`. Запуск из каталога другого репозитория уводит
      корень к его `.git`: журнал и состояние уезжают туда, и «гейт не взведён» звучит честно —
      но про другой проект. Поэтому `status` и отказы печатают корень со способом опознания, а
      при работе из стороннего каталога корень задаётся явно: `QG_PROJECT_DIR=<корень>` в
      окружении команды.
      
  • SKILL.md 14 KB
    ---
    name: quality-gate
    description: >-
      Оркестратор контроля качества 1С-разработки. Определяет профиль изменения по трём осям
      (объём правки, архетипы кода, сложность), выбирает глубину каждого контура, запускает
      проверки, формирует отчёт с машиночитаемым следом и снимает блокирующий гейт.
      Вызывать после правок BSL или XML метаданных 1С, перед завершением работы и коммитом.
      Триггеры: «прогони гейт качества», «проверь код перед коммитом», «сними гейт»,
      «отревьюй что я написал», «проверь по стандартам», «готов ли код к коммиту».
    license: MIT
    ---
    
    # quality-gate — оркестратор контроля качества 1С
    
    Единственная точка входа плагина. Контуры (`code`, `arch`, `xml`, `hygiene`) не вызываются
    напрямую: сначала считается **профиль изменения**, он решает глубину.
    
    > **Главное правило.** Полный прогон на правке комментария — налог, из-за которого гейт
    > начинают обходить; пропуск без следа — ложная зелень. Глубина адаптивная, но **любой
    > пропуск фиксируется явной записью с причиной**.
    
    <ЖЁСТКИЙ-ШЛЮЗ>
    По умолчанию — только проверка и отчёт. НЕ переписывай бизнес-логику и НЕ меняй метаданные по
    своей инициативе. Правки — только в режиме `--fix`, из безопасных категорий. Critical и любые
    изменения логики, проведения, запросов, прав — никогда без явного подтверждения пользователя.
    </ЖЁСТКИЙ-ШЛЮЗ>
    
    ---
    
    ## Инварианты прогона
    
    Девять утверждений: усвоен только этот блок — прогон ещё имеет смысл; нарушено любое —
    уже нет.
    
    1. **Профиль считается один раз**, до контуров; контуры его не пересчитывают.
    2. **Глубина — по профилю, не по привычке**: не гонять архитектуру на опечатке, не
       ограничиваться гигиеной на новом модуле проведения.
    3. **Контур исполняется вызовом навыка**, а не по памяти.
    4. **Строку следа печатает инструмент**, где он есть — не модель по смыслу.
    5. **Любой пропуск — запись `skipped` с причиной.** Молчание неотличимо от выполнения.
    6. **Вердикт «чисто» признаёт непроверяемое** — записью `not_verified`.
    7. **Находка — с номером стандарта, кодом диагностики или эвристикой** и значением против
       порога; 🔴/🟠 блокируют «Чисто», вне `--fix` — только отчёт.
    8. **Гейт снимается утилитой** `gate.mjs release`, а не удалением файла состояния.
    9. **План печатает `gate.mjs plan`**; модель вправе поднять глубину с причиной, понизить —
       нет.
    
    ---
    
    ## Шаг 1. План
    
    ### Путь к инструментам плагина (`$QG`)
    
    Все команды ниже используют `$QG` — каталог плагина. Под OpenCode он готов в `QG_ROOT`; в
    Claude Code `CLAUDE_PLUGIN_ROOT` оболочке не видна — путь схлопнулся бы в `/tools/...`.
    
    Разреши путь **первой командой прогона**, дальше подставляй значение буквально. Кандидат —
    только после `test -d "$QG/tools"`: переменная сама по себе не гарантирует актуальный плагин.
    
    ```bash
    QG="${QG_ROOT:-}"
    [ ! -d "$QG/tools" ] && QG="${CLAUDE_PLUGIN_ROOT:-}"
    [ ! -d "$QG/tools" ] && QG="$(node -e "const p=require(require('node:os').homedir()+'/.claude/plugins/installed_plugins.json').plugins;const k=Object.keys(p).find(n=>n.startsWith('1c-quality-gate@'));if(k&&p[k][0])process.stdout.write(p[k][0].installPath)" 2>/dev/null)"
    [ ! -d "$QG/tools" ] && QG="$(ls -d ~/.claude/plugins/cache/*/1c-quality-gate/*/ 2>/dev/null | sort -V | tail -1)" && QG="${QG%/}"
    test -d "$QG/tools" && echo "$QG" || { echo "Плагин не найден ни в одном харнессе" >&2; exit 1; }
    ```
    
    `sort -V` обязателен: без него берётся устаревшая версия плагина
    (`references/run-environment.md`).
    
    ### Профиль и план прогона
    
    ```bash
    node "$QG/tools/gate.mjs" plan --files <f> [<f> ...] [--no-analyzer]
    ```
    
    Без `--files` — состав из взведённой сессии (`gate.mjs status`). Печатает профиль по трём
    осям, строку `scope` (с `config=...`), инструменты по порядку, что закрыть в следе.
    `--no-analyzer` — когда анализатор недоступен или падает; ось сложности тогда `not_computed`,
    `driver` не бывает `complexity:*`.
    
    **Строку `[qg scope: ...]` перенеси в отчёт дословно** — валидатор пересчитывает профиль сам.
    Расхождение — сверь порог: `node "$QG/tools/config.mjs" show`. **Профиль можно поднять,
    нельзя понизить**: увидел то, чего план не видит (правка тише архетипа, но трогает деньги) —
    подними, добавь «глубина поднята: <причина>»; обратное валидатор не пропустит.
    
    Почему оси устроены так — `references/profile-axes.md`; данные — `tools/profile.mjs`,
    `tools/gate.mjs`.
    
    ---
    
    ## Шаг 2. Инструменты
    
    Выполни команды раздела «## Инструменты» плана по порядку — файлы уже подставлены. У части
    есть исполняемый инструмент: он печатает строку следа (`[qg applied: ...]` / `[qg skipped:
    ...]`) сам — переноси дословно, не сочиняй.
    
    Прогон отмечается в журнале `qg-runs.jsonl`: валидатор сверяет по нему каждую запись
    `applied` — инструмент видел **весь** состав правки, а не файл из десяти; `skipped ...
    reason=not_applicable` — тоже утверждение о работе. Прогоны по частям складываются.
    `platform-context-run.mjs`, когда включён планом, обязателен: анализатор знает имена
    конфигурации, но не платформы. `query-lint` берёт и изменённые XML, не только `.bsl`.
    
    **Перед повторным прогоном слоя проверь `gate.mjs status`:** отработавший по этому
    содержимому слой пропускается с причиной `verified_earlier` (`references/evidence-format.md`).
    
    ---
    
    ## Шаг 3. Контуры
    
    Запускай контуры и глубины из `resolved: code=... arch=... xml=... hygiene=...` плана.
    Каждый контур обязан вернуть `applied` либо `skipped` — **молчание не допускается**.
    
    | Контур | Навык |
    |---|---|
    | `code` | `bsl-code-review` |
    | `arch` | `bsl-architecture-review` |
    | `xml` | `xml-structure-review` |
    | `hygiene` | `file-hygiene` |
    
    Передавай контуру профиль целиком: класс, архетипы, файлы.
    
    ### Контур исполняется вызовом навыка, а не по памяти
    
    Таблица называет навыки, а не проверки: чем проверяется признак — в SKILL.md контура. Прогон
    без открытия навыка — чеклист для чтения глазами, не проверка
    (`references/run-environment.md`).
    
    ### Субагенты в составе прогона
    
    Четыре штатных, **только читающие**: возвращают факты, запись формирует контур. Три дешёвые;
    `antipattern-reader` — на модели уровня контура: семантика LLM-антипаттернов не
    механизируется до haiku.
    
    | Субагент | Вызывает | Факты | Спавнов |
    |---|---|---|---|
    | `bsl-verifier` | `code` | сигнатуры платформы, экспортность модулей, метаданные | на список файлов |
    | `antipattern-reader` | `code` | кандидаты с цитатами; аттестует `tools/catalog.mjs` | на контур |
    | `bsl-scout` | `arch` | вызывающие, экспорты, триггеры XML | по вопросу |
    | `xml-runner` | `xml` | «диск ↔ состав», валидаторы структуры | на контур |
    
    Недоступность субагента, контура или инструмента проверку не отменяет — она проходит сама
    либо получает `skipped` с причиной (`references/run-environment.md`).
    
    ---
    
    ### Слой 3: состязательный аудит
    
    Самая дорогая проверка, никогда не запускается сама: контуры предлагают её при классе C3 с
    находками 🔴/🟠, запуск — только по явному согласию пользователя
    (`references/adversarial-audit.md`).
    
    ---
    
    ## Шаг 4. Sentinel — проверка живости источника стандартов
    
    Один раз за прогон запроси через MCP `v8std` стандарт, чей номер — `sentinel.id` настройки
    (умолчание `std454`): `v8std_get_page("<id>")`, ожидание — страница найдена.
    
    Без неё «нарушений не найдено» неотличимо от «сервис недоступен»: неподтверждённый часовой
    делает прогон недостоверным (`references/run-environment.md`).
    
    ---
    
    ## Шаг 5. Отчёт и след
    
    Отчёт для человека — находки по важности (🔴 Critical / 🟠 Major / 🟡 Minor). Ниже, в
    `## quality evidence`, — машиночитаемый след: строка на проверку
    (`references/evidence-format.md`).
    
    **Каждая запись `not_verified` повторяется в человекочитаемой части фразой**: «Ошибок: 0»
    рядом с невидимым `not_verified` читается как «проверено».
    
    Минимум следа: `scope` (Шаг 1), `sentinel` (Шаг 4), запись от каждого контура и всё из плана
    «Закрыть в следе» — включая `not_verified: dimension=compilation`, если платформа не
    запускалась (её не проверяет ничто другое).
    
    **Сработал архетип «Запрос» — выполни запрос до вердикта** (консоль запросов, тестовые
    параметры): текст остаётся строковым литералом, «Неоднозначное поле» доживает до продуктива.
    Выполнить негде — законный исход, но записанный:
    
    ```
    [qg applied: layer=code, scope=query-execution, ids=[qg:QRY-EXECUTED], verdict=clean]
    [qg not_verified: dimension=query-execution, reason=no_platform]
    ```
    
    **Файл, до которого не добрался анализатор, не проверен** — `analyzer-run.mjs` печатает
    запись сам, переноси дословно.
    
    ```bash
    node "$QG/tools/evidence-validator.mjs" <файл отчёта> --gate
    ```
    
    ---
    
    ## Шаг 6. Снятие гейта
    
    Гейт снимается **только** утилитой, не удалением файла состояния:
    
    ```bash
    node "$QG/tools/gate.mjs" release --evidence <файл отчёта>        # по результатам прогона
    node "$QG/tools/gate.mjs" release --class C0 --reason "<почему>"  # C0/C1 без прогона
    ```
    
    Отчёт — во временный каталог, не в проект: `release` копирует его в архив вне git;
    ссылайся на путь `Копия отчёта:` (`references/run-environment.md`).
    
    НЕ снимай гейт, если прогон прерван на полпути и отчёт не сформирован: он должен остаться
    для повторной проверки.
    
    **Чужие сессии не трогай.** Гейт разделён по сессиям: несколько в `gate.mjs status` —
    `verify`/`release` без `--session <id>` отказывают, «самая свежая» может быть чужой.
    Идентификатор — в подсказке при взводе и в сообщении о блокировке
    (`references/run-environment.md`).
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related