bsl-code-review · git:20260901.3570af2 · 2026-09-01 · sha256 2de32f02d5c60c2b
bsl-code-review git:20260901.3570af2A
Immutable. This exact content is served forever at /api/v1/blob/2de32f02d5c60c2b.
--- name: bsl-code-review description: >- Контур проверки кода BSL: диагностики статического анализатора, антипаттерны производительности и механики платформы, стандарты разработки #stdNNN, именование, верификация сигнатур API и существования общих модулей. Уровень «внутри тела метода» — то, что чинится заменой строк. Вызывается оркестратором quality-gate с готовым профилем изменения; напрямую — по запросу «проверь код», «отревьюй что я написал», «проверь на антипаттерны». license: MIT --- # bsl-code-review — контур кода Проверяет то, что чинится **внутри тела метода**: замена строк, без нового шва. Всё, что требует выделения метода, переноса в другой модуль, нового экспорта или изменения «кто кого вызывает», принадлежит контуру `bsl-architecture-review` — граница и правила дедупликации находок в `shared/routing-contract.md`. <ЖЁСТКИЙ-ШЛЮЗ> Только проверка и отчёт. НЕ переписывай логику, запросы, транзакции и права по своей инициативе. В режиме `--fix` допустимы лишь безопасные категории (см. ниже). </ЖЁСТКИЙ-ШЛЮЗ> ## Инварианты контура Пять утверждений, без которых прогон контура недействителен. 1. **Оба файла антипаттернов прогоняются всегда** — и на мелкой правке тоже. Находка 🔴 из любого блокирует вердикт «чисто». 2. **Строку следа инструментальной проверки печатает инструмент** — переноси дословно, своих находок этого класса не добавляй: результат детерминирован. 3. **Каждое замечание доказуемо**: номер стандарта, код диагностики или название антипаттерна плюс строка кода. «Так лучше» — не находка. 4. **Пропуск фиксируется.** Недоступный инструмент или субагент даёт `skipped` с причиной; молчание неотличимо от выполнения. 5. **Файл, который анализатор не разобрал, не проверен** — вердикт «чисто» по нему невозможен, и в отчёте он назван поимённо. ## Вход От оркестратора: класс изменения (C0…C3), сработавшие архетипы, список изменённых файлов. При прямом вызове — определи профиль сам по правилам `quality-gate`. | Класс | Глубина | |---|---| | C0 | контур не запускается | | C1 | Слой 1 | | C2 | Слой 1 + Слой 2 | | C3 | Слой 1 + Слой 2, предложить Слой 3 | Архетип поднимает глубину независимо от класса: запрос, транзакция, запись наборов записей, обработчик события объекта, интеграция, права, CFE-перехват, регламентное задание — минимум Слой 2. Итоговая глубина — максимум из требований объёма, архетипов и сложности. --- ## Слой 1а — статический анализ Одна команда: она находит корень конфигурации, прогоняет только изменённые файлы, проверяет часового и формирует записи следа. ```bash node "$QG/tools/analyzer-run.mjs" --changed <файл> [--changed <файл> ...] ``` Вывод — находки по файлам и готовый блок `## 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 — это законный исход. Два правила: **`info` «низкая уверенность» не отбрасывать** (класс смешанный) и **часовой `not_found` — «чисто» запрещено**. Остальное — `references/platform-api.md`. ## Слой 1б — то, чего анализатор не видит ### 1. Антипаттерны производительности и механики платформы Источник: `references/bsl-anti-patterns.md` — прогоняется **всегда**, при любой глубине. | Антипаттерн | Что искать | Severity | |---|---|---| | Запрос в цикле | `Новый Запрос` внутри `Для Каждого` | 🔴 | | Чтение реквизита через точку | `.Реквизит` у ссылочного типа — грузит объект целиком | 🔴 | | Подзапрос в списке полей | вложенный `ВЫБРАТЬ` в секции выборки (N+1) | 🔴 | | Коррелированный подзапрос в условии | вложенный `ВЫБРАТЬ` в `ГДЕ`, ссылающийся на внешнее поле | 🔴 | | Фильтр виртуальной таблицы в ГДЕ | условие на результате ВТ вместо её параметров | 🟠 | | Временная таблица без индекса | `ПОМЕСТИТЬ` без `ИНДЕКСИРОВАТЬ ПО`, далее соединение по этому полю | 🟠 | | Отсутствие ограничения выборки | большой запрос без `ПЕРВЫЕ N` | 🟠 | | Множественные серверные вызовы | последовательные вызовы сервера с клиента | 🟠 | | Контекстный вызов без нужды | `&НаСервере` там, где хватает `&НаСервереБезКонтекста` | 🟠 | | Транзакция внутри Попытки | `НачатьТранзакцию()` внутри `Попытка`, а не наоборот | 🟠 | | Транзакция внутри неявной транзакции | `НачатьТранзакцию()` в `ОбработкаПроведения`, `ПередЗаписью`, `ПриЗаписи` | 🟠 | | `Сообщить()` как уведомление | сообщение без привязки к объекту и вне журнала регистрации | 🟠 | | Отсутствие кеширования | повторные дорогие вызовы с теми же параметрами | 🟡 | | Квадратичный поиск | вложенные циклы сопоставления вместо `Соответствие` | 🟡 | ### 2. Антипаттерны кода, порождаемого моделью Источник: `references/ai-antipatterns.md` — прогоняется **всегда, наравне с предыдущим**. Ошибки, характерные для кода языковой модели: перепроверка контракта собственной функции, форк парсера на каждый вариант ответа, отчёт о непрогнанной проверке. Типовые своды их не покрывают — человек таких ошибок обычно не делает. ### 3. Лексические проверки инструментами ```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> # правился модуль формы ``` `query-lint` читает и XML-носители запросов — `<query>` схем компоновки данных и `<QueryText>` динамических списков, — поэтому **изменённые XML передаются ему наравне с модулями**; номер строки в такой находке считается от начала текста запроса, как в сообщениях платформы. Оба инструмента печатают готовые записи следа и отмечаются в журнале прогонов. | Признак | 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 | **`attribute-access` покрыт инструментом лишь частично.** Доказать ссылочность в пределах одного файла удаётся не всегда: ссылка из чужой функции или из недокументированного параметра остаётся неопознанной. `clean` здесь означает «механическая часть чиста» и разбора #std437 глазами не отменяет — инструмент задаёт нижнюю границу, а не верхнюю. **Записи переносятся дословно, своих находок этого класса не добавляй.** Результат детерминирован, а строка, составленная по прочтении кода, выглядит в отчёте точно так же — поэтому валидатор следа её отвергает. Оба инструмента видят один файл и графа вызовов не строят, а запрос, собранный конкатенацией или `СтрШаблон`, виден им лишь частями. Эти области остаются непроверенными, и вердикт «чисто» по инструменту их не закрывает — разбор приближений у каждого правила в его справочнике. ### 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` | Плюс `references/checklist-code.md` — 17 разделов по областям; бери разделы под затронутый архетип. Тексты самих стандартов запрашивай через MCP `v8std` по номеру. ### 5. Именование `#std454` — частая и легко пропускаемая ошибка: сокращения-префиксы, не-CamelCase, булево не в утвердительной форме. Детали и примеры — в `references/checklist-code.md`. ### 6. Символы в исходнике В коде и комментариях только ASCII-дефис. Длинное тире и его родственники дают у анализатора ошибку недопустимого символа. Кавычки-ёлочки допустимы. ### 7. Верификация 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:<Код> Проблема: <что именно не так здесь> Как исправить: <конкретно; для 🔴 — со ссылкой на пример из справочника> ``` Ключ локации `<путь>::<Метод>:<строка>` обязателен — по нему оркестратор дедуплицирует находки с архитектурным контуром (правила — в `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б) её не заменяет — «Поле не найдено» и несовместимость типов в `ОБЪЕДИНИТЬ` всплывают только при выполнении. Почему оба измерения устроены так — `../quality-gate/references/evidence-format.md`.