Commit Graph
22 Commits
Author SHA1 Message Date
Nick ShirokovandClaude Opus 5 4fa57c73b5 docs(skills): Language исключён из сортировки без замера — так и написать
В комментарии семьи is_order_sensitive_type и в гайде причина исключения Language была
подана как установленная: «порядок языков задаёт порядок <v8:item> в мультиязычных
строках по всей выгрузке». Это гипотеза, высказанная при обсуждении, и мы её не
проверяли. Основания у четырёх исключений разные, и теперь это видно: CommonAttribute —
исключение самого стандарта, Subsystem и CommandGroup — замер по корпусу (в ACC 15 живых
групп команд из 39 вне GroupsOrder), Language — осторожность без замера.

Правка задевает тело семьи, поэтому идёт по всем пяти навыкам в обоих портах.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
2026-08-30 14:37:15 +03:00
Nick ShirokovandClaude Opus 5 1bc943e846 feat(skills): новая группа вида встаёт в канонический порядок, а не в конец блока
Навыки-создатели дописывали первый объект нового вида перед </ChildObjects>. Собранная
ими конфигурация выходила в неканоническом порядке видов, и первая же загрузка-выгрузка
давала диф: на стенде подали Language -> Catalog -> Document -> CommandGroup ->
CommonCommand, платформа вернула Language -> CommonCommand -> CommandGroup -> Catalog ->
Document. При этом cf-edit add-childObject и cfe-borrow канонический порядок уже держали —
очередная «одна работа, разные реализации».

Логика перенесена из cf-edit в семью Register-InChildObjects и в вариант nested-parent:
запись встаёт перед первой группой вида старше по CHILD_OBJECT_TYPES. Для этого список
канонического порядка добавлен в meta-compile, role-compile, xdto-compile и
subsystem-compile (оба порта) и заведён в check-type-maps — копий стало 9 вместо 5, зато
все под гардом.

У подсистем нашлось больше, чем планировалось: вставка шла в конец ВСЕГО блока, поэтому
подсистема покидала и собственную группу, если ниже были другие виды. Одно правило
закрывает оба случая. Во вложенном Subsystem.xml порядок видов неприменим — потомок там
всегда один, поведение не изменилось.

Сдвиг эталонов широкий, но проверяемый: 42 файла в 12 навыках, 53 добавленные строки
против 53 удалённых с совпадающим мультимножеством — чистая перестановка, все изменённые
строки вида <Тег>Имя</Тег>. Из 425 эталонов с ChildObjects неканоничными осталось 15: 11
фикстур сортировки (неканоничны по замыслу) и 4 статические фикстуры на сохранение EOL и
разбор форм, которые мы намеренно не переупорядочиваем.

Версии подняты в обоих портах и за эту правку, и за предыдущую (карты типов).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
2026-08-29 21:53:52 +03:00
Nick ShirokovandClaude Opus 5 1046e6018c feat(cf-edit): виды с осмысленным порядком не сортируются автоматически
Правило «Subsystem не упорядочиваем» распространено на четыре вида и вынесено в
семью is_order_sensitive_type / Test-OrderSensitiveType: раньше литерал "Subsystem"
был размазан по четырём точкам — регистрация в Register-InChildObjects,
add-childObject, заимствование в расширение и сортировка.

CommonAttribute — исключение самого стандарта #std467: у общих реквизитов-разделителей
порядок в дереве задаёт порядок установки параметров сеанса
(https://github.com/1C-Company/v8-code-style/issues/78). Единственный из четырёх, где
сортировка ломает поведение, а не только диф.

CommandGroup и Language добавлены по замерам. В ACC 15 живых групп команд из 39 не
перечислены ни в одном GroupsOrder — как и подсистемы, они падают на порядок дерева.
Порядок языков задаёт порядок <v8:item> в мультиязычных строках по всей выгрузке.

Прежний вывод «сортировать их безопасно» опирался на стендовый прогон, где не было
ни подсистем (а значит и CommandInterface.xml), ни второго языка у объектов: ломаться
там было нечему, и отсутствие изменений ничего не доказывало.

PaletteColor сортируется — это переменные цветов.

Второе послабление стандарта (объекты «Удалить*» можно держать в конце ветки) не
реализуем: типовые им не пользуются — в ACC 360 таких объектов из 373 стоят по
алфавиту, а сортировка стандарту не противоречит. Оговорено в гайде.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
2026-08-29 20:21:55 +03:00
Nick ShirokovandClaude Opus 5 ecde0d1c41 fix(skills): единый порядок поиска .v8-project.json для newObjectPosition
Резолвер настройки искал файл от каталога конфигурации и лишь потом от cwd —
единственное место в репозитории с таким отсчётом. Остальные ищут от рабочего
каталога: support-guard (18 навыков) с фолбэком на каталог конфигурации, группа
db-* (5 навыков) вообще без фолбэка, потому что каталога конфигурации у них нет.

Обоснование прежнего порядка («cwd почти всегда чужой проект») опиралось на нашу
стендовую раскладку — вызов из репозитория навыков по выгрузке в cfsrc. У
пользователя cwd и есть каталог проекта с .v8-project.json, а скрипт навыка
зовётся по абсолютному пути и cwd не меняет, так что оба порядка эквивалентны.

Кейс cfe-borrow/newobjectposition-byname клал настройку внутрь каталога расширения
и тем закреплял несуществующую раскладку — конфиг проекта лежит в корне проекта,
расширение внутри него. Поправлен на реальную.

Из гайда убран абзац, объяснявший особый порядок: объяснять больше нечего.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
2026-08-29 19:14:27 +03:00
c7b0dce151 feat(skills): порядок объектов метаданных в ChildObjects
Навыки-создатели дописывали новый объект в конец группы своего вида, а стандарт
требует порядка по имени (АПК:1108). Замеры на 8 боевых выгрузках: 103 385 объектов
лежат по алфавиту, нарушают его ровно дописки в хвост. Стенд подтвердил, что
беспорядок вечен: платформа нормализует порядок ВИДОВ (возвращает канонический),
но порядок имён внутри вида не трогает.

Настройка newObjectPosition в .v8-project.json (end по умолчанию | byName) —
решение проекта, а не вызова: читают её meta-compile, role-compile, xdto-compile,
cfe-borrow и cf-edit add-childObject. Компаратор при этом константа, моделирующая
дерево Конфигуратора: ключ «ранг+символ» без культурных таблиц, одинаковый в обоих
портах на любой ОС. На корпусе он даёт 4 нарушения на 125 088 пар против 2866 у
ordinal-сравнения, которым cf-edit и cfe-borrow сортировали до сих пор.

Subsystem не упорядочивается автоматически нигде: пока подсистемы не перечислены
в <SubsystemsOrder>, порядок дерева задаёт порядок разделов в панели, а платформа
этот список сама не заводит.

Настройка не чинит накопленное, поэтому cf-edit получил операцию sort-childObjects
(вся конфигурация или названные виды). Она переставляет значения узлов, а не узлы,
поэтому диф — чистая перестановка строк. Имя вида принимается в любом регистре,
во множественном числе и по-русски; неизвестный вид — отказ со списком допустимых.

Попутно:
- PS-порты создателей переведены с DOM-сериализации на текстовую вставку: на
  выгрузке не в каноне Конфигуратора они переписывали заголовок без просьбы;
- PS-сторона семьи detect_xml_style/finalize_xml_bytes закрыта в cf-edit и
  cfe-borrow — правка чужого файла наследует его BOM/EOL/заголовок;
- xdto-compile присоединён к семье Register-InChildObjects вместо инлайн-копии;
- закрыт ложный успех в py subsystem-compile: <ChildObjects /> с пробелом ET
  разбирает, а текстовые ветки не находили — файл писался без вставки;
- cfe-borrow py не импортировал json, из-за чего резолвер молча возвращал end;
- cf-edit add-childObject ставил объект в конец блока, за пределы своей группы,
  когда видов старше в файле не было.

Проверки: 906/906 на PowerShell и 903/906 на Python, 10 гардов, верификация
эталонов платформой без падений, раундтрип отсортированной конфигурации на
8.3.24 и 8.3.27, сортировка боевой выгрузки ACC (1,4 МБ) и расширения из корпуса.

Задачу принёс PR #85; часть кода взята оттуда.

Co-Authored-By: Sergei Pleshanov <72200277+Abacadabras@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
2026-08-29 17:34:09 +03:00
Nick ShirokovandClaude Opus 5 0a0322faab fix(skills): пишущие навыки — запрет позиционного связывания параметров
Значение с пробелом без кавычек разрывалось на токены, и лишние молча растекались по свободным
позиционным параметрам. На документированной форме вызова (powershell -NoProfile -File):

  form-compile.ps1 -JsonPath a.json -OutputPath b.xml -Purpose Форма списка
  → Purpose=[Форма] ObjectPath=[списка], ошибки НЕТ

Навык отрабатывал успешно с усечённым значением; реальный триггер — путь с пробелом в -EmitDsl
или -ObjectPath, результат уезжал не туда молча. У навыков с парой -DefinitionFile/-Operation
осколок уезжал в -DefinitionFile, и навык жаловался на параметр, которого в команде не было.

Расхождение портов: py на том же вызове отвечает «unrecognized arguments: списка» — там все
аргументы объявлены опциями. Правка выравнивает PS по py, python не менялся.

41 навык получает [CmdletBinding(PositionalBinding=$false)] и ни одного позиционного параметра.
Без Position=0 (в отличие от read-only навыков): «главный» параметр механически не выводится —
у meta-edit первым объявлен -DefinitionFile, а путь к объекту вторым, — а ошибка выбора тестами
не ловится, раннер передаёт только именованные флаги. Позиционной формы вызова нет ни в одной из
140 строк SKILL.md, внутренние вызовы навыков друг из друга тоже именованные.

check-positional-binding.mjs расширен на пишущие навыки статической проверкой. Поведенческая
(канареечный файл) остаётся только у read-only: у web-stop и db-run все параметры
Mandatory=$false, связывание прошло бы и тело выполнилось — гард остановил бы Apache и запустил 1С.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 18:24:58 +03:00
Nick ShirokovandClaude Opus 5 d325a3d5af fix(skills): регистронезависимые параметры во всех py-портах
Дорожка CLI из кампании паритета: PowerShell не различает регистр ни в именах
параметров, ни в значениях [ValidateSet], argparse различает и в том и в
другом. Проверено на читающем навыке: cf-info -Mode BRIEF на PS отрабатывает,
на py падал.

ci_parse_args (эталон — meta-compile) вставлен в 68 py-портов, вызовы
parser.parse_args заменены. Навыков с DSL меньше трети, поэтому именно эта
правка делает паритет общим: *-info, *-validate, db-*, cfe-*, web-* тоже
принимают ввод так же, как их .ps1.

Реестр check-inline-drift дополнен списком потребителей — и сразу окупился:
поймал, что массовая замена переписала последнюю строку внутри самого
хелпера и копии стали рекурсивными.

Версии бампнуты синхронно в обоих портах всех 68 навыков.

Проверка: 673/673 (PS), 670+3 skipped (PY), гарды 4/4; паритет версий портов
проверен по заголовкам.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 20:00:31 +03:00
Nick ShirokovandClaude Opus 5 78ac3a8c36 fix(xdto-compile,xdto-edit): заменить урезанный support-guard на общую реализацию
Своя версия решала по правилу «файл Ext/ParentConfigurations.bin существует →
запретить». Общая разбирает заголовок bin и решает по конкретному объекту; в
частности у неё есть ветка `if k == 0: return` — если на поддержке нет ни одного
объекта, запрещать нечего.

Эксперимент на копии каталога конфигурации ERP (заголовок bin = {6,0,0,0,1,0},
K=0): meta-compile создавал объект, xdto-compile на том же каталоге отказывал.
То есть XDTO-пакет нельзя было добавить туда, где справочник добавляется
свободно. У acc_8.3.24 и ut_8.3.27 заголовок {6,1,1,...} (G=1, вся конфигурация
read-only) — там обе реализации отказывали одинаково.

Своя версия строго более ограничительна, поэтому это не дыра, а ложные отказы
плюс неинформативное сообщение. После правки ERP разрешает, acc по-прежнему
отказывает — и уже с диагнозом и шагами через support-edit.

Долг реестра inline-реализаций обнулился: 24 семьи, 310 копий, вариантов без
обоснования не осталось.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 14:23:37 +03:00
Nick ShirokovandClaude Opus 5 d99d63288e refactor(skills): detect_format_version — один вариант на всех потребителей
Семья была разбита на «знает про автономную EPF/ERF» (form-*, mxl-compile,
template-add, help-add) и «не знает» (cfe-borrow, interface-edit, meta-compile,
role-compile, subsystem-compile, xdto-compile), плюс редакторски разошедшаяся
PS1-копия help-add.

Расширенный вариант корректен и для конфигурационных навыков: ветка срабатывает
только если <каталог>.xml — корень ExternalDataProcessor/ExternalReport, чего в
дереве конфигурации не бывает. Переключатель не нужен — раскопирован как есть.

Семья в реестре схлопнута до одного варианта: 11 копий, оба порта.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 18:18:52 +03:00
Nick ShirokovandClaude Opus 5 53c1d59ec1 feat(cfe-borrow,cfe-init,form-add,form-compile,role-compile,subsystem-compile,subsystem-edit,template-add,xdto-compile): xmlns:pal в формате 2.21
Платформа 8.5 объявляет пространство палитры в шапках MetaDataObject и Form.
Радиус снят по выгрузке УНФ 8.5, а не угадан: pal есть у MetaDataObject, Form,
document (тело MXL), Style, AppearanceTemplate, GraphicalSchema; у Rights, Help,
CommandInterface, DataCompositionSchema и корней extrnprops его нет. Поэтому
role-compile правит шапку Role.xml и не трогает Rights.xml.

Вставка идёт на место — после lf, перед style: объявления платформа держит по
алфавиту, дописать в конец нельзя.

Попутно в cfe-borrow тег <Form> больше не копируется из исходной формы целиком:
из него берутся только объявления пространств, а version подставляется своя.
Раньше версия источника молча побеждала — вопреки комментарию рядом.

Тесты: снята нормализация xmlns в снэпшотах py-прогона (она прятала ровно этот
класс расхождений и к моменту снятия была мёртвой — ни один кейс на неё не
опирался); добавлены expect.fileContains/fileNotContains по сырым байтам и гейт
на неизвестные ключи expect.* — он вскрыл два кейса-пустышки (skd-compile,
skd-edit), они починены.

648/648 ps1, 645/648 py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 20:57:55 +03:00
Nick ShirokovandClaude Opus 5 d60dd47f0b refactor(cfe-init,form-add,form-compile,subsystem-compile,subsystem-edit,template-add,xdto-compile): шапка XML одной переменной
Объявления пространств имён вынесены в переменную рядом с определением версии
формата; места эмиссии её только интерполируют. Раньше шапка была вклеена в
литерал в каждой точке записи, и любая правка шапки ложилась в каждый навык
по-своему.

Попутно в form-compile убрана мёртвая эмиссия Title и шапки перед пересозданием
буфера (её результат затирался).

Вывод байт-в-байт: 648/648 ps1, 645/648 py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 20:33:42 +03:00
Nick ShirokovandClaude Opus 5 3ef0d74158 fix(19 навыков): единая финализация XML — стиль исходника и корректный CDATA (#57)
Три остатка волны #57, все в одной точке — блоке записи XML.

1. Порты расходились по EOL. PS гонял документ через XmlWriter с дефолтным
   NewLineHandling.Replace и принудительно переводил весь файл в CRLF, а py
   сохранял стиль исходника: role-compile на LF-ном Configuration.xml давал
   10311 байт против 10062. Ещё в пяти навыках None стоял, но финальной
   нормализации не было, и на LF-исходнике выходил смешанный EOL (meta-edit:
   40 CRLF + 136 одиночных LF) — та же форма, что у исходного дефекта #57.
   Приведено к форме cf-edit.ps1: None + канонизация к LF + целевой перевод
   строки (стиль файла-назначения, для создаваемого файла — канон CRLF).
   Replace не годится как замена: он превращает переводы строк внутри значений
   атрибутов в &#13;.

2. Гард `if (-notmatch CDATA)` не обрабатывал CDATA, а отказывался от канона
   во всём файле — то есть деградировал до «не сделал ничего». Заменён
   альтернацией: участки CDATA и комментариев возвращаются как есть, замена
   идёт только вне них. На реальных данных поведение не меняется — в корпусе
   из 476 942 XML нет ни одного CDATA и ни одного комментария.

3. py: три реализации одного правила детекта EOL сведены к одной. Мажоритарное
   правило в role-compile/subsystem-compile давало ДРУГОЙ ответ на смешанном
   входе. Дефолты _finalize_xml_bytes для нового файла приведены к канону
   (UTF-8, CRLF, без хвостового перевода); meta-edit срезает хвост в обеих
   ветках, а не только при создании файла.

Попутно, найдено байтовой сверкой портов:
- py вставлял <Role>/<Form>/<Subsystem> с пятью табами вместо трёх —
  подстановка по голому </ChildObjects> удваивала отступ строки; снэпшоты
  этого не видели, так как схлопывают пробелы между тегами;
- py-порты xdto-* писали XML-декларацию одинарными кавычками (так отдаёт
  lxml), платформа и PS пишут двойные.

Регресс: runner 647/647 ps1, 644/647 py (3 skipped), дрейфа снэпшотов нет.
Корпусный раундтрип метаданных: 4897 объектов, match 100%, совпадает с
эталонами захода #57.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 21:41:55 +03:00
Nick ShirokovandClaude Opus 5 750ba3bb13 refactor(эмиттеры): одна формулировка на повторяющийся комментарий
Код нормализации пустого тега повторён в 24 местах, а комментарий к нему за время
работы над #57 расплодился в шесть вариантов формулировки. Это то же расхождение
копий, что и в коде, только в тексте: читающий не понимает, значима разница или нет.

Сведено к одной паре формулировок — про сам тег с гардом и про запись через
MemoryStream. Комментарий теперь начинается с ПРАВИЛА, а не с причины: раньше
верхняя строка рассказывала, что here-string наследует LF от .ps1, и читалось это
как «переводы строк берём из скрипта навыка» — тогда как правило обратное:
создаём файл — пишем канон, правим существующий — наследуем его стиль.

Два пояснения в cfe-borrow унификация чуть не стёрла, они возвращены: источником
там служит OuterXml, а не XmlWriter, и нормализация стоит после вставки реквизитов,
чтобы накрыть и их. Это причина местного отличия, а не дубль.

Ссылки на ишью прорежены: #44/#46/#47 осталась по одной на файл — там, где правило
формулируется, — и убрана оттуда, где была эхом. #57 в скриптах не было вовсе.

Заодно выровнены две мелочи, найденные той же сверкой копий: interface-edit в
py-порте не обрезал хвостовой перевод там, где PS обрезает (поведение совпадало,
код — нет), а cf-edit нормализовал EOL инлайном вместо общей двухшаговой формы.

Версии подняты в ОБОИХ портах всех затронутых навыков, включая те, где текстуально
менялся только .ps1: номер версии в этом проекте читается как «одно и то же
поведение», и разводить его из-за комментария нельзя. Паритет проверен — 69 пар,
ноль расхождений.

Windows 641/641 обоими портами.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 20:06:38 +03:00
Nick ShirokovandClaude Opus 5 0b8231c467 fix(17 навыков): CRLF в разделителях строк XML (#57)
Головной дефект тикета: cfe-init выдавал Configuration.xml с 10 CR на 70 строк, а
роль и язык — вовсе без CR. Теперь 70/70, последний байт `>`.

Правило: меняем только РАЗДЕЛИТЕЛИ строк, содержимое текстовых узлов не трогаем.
Платформа его не трогает тоже — в запросах СКД из чистой выгрузки встречаются и
CRLF, и одиночный LF. Поэтому сплошная нормализация файла применяется только там,
где многострочных текстовых узлов нет по построению (скелеты), а в объектном XML
и формах разделители правятся в точке сборки.

Источников оказалось четыре, а не один:

1. Скелеты (cf-init, cfe-init, epf-init, erf-init, form-add, help-add,
   template-add) собираются here-string'ами, а .ps1/.py в репозитории хранятся с
   LF — отсюда LF и смешанный EOL.
2. py-порты склеивали документ через '\n'.join(lines); PS в тех же местах давал
   CRLF через AppendLine — порты расходились побайтово.
3. Билдеры Predefined в meta-compile собирались с явным LF и даже сворачивали
   CRLF→LF из общего эмиттера типов.
4. Чтение существующего файла в python БЕЗ newline='' молча схлопывает CRLF в LF
   (универсальные переводы строк), и запись потом кладёт LF. Так role-compile и
   subsystem-compile переписывали в LF весь Configuration.xml. meta-compile это
   уже делал правильно — там newline='' стоял с фикса #44/#46/#47.

Отдельно: XML-парсер по спецификации схлопывает CRLF при разборе, поэтому
lxml-порты (xdto-compile, xdto-edit) отдавали LF-документ там, где .NET возвращал
CRLF через NewLineHandling. Восстанавливаем EOL исходного файла.

Разделение канона и сохранения стиля:

- файл СОЗДАЁМ — канон (CRLF, без хвоста);
- существующий ПРАВИМ — наследуем его EOL, включая перевод строки вставки
  (контракт #44/#46/#47). Иначе LF-проект получал бы смешанные файлы — ровно то,
  на что заведён #57. Кейсы roundtrip-crlf-preserve остаются зелёными.

Хелпер записи скопирован в каждый навык (навыки автономны). У meta-compile он
называется Write-XmlFileKeepEol / write_xml_file_keep_eol: там нормализовать EOL
НЕЛЬЗЯ (многострочные запрос, синоним, значение заполнения), и одинаковое имя при
разном поведении было бы ловушкой.

Заодно: имя временного файла батча в meta-compile.ps1 получило GUID. Фиксированное
"meta-compile-batch-$idx.json" в общем %TEMP% сталкивало два параллельных запуска
(«file is being used by another process») — из-за этого полный набор приходилось
гонять с урезанной параллельностью. py-порт уже брал mkstemp.

Аудит: было ~250 файлов с дефектом EOL, стало 0 в обоих портах. Осталось четыре
законных случая — фикстуры roundtrip-crlf-preserve (сохранение стиля) и запрос
динсписка с многострочным текстом. Дрейф снэпшотов — 1 файл. Тесты 641/641.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:37:26 +03:00
Nick ShirokovandClaude Opus 5 39cf8e49cf fix(17 навыков): самозакрывающийся тег без пробела и UTF-8 в декларации (#57)
.NET XmlWriter пишет `<a />`, а Конфигуратор — `<a/>`. Поскольку Save переписывает
файл целиком, meta-edit при добавлении одного реквизита переводил в пробельную форму
все пустые теги документа. Дефект только у PS-порта: lxml (45 из 49 py-портов) уже
пишет плотно, то есть порты расходились побайтово.

Канон измерен, а не предположен: чистая выгрузка пустой ИБ на Windows и macOS плюс
сплошной скан 8 выгрузок в cfsrc — 476 943 XML, 21 294 119 самозакрывающихся тегов,
пробельных 0. Форма не зависит от ОС, версии платформы (8.3.20-8.5) и наличия
атрибутов. Правило уже было реализовано в skd-edit — оттуда и взято.

Замена безопасна доказуемо: .NET экранирует `>` как `&gt;` и в тексте, и в атрибутах,
поэтому ` />` после Save — только конец тега. Лазейки (CDATA, комментарии) в
1С-метаданных не встречаются — 0 из 476 943 файлов; гард на них всё равно стоит.

Одиннадцать скриптов писали прямо в FileStream без пост-обработки — переведены на
MemoryStream, что заодно чинит `encoding="utf-8"` строчными (335 файлов в снэпшотах).
В cfe-borrow правится и сборка Form.xml: куски берутся из OuterXml, а он спацовывает
так же, как XmlWriter.

Дрейф снэпшотов: 12 838 строк тегов + 335 деклараций, содержательных изменений ноль.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 13:28:58 +03:00
Nick ShirokovandClaude Opus 5 f552def817 fix(xdto-compile): версия формата из Configuration.xml вместо хардкода 2.17
Раундтрип по 1173 пакетам XDTO трёх конфигураций дал 760 совпадений и 413
расхождений — все ровно по две строки и все на УТ: штамп версии. Оригинал
2.20, наш вывод 2.17. Содержательных расхождений нет ни одного.

xdto-compile единственный из навыков, пишущих XML внутрь конфигурации, не
определял версию формата. Причина не в XDTO: волна авто-детекта прошла
2026-04-06 (d1550864) и накрыла существовавшие тогда навыки, а xdto-compile
появился 2026-07-25 — соглашение к нему просто не применили.

Добавлен Detect-FormatVersion / detect_format_version — дословная копия из
остальных навыков (включая сегодняшнюю правку про символы вместо байтов),
разрешение пути на вызывающей стороне.

Аудит остальных навыков: литеральный хардкод остался только в epf-init и
erf-init (автономные обработки, конфигурации рядом нет — дефолт законен) и в
тестовой заглушке stub-db-create.ps1.

Проверка: XDTO 1173/1173 без расхождений (было 760), сюита xdto 43/43 на
обоих портах.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 16:21:22 +03:00
Nick ShirokovandClaude Opus 5 79beba2d1f fix(xdto-compile,xdto-validate): признак фиксированного значения без самого значения
В модели XDTO fixed — булев признак, а значение лежит в default; в XML-схеме
fixed="V" совмещает и признак, и значение. Компилятор оба идиома принимал, но
не проверял принятое: зеркало xdto:fixed="true" без default собиралось молча
в пакет, который платформа отвергает («Отсутствует фиксированное значение
свойства»). Прощающий ввод был сделан наполовину.

xdto-validate v1.1 — два ERROR: значение попало в признак (fixed не булев)
и признак без значения. Формулировка второго повторяет платформенную дословно,
чтобы отказ загрузки и наш вывод читались как одно и то же.

xdto-compile v1.1 — то же условие предупреждением на сборке, то есть на шаг
раньше db-update, где починить дешевле.

Кейсы: оба идиома плюс только default и атрибут (загружается в базу);
зеркало без значения — проверка диагностики, из платформенной верификации
исключено штатным skipPlatformVerify, пакет невалиден by design.

Правило откалибровано корпусом (760 пакетов, оба рантайма): 0 ложных
срабатываний, состав предупреждений не изменился. Round-trip остался
760/760 байт-в-байт.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 16:25:38 +03:00
Nick ShirokovandClaude Opus 5 5d5a1bc36a fix(xdto): три дефекта, найденных платформенной верификацией снэпшотов
verify-snapshots загружает результат каждого кейса в 1С. Раньше навыки xdto
через него не проходили вовсе; первый прогон дал 5 из 9. Ни корпусная сверка,
ни валидатор такого не ловили: корпус состоит из заведомо валидных пакетов,
а синтетические кейсы до сих пор в базу не грузились.

1. fixed. В модели XDTO это булев флаг, значение лежит в default; в XSD наоборот —
   fixed="V" несёт значение. Компилятор писал значение прямо в fixed, и платформа
   отвергала пакет («Отсутствует фиксированное значение свойства»). Перевод сделан
   в обе стороны; по принципу прощающего ввода принимается и модельная форма через
   зеркало xdto:fixed. Отображение выведено по корпусу: fixed встречается только
   вместе с default, значений всего два.

2. Импорт на несуществующий пакет платформа отвергает («xdto-package-3.3 …
   не определен»), а у нас проверки не было. Добавлена ошибка валидатора и,
   что важнее, предупреждение прямо на сборке — отказ при db-update дешевле
   поймать на шаг раньше. Правило пришлось калибровать корпусом: сначала оно
   дало 67 ложных срабатываний на платформенных пространствах имён, их список
   выведен и исключён.

3. localName проверяется как NCName — фикстура с пробелом в имени была негодной,
   заменена на реалистичный дефис (name="alpha_3" localName="alpha-3").

Харнесс получил skipPlatformVerify с обязательной причиной: результат
set-namespace невалиден by design, операция намеренно оставляет висящий импорт
у зависящего пакета.

Итог: 9/9 компилятора, 9/10 + 1 осознанный пропуск у edit, round-trip 760/760,
валидатор 0 ложных, 40 тестов на обоих рантаймах.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:51:08 +03:00
Nick ShirokovandClaude Opus 5 6486f433de fix(xdto-compile,xdto-info,xdto-edit): правки по итогам прогона на субагентах
Четырём субагентам выданы реалистичные задачи по песочнице, навыки в
формулировках не назывались. Разбор — в debug/xdto/FINDINGS.md, §13.

ГЛАВНОЕ — дефект компилятора. При уплощении вложенного xs:choice ветки
оставались обязательными: схема «самовывоз ИЛИ адрес доставки» давала пакет,
требующий заполнить оба, и ни один реальный документ в него не ложился.
Компилятор предупреждал о потере выбора, но молчал о последствии, а валидатор
показывал «0 ошибок, 0 предупреждений» — структурно пакет корректен,
семантически мёртв. Теперь ветки становятся необязательными (единственное
уплощение, оставляющее тип заполнимым), предупреждение называет их поимённо.
Корневой xs:choice не затронут: он отображается в ordered="false".

xdto-edit: -Value "@файл" по конвенции skd-edit — на кавычках при инлайновой
передаче XSD споткнулись двое агентов из четырёх, причём сырой LoadXml уводил
чинить схему вместо транспорта. При сбое разбора теперь понятное сообщение.

xdto-info: поиск, законно ничего не нашедший, падал throw'ом со стектрейсом и
читался как поломка инструмента — теперь строка и exit 1. Блок «Создание»
покрывал только корневой тип, хотя вся реальная работа в XDTO — вложенные и
анонимные типы; добавлены рецепты по факту наличия. Отсутствие раздела «Точки
входа» было неоднозначным — теперь явная строка. Новый -RequiredOnly даёт
скелет «заполни обязательное»: необязательный объект уходит вместе с поддеревом.
По умолчанию выключен — иначе список читался бы как полный.

xdto-validate: предупреждение про anyType описывало историю («платформа заменяет
при импорте»), хотя в файле уже зафиксирован anyType; сначала состояние, потом
происхождение.

Проверено: 40 тестов на обоих рантаймах, round-trip 760/760, валидатор
0 ложных срабатываний на корпусе.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 13:44:46 +03:00
Nick ShirokovandClaude Opus 5 a4a55cf883 feat(xdto-info): структура пакета и типа в терминах 1С
Навык отвечает на вопрос «что присвоить и что обязательно», а не показывает
модель как есть. Типы переведены в нотацию 1С с учётом ограничений
(xs:decimal + totalDigits → Число(18,2)), псевдонимы развёрнуты со стрелкой
на исходное имя, кратность вынесена во флаги, для перечислимых типов выводятся
допустимые литералы. Различие атрибут/элемент в таблице свойств не показывается:
в коде 1С обращение одинаковое.

Флаг ставится на обязательные, хотя в модели XDTO умолчание обратное. Причина —
соседний meta-info, где непомеченный реквизит необязательный: один значок,
означающий в двух навыках противоположное, сам по себе источник ошибок.

Режимы: список пакетов конфигурации, состав пакета с точками входа, структура
типа с разузлованием на -Depth и used-by. Разузлование идёт через границы
пакетов с пометкой источника, анонимные типы раскрываются всегда, циклы
обрываются. Пакет адресуется путём, именем или namespace — последнее потому,
что модель приходит к задаче от строки ФабрикаXDTO.Тип(ns, имя), а не от имени
пакета в конфигурации.

Попутно закрыт баг паритета во всех четырёх py-портах: платформа допускает
в targetNamespace произвольную строку (в БП есть пакет с кириллическим
«ДопФайлУниверсальный»), .NET такое принимает, а libxml2 отвергает как
невалидный URI. Добавлено узкое отступление на восстанавливающий разбор —
только для этой ошибки, чтобы валидатор не перестал замечать битый XML.
Обнаружено это только потому, что корпус впервые прогнан на Python: раньше
все 760 гонялись лишь на PowerShell. Теперь 760/760 на обоих рантаймах.

Сортировка в PS переведена на ординальную: Sort-Object сортирует по культуре,
sorted() в Python — по кодам, и на смешанных латиница/кириллица имена
расходились бы.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 12:05:08 +03:00
Nick ShirokovandClaude Opus 5 7ca6dfa6b2 fix(xdto-compile,xdto-validate): не терять конструкции молча; вычитать инструкции
xdto-compile терял свойства без единого слова: на реалистичной чужой схеме
из шести объявленных доезжало одно. Вложенные xs:sequence/xs:choice теперь
уплощаются (модель хранит плоский список), xs:all трактуется как
последовательность, xs:group и xs:attributeGroup раскрываются по ссылке —
и о каждом приближении навык пишет предупреждение. Молчаливая потеря — тот же
класс дефекта, что мы ловим у платформы, лечится так же: сообщением, не отказом.

xdto-validate получил проверки на грабли, найденные при разработке: порядок
элементов верхнего уровня (платформа отвергает пакет, не называя причины),
конфликты объявлений (name+ref, type+вложенный тип, тип без разновидности),
несовпадение рода базового типа, дубли имён свойств.

Новые правила прогнаны по всем 760 пакетам выгрузок: всё, что породила
платформа, валидно по определению, поэтому каждая ошибка там — ошибка правила.
Первый прогон дал 7, и все три класса оказались реальным поведением платформы:
length вместе с minLength/maxLength встречается, два пакета делят один
targetNamespace (Envelope и SOAP_Envelope_1_1 в БП), form="Text" называется
не только __content. Правила понижены до предупреждений либо сняты. Заодно
убран шум: предупреждение о неиспользуемом import срабатывало на четверти
корпуса — теперь только вместе с anyType, где оно и означает проблему.
Итог: 0 ошибок на корпусе, предупреждений 53 вместо 242.

Инструкции переписаны под читателя-исполнителя: убраны детали реализации
и наши мерки, каталог проверок валидатора (его вывод самодостаточен),
локальные пути в примерах заменены нейтральными. Таблица соответствий
XSD и справочник аннотаций вынесены в xdto-compile/xsd-reference.md.

Round-trip 760/760 сохранён, паритет PS/PY сохранён.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 21:48:48 +03:00
Nick ShirokovandClaude Opus 5 d05aef54b4 feat(xdto-compile,xdto-decompile,xdto-validate): пакеты XDTO из XML-схемы
Три навыка для работы с пакетами XDTO. Формат описания — обычная XSD,
своего DSL нет: рутину снимает конвертер (локальные объявления префиксов
dNpM на каждой ссылке, инвертированная кратность lowerBound/upperBound,
фасеты атрибутами вместо дочерних элементов, обязательный порядок
элементов верхнего уровня). То, чего XSD выразить не может — nillable
у атрибута, qualified у свойства, «атрибут записан явно» — едет
атрибутами из пространства имён модели XDTO по правилу «то же имя,
что в Package.bin». Свойства объекта метаданных живут в xs:appinfo,
поэтому пара decompile → compile замыкается без потерь.

Инвариант bin → xsd → bin проверен побайтово на 760 пакетах выгрузок
Бухгалтерии и ERP 8.3.24 (харнесс debug/xdto/roundtrip-corpus.mjs).
Сборка из рукописной XSD проверена загрузкой в базу 8.3.24 — именно
она вскрыла обязательный порядок import→property→valueType→objectType,
невидимый для корпусной сверки: все выгрузки уже канонические.

xdto-validate ловит два класса тихих дефектов, которые платформа не
диагностирует: подмену неразрешённого чужого типа на xs:anyType при
импорте XML-схемы и nillable у свойства-атрибута, теряемый экспортом
схемы в Конфигураторе.

Тесты: 18 снэпшот-кейсов на синтетических схемах (типовые конфигурации
в репозиторий не тащим), паритет PS↔PY на общих эталонах. Раннер
получил caseFiles — копирование файлов кейса в workDir для навыков
с файловым, а не JSON входом.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 21:21:57 +03:00