В перечислении вида макета не хватало географической схемы: значение GeographicalSchema есть в
перечислении TemplateType платформы (backend.dll, рядом с GraphicalSchema и AddIn), но в разобранных
выгрузках не встречается — поэтому список, собранный по спецификации, его пропустил. Дескриптор
такого макета на входе давал ложную ошибку. Список дополнен и в спецификациях.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1c-configuration-spec §7.1 — правило, по которому платформа принимает или отвергает выгрузку с
разными версиями формата (измерено на 8.3.27.1859: конфигурация, расширение, внешняя обработка),
и какие проверки валидаторов на нём стоят. 1c-config-objects-spec §6.3/6.4: Ordinary был подписан
управляемой формой (наоборот: Managed — управляемая), список TemplateType приведён к девяти
значениям, путь к дескриптору формы исправлен (Forms/<Имя>.xml).
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
У заимствованной формы расширения в теле второй штамп версии — <BaseForm version=…>; платформа
сверяет с дескриптором формы и его (проверено на 8.3.27.1859). Расхождение только в нём — ошибка
на объекте-владельце, как и у cfe-validate и form-validate.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
У заимствованной формы расширения в теле второй штамп версии — <BaseForm version=…>, и платформа
сверяет с дескриптором формы его тоже: расхождение только в нём — отказ загрузки (проверено на
8.3.27.1859). Штамп корня совпадает, <BaseForm> нет — теперь ошибка.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Объекты расширения платформа загружает по тому же правилу, что и объекты конфигурации: дескриптор
и его штампованные тела (у заимствованной формы — ещё и вложенный <BaseForm version=…>) только в
одной версии формата, иначе «Версия формата загружаемого файла … отличается от версии формата ранее
загруженных файлов». Проверено загрузкой расширения на 8.3.27.1859: тело формы или <BaseForm> не в
версии дескриптора — отказ; заимствованный объект целиком в 2.18 в расширении 2.20 загружается.
Первое — ошибка, второе — предупреждение о неоднородной выгрузке. Так же сверяются Configuration.xml
расширения и его Ext/*.xml.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Корень обработки и его штампованные тела (Ext/Help.xml), дескриптор формы или макета и его тело
платформа загружает только в одной версии формата — иначе сборка отменяется с «Версия формата
загружаемого файла … отличается от версии формата ранее загруженных файлов». Проверено сборкой
на 8.3.27.1859: тело формы или справка не в версии своего дескриптора — отказ; форма целиком в
2.18 внутри обработки 2.20 собирается. Первое — ошибка, второе — предупреждение о неоднородных
исходниках. Работает и для внешних отчётов (erf-validate).
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Справка — тело объекта, а тело и дескриптор платформа загружает только в одной версии формата.
Версия выгрузки тут не годится: после мержа веток объект бывает старше выгрузки (2.18 в 2.20), и
справка по версии выгрузки вышла бы 2.20 — платформа отвергла бы объект. Версия теперь берётся из
дескриптора <объект>.xml, как это уже делает template-add, и только без него — подъёмом от каталога
объекта.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Версия формата искалась подъёмом от SrcDir, а корень внешней обработки <Имя>.xml лежит в самом
SrcDir — подъём его не видел и отдавал дефолт 2.17. Справка к обработке 2.20 выходила со штампом
2.17, и платформа отказывала в сборке: тело объекта и его дескриптор обязаны быть в одной версии
формата («Версия формата загружаемого файла … отличается от версии формата ранее загруженных
файлов»). Версия теперь ищется от каталога объекта. Кейсы на обработке шли на дефолтной 2.17, где
дефолт совпадал с правдой, — добавлен кейс на 2.20; сборка подтверждена на 8.3.27.1859.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Дескриптор подсистемы и её штампованные тела (CommandInterface.xml, Help.xml) платформа загружает
только в одной версии формата («Версия формата загружаемого файла … отличается от версии формата
ранее загруженных файлов»), проверено на 8.3.27.1859. Расхождение — ошибка. Вложенные подсистемы —
отдельные объекты и сюда не входят. Подсистема целиком в другой версии, чем выгрузка, грузится —
это лишь неоднородность выгрузки, поэтому такое расхождение только предупреждение.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Командный интерфейс и его владелец — дескриптор подсистемы или Configuration.xml (для
Ext/CommandInterface.xml и Ext/MainSectionCommandInterface.xml конфигурации) — платформа загружает
только в одной версии формата («Версия формата загружаемого файла … отличается от версии формата
ранее загруженных файлов»), проверено на 8.3.27.1859. Расхождение с владельцем — ошибка. Подсистема
целиком в другой версии, чем выгрузка, грузится — это лишь неоднородность выгрузки, поэтому
такое расхождение только предупреждение.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Права и дескриптор роли платформа загружает только в одной версии формата («Версия формата
загружаемого файла … отличается от версии формата ранее загруженных файлов»), проверено на
8.3.27.1859 в обе стороны. Rights.xml не в версии Roles/<Имя>.xml — ошибка. Роль целиком в другой
версии, чем выгрузка, платформа грузит — это лишь неоднородность выгрузки (типично после мержа
веток, выгруженных разными платформами), поэтому такое расхождение только предупреждение.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Конфигурация для платформы — такой же объект, как остальные: Configuration.xml и его штампованные
тела Ext/*.xml (CommandInterface, MainSectionCommandInterface, HomePageWorkArea, Splash,
MainSectionPicture, Logo) загружаются только в одной версии формата — иначе «Версия формата
загружаемого файла … отличается от версии формата ранее загруженных файлов». Проверено на
8.3.27.1859: расхождение любого из этих файлов с Configuration.xml — отказ; Configuration.xml вместе
со всеми своими телами в другой версии, чем объекты, — грузится. Сквозного обхода выгрузки нет:
на ERP это 74 тыс. файлов, а платформа при загрузке и так называет файл.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Тело формы сравнивалось с Configuration.xml, а платформа сверяет его с дескриптором формы
(Forms/<Имя>.xml, у общей формы CommonForms/<Имя>.xml): только эту пару она отвергает при
расхождении («Версия формата загружаемого файла … отличается от версии формата ранее загруженных
файлов»). Отсюда были ошибки в обе стороны: согласованная пара 2.18 в выгрузке 2.20 (грузится)
получала ERROR, а пара 2.20/2.18 при Configuration.xml 2.18 (не грузится) проходила. Кроме того,
при якоре без версии генераторный detect_format_version отдавал дефолт 2.17 — ложное расхождение.
Теперь: форма ≠ своему дескриптору — ошибка; иначе форма ≠ выгрузке — предупреждение (платформа
грузит, но выгрузка неоднородна). Контекст «конфигурация / EPF» берётся тем же поиском якоря
(find_dump_anchor). Новая общая функция ext_body_owner — семья в check-inline-drift.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Платформа загружает части одного объекта — дескриптор X.xml и штампованные тела X/Ext/*.xml — только
в одной версии формата («Версия формата загружаемого файла … отличается от версии формата ранее
загруженных файлов»). Формы и макеты объекта — такие же пары. Проверка 24 обходит части объекта,
который передан на вход: тело не в версии своего дескриптора — ошибка. Объекты разных версий между
собой платформа грузит, это лишь неоднородность выгрузки (типично после мержа веток, выгруженных
разными платформами), — поэтому расхождение дескрипторов с якорем выгрузки (Configuration.xml или
корень EPF/ERF) только предупреждение. Без якоря сверки с выгрузкой нет.
Дескрипторы Forms/<Имя>.xml и Templates/<Имя>.xml больше не «Unrecognized metadata type»: проверяются
имя и вид (FormType, TemplateType), чтобы пакетный прогон по списку изменённых файлов не падал на них.
Поведение платформы измерено на 8.3.27.1859: Form, Rights, Help, Predefined, ExchangePlanContent,
JobSchedule, Style, ExtPicture, CommandInterface, тела макетов GraphicalSchema и HTML — отказ при
расхождении с дескриптором в обе стороны, Full и Partial; объект целиком в 2.18 в выгрузке 2.20
грузится. Новые общие функции root_version и find_dump_anchor — семьи в check-inline-drift.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Навык вырезал у элементов заимствованной формы все StdPicture.*, кроме Print.
Сверка с Конфигуратором 8.3.27 (заимствование форм УТ с основным реквизитом
и без) правило опровергла: у кнопок, подменю и страниц — в командной панели
формы, контекстных меню и теле формы — стандартная картинка остаётся в обеих
частях формы байт в байт, 26 элементов из 26. Выбрасывается только картинка
декорации, это уже учтено. Вырезание убрано в обоих портах, v1.39.
На шести эталонах Конфигуратора картинки элементов и заимствованные общие
картинки теперь совпадают полностью. Спецификация (§5.5.2) приведена
к фактическому поведению, включая файлы встроенных картинок.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Значение xr:Abs и имя элемента с разделителем пути или «..» пропускаются
с предупреждением: иначе чтение и запись уходили за пределы Items, а порты
расходились на имени со «/». Источник-каталог больше не принимается за файл
картинки в PS-порте (как и в py).
Кейсы: предупреждение о картинке, которой нет в источнике; повторное
заимствование проверяет и вывод «Preserved existing».
Co-Authored-By: AndromanPro <AndromanPro@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Form.xml заимствованной формы ссылается на встроенные картинки элементов
(<xr:Abs>Picture.png</xr:Abs>), а сами файлы Ext/Form/Items/<Элемент>/<Файл>
в расширение не попадали — платформа отвергала загрузку: «Файл не найден».
Поведение сверено с Конфигуратором (8.3.27, формы УТ):
- Picture кнопок, RowsPicture/HeaderPicture/ValuesPicture таблиц и полей
картинки остаются в обеих частях формы, файлы копируются байт в байт;
- у PictureDecoration картинка любого вида выбрасывается из обеих частей,
без файла и без заимствования общей картинки.
Навык копирует ровно те файлы, на которые ссылается итоговый скелет формы
(ссылка xr:Abs -> Items/<имя владельца>/<файл>, точно на 666 ссылках корпуса).
Уже лежащий в расширении файл не перезаписывается. Оба порта, v1.38.
Раннер тестов пишет и сверяет двоичные файлы эталона побайтно: текстовый путь
через UTF-8 портил картинку, и сверка испорченного с испорченным проходила.
Проблема и первое решение — PR #102.
Co-Authored-By: AndromanPro <AndromanPro@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
add/set на файле без элемента уже отказывали («файл не из выгрузки платформы?»),
а remove печатал WARN и выходил с кодом 0. У заимствованного объекта отсутствие
элемента по-прежнему значит «удалять нечего» — предупреждение.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ps1 сопоставлял ключ свойства без учёта регистра, но при создании элемента писал
тег как подан: registerRecords у заимствованного документа давал <registerRecords>,
и meta-validate его пропускал. py сравнивал точно и на том же входе отказывал.
Ключ свойства объекта и дочернего элемента теперь сводится к каноническому имени
(ключи веток modify — name/type/synonym… — впереди списка свойств), дальше в обоих
портах только оно. Попутно: py при переименовании сравнивал синоним с разбивкой
старого имени с учётом регистра и не обновлял авто-синоним «ИНН»; сравнение теперь
без учёта регистра, как в ps1.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
modify.properties создавал отсутствующий элемент до Set-ComplexProperty, и решение
Get-ListPropertyElement обходилось: пустой список дописывал <RegisterRecords/>
заимствованному объекту, а у обычного объекта без элемента не было отказа.
Свойство-список теперь уходит в Set-ComplexProperty до create-if-missing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WARN с кодом 0 означал «не смог»: в пакетном прогоне это выглядело как успех.
Теперь предупреждение остаётся только там, где нужное уже есть (элемент уже
добавлен, удалять нечего, позиция after/before не найдена — добавлено в конец).
Остальное — ошибка в stderr и код 1: неизвестный ключ или операция, неверный
формат, недопустимый у типа ребёнок, изменение несуществующего элемента, формы и
макеты (их делают form-add/template-add).
Отказ атомарен: побочные файлы (таблица внешнего источника, модуль команды,
предопределённые) пишутся после основного XML, так что отказ посреди определения
не оставляет на диске ни одного изменения.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
У заимствованного объекта расширения в Properties только изменённые свойства.
add-registerRecord и прочие add-*/set-* над списками не находили элемент, печатали
WARN, файл не меняли и выходили с кодом 0.
- Движения заимствованного документа: отсутствующий RegisterRecords создаётся.
- Прочие списки у заимствованного объекта — отказ. Проверено на 8.3.27: Owners и
RegisteredDocuments платформа контролирует на равенство основной конфигурации
(расширение не применяется), BasedOn/InputByString/DataLockFields и движения
последовательности молча выбрасывает при загрузке.
- Свойство, которого у типа объекта нет (движения у справочника), — отказ; раньше
modify.properties дописывал его, и meta-validate это пропускал.
- У обычного объекта отсутствие элемента — отказ вместо WARN.
- reference/properties.md: типы объектов в таблице — по выгрузкам ERP, БП, УТ, УНФ.
- verify-snapshots: двухэтапная загрузка и по params.extensionPath кейса.
Co-Authored-By: Roman Syuzyov <rsyuzyov@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Платформа выбрасывает Command из внешней обработки/отчёта при сборке
(«не является подчиненным»), а Ext/ManagerModule.bsl — вовсе без
сообщения; epf-build при этом сообщает об успехе. Цепочка молча теряла
написанное: meta-edit добавлял команду, epf-validate её пропускал.
meta-edit: внешние типы внесены в таблицу допустимых детей — команду
не добавляет, предупреждает. epf-validate: Command и ManagerModule.bsl —
ошибки с пояснением.
Refs #108
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Автопроверка после правки жёстко звала meta-validate, который
ExternalDataProcessor/ExternalReport не знает: корректная правка
заканчивалась «Unrecognized metadata type». Валидатор теперь выбирается
по типу объекта. В py-порту stdout сбрасывается перед запуском
валидатора — его вывод больше не обгоняет строки meta-edit.
verify-snapshots: кейс meta-edit с setup none собирается epf-build,
а не грузится пустой конфигурацией, где обработку платформа не видит.
Fixes#108
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Перезапись брала адрес только из первой строки Listen: при привязке
к loopback по обоим стекам (127.0.0.1 + [::1]) повторная публикация
молча выбрасывала IPv6-строку. Теперь сохраняются все адреса блока
(повторы схлопываются), меняется только порт; URL — по первому.
Refs #107
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
web-info брал первое число строки Listen: при `Listen 127.0.0.1:19081`
порт выходил 127, URL и проверка TCP были ложными. Теперь разбирается
`Listen [host:]port [proto]` (включая IPv6 в скобках), сначала в своём
глобальном блоке; явный адрес идёт в URL и в пробу порта.
web-publish при перезаписи глобального блока сохраняет адрес привязки,
вписанный вручную, — повторная публикация больше не открывает loopback
на все интерфейсы; URL в выводе строятся по нему. Стандартный Listen
комментируется целой строкой (раньше `Listen 0.0.0.0:80` превращался
в мусор, а `Listen [::]:80` оставался активным).
Refs #107
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Списки объектов и отчёт db-repo лежали в общем временном каталоге под
постоянными именами: параллельный запуск молча подменял список, по которому
модель перевыгружает устаревшие исходники, а при отказе платформы навык мог
напечатать отчёт предыдущего запуска. Имена получают случайный суффикс —
путь и так печатается в вывод.
Логи /Out базы-заглушки переехали в каталог самой базы: он уникален на запуск
и удаляется вместе с ней, чужой или устаревший лог в вывод не попадёт.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Временный каталог брался из $env:TEMP, которой вне Windows нет. Join-Path
падал на привязке параметра внутри try/finally без catch: try прерывался,
finally отрабатывал, и скрипт выходил с кодом 0 — платформа не запускалась,
постусловие не проверялось (#106).
- временный каталог — [IO.Path]::GetTempPath() (на Windows тот же путь);
- верхнеуровневый trap { … exit 1 } в 15 скриптах db-*/epf-*, запускающих
платформу: любая необработанная ошибка даёт код 1 и печатает место;
- уборка временного каталога в finally не падает на пустом пути;
- гард check-ps-portability.mjs держит оба правила.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Проверка исходников идёт в конфигурации-заглушке, где общих модулей не было:
обработка с кодом БСП не собиралась без -Checks off.
- stub-db-create -ConfigSrc: общие модули, к которым обращается код, получают
пустых двойников с флагами контекста из выгрузки; неизвестные имена не
угадываются
- epf-build -ConfigSrc, по умолчанию configSrc базы из .v8-project.json
- подсказки к «Переменная не определена (X)»: модуль не в том контексте /
модуля нет в configSrc / выгрузка не передана (с -ConfigSrc и -Checks off)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- убран несуществующий ТипКомандыСценарийВБезопасномРежиме(), добавлен
ТипКомандыЗагрузкаДанныхИзФайла() с шаблонами трёх процедур загрузки;
список типов помечен исчерпывающим, указана применимость к видам
- СозданиеСвязанныхОбъектов: серверный ВыполнитьКоманду с СозданныеОбъекты
(4 параметра) — также в epf-bsp-init
- клиентские обработчики: Печать для печатных форм, вариант для создания
связанных объектов
Refs #82
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
Команда формы объявлена с действием, а процедуры в модуле не было —
epf-build проверяет исходники с -HandlersExistence и отменял сборку.
Обработчик вставляется в модуль формы; добавлена проверка, что он
пережил сборку и разборку.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
Удалённый объект уходит из базы, только если в списке его владелец и в
составе владельца объекта уже нет; удалённый файл-часть (модуль, Ext/)
загрузка частями не удаляет никогда. Скрипт классифицирует удаления:
применимые — [note], остальные — [ВНИМАНИЕ] с причиной; если загружать
нечего, а неприменимые удаления есть — код 1. git diff с --no-renames,
чтобы старый путь переименования был виден. -AllExtensions отвергается
на обоих движках: частями грузится одно расширение за раз.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
- -update без -force: при другой версии формата — отказ с подсказкой
про -Mode Full, а не молчаливая перезапись всего дерева в новом формате.
- Первая выгрузка режимом по умолчанию: в каталог без выгрузки (пусто,
только .git/README) — полностью; выгрузка без ConfigDumpInfo.xml — ошибка.
- ibcmd: Changes через --sync; Full в непустой каталог — через временный
каталог и копирование поверх, как пишет конфигуратор; ошибка копирования
даёт код 1. UpdateInfo с -AllExtensions отвергается, а не выгружает всё.
- py-порт печатает вывод ibcmd, как PS.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
Кейсы навыков, читающих изменения из git, строят репозиторий шагом
{ "git": [...] }. Общая реализация для runner.mjs и verify-snapshots.mjs
в tests/common/git-step.mjs — настройки машины на фикстуру не влияют.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
Параметр звался СуммаДокумента и сравнивался с кодом справочника: имя пришло из кейса
реквизита, где оно нужно ради авто-вывода «Сумма документа», а здесь заголовок задан
явно и от имени не зависит. Теперь КодОрганизации — запрос читается.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
Кейс сравнивал числовые литералы со строковыми параметрами
(«ГДЕ 1 = &ОрганизацииВЕТИС» при "type": "string") и обходился без таблицы.
На проверяемое поведение это не влияет — навык заголовки читает, а текст запроса
переносит строкой, и платформа при загрузке конфигурации запрос не разбирает
(verify-snapshots проходил и на прежнем варианте). Но кейс читают как образец,
поэтому запрос теперь нормальный: динамический список над справочником, строковые
параметры сравниваются со строковыми полями.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
Ветка параметра читала заголовок обычным Get-LangText, который схлопывает whitespace-only
в пустую строку; ветка реквизита давно читает его через Get-LangTextWS. Заголовок-пробел —
им гасят подпись — превращался в "" , а компилятор трактует пустую строку как отсутствие
ключа и подставляет авто-вывод из имени: " " молча становилось «Организации ВЕТИС».
Тот же класс, что и потерянный регистр, соседняя ветка той же функции.
В корпусе (13891 узел dcssch:title в четырёх типовых) платформа не пишет ни пустой,
ни whitespace-only заголовок, поэтому на разборе чужих выгрузок правка ничего не меняет —
она чинит наш собственный раундтрип compile → decompile → compile.
Кейс dl-parameter-title-case расширен вторым параметром с заголовком-пробелом; на исходной
версии падает в обоих портах. Снэпшоты приняты платформой (verify-snapshots, 3/3).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
Title-FromName/title_from_name продублирован в form-compile и form-decompile: компилятор
пишет заголовок, которого нет в DSL, а декомпилятор на этом же выводе решает, можно ли
заголовок опустить. В комментарии копия заявлена «ТОЧНЫМ зеркалом», но ничем не держалась.
Разъедутся копии — заголовок либо теряется, либо дублируется, и в обоих случаях текст на
форме меняется молча, без ошибки платформы.
Эталон варианта — form-compile: компилятор авторитетен, декомпилятор его зеркалит. Тело
копии приведено к эталону без смены поведения (разбиение строк в ps1; в py — выражение
вместо индексного цикла и `not parts` вместо `len(parts) == 0`; `p.isupper()` и
`p == p.upper()` расходятся только на частях без буквенных символов, а после двух
разделяющих регулярок такая часть недостижима).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
Заголовок реквизита формы и параметра динамического списка опускается, если равен
авто-выводу из имени: компилятор восстановит его сам. Сравнение было
регистронезависимым (в ps1 -eq/-ne, в py зеркалящий их _ps_ieq), поэтому
«Организации ВетИС» при имени ОрганизацииВЕТИС считался равным авто-выводу
«Организации ВЕТИС», выпадал из DSL, и обратная сборка молча меняла текст на форме.
Эталон сравнения — не PowerShell, а компилятор: он пишет заголовок как есть, значит
опускать можно только при побайтовом совпадении. В ps1 этого не даёт и -ceq — он
культурный, мягкий перенос и NFD-разложение для него ничего не весят, и порты
разошлись бы между собой. Сравнение идёт через [string]::Equals(..., Ordinal),
что и есть зеркало питоновского ==. _ps_ieq больше не нужен и удалён.
Два регресс-кейса: реквизит формы (регистр + невидимый мягкий перенос) и параметр
динамического списка. Оба падают на исходной версии в обоих портах.
Найдено на типовой конфигурации: Документ.ИсходящаяТранспортнаяОперацияВЕТИС.ФормаСписка.
Co-Authored-By: Шпаков Антон Александрович <shpakov.anton.job@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
В предыдущем коммите бампнулся только py-порт: ps1 остался v1.26 при py v1.27.
Правила «версия в обоих файлах» набор тестов не проверяет — расхождение видно
только глазами в шапке.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
help-add терял текст справки. Отказ смотрит только на Help.xml, а страница может
пережить его (удалённый дескриптор, частичная выгрузка, справка, сделанная руками):
тогда запись шла безусловно и стирала содержимое с кодом 0 и рапортом [OK].
Теперь существующая страница сохраняется, создаётся только дескриптор — та же
защита, что уже стоит в template-add. Прежняя оценка «в help-add потери данных нет»
была неверной: она опиралась на чтение кода, а не на замер.
Код языка: регулярка пропускала имена устройств Windows. При -Lang nul py-порт
молча писал <Page>nul</Page> и пустой каталог с кодом 0 (страница уходила в NUL),
а PS падал исключением — то есть порты ещё и расходились. Якоря \A…\z вместо ^…$:
последние в обоих языках допускают перевод строки в конце.
Проверка вынесена в Test-LangCode / is_valid_lang и внесена в реестр
check-inline-drift: inline-блок гард не видел, а копий у него две.
Плюс мелочи оттуда же: мёртвый дизъюнкт в $pageExists убран; полумигрированное
дерево (дескриптор есть, старый Ext/Template.html остался рядом) теперь получает
предупреждение, а не молчание; существование страницы в py сверяется без учёта
регистра — иначе на Linux -Lang RU писал бы вторую страницу мимо дескриптора.
Четыре кейса на каждый сценарий. Существующие эталоны не сдвинулись.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-Lang брался как есть и шёл и в текст XML, и в имя файла страницы. Измерено на
обоих портах: `-Lang "../../beyond"` писал beyond.html двумя уровнями выше
каталога Ext/Help и клал <Page>../../beyond</Page> в дескриптор, а `-Lang ""`
давал скрытый файл «.html» и пустой <Page></Page>. Оба случая — код возврата 0
и рапорт об успехе, то есть отказ платформы был бы тихим.
Проверка — дословная копия той, что появилась в template-add: навыки автономны,
но формат «дескриптор + страница» у них общий, и расходиться копиям незачем.
Дефект не регрессия, он был до текущей ветки; замечен при ревью соседней правки.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ревью вскрыло два дефекта во вчерашней ветке добавления языка — оба
воспроизведены руками.
1. Миграция старой раскладки шла ДО проверки дубля языка. При дефолтном -Lang ru
(самый вероятный способ повторно позвать навык на старом макете) Template.html
уже был перенесён, затем срабатывал отказ «страница ru уже существует» — и
дескриптор не записывался вовсе. Макет оставался разобранным: страница есть,
дескриптора нет, платформа такую раскладку снова молча игнорирует. Печатавшийся
при этом [WARN] «создан дескриптор» был неправдой.
2. Файл страницы писался безусловно, а проверялся только список <Page> в
дескрипторе. При рассинхроне (страница на диске есть, в дескрипторе нет —
в том числе после дефекта 1) содержимое затиралось пустым скелетом с кодом 0.
Теперь ветка сначала разбирает состояние целиком и только потом пишет, файл
страницы не перезаписывается никогда: существующая страница подхватывается, в
дескриптор дописывается <Page>. Старая раскладка с -Lang ru — это не коллизия,
а ровно тот случай, ради которого миграция и нужна. Состояние, где есть и
Template.html, и Template/ru.html, навык не разруливает сам: отказ до изменений.
Плюс валидация -Lang: код языка идёт и в текст XML, и в имя файла, поэтому пустое
значение давало файл «.html» с пустым <Page></Page>, а разделитель пути — запись
мимо Ext/Template. Оба отказа платформы были бы тихими.
Четыре новых кейса закрывают каждый сценарий; паритет портов сверен побайтово.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Повторный вызов на существующем макете безусловно отказывал, поэтому второй язык
приходилось добавлять руками — дописывать <Page> в дескриптор и заводить файл
страницы. Случай не редкий: в выгрузке ERP 54 макета из 70 двуязычные.
Теперь при -TemplateType HTML существующий макет — повод добавить страницу, а не
отказать. Порядок <Page> — по коду языка (в ERP так во всех 54). Версия формата
берётся из самого дескриптора, чтобы правка чужой выгрузки не меняла формат.
Метаданные макета и ChildObjects не трогаются: перезапись сменила бы UUID.
Отказ остался там, где он по делу: тот же язык повторно, нехтмловый макет под тем же
именем, любой не-HTML тип.
Макет в старой раскладке (Ext/Template.html) мигрируется с громким [WARN]: платформа
такую раскладку игнорирует, то есть состояние и так нерабочее, а создать её могла
только версия навыка без -Lang — значит это страница на языке по умолчанию.
Проверено на 8.3.27.1859: двуязычный макет доходит до базы и возвращается обратно
байт в байт — и дескриптор, и обе страницы. Попутно измерено: страницу на языке,
не объявленном в Languages/, платформа принимает — поэтому состав языков не проверяем.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Скелет страницы писал самозакрывающиеся <meta …/> и <link …/>. Платформа пишет их
парными: на 400 страницах справки из выгрузки acc_8.3.27 — 400 раз </meta>, нулей нет.
Расхождение безвредно для загрузки, но даёт лишний дифф при первом сохранении
страницы в Конфигураторе.
Замечено при разборе раскладки HTML-макета: template-add берёт скелет отсюда же.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Навык создавал Templates/<Макет>/Ext/Template.html. Платформа такой файл молча
игнорирует: загрузка проходит без ошибок и предупреждений, а макет в базе пустой.
HTML-макет платформа хранит парой, как справку: дескриптор Ext/Template.xml со
списком страниц и сама страница Ext/Template/<язык>.html (картинки — в _files/).
Раскладка снята с выгрузки acc_8.3.27: 136 HTML-макетов из 136, дескриптор во всех
байт в байт одинаков. Шапка страницы — в виде редактора платформы (одной строкой,
парный </meta>), чтобы первое сохранение в Конфигураторе не давало диффа.
Язык страницы задаётся параметром -Lang (дефолт ru) — как в help-add.
Вывод навыка теперь различает содержимое и дескриптор: «Содержимое» указывает на
страницу, иначе правка ушла бы в дескриптор.
Проверено на 8.3.27.1859: LoadConfigFromFiles + UpdateDBCfg + обратная выгрузка —
текст макета доходит до базы и возвращается байт в байт, дескриптор тоже.
Контроль — тот же макет в старой раскладке: в выгрузке из базы тела нет вовсе.
EPF: epf-build → epf-dump — раскладка возвращается с содержимым.
Диагноз и раскладка — из PR #98.
Co-Authored-By: Roman Syuzyov <rsyuzyov@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>