---
name: xml-structure-review
description: >-
  Контур проверки XML-структур метаданных 1С: корректность файлов объектов, форм, схем
  компоновки, ролей и макетов; регистрация объекта в составе конфигурации; сверка «диск ↔
  состав» в обе стороны; права на объекты в ролях расширения. Вызывается оркестратором
  quality-gate при изменении XML; напрямую — по запросу «проверь метаданные»,
  «провалидируй расширение», «почему объект не попал в сборку».
license: MIT
---

# xml-structure-review — контур XML метаданных

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

| Класс | Глубина |
|---|---|
| C0 | пропуск |
| C1 | не применим, если XML не менялся; иначе — валидация изменённых файлов |
| C2 | валидация изменённых объектов **плюс проверка регистрации** |
| C3 | полный проход: валидация, сверка диск↔состав в обе стороны, права в ролях |

## Прогон механики — субагент `xml-runner`

Запуск скриптов и разбор их вывода делегируй субагенту `xml-runner`, передав список изменённых
файлов и каталог выгрузки. Он вернёт вердикт, находки с путями и строками, а также раздел «не
проверено».

Причина в контексте: валидаторы печатают до тридцати ошибок на файл, и на правке класса C3 их
вывод вытесняет всё остальное раньше, чем дело дойдёт до отчёта. Твоя работа начинается после
его отчёта — триаж находок, привязка к типовым дефектам и решение, что чинить.

Субагента может не быть — тогда прогоняй скрипты сам по разделам ниже. Записи следа в обоих
случаях формируешь **ты**: субагент возвращает факты и в формат следа их не оформляет.

---

## 1. Сверка «диск ↔ состав» — главная проверка контура

```bash
node "$QG/tools/xml/orphan-check.mjs" <каталог выгрузки>
```

**Почему это первое, что нужно проверять.** Объект метаданных, лежащий на диске, но не
внесённый в секцию `ChildObjects` файла `Configuration.xml`, **не попадает в собранный
артефакт**: сборка зелёная, валидаторы молчат, падение происходит в рантайме у пользователя.
Это слепое пятно всей штатной цепочки, и закрывает его только явная сверка; почему молчит
каждое звено — `references/pipeline-blind-spots.md`.

Обратное направление не менее важно: имя в составе без файла на диске ломает саму сборку.

**Коды возврата:** 0 — расхождений нет, 2 — есть сироты либо отсутствующие файлы.

**Каталоги вне карты типов** скрипт не додумывает, а выносит в раздел «не проверено»:
молчаливый пропуск здесь означал бы ровно ту дыру, ради которой проверка и написана.

### Как чинить

Найденную сироту **регистрируют**, а не пересоздают: добавляют строку `<Тип>Имя</Тип>` в
нужную группу `ChildObjects`. Пересоздание объекта средствами генерации перезапишет его XML и
может затереть уже описанные реквизиты, измерения и ресурсы.

---

## 2. Уникальность UUID — вторая проверка того же класса

```bash
node "$QG/tools/xml/uuid-unique.mjs" <каталог выгрузки>
```

Объект, скопированный вместе со своим `uuid`, и заглушка вида `a1b2c3d4-…` дают два разных
объекта с одним идентификатором. Платформа при загрузке либо отвергает выгрузку, либо
оставляет один из двух — второй исчезает бесшумно, ровно как файл-сирота. Валидаторы
структуры совпадение между файлами не видят по устройству — разбор в
`references/pipeline-blind-spots.md`.

Проверяются файлы с корневым элементом `MetaDataObject`; повтор внутри одного файла — тоже
находка, продублированный блок реквизита уносит с собой `uuid` оригинала.

**Графические схемы не читаются:** точки карты маршрута бизнес-процесса платформа штатно
копирует между процессами, и скан `Ext/Flowchart.xml` давал бы находку на каждой типовой
конфигурации. Что это измерено на полной выгрузке, а не предположено, — там же, в справочнике.

**Коды возврата:** 0 — дублей нет, 2 — есть дубли либо каталог не прочитан.

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

---

## 3. Валидация структуры файлов

**Путь передавай параметром `-Path`** — он принимается всеми валидаторами без исключения:

```bash
python "$QG/tools/xml/meta-validate.py" -Path "<путь>"
```

У каждого скрипта есть ещё собственное имя параметра, и они разные (`-ObjectPath`,
`-FormPath`, `-RightsPath`, `-SubsystemPath`, `-TemplatePath`, `-CIPath`, `-ConfigPath`,
`-ExtensionPath`). Не подставляй имя от одного скрипта другому: `allow_abbrev=False`, и
вызов упадёт с ошибкой разбора аргументов. `-Path` снимает вопрос целиком.

| Что проверяется | Валидатор | Что передавать |
|---|---|---|
| Объект метаданных (справочник, документ, регистр, перечисление) | `meta-validate.py` | файл `Объект.xml` |
| Управляемая форма | `form-validate.py` | файл `Form.xml` |
| Схема компоновки данных | `skd-validate.py` | файл макета СКД |
| Роль и права | `role-validate.py` | любое из трёх: `Roles/Имя.xml`, каталог `Roles/Имя`, `Roles/Имя/Ext/Rights.xml` |
| Подсистема | `subsystem-validate.py` | файл подсистемы |
| Макет табличного документа | `mxl-validate.py` | файл макета |
| Командный интерфейс | `interface-validate.py` | файл командного интерфейса |
| Конфигурация целиком | `cf-validate.py` | корень выгрузки |
| Расширение конфигурации | `cfe-validate.py` | корень расширения |
| Внешняя обработка или отчёт | `epf-validate.py` | корень исходников обработки |

Роль — единственный объект, где проверяется не файл объекта, а `Rights.xml`; валидатор
приводит к нему любую из трёх форм пути сам.

Флаги: `-Detailed` — подробный вывод, `-MaxErrors N` — ограничение числа сообщений,
`-OutFile <путь>` — вывод в файл.

**Если Python недоступен** — запиши `[qg skipped: layer=xml, scope=structure-validation,
reason=python_unavailable]` и всё равно выполни пункт 1: сверка диск↔состав работает на Node и
от Python не зависит. Она же и есть самая ценная часть контура.

**Если валидатор упал с `ModuleNotFoundError: No module named 'lxml'`** — это не находка в
проверяемом коде, а недоступность инструмента: все валидаторы разбирают XML через `lxml`.
Запиши `[qg skipped: layer=xml, scope=structure-validation, reason=lxml_unavailable]`, назови
лечение (`pip install lxml`) и так же выполни пункт 1. Молча выдать это за ошибку файла —
худший исход: правка пойдёт в исправный XML.

---

## 4. Права на объекты в ролях расширения

Для расширений, содержащих собственные роли: **собственный объект расширения без явно
выданных прав невидим пользователю**, и ни сборка, ни валидаторы этого не показывают.

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

**Контр-сигналы — где отсутствие прав законно.** Прежде чем выпускать находку, сверься с
`references/role-rights-model.md`:

- у `Перечисление` и `РегламентноеЗадание` объектных прав **не существует** — пустой набор
  здесь никогда не находка, а запись о правах в роли, наоборот, дефект;
- право `Use` у HTTP- и веб-сервисов выдаётся точке вызова
  (`HTTPService.<Имя>.URLTemplate.<Шаблон>.Method.<Метод>`, `WebService.<Имя>.Operation.<Имя>`),
  а не сервису целиком;
- отсутствующий `Ext/Rights.xml` — валидное состояние пустой роли, а не потерянный файл.

**Собственное против заимствованного.** Права требуются собственным объектам и собственным
реквизитам расширения; у заимствованных они наследуются от конфигурации. Признак
принадлежности задан **отсутствием** тега, а не пометкой — разбор в
`references/cfe-object-belonging.md`.

---

## 5. Дефекты, которые проходят валидацию

Валидаторы разбирают XML по схеме формата и молчат о конструкциях, законных по схеме, но
ломающих платформу. Такой дефект опаснее обычного: отчёт зелёный, а артефакт не собирается.

**`AutoCommandBar` таблицы с `Autofill` и вложенным `ExtendedTooltip`.** Загрузка внешней
обработки или отчёта уходит в бесконечный цикл со 100% CPU — не ошибка и не диалог, а
зависание; `form-validate.py` при этом даёт «OK». **Контр-сигнал:** у `CommandBar` та же
конструкция штатна — признак действует только внутри `AutoCommandBar` **таблицы**. Законные
формы разметки и второй кандидат, снятый тем же разбором, но не изолированный, —
`references/pipeline-blind-spots.md`.

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

---

## 6. Типовые дефекты структуры

| Дефект | Признак | Чем ловится |
|---|---|---|
| Файл-сирота | объект на диске вне состава | пункт 1 |
| Отсутствующий файл | имя в составе без файла | пункт 1 |
| Дубль UUID | два объекта или реквизита с одним идентификатором | пункт 2 |
| Нарушен порядок объектов в составе | несоответствие каноническому порядку типов | `cfe-validate.py` |
| Невалидные элементы формы | элементы вне схемы формата | `form-validate.py` |
| Рассогласованные версии формата | разные версии в связанных файлах | `meta-validate.py` |
| Обработчик формы без процедуры | `qg:XML-FORM-HANDLER-MISSING`: `<Event>` называет имя, которого нет ни в модуле формы, ни в модуле базовой формы. Открытию формы это не мешает (проверено на платформе) — дефект спит до наступления события, отсюда 🟠, а не блокировка | `form-validate.py` |
| Действие команды без процедуры | `qg:XML-FORM-ACTION-MISSING`: то же для `<Action>` команды формы | `form-validate.py` |
| Отсутствие хранилища вариантов у отчёта | не задано хранилище настроек | `epf-validate.py` |
| Права объекта не заданы в роли | объект расширения не виден пользователю | пункт 4 |
| Права выданы типу, у которого их нет | запись `Enum.*` или `ScheduledJob.*` в файле прав | `role-validate.py`, пункт 4 |
| Неполное заимствование | `Adopted` без `ExtendedConfigurationObject` | `cfe-validate.py`, пункт 4 |
| Зависание загрузки на командной панели | `Autofill` и `ExtendedTooltip` внутри `AutoCommandBar` таблицы | пункт 5, валидаторами не ловится |
| Параметр СКД против виртуальной таблицы | `qg:SKD-PARAM-VT-COLLISION`: параметр `Период`/`НачалоПериода`/`КонецПериода` типа `StandardPeriod` при периодической ВТ без явных слотов — «Несоответствие типов» при формировании | `skd-validate.py` |
| Недопустимое поле в выборке группировки СКД | `qg:SKD-GROUP-NONAGGREGATE-FIELD`: поле — не поле группировки (с учётом родителей и реквизитов) и не ресурс — полный отказ формирования | `skd-validate.py` |
| Группировка СКД без выбранных полей | `qg:SKD-GROUP-EMPTY-SELECTION`: ни полей, ни Авто — запрос выполняется, отчёт молча пуст (предупреждение) | `skd-validate.py` |

---

## Записи следа

```
[qg applied: layer=xml, scope=registration-check, ids=[qg:XML-ORPHAN], verdict=violation:qg:XML-ORPHAN]
[qg applied: layer=xml, scope=uuid-uniqueness, ids=[qg:XML-UUID-DUP], verdict=clean]
[qg applied: layer=xml, scope=structure-validation, ids=[qg:XML-STRUCT], verdict=clean]
[qg applied: layer=xml, scope=form-binding, ids=[qg:XML-FORM-HANDLER-MISSING,qg:XML-FORM-ACTION-MISSING], verdict=clean]
[qg skipped: layer=xml, reason=not_applicable]
```

**Первые три строки печатают сами инструменты** — переноси их вывод дословно. Запись
`structure-validation` печатает любой из валидаторов XML (`meta-`, `form-`, `role-`, `skd-` и
остальные семь): проверка структуры называется одним именем независимо от вида файла. Каждый
инструмент отмечается в журнале прогонов, и валидатор следа сверяет: запись `applied` по
проверке, инструмент которой не запускался, снятие гейта не пройдёт.

Семантические находки СКД (`qg:SKD-PARAM-VT-COLLISION`, `qg:SKD-GROUP-NONAGGREGATE-FIELD`,
`qg:SKD-GROUP-EMPTY-SELECTION`) печатает тот же `skd-validate.py` внутри прогона
`structure-validation` — отдельного вызова для них нет. **Тексты запросов в `<query>` схемы
он не разбирает** — их проверяет `query-lint.mjs` контура `code`, которому изменённые XML
передаются наравне с `.bsl`.

Формат — `../quality-gate/references/evidence-format.md`.

**Валидность структуры не означает компилируемость.** Валидаторы разбирают XML и не
компилируют тела модулей; синтаксическая ошибка внутри процедуры проходит их все. Если
проверка конфигурации платформой не запускалась, нужна запись
`[qg not_verified: dimension=compilation, reason=no_platform]`.

---

## Принципы

- **Регистрация проверяется раньше структуры.** Идеально валидный XML вне состава бесполезен.
- **Сверка идёт в обе стороны.** Сирота ломает рантайм, отсутствующий файл ломает сборку.
- **Зелёная сборка ничего не доказывает.** Загрузка конфигурации из файлов игнорирует
  незарегистрированное молча.
- **Сироту регистрируют, а не пересоздают** — пересоздание затирает содержимое объекта.
- **Отсутствие прав — не всегда упущение.** У части типов объектных прав не существует, и
  требование выдать их отправляет искать несуществующую настройку.
- **Валидатор молчит и о законном, и о неразобранном.** «OK» на форме, которая вешает
  загрузку, — не вердикт о работоспособности, а граница схемы формата.

> `$QG` — каталог установленного плагина. Как его разрешить (переменная `CLAUDE_PLUGIN_ROOT`
> в оболочке пуста) — см. раздел «Путь к инструментам плагина» в навыке `quality-gate`.
