Claude Skill

xml-structure-review

Контур проверки XML-структур метаданных 1С: корректность файлов объектов, форм, схем компоновки, ролей и макетов; регистрация объекта в составе конфигурации; сверка «диск ↔ состав» в обе стороны; права на объекты в ролях расширения. Вызывается оркестратором quality-gate при измен

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

Full trust report

Download romandredan-1c-quality-gate-skills_xml-structure-review-c92a1dd.zip · 16 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/xml-structure-review
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

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

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

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

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

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

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

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


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

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

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

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

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

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

Как чинить

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


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

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

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

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

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

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

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


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

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

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. Принадлежность считается у самого объекта, а не у владельца: команда или реквизит, добавленные расширением в заимствованный отчёт, — собственные, права владельца их не покрывают (у типовых команд того же отчёта права выданы отдельными записями в ролях конфигурации).

Находку снимает только прочитанный источник. Довод «права наследуются», «объект не собственный», «доступ идёт от владельца» называет файл, на который опирается: роль, её Rights.xml, описание объекта. Без него находка остаётся с пониженной уверенностью и называет проверку, которая её решит. В A/B-прогоне ревью сняло находку о команде без прав одним таким доводом, а соседний прогон, прочитавший роли конфигурации, подтвердил её.


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

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

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

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

Тип поля схемы компоновки на объект вне состава расширения. Ссылка вида CatalogRef.Пользователи на незаимствованный объект теряется при загрузке в базу целиком и молча: файл на диске тип содержит, а в базе поле остаётся без типа, и форма отчёта не открывается — падают все варианты, включая типовые. Ловится пунктом 14 cfe-validate.py (qg:CFE-TYPE-REF-NOT-ADOPTED). Контр-сигнал: макет, побайтово равный вендорному, тип сохраняет — расширение не хранит свою копию. Измерения и разбор — в references/pipeline-blind-spots.md.

Спорить надо с базой, а не с файлом. Когда поведение платформы противоречит содержимому исходника, сверяют выгрузку из базы (DESIGNER /DumpConfigToFiles <каталог> -Extension <имя>), а не файл на диске: загрузка — не побайтовый перенос, часть конструкций она отбрасывает.


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:CFE-TYPE-REF-NOT-ADOPTED: ссылка вида CatalogRef.Имя на незаимствованный объект — платформа молча выбрасывает <valueType> при загрузке, поле остаётся без типа, форма отчёта не открывается (предупреждение) cfe-validate.py, пункт 14

Записи следа

[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.

Files (1c-quality-gate)
  • references
    • cfe-object-belonging.md 5.3 KB
      # Собственное и заимствованное в расширении — как различать
      
      Справочник контура XML для работы с выгрузкой расширений. Отвечает на вопрос, который встаёт
      перед любой проверкой файлов CFE: **этот реквизит принадлежит расширению или пришёл из
      конфигурации вместе с заимствованным объектом.** Правила проверки для двух случаев разные, и
      перепутать их — значит либо потребовать реквизиты заимствования у собственного элемента, либо
      пропустить неполное заимствование.
      
      ## Признак принадлежности задан отсутствием тега
      
      Тег `ObjectBelonging` имеет три состояния, и прямолинейное «своё помечено как своё» **неверно**:
      
      | Сущность | Как помечена |
      |---|---|
      | Заимствованный объект (справочник, документ, регистр…) | `<ObjectBelonging>Adopted</ObjectBelonging>` в блоке `Properties` объекта |
      | Заимствованный реквизит или табличная часть | `<ObjectBelonging>Adopted</ObjectBelonging>` |
      | **Собственный** элемент расширения | тега `ObjectBelonging` **нет** — значение `Native` подразумевается и не выгружается |
      
      Отсюда рабочее правило: **собственный элемент распознаётся по `ObjectBelonging != Adopted`**
      (тег отсутствует либо равен `Native`), а не по наличию явной пометки. Проверка «есть ли тег
      `Own`» не найдёт ничего: такого значения в выгрузке не бывает.
      
      У заимствованного объекта в заголовке присутствует ещё и
      `<ExtendedConfigurationObject>{uuid}</ExtendedConfigurationObject>` — ссылка на исходный объект
      конфигурации. Заимствование без этой ссылки — неполное и ломает загрузку.
      
      ## Раскладка файла заимствованного объекта
      
      ```
      MetaDataObject / <ТипОбъекта> uuid=…
        ├─ InternalInfo (GeneratedType …)
        ├─ Properties
        │    ├─ ObjectBelonging = Adopted          ← признак заимствования
        │    ├─ Name
        │    └─ ExtendedConfigurationObject = uuid ← ссылка на объект конфигурации
        └─ ChildObjects
             ├─ Attribute / Properties / (ObjectBelonging?) + Name
             ├─ TabularSection / Properties + ChildObjects (вложенные Attribute)
             └─ Dimension / Resource / EnumValue / Form …
      ```
      
      Элементы, у которых принадлежность проверяется: `Attribute`, `TabularSection`, `Dimension`,
      `Resource`, `AccountingFlag`, `ExtDimensionAccountingFlag`, `AddressingAttribute`, `Column`.
      Вложенные `Attribute` внутри табличной части проверяются наравне с реквизитами верхнего уровня.
      
      ## Что из этого уже делает валидатор, а что остаётся на контуре
      
      `cfe-validate.py` это правило соблюдает: заимствованным считается элемент с непустым
      `ObjectBelonging` **или** `ExtendedConfigurationObject`, и только для таких требуется `Adopted`
      плюс корректный UUID исходного объекта. Собственные элементы он молча пропускает — и это
      правильное поведение, а не пробел.
      
      На контур остаётся то, чего валидатор не проверяет: осмысленность самой принадлежности.
      Собственный реквизит в заимствованном объекте — законная и частая конструкция, но именно он
      живёт только в расширении, а значит требует прав в роли расширения и переносится отдельно при
      любой миграции в конфигурацию. Заимствованный реквизит таких обязательств не несёт.
      
      ## Чего этот признак не касается
      
      Не путать с **собственными объектами** расширения — новыми справочниками, документами,
      регистрами. У них `ObjectBelonging != Adopted` на уровне самого объекта, и их реквизиты — не
      «собственные реквизиты заимствованных объектов», а обычный состав собственного объекта.
      Для них работают правила регистрации в составе и прав, а не правила заимствования.
      
    • pipeline-blind-spots.md 11.5 KB
      # Слепые пятна штатной цепочки: почему зелёный прогон молчит
      
      Правила и команды — в `SKILL.md`; здесь разбор того, почему дефекты этого класса не ловит ни
      одно штатное звено, и измерения, на которых держатся пороги и исключения.
      
      ---
      
      ## Файл-сирота: почему молчит каждое звено
      
      Объект метаданных, лежащий на диске, но не внесённый в секцию `ChildObjects` файла
      `Configuration.xml`, не попадает в собранный артефакт. При этом:
      
      - **загрузка конфигурации из файлов** такие файлы молча игнорирует и не разрешает ссылки на
        них вглубь BSL — сборка проходит «успешно», лог пуст;
      - **валидаторы структуры** это тоже не ловят: они проверяют порядок **уже
        зарегистрированных** объектов и про файл вне состава ничего не знают.
      
      Итог: объект отсутствует в поставке, все проверки зелёные, падение происходит в рантайме у
      пользователя. Закрывает дыру только явная сверка «диск ↔ состав» — поэтому она и стоит
      первой проверкой контура.
      
      ## Дубли UUID: почему валидаторы пропускают их по устройству
      
      Каждый валидатор структуры разбирает свой файл и проверяет формат GUID; совпадение
      идентификатора с соседним файлом лежит за пределами его области зрения. Дубль видит только
      проверка, читающая выгрузку целиком, — `uuid-unique.mjs`.
      
      ### Исключение для графических схем — измерено, а не предположено
      
      На полной выгрузке типовой УТ — 19 693 файла метаданных, 63 837 идентификаторов — дублей
      между объектами ноль, а все повторы пришлись на `Ext/Flowchart.xml`: точки карты маршрута
      бизнес-процесса, которые платформа копирует между процессами. Скан схем давал бы находку на
      каждой типовой конфигурации, поэтому схемы исключены из проверки осознанно, а не «пока не
      поддержаны».
      
      ## Зависание загрузки: `AutoCommandBar` таблицы
      
      `Autofill` вместе с вложенным `ExtendedTooltip` внутри `AutoCommandBar` таблицы вешает
      загрузку внешней обработки или отчёта: бесконечный цикл со 100% CPU, без ошибки и без
      диалога. `form-validate.py` при этом даёт «OK» — конструкция законна по схеме формата.
      
      ```xml
      <!-- Вешает загрузку -->
      <AutoCommandBar name="ТаблицаКоманднаяПанель" id="10">
          <Autofill>false</Autofill>
          <ExtendedTooltip name="…" id="11"/>
      </AutoCommandBar>
      
      <!-- Законные формы -->
      <AutoCommandBar name="ТаблицаКоманднаяПанель" id="10"/>
      <AutoCommandBar name="ТаблицаКоманднаяПанель" id="10">
          <ChildItems>…кнопки и группы…</ChildItems>
      </AutoCommandBar>
      ```
      
      **Контр-сигнал.** У `CommandBar` — отдельного элемента формы — `Autofill` и вложенный
      `ExtendedTooltip` встречаются штатно. Признак действует только внутри `AutoCommandBar`
      **таблицы**.
      
      **Заявленная неточность.** В том же разборе снят заодно `<TitleLocation>Top</TitleLocation>`
      у `Table`, но отдельно он не изолирован. Как самостоятельная находка не выпускается — только
      как кандидат при продолжении бисекции.
      
      **Откуда правило времени.** На рабочей базе штатная загрузка обработки занимает порядка
      семи-восьми минут — ожидание там ничего не доказывает; на пустой базе загрузка длится
      секунды, поэтому порог «дольше трёх-пяти минут» действует именно на ней. Зациклившийся
      процесс снимают и бисектят форму по группам элементов, а не ждут.
      
      ## Тип поля схемы компоновки, выброшенный при загрузке расширения
      
      Ссылка на тип конфигурации (`CatalogRef.Пользователи` и подобные) в макете схемы компоновки
      данных расширения разрешается **в границах расширения**. Объект, не заимствованный в это
      расширение, платформа не находит и молча выбрасывает весь блок `<valueType>`. Ни ошибки, ни
      предупреждения при загрузке нет.
      
      ```xml
      <!-- Молча теряется, если Справочник.Пользователи не заимствован в расширение -->
      <field xsi:type="DataSetFieldField">
          <dataPath>Ответственный</dataPath>
          <field>Ответственный</field>
          <valueType>
              <v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Пользователи</v8:Type>
          </valueType>
      </field>
      
      <!-- Переживает загрузку: ПланСчетов.Хозрасчетный в составе расширения есть -->
      <field xsi:type="DataSetFieldField">
          <dataPath>Счет</dataPath>
          <field>Счет</field>
          <valueType>
              <v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:ChartOfAccountsRef.Хозрасчетный</v8:Type>
          </valueType>
      </field>
      ```
      
      ### Почему молчит каждое звено
      
      - **валидаторы структуры** дают «OK»: по схеме формата разметка безупречна, а состав
        расширения лежит в другом файле, куда валидатор макета не смотрит;
      - **синтаксический контроль конфигурации** молчит: код здесь ни при чём;
      - **загрузка и сборка** проходят с нулевым кодом возврата и пустым логом;
      - **файл на диске сохраняет тип** — сверка исходников с эталоном расхождений не покажет.
      
      Дефект виден только в том, что легло **в базу**. Отсюда практическое правило разбора: когда
      поведение платформы противоречит содержимому файла, сверяют не файл, а выгрузку из базы
      (`DESIGNER /DumpConfigToFiles <каталог> -Extension <имя>`) — это два разных состояния, и
      спорить надо с тем, которое исполняется.
      
      ### Во что это обходится
      
      Поле остаётся без типа, и типовой код быстрых настроек отчёта берёт первый тип поля отбора
      без проверки на пустоту:
      
      ```bsl
      Если Справочники.ТипВсеСсылки().СодержитТип(ОписаниеТипаЗначения.Типы()[0]) Тогда
      ```
      
      Форма отчёта не открывается вообще — «Индекс находится за границами массива». Падает не
      доработанный вариант, а **все** варианты отчёта, включая типовые, которых правка не касалась:
      поле отбора общее. Сбросить пользовательские настройки в интерфейсе нельзя — форма не
      открывается.
      
      ### Измерения
      
      На отчёте типовой конфигурации: 22 блока `<valueType>` в файле на диске, 21 в выгрузке из
      базы. Потерян ровно один — единственный, который ссылается на незаимствованный объект. Все
      примитивы (`xs:string`, `xs:dateTime`, `xs:decimal`) целы. После заимствования объекта —
      23 на диске, 23 в базе. Соседний отчёт того же расширения со ссылкой на заимствованный
      план счетов: 6 и 6 на обоих концах.
      
      ### Контр-сигналы
      
      - **Примитивные типы** (`xs:*`) и типы платформы состава расширения не касаются — проверка
        их не смотрит.
      - **Макет, побайтово равный вендорному**, тип сохраняет: расширение не хранит собственную
        копию, ссылка разрешается против основной конфигурации. Поэтому дефект появляется ровно
        в тот момент, когда макет впервые изменили, и выглядит как «сломалось от правки запроса».
      - **Ссылка на объект, заимствованный в это же расширение**, законна и обязана молчать.
      
      ### Что проверяет инструмент
      
      `cfe-validate.py`, пункт 14 (`qg:CFE-TYPE-REF-NOT-ADOPTED`) — сверяет ссылки на типы
      конфигурации в макетах схем компоновки расширения с составом `ChildObjects`. Проверка
      работает по ссылочным типам из таблицы соответствий (`CatalogRef`, `DocumentRef`, `EnumRef`,
      `ChartOfAccountsRef`, `ChartOfCharacteristicTypesRef`, `ChartOfCalculationTypesRef`,
      `ExchangePlanRef`, `BusinessProcessRef`, `TaskRef`); формы вне этого списка она пропускает
      молча, а не додумывает.
      
      Уровень — предупреждение, не блокировка: у макета, не отличающегося от вендорного, ссылка
      законна, а сравнить с основной конфигурацией валидатору нечем — путь к ней ему не передаётся.
      
      Тем же правилом ограничены **прочие XML расширения** (типы реквизитов собственных объектов,
      определяемые типы): это не измерялось и проверкой не покрыто.
      
    • role-rights-model.md 5.2 KB
      # Модель прав ролей — что выдаётся, а что не существует
      
      Справочник контура XML к пункту «Права на объекты в ролях расширения». Отвечает на один
      вопрос: **отсутствие прав — это упущение разработчика или устройство платформы.** Без такого
      разделения проверка прав генерирует находки на файлах, которые нельзя исправить: права
      запрошенного вида не существует, и «починка» сводится к поиску несуществующей настройки.
      
      Основа — сплошной разбор файлов прав типовой конфигурации (порядка 1100 ролей) и ролей
      расширений в реальном внедрении. Проверено на выгрузке формата `Rights` версии 2.20.
      
      ## Типы объектов без объектных прав
      
      | Тип | Что означает пустой набор прав |
      |---|---|
      | `Перечисление` (`Enum`) | прав не существует: доступность значений следует из прав на объекты, где перечисление используется |
      | `РегламентноеЗадание` (`ScheduledJob`) | настраиваемых прав доступа у объекта нет |
      
      При регистрации нового перечисления или регламентного задания права в роль **не
      добавляются**. Это не пропуск, а отсутствие такой сущности в модели прав.
      
      Обратное тоже верно: запись `<object><name>Enum.Имя</name>…` в файле прав — сама по себе
      дефект, конфигуратор её отвергнет. `role-validate.py` называет это прямо
      (`type 'Enum' has no object rights`), а не «неизвестным типом»: вторая формулировка читается
      как пробел валидатора и провоцирует искать способ выдать право, которого нет.
      
      ## Права на точки вызова сервисов
      
      У HTTP- и веб-сервисов право `Использование` (`Use`) выдаётся **не сервису целиком, а точке
      вызова** — методу шаблона URL либо операции:
      
      ```
      HTTPService.<ИмяСервиса>.URLTemplate.<ИмяШаблона>.Method.<ИмяМетода>
      WebService.<ИмяСервиса>.Operation.<ИмяОперации>
      ```
      
      Имена шаблона и метода берутся из XML самого сервиса, а не придумываются: в файле прав
      должен стоять тот же идентификатор, что объявлен в определении сервиса.
      
      Путь с двумя и более точками разбирается валидатором как вложенный объект, а вложенным
      объектам данных свойственны `View`/`Edit`. Для точек вызова сервисов это неверно — там `Use`.
      До версии, добавившей `ENDPOINT_RIGHTS`, валидатор выдавал предупреждение на единственно
      правильной форме записи.
      
      ## Отсутствующий файл прав
      
      Роль расширения может не иметь `Ext/Rights.xml` вообще — это валидное состояние пустой роли
      (ноль прав на любые объекты), а не потерянный файл. При первом добавлении прав файл создаётся
      с обязательной шапкой:
      
      ```xml
      <Rights xmlns="http://v8.1c.ru/8.2/roles" ... version="2.20">
        <setForNewObjects>false</setForNewObjects>
        <setForAttributesByDefault>true</setForAttributesByDefault>
        <independentRightsOfChildObjects>false</independentRightsOfChildObjects>
        <object>…</object>
      </Rights>
      ```
      
      Три флага верхнего уровня обязательны; валидатор проверяет их наличие отдельно от прав.
      
      ## Что остаётся настоящей находкой
      
      Сужение контр-сигналами не отменяет исходное правило контура: **собственный объект расширения
      без явно выданных прав невидим пользователю**, и ни сборка, ни валидаторы этого не покажут.
      Находкой остаётся отсутствие прав у справочника, документа, регистра, обработки, отчёта,
      команды — то есть у типов, где права существуют и требуются. Пустой набор у перечисления и
      регламентного задания находкой не является никогда.
      
  • SKILL.md 23.8 KB
    ---
    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`. Принадлежность считается у самого объекта, а не у
    владельца: команда или реквизит, добавленные расширением в заимствованный отчёт, — собственные,
    права владельца их не покрывают (у типовых команд того же отчёта права выданы отдельными
    записями в ролях конфигурации).
    
    **Находку снимает только прочитанный источник.** Довод «права наследуются», «объект не
    собственный», «доступ идёт от владельца» называет файл, на который опирается: роль, её
    `Rights.xml`, описание объекта. Без него находка остаётся с пониженной уверенностью и называет
    проверку, которая её решит. В A/B-прогоне ревью сняло находку о команде без прав одним таким
    доводом, а соседний прогон, прочитавший роли конфигурации, подтвердил её.
    
    ---
    
    ## 5. Дефекты, которые проходят валидацию
    
    Валидаторы разбирают XML по схеме формата и молчат о конструкциях, законных по схеме, но
    ломающих платформу. Такой дефект опаснее обычного: отчёт зелёный, а артефакт не собирается.
    
    **`AutoCommandBar` таблицы с `Autofill` и вложенным `ExtendedTooltip`.** Загрузка внешней
    обработки или отчёта уходит в бесконечный цикл со 100% CPU — не ошибка и не диалог, а
    зависание; `form-validate.py` при этом даёт «OK». **Контр-сигнал:** у `CommandBar` та же
    конструкция штатна — признак действует только внутри `AutoCommandBar` **таблицы**. Законные
    формы разметки и второй кандидат, снятый тем же разбором, но не изолированный, —
    `references/pipeline-blind-spots.md`.
    
    **Правило времени вместо ожидания.** Загрузка обработки на пустой базе — секунды. Прогон
    дольше трёх-пяти минут **на пустой базе** означает зацикливание: процесс снимают и бисектят
    форму по группам элементов, а не ждут. Почему порог действует только на пустой базе — в том
    же справочнике.
    
    **Тип поля схемы компоновки на объект вне состава расширения.** Ссылка вида
    `CatalogRef.Пользователи` на незаимствованный объект теряется при загрузке в базу целиком и
    молча: файл на диске тип содержит, а в базе поле остаётся без типа, и форма отчёта не
    открывается — падают все варианты, включая типовые. Ловится пунктом 14 `cfe-validate.py`
    (`qg:CFE-TYPE-REF-NOT-ADOPTED`). **Контр-сигнал:** макет, побайтово равный вендорному, тип
    сохраняет — расширение не хранит свою копию. Измерения и разбор — в
    `references/pipeline-blind-spots.md`.
    
    **Спорить надо с базой, а не с файлом.** Когда поведение платформы противоречит содержимому
    исходника, сверяют выгрузку из базы (`DESIGNER /DumpConfigToFiles <каталог> -Extension <имя>`),
    а не файл на диске: загрузка — не побайтовый перенос, часть конструкций она отбрасывает.
    
    ---
    
    ## 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:CFE-TYPE-REF-NOT-ADOPTED`: ссылка вида `CatalogRef.Имя` на незаимствованный объект — платформа молча выбрасывает `<valueType>` при загрузке, поле остаётся без типа, форма отчёта не открывается (предупреждение) | `cfe-validate.py`, пункт 14 |
    
    ---
    
    ## Записи следа
    
    ```
    [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`.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related