bsl-code-review · diff
git:20260906.74b3426 to git:20260907.d35356c
56 added, 103 removed. Audit A to A.
---
name: bsl-code-review
description: >-
Контур проверки кода BSL: диагностики статического анализатора, антипаттерны производительности
и механики платформы, стандарты разработки #stdNNN, именование, верификация сигнатур API и
существования общих модулей. Уровень «внутри тела метода» — то, что чинится заменой строк.
Вызывается оркестратором quality-gate с готовым профилем изменения; напрямую — по запросу
«проверь код», «отревьюй что я написал», «проверь на антипаттерны».
license: MIT
---
# bsl-code-review — контур кода
Проверяет то, что чинится **внутри тела метода**: замена строк, без нового шва. Всё, что
требует выделения метода, переноса в другой модуль, нового экспорта или изменения «кто кого
вызывает», принадлежит контуру `bsl-architecture-review` — граница и правила отсева повторов
находок в `shared/routing-contract.md`.
<ЖЁСТКИЙ-ШЛЮЗ>
Только проверка и отчёт. НЕ переписывай логику, запросы, транзакции и права по своей
инициативе. В режиме `--fix` допустимы лишь безопасные категории (см. ниже).
</ЖЁСТКИЙ-ШЛЮЗ>
## Инварианты контура
Пять утверждений, без которых прогон контура недействителен.
- 1. **Оба файла антипаттернов прогоняются всегда** — и на мелкой правке тоже. Находка 🔴 из
- любого блокирует вердикт «чисто».
+ 1. **Каталог антипаттернов проходится всегда** — читателем либо самостоятельно, но след
+ печатает `catalog.mjs attest`.
2. **Строку следа инструментальной проверки печатает инструмент** — переноси дословно,
своих находок этого класса не добавляй: результат детерминирован.
3. **Каждое замечание доказуемо**: номер стандарта, код диагностики или название
антипаттерна плюс строка кода. «Так лучше» — не находка.
4. **Пропуск фиксируется.** Недоступный инструмент или субагент даёт `skipped` с причиной;
молчание неотличимо от выполнения.
5. **Файл, который анализатор не разобрал, не проверен** — вердикт «чисто» по нему
невозможен, и в отчёте он назван поимённо.
## Вход
От оркестратора: класс изменения (C0…C3), сработавшие архетипы, список изменённых файлов.
- При прямом вызове — определи профиль сам по правилам `quality-gate`.
-
- | Класс | Глубина |
- |---|---|
- | C0 | контур не запускается |
- | C1 | Слой 1 |
- | C2 | Слой 1 + Слой 2 |
- | C3 | Слой 1 + Слой 2, предложить Слой 3 |
-
- Архетип поднимает глубину независимо от класса: запрос, транзакция, запись наборов записей,
- обработчик события объекта, интеграция, права, CFE-перехват, регламентное задание — минимум
- Слой 2. Итоговая глубина — максимум из требований объёма, архетипов и сложности.
+ Глубину (Слой 1 / Слой 1+2 / Слой 1+2 с предложением Слоя 3) печатает план (`gate.mjs plan`,
+ поле `resolved: code=...`) — архетип может поднять её сверх класса, понизить нельзя. При
+ прямом вызове — определи профиль сам по правилам `quality-gate`.
---
## Слой 1а — статический анализ
- Одна команда: она находит корень конфигурации, прогоняет только изменённые файлы, проверяет
- часового и формирует записи следа.
-
- ```bash
- node "$QG/tools/analyzer-run.mjs" --changed <файл> [--changed <файл> ...]
- ```
-
- Вывод — находки по файлам и готовый блок `## quality evidence`. Перенеси его в отчёт как
- есть: записи следа по слою `code` сочинять руками не нужно и нельзя.
+ Строка плана для `analyzer-run.mjs` — одна команда: она находит корень конфигурации, прогоняет
+ только изменённые файлы, проверяет часового и формирует записи следа. Вывод — находки по
+ файлам и готовый блок `## quality evidence`. Перенеси его в отчёт как есть: записи следа по
+ слою `code` сочинять руками не нужно и нельзя.
**Твоя работа здесь — триаж, а не припоминание.** Список нарушений детерминирован. От тебя
требуется отделить то, что надо чинить сейчас, от того, что является осознанной нормой этого
проекта, и назвать последствие каждой оставленной находки. Коды расшифровывай через
`v8std_explain_diagnostics` и привязывай к номеру стандарта.
Четыре режима вывода, каждый из которых меняет то, что можно утверждать по результату:
- **Информационные находки свёрнуты** в одну строку, полный список — флаг `--all`. В след
коды попадают в любом случае.
- **Проект без основной конфигурации** (репозиторий одного расширения): диагностики о
неразрешённых именах понижены до информационных — обратно **не поднимай**, отличить их от
настоящих ошибок в этом режиме нечем.
- **«НЕ РАЗОБРАНО файлов»** — по этим файлам не проверено **ничего**. Назови их в отчёте
поимённо: вердикт «чисто» по ним невозможен.
- **Часовой `status=not_found`** — прогон недостоверен, вердикт «чисто» запрещён; разберись
с анализатором и повтори.
Что стоит за каждым режимом и известные случаи — `references/analyzer-output.md`. Гейтовый
анализ идёт с конфигом из состава плагина: проектный `subsystemsFilter` вывести изменённые
файлы из проверки не может.
**Если анализатор недоступен** — команда сама запишет
`[qg skipped: layer=code, scope=static-analysis, planned=[bslls:*], reason=analyzer_unavailable]`
и вернёт код 1. Продолжай со Слоя 1б: он ловит другое и от анализатора не зависит.
### Второй движок — сверка со справочником платформы
- ```bash
- node "$QG/tools/platform-context-run.mjs" --changed <файл> [--changed <файл> ...]
- ```
-
- Ловит то, чего анализатор не видит вовсе: несуществующий член платформенного типа, значение
- системного перечисления, конструктор, свойство объекта. Всё это компилируется и падает при
- выполнении. Сервер справки движок заводит сам: ищет поднятый, а не найдя — ставит закреплённый
- релиз и поднимает свой по установленной платформе. Где платформы на машине нет, пишет `skipped`
- с причиной и возвращает код 1 — это законный исход.
+ Строка плана для `platform-context-run.mjs`. Ловит то, чего анализатор не видит вовсе:
+ несуществующий член платформенного типа, значение системного перечисления, конструктор,
+ свойство объекта. Всё это компилируется и падает при выполнении. Сервер справки движок
+ заводит сам: ищет поднятый, а не найдя — ставит закреплённый релиз и поднимает свой по
+ установленной платформе. Где платформы на машине нет, пишет `skipped` с причиной и
+ возвращает код 1 — это законный исход.
Два правила: **`info` «низкая уверенность» не отбрасывать** (класс смешанный) и **часовой
`not_found` — «чисто» запрещено**. Остальное — `references/platform-api.md`.
## Слой 1б — то, чего анализатор не видит
- ### 1. Антипаттерны производительности и механики платформы
-
- Источник: `references/bsl-anti-patterns.md` — прогоняется **всегда**, при любой глубине.
-
- | Антипаттерн | Что искать | Важность |
- |---|---|---|
- | Запрос в цикле | `Новый Запрос` внутри `Для Каждого` | 🔴 |
- | Чтение реквизита через точку | `.Реквизит` у ссылочного типа — грузит объект целиком | 🔴 |
- | Подзапрос в списке полей | вложенный `ВЫБРАТЬ` в секции выборки (N+1) | 🔴 |
- | Коррелированный подзапрос в условии | вложенный `ВЫБРАТЬ` в `ГДЕ`, ссылающийся на внешнее поле | 🔴 |
- | Фильтр виртуальной таблицы в ГДЕ | условие на результате ВТ вместо её параметров | 🟠 |
- | Временная таблица без индекса | `ПОМЕСТИТЬ` без `ИНДЕКСИРОВАТЬ ПО`, далее соединение по этому полю | 🟠 |
- | Отсутствие ограничения выборки | большой запрос без `ПЕРВЫЕ N` | 🟠 |
- | Множественные серверные вызовы | последовательные вызовы сервера с клиента | 🟠 |
- | Контекстный вызов без нужды | `&НаСервере` там, где хватает `&НаСервереБезКонтекста` | 🟠 |
- | Транзакция внутри Попытки | `НачатьТранзакцию()` внутри `Попытка`, а не наоборот | 🟠 |
- | Транзакция внутри неявной транзакции | `НачатьТранзакцию()` в `ОбработкаПроведения`, `ПередЗаписью`, `ПриЗаписи` | 🟠 |
- | `Сообщить()` как уведомление | сообщение без привязки к объекту и вне журнала регистрации | 🟠 |
- | Отсутствие кеширования | повторные дорогие вызовы с теми же параметрами | 🟡 |
- | Поиск перебором во вложенных циклах | сопоставление наборов циклом вместо `Соответствие` | 🟡 |
-
- ### 2. Антипаттерны кода, порождаемого моделью
+ ### 1. Каталог антипаттернов — субагент `antipattern-reader`
- Источник: `references/ai-antipatterns.md` — прогоняется **всегда, наравне с предыдущим**.
+ Строка плана для `catalog.mjs index` печатает индекс триггеров; полная карточка читается по
+ попаданию, не заранее. **Делегируй субагенту `antipattern-reader`**: передай ему вывод
+ `index`, список изменённых `.bsl` и `git diff HEAD --` по этим файлам — признаку `qg:AI-11`
+ (строки индекса с `*`) нужно сравнение версий, а не только текущий код. Он не знает задачи и
+ возвращает JSON — сохрани его в файл: путь к этому файлу и список файлов правки подставляются
+ в строку плана для `"$QG/tools/catalog.mjs" attest`.
- Ошибки, характерные для кода языковой модели: перепроверка контракта собственной функции,
- форк парсера на каждый вариант ответа, отчёт о непрогнанной проверке. Типовые своды их не
- покрывают — человек таких ошибок обычно не делает.
+ Инструмент сверяет полноту списка проверенных признаков, состав файлов и цитату каждой
+ находки с самим файлом, после чего печатает строки следа `ai-antipatterns` и
+ `platform-antipatterns` и пишет журнал. Отвергнутый результат — повтори запуск читателя с
+ его замечаниями, не правь JSON руками. Читателя в среде нет — прогони индекс сам по той же
+ процедуре и аттестуй так же: строку следа в обоих случаях печатает инструмент.
- ### 3. Проверки по тексту кода
+ Признаки с инструментом (`bsl-lint`, `query-lint`, `rename-check`) в проход не входят — их
+ строки печатают инструменты, а карточка нужна для «как чинить»:
+ `node "$QG/tools/catalog.mjs" card <ID>`.
- ```bash
- node "$QG/tools/query-lint.mjs" <файл.bsl|файл.xml> [<файл> ...]
- node "$QG/tools/bsl-lint.mjs" <файл.bsl> [<файл.bsl> ...]
- node "$QG/tools/rename-check.mjs" <файл.bsl> [<файл.bsl> ...]
- python "$QG/tools/xml/form-validate.py" -Path <Form.xml> # правился модуль формы
- ```
+ Почему проход изолирован: 131 КБ прежних справочников в контексте не давали находок за 25
+ прогонов — разбор в `../../docs/superpowers/specs/2026-09-07-context-routing-analysis.md`.
- `query-lint` читает и запросы, лежащие в XML, — `<query>` схем компоновки данных и
- `<QueryText>` динамических списков, — поэтому **изменённые XML передаются ему наравне с
- модулями**; номер строки в такой находке считается от начала текста запроса, как в
- сообщениях платформы. Оба инструмента печатают готовые записи следа и отмечаются в журнале
- прогонов.
+ ### 2. Проверки по тексту кода
- | Признак | Sev | Что ловит | Разбор |
- |---|---|---|---|
- | `qg:QRY-ALIAS-SHADOWS-FIELD`, `qg:QRY-ALIAS-SHADOWS-NESTED-TABLE` | 🔴 / 🟠 | псевдоним совпал с именем колонки ВТ пакета или табличной части, чей владелец в той же ветке: «Неоднозначное поле». Разыменование — 🔴, без обращения через точку — 🟠 | `references/bsl-query-reference.md` |
- | `qg:QRY-TOP-WITHOUT-ORDER` | 🟡 | `ПЕРВЫЕ N` без `УПОРЯДОЧИТЬ ПО` — набор строк недетерминирован | `references/bsl-anti-patterns.md` п. 5 |
- | `qg:BSL-TXN-IN-HANDLER` | 🟠 | своя `НачатьТранзакцию` внутри обработчика, который платформа уже выполняет в транзакции (#std783 п. 1.4) | `references/bsl-anti-patterns.md` п. 8б |
- | `qg:BSL-ENUM-STRING-ASSIGN` | 🟠 | примитив в поле строго ссылочного типа: сборка молчит, падает при записи | `references/bsl-anti-patterns.md` п. 8в |
- | `qg:BSL-STALE-LOCAL-CALL` | 🔴 | вызов метода, чьё объявление было в HEAD и исчезло в правке: переименование не доведено до точек вызова | `references/bsl-anti-patterns.md` п. 8г |
- | `qg:BSL-UNBOUNDED-STRING-COLUMN` | 🟠 | строковая колонка без квалификатора у таблицы, уходящей в параметр запроса (#std432 п. 3.1) | `references/ai-antipatterns.md`, `qg:AI-16` |
- | `qg:BSL-REF-DOT-ACCESS` | 🔴 / 🟠 | обращение к реквизиту ссылки через точку: объект читается целиком ради одного поля (#std437). Ссылочность доказывается присваиванием в методе или типом параметра из описания #std453 (🟠 — описание могло устареть), либо именем на «Ссылка» | `references/bsl-anti-patterns.md` п. 2 |
- | `qg:BSL-FORM-ATTR-SHADOW` | 🔴 | имя реквизита формы у переменной | `references/bsl-anti-patterns.md` п. 8д |
- | `qg:BSL-DISPATCH-NO-FALLBACK` | 🟠 | перебор значений перечисления или типов по трём и более веткам без `Иначе`: непредусмотренное значение проходит цепочку молча | `references/bsl-anti-patterns.md` п. 8е |
- | `qg:BSL-DB-READ-IN-LOOP` | 🟠 | чтение базы, достижимое из тела цикла через вызов метода: то же N+1, что в п. 1, только распределённое по методам (#std436). Прямую форму ловит анализатор | `references/bsl-anti-patterns.md` п. 1а |
+ Команды — строки плана для `query-lint.mjs`, `bsl-lint.mjs`, `rename-check.mjs`: план
+ печатает их только когда применимо, а полный список признаков с разбором каждого — в
+ `references/catalog/INDEX.md` (`qg:BSL-UNBOUNDED-STRING-COLUMN` там же — механическая
+ половина AI-16). XML идёт в `query-lint` наравне с `.bsl`: `<query>` СКД и `<QueryText>`
+ динамического списка — тоже носитель запроса.
**`attribute-access` покрыт инструментом лишь частично.** Доказать ссылочность в пределах
одного файла удаётся не всегда: ссылка из чужой функции или из недокументированного параметра
остаётся неопознанной. `clean` здесь означает «механическая часть чиста» и разбора #std437
глазами не отменяет — инструмент задаёт нижнюю границу, а не верхнюю.
- **Записи переносятся дословно, своих находок этого класса не добавляй.** Результат
- детерминирован, а строка, составленная по прочтении кода, выглядит в отчёте точно так же —
- поэтому валидатор следа её отвергает.
+ Записи следа этих двух инструментов подчиняются инварианту 2: переноси дословно, своих
+ находок этого класса не добавляй.
Оба инструмента видят один файл и графа вызовов не строят, а запрос, собранный конкатенацией
или `СтрШаблон`, виден им лишь частями. Эти области остаются непроверенными, и вердикт
«чисто» по инструменту их не закрывает — разбор приближений у каждого правила в его
справочнике.
- ### 4. Стандарты под архетип
-
- Не весь свод подряд — только релевантное:
-
- | Архетип | Справочники |
- |---|---|
- | Запрос | `bsl-query-optimization.md`, `bsl-query-reference.md` |
- | Модуль формы | `bsl-form-module-rules.md` |
- | Асинхронный клиент | `bsl-async.md` |
- | Новый модуль, форматирование, транзакции | `bsl-coding-standards.md` |
- | Кастомная утилита | `bsp-common-modules.md` — есть ли готовый метод библиотеки |
- | Глубокая вложенность, длинные методы | `bsl-refactoring.md` |
+ ### 3. Стандарты под архетип
- Плюс `references/checklist-code.md` — 17 разделов по областям; бери разделы под затронутый
- архетип. Тексты самих стандартов запрашивай через MCP `v8std` по номеру.
+ Не весь свод подряд — только релевантное: какие справочники открыть и какие разделы
+ `checklist-code.md` прочесть под сработавший архетип — печатает план (`gate.mjs plan`);
+ полный перечень по архетипам — `ARCHETYPES[*].refs` в `tools/profile.mjs`. Глубокая
+ вложенность и длинные методы архетипом не считаются и в план не попадают — при такой правке
+ открывай `bsl-refactoring.md` сам. Тексты самих стандартов запрашивай через MCP `v8std` по
+ номеру.
- ### 5. Именование
+ ### 4. Именование
`#std454` — частая и легко пропускаемая ошибка: сокращения-префиксы, не-CamelCase,
булево не в утвердительной форме. Детали и примеры — в `references/checklist-code.md`.
- ### 6. Символы в исходнике
+ ### 5. Символы в исходнике
В коде и комментариях только ASCII-дефис. Длинное тире и его родственники дают у анализатора
ошибку недопустимого символа. Кавычки-ёлочки допустимы.
- ### 7. Верификация API — субагент `bsl-verifier`
+ ### 6. Верификация API — субагент `bsl-verifier`
Сигнатуры платформенных методов, существование и экспортность общих модулей, состав объектов
метаданных. Процедура — `references/api-verification.md`.
**Делегируй субагенту `bsl-verifier`**, передав ему список изменённых `.bsl`-файлов. Он
дешёвый, работает по той же процедуре и возвращает вердикт, список нарушений с локациями и
раздел «Не проверено». Вызов **один на весь список**: каждый лишний инстанс поднимает свою
сессию индекса кода, а справочник платформы на stdio-транспорте вдобавок не переносит
параллельных обращений.
Если прогнан второй движок слоя 1а, платформенная часть уже закрыта: субагенту остаются
общие модули, метаданные и контекст доступности.
Субагента в среде может не быть — тогда прогоняй `api-verification.md` сам. **Результат
обязан попасть в след одинаково в обоих случаях** (инвариант 4):
```
[qg applied: layer=code, scope=api-verification, ids=[qg:API-SIGNATURE,qg:API-MODULE], verdict=clean]
[qg skipped: layer=code, scope=api-verification, reason=platform_unavailable]
```
> Для класса C1 на этом контур завершается — переходи к отчёту.
---
## Слой 2 — ревью логики моделью
Вызови `advisor()`. Более сильная модель видит весь транскрипт: задачу, шаги, написанный код.
Ловит то, что статика не видит в принципе — неверную бизнес-логику, упущенные сценарии,
неучтённые состояния. Замечаниям давай весомый вес.
### Холодный читатель — второй взгляд с противоположным входом
Дополнительно к `advisor()`, когда цена ошибки высока: класс C3 либо затронуты проведение,
деньги, права, необратимые операции. Ценность даёт противоположность входов, а не второе
мнение — почему, разбирает `references/cold-reader.md`.
**Передавать:** только сравнение версий и содержимое изменённых файлов. **Не передавать:** формулировку
задачи, свои выводы, названия найденных проблем — узнавший намерение читатель перестаёт
быть холодным.
Три вопроса, на которые он отвечает:
1. Что этот код делает **как написан**, а не как задуман?
2. На каких входных данных он ломается или ведёт себя неожиданно?
3. Какое ожидаемое поведение из него не следует?
**Модель — не дешёвая:** выносится суждение о логике, уровень не ниже основной модели
сессии. Расхождение его выводов с `advisor()` — сигнал, а не шум: код допускает два прочтения.
## Слой 3 — состязательный аудит (только по подтверждению)
**Никогда не запускается сам** — контур лишь предлагает его в отчёте и ждёт явного согласия.
Суть: веер независимых ревьюеров по измерениям, затем по каждой находке несколько
проверяющих, которым поставлена задача её **опровергнуть**. Проходит только то, что
опровергнуть не удалось.
Состав измерений, пороги, правила голосования, асимметрия для находок 🔴 и порядок действий,
когда оркестрация недоступна, — в `../quality-gate/references/adversarial-audit.md`.
---
## Автофикс (`--fix`)
**Можно:** именование (через переименование символа анализатором, не текстовой заменой),
форматирование и отступы, канонические ключевые слова, магические литералы на системные
константы, очевидные quick-fix анализатора.
**Нельзя без подтверждения:** любая правка логики, проведения, запросов; транзакции и
блокировки; права и привилегированный режим; всё, помеченное 🔴; сигнатуры экспортных методов
(ломает вызывающих).
После автофикса прогони Слой 1 заново — правки могли внести новые диагностики.
---
## Выход
### Находки
```
[🔴/🟠/🟡] <краткая суть>
Где: <путь:строка>
Правило: #stdNNN п.X | антипаттерн «<название>» | #bslls:<Код>
Проблема: <что именно не так здесь>
Как исправить: <конкретно; для 🔴 — со ссылкой на пример из справочника>
+ Уверенность: средняя | требует проверки — опускается при высокой
```
+ При не-высокой уверенности следом — проверка, которая находку закроет. Довод, снимающий
+ находку без такой проверки, обязан опираться на прочитанный источник — правило и пример в
+ `../quality-gate/references/evidence-format.md`.
+
Ключ локации `<путь>::<Метод>:<строка>` обязателен — по нему оркестратор дедуплицирует
находки с архитектурным контуром (правила — в `shared/routing-contract.md`).
### Записи следа
Минимум одна на каждый слой — выполненный или пропущенный:
```
[qg applied: layer=code, scope=query-in-loop, ids=[std436,bslls:QueryInLoop], 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=code, scope=static-analysis, planned=[bslls:*], reason=analyzer_unavailable]
```
Вторая строка — из тех, что печатает инструмент: `attribute-access` стал инструментальным, и
написанная руками, она валидатор больше не проходит.
Формат — `../quality-gate/references/evidence-format.md`.
Два измерения контур закрыть не может и обязан об этом сказать. **Компилируемость тел
модулей** проверяет только платформа: без запуска проверки конфигурации нужна запись
`[qg not_verified: dimension=compilation, reason=no_platform]`, иначе полностью чистый вердикт
валидатор отклонит. **Выполнимость запроса** — то же самое при сработавшем архетипе «Запрос»:
```
[qg applied: layer=code, scope=query-execution, ids=[qg:QRY-EXECUTED], verdict=clean]
[qg not_verified: dimension=query-execution, reason=no_platform]
```
- Проверка по тексту кода (пункт 3 Слоя 1б) её не заменяет — «Поле не найдено» и
+ Проверка по тексту кода (пункт 2 Слоя 1б) её не заменяет — «Поле не найдено» и
несовместимость типов в `ОБЪЕДИНИТЬ` всплывают только при выполнении. Почему оба измерения
устроены так — `../quality-gate/references/evidence-format.md`.