Фикстуры roundtrip-кейсов объявляли формат 2.17 (это 8.3.24) и при этом режим
совместимости Version8_3_27. Из-за этого verify-snapshots на автодетекте платформы
(8.3.24) не мог загрузить их вообще: «Для работы с конфигурацией необходима версия
платформы не меньше, чем 8.3.27». Падение выглядело регрессией навыка, а было
свойством фикстуры, и платформенная верификация девяти кейсов молча не работала.
Режим приведён к формату (Version8_3_24) во всех девяти фикстурах — они дублируются
по навыкам и обязаны оставаться копиями. Эталоны пересняты точечно: дифф —
ровно две строки на файл.
verify-snapshots без --v8path: role-compile 13/0, cf-edit 14/0, meta-edit 20/0,
meta-compile 79/0, subsystem-edit 9/0, xdto-compile 12/0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Раннер сам прятал дефекты, которые чинит #57. normalizeXmlContent при
runtime=python снимал ровно четыре измерения: пробел перед `/>`, whitespace между
тегами, пустую пару <Tag></Tag> и хвостовой пробельный мусор. Паритет PS<->PY по
этим измерениям проверялся ЧЕРЕЗ маску — расхождение физически не могло упасть.
Снято три из четырёх (пробел, пустая пара, хвост). Схлопывание whitespace между
тегами оставлено: порты кое-где расставляют отступы иначе, это форматирование, а
не байтовый канон.
Ужесточён checkPreserves: eol теперь считает ОДИНОЧНЫЕ LF, а не «есть ли хоть один
CR». Прежняя проверка пропускала смешанный выход — cfe-init давал 10 CR на 70
строк и проходил её, то есть головной дефект тикета был ей невидим. Добавлены
ключи selfClose:"tight" и noEmptyPairs; preserves проставлен в 13 кейсах
навыков-эмиттеров (по одному на навык, на его СОБСТВЕННЫЙ артефакт).
Снятие масок сразу вскрыло четыре реальных расхождения портов:
- form-edit собирает выход из OuterXml и писал `<a />`; в списке 17 навыков его не
было, потому что искал по вызовам Save — здесь другой путь. Тот же случай, что
с Form.xml в cfe-borrow;
- meta-edit py дописывал хвостовой перевод в создаваемый Ext/Predefined.xml и
читал существующий без newline='' (терял CRLF);
- skd-info py писал отчёт -OutFile без хвостового перевода, PS через WriteAllLines
— с ним. Это текстовый отчёт, канон Конфигуратора к нему не относится, поэтому
выровнял py по существующему эталону;
- фикстуры кейсов содержали <Vendor></Vendor> — снимок нашего же старого вывода.
Конфигуратор пустых пар не пишет, .NET их сохраняет, lxml схлопывает. Поправлены
10 фикстур: пары → самозакрывающиеся, EOL и BOM не тронуты.
Результат: PS 641/641, python 638/641 (+3 runtimeOnly-скипа) — со снятыми масками.
Дрейф снэпшотов: 10 файлов, только пробельные теги и пустые пары.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
.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 экранирует `>` как `>` и в тексте, и в атрибутах,
поэтому ` />` после 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>
Кейсы проверяли только БАЙТОВЫЙ стиль файла (expect.preserves: BOM/CRLF/
encoding/finalNewline/noCR13) и что валидатор не ругнулся. Что в CRLF-файл
записан КОРРЕКТНЫЙ XML, не проверял никто: снэпшота не было, а
compareSnapshot при отсутствии эталона молча возвращает pass.
Снэпшот ортогонален preserves — сравнивается нормализованное содержимое
(структура), preserves остаётся на байтовых характеристиках. Дублирования нет.
Регресс 12/12, 20/20, 6/6 — ps1 и py. 1С-сертификация 3/3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>