xml-structure-review
Контур проверки XML-структур метаданных 1С: корректность файлов объектов, форм, схем компоновки, ролей и макетов; регистрация объекта в составе конфигурации; сверка «диск ↔ состав» в обе стороны; права на объекты в ролях расширения. Вызывается оркестратором quality-gate при измен
Install
npx skills add https://github.com/Romandredan/1c-quality-gate/tree/main/skills/xml-structure-review
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install romandredan-1c-quality-gate@llmmart
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.
Reviews (0)
No reviews yet.
No comments yet.