# Что стенд ловит, а что нет

Таблица проверена прогонами, а не документацией. Каждая строка — реальный
эксперимент на конфигурации «НоваяКонфа» и платформе 8.3.27.1859.

## Ловит

| Дефект | Слой | Пример вывода |
|---|---|---|
| Одиночная `"` внутри текста запроса (рвёт литерал) | gates, lint, platform | `Ожидается ключевое слово 'КонецФункции'` с указателем `<<?>>` на позицию |
| Незакрытый строковый литерал | gates | `строка N не начинается с '\|'` |
| Баланс `Функция`/`КонецФункции`, `Процедура`/`КонецПроцедуры` | gates | `Функция: 12, КонецФункции: 11` |
| Любая ошибка разбора BSL | lint (BSL LS) | `ParseError` с файлом, строкой, колонкой |
| Ошибка компиляции в контексте (Сервер / Тонкий клиент / Толстый клиент) | platform | `(Проверка: Тонкий клиент)` в каждом сообщении |
| Несоответствие числа фактических параметров вызова | platform | `Слишком много фактических параметров (ИмяМетода)` |
| Ошибка в модуле формы, а не только объекта | platform | `Обработка.X.Форма.Форма.Форма(951,29)` |
| Несуществующая таблица в тексте запроса | meta | `Документ.РеализацияТоварвоУслуг — такого объекта нет в конфигурации` |
| Переименованный/несуществующий реквизит в запросе | meta (предупреждение) | `Т.СчетУчетаРасчетовСАгентом — такого поля не видно` |
| Имена файлов в NFD вместо NFC | gates (предупреждение) | ловит будущую 422 «Файл объекта не существует» |
| Модуль без UTF-8 BOM | gates (предупреждение) | платформа пишет модули с BOM |
| Состав формы: кнопка, колонка, флаг «только просмотр», порядок элементов | render (L3a) | HTML-карта формы + JSON-структура |
| Несуществующая/переименованная таблица внутри `<query>` схемы СКД (`.erf`) | meta | `Документ.ЗаказПокупателяОпечатка — такого объекта нет в конфигурации` — платформа это НЕ проверяет вообще, только meta_check |
| Опечатка в имени макета/области (`ПолучитьМакет`/`ПолучитьОбласть`) | mxl | `нет такой области в макете "X"` — платформа не проверяет вообще, только mxl_check |

## Исправленные ложные срабатывания (2026-07-30, прогон на 2 крупных реальных обработках)

Два новых, ранее не гонявшихся реальных файла (`ОбщийАкт_Новый_13.01.2026.epf`,
~1 МБ, 10 макетов; `ПисьмаОтелям_28.07.2026.epf`, 8 форм) сразу вскрыли
4 настоящих бага в самом стенде — не в проверяемых обработках. Платформа
(`/CheckModules`, L1) и BSL LS оба говорили «ошибок нет» на этом же коде —
именно это расхождение и указало, что не так статические чекеры, а не файл:

1. **`gates.py`: маркер-комментарий `//+ Бурдуков ... //-` ВНУТРИ
   многострочного текста запроса** (между `|`-продолжениями) ошибочно
   трактовался как «строка не начинается с `\|`» → 23 ложных блокирующих
   находки на одном файле. Это легальная и частая практика этого
   разработчика — платформа и BSL LS компилируют её чисто. Фикс: строки-
   комментарии внутри литерала пропускаются целиком, не разрывая состояние.
2. **`meta_check.py`: сверка имён полей была регистрозависимой**, а BSL и
   язык запросов — нет (`Т.ссылка` и `Т.Ссылка` — одно и то же). Реальный
   код с `.ссылка` (строчными) давал ложные предупреждения. Фикс: сравнение
   по `.lower()`.
3. **`meta_check.py`: не было записи `РегистрБухгалтерии` в `STANDARD`** —
   стандартные поля регистра бухгалтерии (`Регистратор`, `СчетДт`, `СчетКт`
   и т.п.) ложно считались «не видно». Добавлена запись.
4. **`mxl_check.py`: та же регистрозависимость для имён областей** (область
   `подвал` в XML макета vs вызов `ПолучитьОбласть("Подвал")` в коде — легально)
   **и не пропускались закомментированные строки** (`//ОбластьМакета = ...`
   ложно проверялся, хотя код не исполняется). Оба исправлены.

**Известное непроверенное ограничение (не исправлено, осознанный компромисс):**
`meta_check.py` не понимает границы `ОБЪЕДИНИТЬ ВСЕ` (UNION) — если один и тот
же алиас переиспользуется в нескольких ветках объединения с разной привязкой
таблицы (реальный паттерн: `Документ.X.Товары КАК Т` / `ОБЪЕДИНИТЬ ВСЕ` /
`Документ.X.Услуги КАК Т`), инструмент считает актуальной только ПОСЛЕДНЮЮ
привязку алиаса на весь запрос — поля первых веток могут дать ложное
предупреждение «поле не видно» (это только `warn`, не блокирует). Полный фикс
требует разбора границ веток UNION — не реализовано, эвристика документирует
это ограничение в докстринге `check_query`.

Все 4 фикса закреплены регрессионными кейсами (`comment_marker_in_query`,
`mxl_typo`, `mxl_case_ok`, `mxl_comment_ok` в `tests/run_corpus.py`) —
13 кейсов.

**Уточнение (тот же день, первый полный прогон с реальной платформой):**
первый прогон 13 кейсов БЕЗ `--fast` дал 2 ложных провала —
`mxl_case_ok` и `mxl_comment_ok` показали `blocking=1`, хотя ожидались
чистыми. Расследование показало: баг не в `mxl_check.py`, а в САМИХ
тестовых кейсах `run_corpus.py` — они вызывали `Обработка.ПолучитьМакет(...)`
в модуле формы напрямую, без предварительного
`Обработка = РеквизитФормыВЗначение("Объект");`. В форме `Обработка` —
необъявленная переменная; платформа (`/CheckModules`, L1) совершенно
справедливо ловила это как «Переменная не определена (Обработка)» —
это НЕ ложное срабатывание стенда, а правильно найденная ошибка в тексте
самого кейса. Реальный продовый код (`Forms/Форма/Ext/Form/Module.bsl:882`)
всегда делает `Обработка = РеквизитФормыВЗначение("Объект");` первой строкой
перед `ПолучитьМакет` — тестовые кейсы это упустили. Фикс: добавлена
недостающая строка в `case_mxl_typo`, `case_mxl_case_insensitive_ok`,
`case_mxl_commented_ok`. После фикса — полный прогон 13/13 чисто, включая
реальную платформенную компиляцию.

**Урок:** `--fast` (без платформы) не ловит ошибки, которые сам тестовый
кейс вносит в код (кейс должен компилироваться платформой, если явно не
проверяет платформенный дефект) — расхождение `--fast` vs полного прогона
на КОНТРОЛЬНЫХ («_ok») кейсах надёжный сигнал искать баг в самом кейсе,
а не сразу чинить проверяемый слой.

## Не ловит — и почему

| Дефект | Почему |
|---|---|
| Что запрос реально вернёт на данных | В базе только конфигурация, без производственных данных. Нужен рантайм-слой, его сознательно не строили. |
| Несуществующий объект через коллекцию: `Справочники.ЭтогоНет` | Обращение динамическое, платформа проверяет его только при исполнении. Проверено: `/CheckModules -ExtendedModulesCheck` молчит. |
| Несуществующий метод общего модуля: `ОбщегоНазначения.НетТакогоМетода()` | В расширении методы основной конфигурации видны, платформа не берётся утверждать, что метода нет. Проверено — молчит. |
| Синтаксис самого языка запросов (`ВЫБРТЬ`, лишняя запятая) | Для платформы текст запроса — строка. BSL LS даёт частичную диагностику SDBL (псевдонимы, разыменование), но не полный разбор. |
| Пиксельный вид формы | Стенд принципиально не кликает по GUI (см. «Только программно» ниже). `render_form` даёт карту по метаданным, не копию клиента. |
| Поведение при клике, заполнение, проведение | Это рантайм и UI-автоматизация — вне периметра стенда по дизайну, не только по ограничению среды. |
| Проверка на реальных прод-данных | Дев-база = только конфигурация. За реальными данными — не подключение агента к проду, а релей через пользователя, см. `tools/context_relay.py` и `sandbox/README.md`. |

## Тупики, которые уже проверены — не тратить время повторно

- **`POST /build` у onec-build-agent (порт 8788) HTTP 200 ≠ код валиден.**
  `/LoadExternalDataProcessorOrReportFromFiles` только упаковывает XML в
  `.epf`, модули не компилирует. Проверено: обработка с разорванным литералом
  собралась с кодом 200.
- **`/CheckConfig -ExternalReportsOrDataProcessors <каталог>` не проверяет
  `.epf` из каталога.** Логи на чистой и на битой обработке совпали побайтово.
  Ключ работает только для обработок, подключённых к конфигурации через
  справочник дополнительных отчётов и обработок.
- **Правильная команда — `/CheckModules -Extension <имя>`**, причём обработку
  нужно предварительно оформить объектом расширения (см. `tools/ext_wrap.py`).
  `/CheckConfig -Extension` модули расширения не проверяет.
- **`ConfigDumpInfo.xml` в выгрузке `НоваяКонфа` отсутствует** → частичная
  загрузка `/LoadConfigFromFiles -files` невозможна, поэтому путь через
  расширение, а не через подмену объекта в конфигурации.
- **BSL LS 1.0.6 требует Java 21** (class file 65). Java 17 падает с
  `UnsupportedClassVersionError`. На машине есть `openjdk@21` из brew.
- **Preview-панель не даёт DOM и скриншот для `file://`** — только статичный
  снимок. Карты форм поэтому отдаются через dev-сервер `1c-form-maps`
  (`.claude/launch.json`, порт 8911).
- **Толстый клиент `1cv8 ENTERPRISE` на этой macOS-машине рендерится ЧИСТО**
  (проверено 2026-07-30: чёткие шрифты, ровные иконки, никаких артефактов) —
  прежнее предположение «GUI 1С рендерится криво» было ошибочным. Это не
  повод его использовать: стенд принципиально не кликает по GUI (см. раздел
  «Только программно» в `SKILL.md`), Accessibility-автоматизация сюда не
  вводилась и вводиться не будет.

## Локальные синтетические тестовые данные — исследовано, тупик

Идея: если бы можно было headless создать записи (документы/справочники) в
`ib_stand`, появился бы функциональный слой — проверка не только «что
компилируется», но и «что реально возвращает запрос» — полностью локально,
без единого байта из прода.

**Эмпирически подтверждено:** `1cv8 ENTERPRISE /F <база> /Execute"<файл.epf>"`
действительно запускает механизм открытия внешней обработки (платформа
опознаёт и показывает её реальные метаданные из `СведенияОВнешнейОбработке`).
Если бы не следующий пункт, код верхнего уровня `ObjectModule.bsl`
(выполняется автоматически при создании экземпляра объекта — обычное
поведение BSL, не нужен отдельный вызов) стал бы headless-генератором данных.

**Стопор — жёсткий, не обходится для нашей конфигурации:** платформа перед
ЛЮБЫМ открытием внешнего файла показывает модальное «Предупреждение
безопасности → Разрешить открывать данный файл? Да/Нет» без программного
подавления для файловой базы на macOS. Проверено документально и на диске:

- `1cv8cmn.pfl` (`~/.1cv8/1C/1cv8/`) хранит `MRUFileList` — автодополнение
  путей в меню «Открыть», НЕ флаг доверия к файлу. Дамп подтвердил: там нет
  структуры, отвечающей за подавление именно этого диалога.
- `conf.cfg` / `DisableUnsafeActionProtection=.*` — это про `БезопасныйРежим`
  выполнения BSL-кода (интернет, файловая система, ОС-команды, COM), НЕ про
  диалог открытия внешнего файла. Не подходит по смыслу, проверено по ИТС.
- Профили безопасности — назначаются администратором **кластера серверов**.
  У нас прямая файловая база без кластера — рычага физически нет.
- `ВнешниеОбработки.Создать(..., БезопасныйРежим=Ложь)` подавляет диалог
  ТОЛЬКО для вызова из уже выполняющегося кода конфигурации — но чтобы код
  начал выполняться, файл сперва нужно открыть. Замкнутый круг без стартовой
  точки.
- COM-коннектор (`V83.COMConnector`) реально обходит диалог программно —
  но это Windows-only API, на macOS недоступен.

**Итог: для файловой базы на macOS без Windows/кластера headless-пути открыть
внешний файл в первый раз нет.** Это ограничение платформы/среды, не пробел
в реализации стенда. Не тратить время на повторное исследование этого пути —
если когда-нибудь понадобится, единственный обходной путь — Windows VM с
COM-коннектором, отдельная и дорогая инвестиция.

### Дополнительный раунд (2026-07-30): попытка обойти диалог через пользователя ИБ

Гипотеза: раз диалог — это `ЗащитаОтОпасныхДействий` конкретного ПОЛЬЗОВАТЕЛЯ
базы (`ПользовательИнформационнойБазы.ЗащитаОтОпасныхДействий`,
`ОписаниеЗащитыОтОпасныхДействий.ПредупреждатьОбОпасныхДействиях = Ложь`,
API с версии 8.3.8) — можно один раз (с ручным кликом) создать пользователя
с отключённой защитой, и дальше подключаться под ним `/N` headless навсегда.

**Дошли до реального создания пользователя** (`ПользователиИнформационнойБазы.
СоздатьПользователя()`), платформа его действительно видит (диалог входа
показывал имя как известное) — но упёрлись в ДВА новых, независимых стопора:

1. **Диалог выбора пользователя/пароля при входе неустраним через `/N`/`/P`
   из командной строки для файловой базы**, даже в задокументированном
   синтаксисе `/N Имя /P Пароль` (с пробелом). Показывается ВСЕГДА, как
   только в базе есть хоть один именованный пользователь — что прямо
   противоречит документации ("если указаны эти параметры, диалог не
   показывается"). `/DisableStartupDialogs` эту проблему не решает иначе —
   он просто пропускает выполнение `/Execute` целиком (в журнале событий нет
   даже записи об открытии файла), т.е. это не обход, а тихий отказ от
   потенциально опасного действия при невозможности показать диалог.
2. **Пустой пароль (`Пароль = ""`) отклоняется политикой паролей при входе**
   — реальная ошибка "Неверно указан пользователь или пароль" при пустом
   поле пароля, хотя запись прошла без ошибок и в БСЛ это официально
   значение по умолчанию. Значит нужен непустой пароль — но тогда стенд
   перестаёт быть headless (пароль надо где-то держать, пусть и локально).

Дальше проверяли: форма с корректно объявленным `<Events><Event
name="OnCreateAtServer">...` (тот же паттерн, что в реальной продовой
`Форма.xml`) — компилируется чисто, файл открывается (лог событий это
подтверждает, ошибок нет), но процедура `ПриСозданииНаСервере` **не
срабатывает** — форма остаётся висеть пустой, маркер не пишется. Отличие от
рабочего продового паттерна не установлено; возможно, `/Execute` вызывает
открытие формы внешней обработки НЕ через тот же путь, что обычное открытие
из UI (`ВнешниеОбработки.Создать().ПолучитьФорму().Открыть()`), и не
инициализирует полный жизненный цикл управляемой формы. Точный контракт
`/Execute` не документирован официально (проверено ИТС и документацией
платформы — прямого описания механизма нет).

**Итог второго раунда: headless-обход через пользователя ИБ не удался** —
не из-за одной решаемой мелочи, а из-за НАКОПЛЕНИЯ независимых препятствий
(диалог входа неустраним, пустой пароль отклоняется, форма не инициализирует
жизненный цикл под /Execute). Продолжать было бы разбором недокументированного
поведения без гарантии результата. `ib_stand` возвращена в чистое состояние
(пересоздана из `ib_u1005954706`, без побочных пользователей) — основной
конвейер L0/L0.5/L1/L3a (компиляция, гейты, метаданные, карты форм) этим
экспериментом не затронут и продолжает работать как проверено ранее.

## Проверено дополнительно (2026-07-30)

- **Ветка `.erf` → `Report`** прогнана на реальном `ПродажиПоКонтрагентам.erf`
  (~/Desktop, чисто СКД-отчёт, `ObjectModule.bsl` пуст). L1 отработал с
  первого раза: `Report.ПродажиПоКонтрагентам` собрался и прошёл
  `/CheckModules` без правок в `ext_wrap.py`. Негативный тест (та же кавычка
  в тексте запроса, вставленная в пустой модуль) поймана всеми тремя слоями
  (gates, BSL LS, платформа) с той же точностью, что и на обработках —
  ветка `.erf` полноценно рабочая, повторно не тестировать с нуля.
- Второй реальный кейс обработки (`аКорректировкаДокументовОператора`,
  v1→v5, другой домен) вскрыл 2 реальных бага в `render_form.py`: акронимы
  («НДС», «СФ») резались по буквам при разбивке CamelCase, а служебные
  префиксы-«глина» `Форма`/`Команда`/`а` (соглашение именования этого
  разработчика) не срезались для команд формы верхнего уровня — оба
  исправлены, регрессия на эталонной форме проверена (не сломалась).

## Схема СКД — проверено (2026-07-30)

`meta_check.py` теперь сканирует `<query>` внутри `DataCompositionSchema`
(любой `Template.xml` с таким корнем, не только по фиксированному пути) —
той же логикой сверки имён, что и для BSL-запросов, включая UNION (несколько
`<query>` в одном `dataSet`). Регрессионный кейс `dcs_typo` в
`tests/run_corpus.py` закрепляет это навсегда.

**Побочная находка при первом прогоне на реальном файле:** `~/Desktop/
ПродажиПоКонтрагентам.erf` (дата файла 2026-06-10) ссылается в схеме СКД на
`Документ.ЗаказПокупателя`, а в текущей конфигурации этот документ называется
`ЗаказПокупателя2` — отчёт не пройдёт `L0.5` без правки. Платформа
(`/CheckModules`) этого не видит вообще: схему СКД не проверяет.

## Печатные макеты — проверено (2026-07-30)

`tools/mxl_check.py` (новый слой L0.5) сверяет `ПолучитьМакет("Имя")` и
`<переменная>.ПолучитьОбласть("Область")` с реальным составом `Templates/`
в XML-выгрузке. `.mxl` (табличный документ) уже приходит от платформы как
читаемый XML `xmlns=".../data/spreadsheet"` с `<namedItem>/<name>` — парсить
бинарник не нужно.

Разбор эвристический построчный (переменная-макет = последнее присвоение
той же переменной выше по тексту модуля, без разбора границ процедур) —
ловит опечатку в литерале имени, не гарантирует ноль ложных срабатываний на
косвенных обращениях (макет получен не по литералу — тихо пропускается,
не блокирует). Проверено на 3 реальных источниках: `КомиссияКлиентов.epf`
(3 макета, чисто), `аКорректировкаДокументовОператора...v5.epf` и
`ПродажиПоКонтрагентам.erf` (0 макетов у обоих — не спутано с DCS-схемой,
у которой корневой тег другой). Негативный тест — опечатка и в имени макета,
и в имени области — поймана корректно с точной строкой.

**Что НЕ проверяется:** параметры макета (`Область.Параметры.X`,
`.Параметры.Заполнить(Структура)`) — обычно заполняются динамически из
таблицы/структуры, статически сверить имена нечем без разбора состава этой
структуры в рантайме.

## CommonCommand на кнопке формы — ложное срабатывание L1 (2026-08-03)

`РеализацияУслугиПоступлениеБайко.epf` (кнопка «Связанные документы», привязка
к `CommonCommand.СвязанныеДокументы`) даёт на L1 **`Неверное имя команды
элемента формы. - CommonCommand.СвязанныеДокументы`** под `ext_wrap`-обёрткой.

**Подтверждено ложным:** прогнан АБСОЛЮТНО нетронутый оригинал файла (до любых
правок в этой сессии) — та же ошибка, то же место. Обработка при этом реально
работает у пользователя в проде (кнопка видна и открывает связанные документы).
Причина, по всей видимости, — уже задокументированный разрыв «Расширение — не
полный эквивалент внешней обработки» (см. ниже): общая команда резолвится в
контексте `DataProcessor.X`, а не `ExternalDataProcessor.X`, и это ломает именно
привязку `CommonCommand`, не влияя на обычную компиляцию модулей.

**Практическое правило:** если L1 находит ТОЛЬКО `Неверное имя команды элемента
формы` для `CommonCommand.*` — прогнать тот же чек на копии файла ДО своих
правок (`git`-бэкап или `.bak-pre-*`); если ошибка идентична и там — это
пре-существующий артефакт стенда для общих команд, не блокер твоей правки.
Если ошибка меняется/добавляется новая — там настоящий регресс. Полноценного
фикса `ext_wrap.py` под общие команды не делали (не хватает данных о том, как
правильно резолвить `CommonCommand` для чужого `ObjectBelonging=Native`
DataProcessor) — фиксируем как известное ограничение, не как решённое.

## Грани, которые стоит помнить

- Расширение — не полный эквивалент внешней обработки: `ЭтотОбъект` внутри
  расширения это `ОбработкаОбъект.X`, а не `ВнешняяОбработкаОбъект.X`. На
  синтаксическую проверку это не влияло ни в одном прогоне, но при обращении
  к специфике внешней обработки расхождение возможно.
- Модули `.bsl` в расширение копируются байт-в-байт. Меняются только
  метаданные (`ExternalDataProcessor` → `DataProcessor`, `ObjectBelonging`,
  `InternalInfo`). Иначе проверялся бы не тот код, который уходит заказчику.
- Стенд работает на копии базы `ib_stand`, не на `ib_u<uid>`, которой
  пользуется onec-build-agent — Конфигуратор не параллелится на одной базе.
