У заимствованной формы расширения в теле второй штамп версии — <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>
Версия формата искалась подъёмом от 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>
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>
Списки объектов и отчёт db-repo лежали в общем временном каталоге под
постоянными именами: параллельный запуск молча подменял список, по которому
модель перевыгружает устаревшие исходники, а при отказе платформы навык мог
напечатать отчёт предыдущего запуска. Имена получают случайный суффикс —
путь и так печатается в вывод.
Логи /Out базы-заглушки переехали в каталог самой базы: он уникален на запуск
и удаляется вместе с ней, чужой или устаревший лог в вывод не попадёт.
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>
Удалённый объект уходит из базы, только если в списке его владелец и в
составе владельца объекта уже нет; удалённый файл-часть (модуль, 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
Параметр звался СуммаДокумента и сравнивался с кодом справочника: имя пришло из кейса
реквизита, где оно нужно ради авто-вывода «Сумма документа», а здесь заголовок задан
явно и от имени не зависит. Теперь КодОрганизации — запрос читается.
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
Заголовок реквизита формы и параметра динамического списка опускается, если равен
авто-выводу из имени: компилятор восстановит его сам. Сравнение было
регистронезависимым (в 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
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>
Список частичной загрузки задаёт объекты метаданных: вместе с объектом платформа грузит
его дочерние объекты (формы, макеты) и файлы Ext/. Configuration.xml — объект
«Конфигурация», поэтому его наличие в списке даёт полную загрузку конфигурации, а не
перечисленных объектов. В db-load-git он попадает в список из диффа сам, без участия
пользователя.
Замерено на обоих движках (1cv8 и ibcmd): правило одинаково.
- SKILL.md обоих навыков: строка про цену Configuration.xml в списке;
- хинт в выводе обеих веток движка;
- два кейса на срабатывание и на отсутствие ложного срабатывания.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Правило было уже реальности: Конфигуратор не даёт включить характеристику в
составной тип вместе ни с каким другим типом — в том числе с другой
характеристикой. Предупреждение теперь ловит и DefinedType.X, и
Characteristic.X.
Корпус подтверждает даже чище, чем для определяемого типа: 1741 характеристика
единственным типом и 0 в составных (у определяемого было 6500 против 1).
Уровень прежний — предупреждение: загрузчик такое принимает, проверено
загрузкой в базу.
Голых метатипов это не касается: DocumentRef + CatalogRef — обычный составной
тип, 525 вхождений в поставляемых ERP и БП.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Условие было шире реальности: предупреждение пропускало и Characteristic.X.
В дереве выбора типа значения ПВХ характеристики нет вовсе — тип значения
характеристики не может быть значением характеристики. Единственное множество,
которое Конфигуратор там предлагает, — ОпределяемыйТип.
Уровень прежний: платформа такую конфигурацию грузит (проверено загрузкой в
базу), поэтому предупреждение, а не отказ. В корпусе erp+acc ни один из 24 ПВХ
множеств в типе значения вообще не использует.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Три находки, все воспроизведены:
1. Счётчик членов составного типа не видел форму с локальной xmlns:
тип из чужого пространства имён пишется как <v8:Type xmlns:mxl="…">, а
регулярка требовала '>' сразу за именем тега. В итоге на одном и том же
файле meta-compile молчал, а meta-validate предупреждал — навыки
расходились в оценке одного содержимого. Радиус: оба порта.
2. Report-OK проверки 23 печатался безусловно — то есть сразу после
собственного ERROR, и раздувал счётчик проверок в итоговой строке.
Соседние проверки (21, 22) так не делают.
3. ToString() копировал весь буфер вывода на каждый реквизит: O(n^2) на
крупном объекте. В файле уже есть ranged-перегрузка ровно для этого
(Emit-TypeContent). py-порт был изначально корректен — он режет список.
Добавлен кейс на тип с локальной xmlns: без него находка 1 вернулась бы
незамеченной, потому что обычный составной тип её не показывает.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Запрет неочевидный, а отказ платформы приходит поздно и одной строкой из
Конфигуратора. Три уровня строгости, все замерены загрузкой в базу на
8.3.24.1691, а не выведены из документации:
- ОШИБКА — множество в составе определяемого типа. Платформа отвергает файл
целиком («ОпределяемыйТип.<Имя> - Недопустимый тип»); проверено на
ОпределяемыйТип, Характеристика, ЛюбаяСсылка и голых ссылках, контрольный
вариант с обычным CatalogRef.<Имя> принят. Отсюда и ноль таких узлов в
корпусе — это запрет, а не совпадение.
- ПРЕДУПРЕЖДЕНИЕ — голый метатип в типе значения ПВХ. Загрузка проходит, но
Конфигуратор такой тип не предлагает: в дереве выбора СправочникСсылка и
ДокументСсылка — папки без флажка, а ЛюбаяСсылки там нет вовсе. Плюс AnyRef
раундтрипом возвращается как AnyIBRef, то есть молча меняется.
- ПРЕДУПРЕЖДЕНИЕ — определяемый тип одним из составного. Конфигуратор даёт
выбрать его только единственным, но отказывать нельзя: в корпусе erp+acc
6500 единственных против 1 составного, и этот один лежит в типовой ERP
(Документ.НачислениеИСписаниеБонусныхБаллов.Баллы — два определяемых типа
подряд). Навык не вправе отказаться собрать то, что поставляет 1С.
В meta-compile состав множеств берётся опросом самого эмиттера, а не второй
копией его регулярок: список видов живёт в Emit-TypeContent, и копия
разъехалась бы с ним молча.
Развёртка по 3428 объектам ERP: ровно 2 срабатывания, оба на том самом
реальном составном реквизите. Ложных нет.
Предупреждения пишутся прямо в stderr, а не через Write-Warning: в PS 5.1 тот
уходит не в тот поток (конвенция из cf-init), и до проверки они не доезжали.
Оба кейса-предупреждения проверены платформой — она эти конфигурации грузит,
что и подтверждает выбор «предупреждение, а не отказ».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Семь находок ревью, все подтверждены воспроизведением:
1. Голый менеджер в источниках печатался по-английски: карта объектных видов
применялась только к форме с точкой, а meta-compile пишет «cfg:DocumentManager»
обычным v8:Type. Восемнадцать добавленных *Manager-записей были недостижимы.
Суффикс «(все)» ему не ставится — это сам тип менеджера, а не класс объектов.
2. Новые регулярки прощающего ввода расходились между портами: -replace в
PowerShell регистронезависим, re.sub — нет. «ДокументОбъект (Все)» проходил в
ps1 и падал в py. Добавлен re.IGNORECASE — это задокументированная ловушка
портирования, и она же снова сработала.
3. В шесть py-портов попал BOM (перекодировка при бампе версии), и перед
«#!/usr/bin/env python3» он ломает shebang на POSIX. Снят там, где его не было
в HEAD; в mxl-compile.py он был изначально и оставлен.
4. Правило со стрелкой из Resolve-TypeStr убрано. form-compile режет тип по [|+]
ДО резолвера, поэтому строка глоссария «A -> B | C» молча превращалась в
составной тип «A | C» вместо одного A. Половина строки, принятая за тип, —
хуже громкого отказа. Остались суффикс «(все)» и счётчик «— типов: N».
5. py-порт meta-info не переиспользовал общий климб до корня конфигурации:
правка тогда молча не применилась, и порты разошлись по структуре.
6. Found выставлялся до разбора состава определяемого типа: падение внутри
давало «состав пуст» — ложь вместо «файл типа не разобран».
7. Порог сворачивания источников считался по сумме множеств и явных типов, а
сворачивались только явные: пять множеств плюс один тип прятали этот тип, а
шесть типов без множеств давали «и ещё» после пустоты. Порог теперь по числу
явных типов, «и ещё» убрано как ложное.
Плюс формы источника подписки описаны в reference самого навыка: раньше они
были только в docs/meta-dsl-spec.md, а навык обязан быть самодостаточным.
Добавлен кейс на голого менеджера — без него находка 1 вернулась бы незамеченной.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
verify-snapshots отверг кейс: «ОпределяемыйТип.Любой - Недопустимый тип».
Спросил платформу — v8:TypeSet внутри определяемого типа запрещён вообще, и не
только AnyRef: отвергнуты также голый CatalogRef, Characteristic.X и вложенный
DefinedType.X, при том что контрольный вариант с обычным CatalogRef.X принят.
Замерено на 8.3.24.1691 загрузкой в базу. Отсюда и ноль таких узлов в корпусе —
это не совпадение, а запрет.
Комментарий в обоих портах утверждал обратное («платформа допускает») — правда
записана, факт добавлен в docs/meta-dsl-spec.md.
Сам разбор v8:TypeSet в выводе определяемого типа оставлен: meta-info читалка,
и рукотворный файл с таким узлом она обязана показать честно, а не потерять его
молча вместе с правдивым счётчиком. Кейс переведён на рукотворную фикстуру с
объявленной причиной пропуска платформенной проверки.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Покрытия не было вообще: ни одного кейса ни на подписку на событие, ни на
определяемый тип. Поэтому дефект и жил — тесты были зелёными всё время.
Добавлено девять кейсов:
- источники-множества в full/overview/brief (класс «все», определяемый тип,
явный тип) — основа взята из PR #96, эталоны под наш формат;
- определяемого типа нет в выгрузке: сказано прямо, а не молчанием;
- односоставный псевдоним в типе реквизита — главный по частоте случай в
реальных выгрузках (7575 вхождений из 8841 в ERP);
- определяемый тип выше порога: вместо состава счётчик и команда раскрытия —
этот кейс держит решение «не раскрывать», без него регрессия вернула бы
шестисотстрочный вывод;
- голый метатип в типе реквизита: суффикс «(все)» против конкретного типа;
- определяемый тип, содержащий множество: счётчик «Типы (N)» его не теряет;
- явных источников больше порога: overview сворачивает их в счётчик.
Фикстуры строятся навыками через preRun, чтобы эталоны не плавали по uuid.
Co-Authored-By: Шпаков Антон Александрович <shpakov.anton.job@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PowerShell ищет функцию в момент вызова и видит только уже выполнившиеся
объявления. В трёх навыках блок Find-V8Project/Test-SamePath/Find-ProjectDatabase
оказался ниже строки, где отрабатывает Find-ProjectV8Path: CommandNotFoundException
уходил в stderr, результат становился $null, и выбор платформы по записи базы молча
откатывался на корневой v8path. Порты .py не задеты — Python связывает имя при
вызове, так что расхождение PS↔PY было бы тихим.
Гард check-ps-define-before-call.mjs проверяет порядок объявлений во всех .ps1,
где отрабатывает эта цепочка (15 навыков), и на прежнем состоянии даёт ровно те
три диагностики. Кейс db-repo/v8path-from-database ловит тот же дефект поведением.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
В одном проекте базы могут жить на разных версиях платформы, а версию формата
XML-выгрузки задаёт та платформа, которая выгружает: без выбора платформы по базе
срабатывает автопоиск и берёт самую старшую установленную — то есть самый новый
формат, который коллега на младшей платформе уже не загрузит.
Теперь порядок такой: явный -V8Path -> v8path записи базы -> корневой v8path ->
автопоиск. Запись базы ищет сам скрипт тем же матчем, которым добывал реквизиты
хранилища (Find-ProjectDatabase): файловую по path, серверную по server + ref.
- в 9 навыков, где матча не было, скопирован блок из авторитета db-repo;
- списки потребителей трёх семей в check-inline-drift расширены; заведена семья
platform: resolve_v8path — обёртка меняла сигнатуру, а под гардом не была;
- раннер подставляет {workDir}/{fakePlatform} и в содержимое preRun.writeFile,
иначе кейс про выбор платформы нельзя написать кроссплатформенно;
- в SKILL.md платформа теперь берётся ПОСЛЕ разрешения базы, строка про
автоопределение через Program Files убрана как деталь реализации.
Живой прогон: две базы в одном проекте при корневом v8path = 8.5 дали формат
2.17 (8.3.24) и 2.20 (8.3.27), оба порта.
Closes#93
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Оба кейса не проходили проверку эталонов платформой, и по разным причинам.
region-reuse клал в расширение декоративный перехватчик &Перед("МетодА"),
чтобы навыку было куда встроиться, но МетодА в конфигурации не было — только
МетодБ. Раннер этого не видел: он сверяет выход навыка, а навык отрабатывал
верно. Платформа же расширение не принимала: «Не найден метод "МетодА",
указанный в аннотации». Метод добавлен в модуль конфигурации; выход навыка не
изменился, сдвинулся только эталон исходного модуля.
resync-conflict моделирует неразрешённый конфликт: навык намеренно паркует
блок под меткой // [РЕСИНК-КОНФЛИКТ] и оставляет его человеку, так что текст
метода заведомо расходится с оригиналом и расширение неприменимо by design.
Это штатный случай для skipPlatformVerify — объявлен с причиной.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Две находки ревью.
Приняв хвостовой комментарий у маркера, разбор запоминал только содержимое
блока, а -Actualize писал на его место голое ключевое слово: авторская пометка
вроде «#Вставка // проверка прав, ТЗ-142» молча исчезала при первой же
актуализации. Теперь строки открывающего и закрывающего маркера едут вместе с
блоком и возвращаются как были — с комментарием, отступом и своим написанием;
ключевое слово остаётся запасным вариантом. Кейс actualize-marker-tail-comment
эту потерю ловит: без правки падает.
Счётчик контролируемых методов в cfe-validate был единственным новым
сопоставлением без учёта регистра, хотя язык регистронезависим: на
&changeAndValidate он показывал ноль и расходился с cfe-patch-method -Check,
на который сам же и ссылается.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Шесть кейсов: дрейф и актуализация английского перехватчика, генерация
ИзменениеИКонтроль и Instead из английского источника, отчёт cfe-diff,
счётчик cfe-validate, плюс русский кейс с хвостовым комментарием у маркера.
Эталоны фиксируют главное: сгенерированный код повторяет язык источника,
а -Actualize оставляет Procedure и #Insert английскими.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Замена ExternalReport. -> Report. шла по всему тексту XML и попадала внутрь
имени объекта, если оно оканчивается так же (ПриёмкаExternalReport). Имя берётся
из <Name> до замены и остаётся авторским везде — в имени файла, GeneratedType и
составе конфигурации, — а ссылка в скопированном XML расходилась с ним, и
заглушка не грузилась: «Неизвестный объект метаданных - Report.ПриёмкаReport…».
Заменяется только префикс вида объекта в начале квалифицированного имени.
Регрессии строят фикстуру навыками (erf-init --WithSKD, epf-init, form-add) —
так в кейс попадает и настоящая ссылка вида cfg:ExternalReportObject.<Имя>.
Соседние кейсы каталога переведены на {fakePlatform} и прямые слэши.
Closes#90
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1. Резолв uuid вложенного объекта переписан со сквозной регулярки на
спуск по дереву. Поиск по всему файлу брал первый узел с подходящим
именем, а реквизит шапки и реквизит табличной части сплошь и рядом
называются одинаково — ключ сортировки был чужим и вдобавок
дублировался, из-за чего порты могли разложить узлы по-разному.
Дочерние подсистемы лежат отдельными файлами и не резолвились вовсе:
431 узел в ролях ACC. Теперь на реальной роли резолвится 90% узлов,
остальное — стандартные реквизиты, у которых uuid в выгрузке нет.
Для них предупреждение больше не печатается: это норма, а не потеря.
2. Фильтр умолчаний уносил право вместе с его ограничением RLS: условие
пропадало молча. Право с ограничением отличается от умолчания самим
ограничением и остаётся.
3. modify-property не обновлял кэш умолчаний, и следующие операции того
же вызова фильтровали по старому флагу — запись попадала в файл ровно
вопреки тому, о чём навык сам предупреждает.
4. Edit-RoleMetadata перечитывал Roles/Имя.xml на каждой операции, и в
пакете первая правка молча терялась: set-synonym + set-comment
сохранял только комментарий. В py-порте такого не было — расхождение
портов закрыто.
5. Атрибуция сообщения в PS шла подстрокой по всему списку отброшенного,
из-за чего причина приписывалась чужому объекту. Теперь префиксное
сравнение, как в py.
Три новых кейса, семьи get_rights_object_uuid и is_standard_kind
заведены в check-inline-drift.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq