Commit Graph
1692 Commits
Author SHA1 Message Date
Nick ShirokovandClaude Opus 5 97f1a0d4e1 fix(switch): навыки без привязки к Claude — порт на другие платформы не уносит хвосты
Три файла из 77 навыков нарушали соглашение о переносимости, и сборка
Codex-порта уносила их as is (#75).

Функциональный из трёх один: json-dsl.md писал путь к скрипту литералом
.claude/skills/meta-edit/..., то есть на любой платформе кроме claude-code
команда указывала в несуществующий каталог и молча не находила скрипт.
Заменено на ${CLAUDE_SKILL_DIR}/ — дальше путь разворачивает switch.py.

Ещё два — тексты: проза с названием агента в mxl-compile (заодно избыточная:
у соседних *-compile это одна строка «принимает X → генерирует Y») и
.claude-путь в комментарии state.mjs.

От регресса — check-agent-portability.mjs: вырезает разрешённый
${CLAUDE_SKILL_DIR}/ и падает на любом оставшемся claude. Ловит и плейсхолдер
без завершающего слеша: switch.py разворачивает только форму со слешем,
остальные проехали бы насквозь. Сторонний node_modules исключён — playwright
содержит ClaudeGenerator и .claude/agents, переписывать его нельзя.

switch.py не менялся: ни одного плейсхолдера и ни одного вызова скрипта вне
*.md верхнего уровня навыка нет, рекурсия не нужна.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 16:35:07 +03:00
Nick ShirokovandClaude Opus 5 9475aa6171 fix(skd-edit): не срезать значимое локальное объявление xmlns при пересборке поля
При пересборке поля навык сохранял <valueType> через OuterXml/tostring и срезал
из фрагмента ВСЕ объявления namespace. Для префиксов, объявленных в корне схемы,
это верно — DOM переобъявляет их на фрагменте избыточно. Но объявление, которого
в корне нет, значимо: xmlns:d5p1 живёт только локально на <v8:Type> и связывает
префикс, которым квалифицировано значение узла (d5p1:CatalogRef.X). Срез оставлял
висячий префикс — XML остаётся well-formed (префикс в тексте парсер не проверяет),
а 1С отвергает тип. Радиус — modify-field.

Слепой срез заменён на Strip-InheritedXmlns / strip_inherited_xmlns: объявление
выбрасывается, только если корень объявляет тот же префикс с тем же URI. Карта
корня снимается с уже имевшегося RawRootOpening; разделитель \s+, поскольку
корневой тег бывает разложен по строкам. Префикс и URI сравниваются ординально —
в PS -eq и @{} регистронезависимы. Подставлено во все пять точек среза каждого
порта; для корневых префиксов поведение не меняется.

Кейс modify-field-ref-type: modify-field по ref-полю не был покрыт. Проверено,
что кейс краснеет до правки — и по снэпшоту, и по skd-validate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 14:27:03 +03:00
Nick ShirokovandClaude Opus 5 b6857c9406 docs(tests): check-positional-binding в таблице гардов
Гард добавлен в check-all.mjs прошлой волной, а в таблицу README не попал.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 11:52:40 +03:00
Nick ShirokovandClaude Opus 5 1c63d9ddcc refactor(skills): регистрация в ChildObjects — именованная функция под анти-дрейфом
Три навыка делали одну работу тремя инлайновыми копиями в main(), и одна копия
молча разъехалась: role-compile.py рапортовал об успехе, не тронув файл. Это
ровно тот класс, от которого заведён check-inline-drift.mjs, но в реестр
регистрацию было не записать — он работает по именованным функциям.

Регистрация вынесена в Register-InChildObjects / register_in_childobjects
с контрактом (родительский XML, тег родителя, тег потомка, имя) и исходом
added | already | no-childobj | no-config. Печать сообщений осталась на
вызывающей стороне: тексты у навыков разные, и сведение их меняло бы вывод.

Эталон — meta-compile, форма функции задана на .ps1. role-compile берёт его
тело копией, и гард это подтверждает. subsystem-compile идёт отдельным
вариантом с обоснованием: родителем бывает вложенный Subsystem.xml
произвольной глубины, поэтому отступ он берёт из документа, а запись
дописывает в конец блока — фиксированные три табуляции там неверны,
и группировать по типу нечего.

Вынос поведенчески нейтрален: 837 кейсов зелёные на обоих рантаймах,
снэпшоты не дрейфовали, матрица «навык × порт × изломанная форма
Configuration.xml» даёт те же исходы, а порты — байт-в-байт одинаковый файл.
Попутно из subsystem-compile.py убран мёртвый ET.SubElement перед pass:
дерево мутировалось и выбрасывалось, правка идёт по сырому тексту.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 11:52:40 +03:00
2b0ac3ea75 fix(role-compile): py-порт рапортовал об успешной регистрации, не тронув Configuration.xml
Configuration.xml без <ChildObjects> и с самозакрытым <ChildObjects/> — в обеих
формах py печатал «<Role>X</Role> added to ChildObjects», а файл оставался
байт-в-байт прежним. Модель считала роль включённой в состав конфигурации,
хотя её там не было. PS-порт обе формы обрабатывал верно: самозакрытый тег
раскрывал, при отсутствующем честно предупреждал.

Ветвление перенесено из meta-compile.py, где та же задача давно решена: есть
блок ChildObjects — вставляем внутрь; есть самозакрытый тег — раскрываем первой
записью; нет ни того ни другого — исход no-childobj и файл не трогаем. Ветка
вывода no-childobj в py уже была и до сих пор оставалась недостижимой.

Порядок вставки и способ правки файла не менялись: проверка на боевых выгрузках
(acc/erp, CRLF и LF) показала, что действующая реализация даёт дельту ровно
в одну вставленную строку, а платформа порядок ChildObjects не нормализует.

Кейсы на обе формы. На неисправленном коде оба падают на python и проходят
на powershell. Проверка «успех не объявлен» опирается на stdoutNotContains
и снапшот: Write-Warning в PS 5.1 идёт в stdout с локализованным префиксом,
а py пишет в stderr, поэтому портируемого stderrContains тут быть не может.

Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 11:32:46 +03:00
Nick ShirokovandClaude Opus 5 1cfc76da2e fix(tests): гард позиционного связывания не зеленеет на незапустившемся интерпретаторе
Поведенческая секция считала успехом любой ненулевой код возврата, а не
запустившийся интерпретатор даёт status: null — проверка проходила вакуумно,
ничего не проверив. Теперь spawn-ошибка сама является нарушением.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 15:13:24 +03:00
Nick ShirokovandClaude Opus 5 9e883d7f4f fix(skills): в read-only навыках позиционным остаётся только путь ко входу
Тот же класс дефекта, что закрыт в role-validate, был возможен во всей семье
*-info / *-validate / cfe-diff: лишний позиционный аргумент связывается со
следующим параметром по порядку объявления. Остальные скрипты спасала
случайность типов — на втором слоте оказался [int]MaxErrors/Limit или строка
с [ValidateSet], которые падают на конвертации. Любая перестановка параметров
в param() открывала дыру заново, а снапшот-тесты сверяют вывод, а не связывание.

Во всех 21 скрипте объявлен [CmdletBinding(PositionalBinding=$false)], входной
путь помечен Position=0. Инвариант: анализирующий навык не пишет в файл, который
ему не назвали по имени. Документированные вызовы в репозитории все именованные,
поведение навыков не меняется — 835 кейсов зелёные на обоих рантаймах без дрейфа
снэпшотов.

Гард check-positional-binding.mjs держит инвариант статически (объявление и
единственный Position=0; в py-порте все add_argument именованные) и поведенчески
(лишний позиционный аргумент роняет вызов, канареечный файл остаётся цел).
Семья определяется по имени навыка, поэтому новый *-info/*-validate попадает под
гард сам. На состоянии до правки гард падает на role-validate обеими проверками.

Версии обоих портов подняты синхронно; из шапок убраны чужие хвосты.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 14:59:52 +03:00
83d63fac47 fix(role-validate): лишний позиционный аргумент затирал файл валидационным отчётом
Вызов role-validate.ps1 с двумя позиционными аргументами — естественная ошибка
по документации role-compile/SKILL.md, где значилось
`/role-validate <RightsPath> [MetadataPath]`, хотя параметра MetadataPath у
навыка нет. Второй аргумент связывался с -OutFile, и скрипт перезаписывал
указанный файл (например, Roles/Имя.xml) текстом отчёта — исходник роли терялся.

PositionalBinding=$false оставляет позиционным только путь ко входу; -OutFile
передаётся строго по имени. Справка приведена к факту. Python-порт уже иммунен:
argparse принимает только именованные параметры.

Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 14:59:37 +03:00
Nick ShirokovandClaude Opus 5 77cef101f7 test(cfe-borrow): причина пропуска на 8.3.27.1688 — с механизмом, а не с наблюдением
Разбор довели до механизма. Сборка не разрешает поля динамического списка заимствованной
формы в авто-режиме (ManualQuery=false + MainTable). Проверено и отброшено: порт скрипта,
ОС, имена латиницей, заимствованность таблицы, порядок загрузки, BaseForm/UseAlways/Settings,
полный эталонный вид с ListSettings и Список.Ref. Проходит только ручной запрос, что меняет
семантику списка.

Решающий факт: ту же базу, где расширение валидно загружено платформой 8.3.24, сборка 1688
выгружает с битыми путями (~Список.Склад) — то есть не разрешает их даже из собственного
хранилища. Наш эмит при этом совпадает с эталоном Конфигуратора (upload/cfe/ut/ФормаСписка2).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 11:22:07 +03:00
Nick ShirokovandClaude Opus 5 9dcaa9b6b1 fix(tests): пропуск платформенной верификации, привязанный к сборке платформы
Кейсы form-main-attr-dynamic-list и form-main-attr-list-query падали на маке. Диагностика:
дело не в порте (Windows+python проходит) и не в ОС (мак на 8.3.24.1691 проходит) — отвергает
конкретная сборка 8.3.27.1688. Решающий опыт: расширение, загруженное на 8.3.24 и выгруженное
самой платформой, сборка 1688 тоже отвергает с теми же «Неверный путь к данным» — то есть она
не принимает собственный артефакт Конфигуратора, а не наш эмит.

Глухой skipPlatformVerify снял бы проверку и на стендах, где она работает, поэтому он теперь
принимает объект {reason, platforms:[…]} — пропуск только на перечисленных сборках.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 23:13:44 +03:00
Nick ShirokovandClaude Opus 5 e9a40a1499 fix(tests): verify-snapshots помечает пропуски в REPORT.md, а не считает их падениями
Консольная сводка пропуски различала, а файл отчёта нет: статус считался как
`passed ? OK : FAIL`, поэтому пропущенный кейс печатался как FAIL, попадал в счётчик
падений и в раздел Findings. Отчёт читают глазами и по нему решают, есть ли проблема, —
расхождение с консолью здесь дороже всего.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 22:55:17 +03:00
Nick ShirokovandClaude Opus 5 eda24ae1e7 fix(tests): verify-snapshots читает оба потока при падении навыка
ps1 печатает строку ошибки в stdout, py — в stderr, а лог платформы оба кладут в stdout.
Верификатор брал `stderr || stdout`, поэтому на python-порте лог терялся целиком: падение
выглядело как «Error loading configuration (code: 1)» без причины, и распознать по нему
неподдерживаемую версию формата было нечем — кейсы format-221 на маке падали вместо пропуска.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 22:35:17 +03:00
Nick ShirokovandClaude Opus 5 54ee6efe93 fix(tests): каталог расширения в кейсах cfe-* — cfe вместо ext (closes #74)
Кейсы клали расширение в `ext`, а cf-init создаёт в корне конфигурации платформенный `Ext/`.
На регистронезависимой ФС (Windows, APFS) это один каталог: эталоны годами фиксировали
слипшееся дерево — в snapshots/<case>/ext/ лежали вперемешку ClientApplicationInterface.xml
конфигурации и Configuration.xml расширения, а имя каталога в коммите зависело от того, кто
создал его первым (ext у одних кейсов, Ext у других при одинаковом входе). На Linux те же
кейсы дали бы два каталога и другой снапшот.

Переименование механическое: 64 кейса пяти навыков, 349 файлов эталонов переехали без
единой правки содержимого. Заодно переписан регистр 21 пути в индексе — git с core.ignorecase
не показывал, что файл конфигурации числится под ext/, а лежит в Ext/.

Гард в runner.mjs: имя каталога расширения из params, совпавшее без учёта регистра с тем, что
уже есть в фикстуре, валит кейс с объяснением. Проверено, что старое имя теперь не проходит.

verify-snapshots.mjs чинится тем же заходом — там нашлись две дыры:
- каталог расширения был захардкожен как 'ext', поэтому кейсы с другим именем МОЛЧА теряли
  вторую половину проверки: блок загрузки расширения обходился по existsSync и в отчёте это
  выглядело успехом;
- setup брался только из _skill.json, и кейсы с гейтом по версии формата (218/220/221)
  проверялись на конфигурации 2.17 — то есть платформа не видела ровно того поведения, ради
  которого кейс написан.
Плюс два honest-skip вместо ложных падений: режим совместимости выше платформы и формат
выгрузки новее платформы (второе читаем из ответа платформы, а не дублируем лестницу версий).

Проверено: регресс 827/0/8 (ps1) и 824/0/11 (py), гарды 5/5. Платформенная верификация:
cfe-borrow 26/28 на 8.3.27, cfe-patch-method 22/23, cfe-init 6/7, все 14 кейсов format-221
на 8.5 — остальное осознанные пропуски.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 22:20:28 +03:00
Nick ShirokovandClaude Opus 5 113dc9e928 fix(cfe-validate): проверка модулей затирала имя расширения в итоговой строке
Новая проверка (сверка «файл модуля ↔ пометка») переиспользовала $objName — ту же
переменную, из которой собирается финальная строка отчёта. В расширении с заимствованным
объектом ps1 печатал «Validation OK: Extension.Цены» — имя последнего проверенного объекта
вместо имени расширения. py-порт был прав, то есть порты разошлись молча.

Расхождение видно только на непустом расширении с чистым результатом, поэтому ни один
существующий кейс его не ловил: тесты сверяют снапшот, а итоговую строку — нет. Добавлены
утверждения на неё в кейсы valid, with-borrowed-object и module-state-file-without-flag;
проверено, что на старой версии скрипта они падают.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 21:20:26 +03:00
Nick ShirokovandClaude Opus 5 8fe727e32f feat(cfe-borrow,cfe-patch-method,cfe-validate): модули заимствованных объектов и пометка расширенного свойства
Объект заимствуют, чтобы дописать в него модуль, — теперь пустой файл модуля создаётся
навыком, а не руками. Тип с единственным модулем (CommonModule, HTTPService, WebService)
получает его без указаний, остальные — по -Module; -Module None отменяет. Существующий
файл не перезаписывается никогда.

Вместе с файлом проставляется <xr:PropertyState> со State=Extended. Замер по лестнице
платформ 8.3.20…8.5.1: элемент появился в формате 2.19 (8.3.26), ниже платформа молча
выбрасывает его при загрузке — отсюда гейт по версии формата. Имя свойства равно базовому
имени файла модуля, у заимствованной формы — Form. Правило владения одно: пометку ставит
тот, кто создал файл, поэтому её ставит и cfe-patch-method.

Заодно закрыта перезапись при повторном заимствовании: прямая ветка писала XML уже
заимствованного объекта начисто, унося собственные реквизиты расширения и регистрацию
формы, — молча, с успешным отчётом. Повторный вызов теперь безопасен и служит способом
дозаимствовать модуль.

cfe-validate сверяет «файл модуля ↔ пометка» в обе стороны (предупреждение: перекос
платформа принимает, но выгрузка Конфигуратора так не выглядит).

Проверено: полный регресс 827/0/8 (ps1) и 824/0/11 (py), гарды 5/5, сквозной раундтрип
на 8.3.26 и 8.3.27 — InternalInfo совпадает с выгрузкой платформы.

Закрывает #70. Разбор и эталоны выгрузки — Romandredan.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 20:59:39 +03:00
Nick ShirokovandClaude Opus 5 89f65ca6a6 fix(tests): чинит фикстуру role-info/multiple-types и иконку скипа в verify
Кейс собирал через meta-compile регистр сведений без измерений и ресурсов —
платформа такую конфигурацию не принимает («Регистр без измерений, ресурсов и
реквизитов»), и verify-snapshots падал на нём на обеих ОС. Регистру добавлены
измерение и ресурс, эталон переснят: дельта только в XML регистра, права роли
не изменились.

Заодно построчный вывод verify рисовал скипы как ✗ (иконка выбиралась по
result.passed, который скип не выставляет): десяток «✗» под «0 failed» читался
как сломанный набор.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 18:44:14 +03:00
Nick ShirokovandClaude Opus 5 da51d8ab7d fix(tests): verify-snapshots скипает недоступную внешнюю выгрузку, а не падает
runner.mjs при отсутствии external-пути возвращает SKIP, verify-snapshots писал
ошибку. Путь к дампу ERP/БП машинозависим: на Mac mini корпуса нет, и два кейса
role-validate краснели при полностью исправном навыке. README предупреждает ровно
об этом: раннеры читают один DSL каждый своей реализацией.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:15:26 +03:00
Nick ShirokovandClaude Opus 5 261ea8c96d test: раскрытие сервисов в role-compile, права на корень и заимствованные объекты
Кейсы: раскрытие HTTP- и веб-сервиса, раскрытие сервиса интеграции, раскрытие
заимствованного сервиса, дедупликация раскрытого листа с явным, отказ без
метаданных, отказ основной роли расширения, право на корень сервиса в
role-validate и в cfe-validate.

Фикстуры HTTP- и веб-сервиса строит preRun через meta-compile — рукописный XML
платформа отвергала (у Operation обязателен замыкающий ChildObjects, у сервиса
нужен Ext/Module.bsl). Оба кейса проходят verify-snapshots: 1С принимает
конфигурацию с раскрытыми правами при полной загрузке.

Вручную написаны только фикстуры, которые навыками не собрать, — сервис
интеграции (meta-compile его не поддерживает) и расширение с заимствованным
сервисом (выгрузка Конфигуратора); оба с skipPlatformVerify и причиной.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:54:08 +03:00
Nick ShirokovandClaude Opus 5 9702a4c485 fix(cfe-validate): основные роли не дают прав на заимствованные объекты
Платформа: «Назначение прав доступа на заимствованные объекты основными ролями
в расширениях недопустимо». Роль вне DefaultRoles так делать вправе, поэтому
проверяются только основные. Ловится статически, а по симптому — отказ загрузки
расширения — причина не читается.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:19:55 +03:00
Nick ShirokovandClaude Opus 5 4be10e3d7f fix(role-validate): право на сервис целиком — ошибка
Роль с правом на `HTTPService.Имя` формально валидна и молча не работает:
платформа проверяет вложенный объект (метод шаблона URL, операцию, канал),
маршруты отвечают 403, и по симптому причина не читается. В Конфигураторе
галки на корне сервиса нет вовсе — это недостижимое состояние, а не стилистика.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:19:55 +03:00
Nick ShirokovandClaude Opus 5 756bd857dd fix(role-compile): права на сервис — на вложенных объектах, а не на корне
Короткая запись `HTTPService.X: Use` писала узел, которого платформа не видит:
роль проходила role-validate и cfe-validate, а маршруты отвечали 403. В корпусе
(acc/erp/ut/unf, 907 записей прав на сервисы) корневого узла нет ни разу — право
живёт на методе шаблона URL, операции, канале; в Конфигураторе галки на корне нет.

Короткая запись теперь раскрывается во все вложенные объекты сервиса по его
метаданным в OutputDir. Заимствованный сервис несёт заимствованные шаблоны и
методы, поэтому раскрытие даёт ровно тот набор, на который права дать можно.
Нет метаданных или вложенных объектов — отказ с подсказкой формата.

Плюс отказ, когда основная роль расширения (из DefaultRoles) даёт права на
заимствованный сервис: платформа такое расширение не принимает.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:19:43 +03:00
Nick ShirokovandClaude Opus 5 4967b752d7 docs(spec): тело XDTO-пакета — Ext/Package.bin, а не Package.xdto
Выгрузка конфигуратора кладёт модель пакета в Ext/Package.bin
(текстовый XML в UTF-8 с BOM). Файла Package.xdto не существует.

Closes #69

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 15:03:37 +03:00
Nick Shirokov f4768e245a docs(mxl-decompile): свернуть каталог раундтрип-форм до одного абзаца
Таблица из пяти записей читалась как меню возможностей и звала писать
apply руками — ровно та развилка, которой мы избегаем в прощающем вводе.
Осталась одна мысль, ради которой это вообще упомянуто: незнакомый ключ
в разобранном макете нельзя удалять «для чистоты» — он несёт состояние
исходного файла.
w-2026-08-16
2026-08-17 14:26:15 +03:00
Nick Shirokov 2a16395951 docs(mxl-compile,mxl-decompile): закрыть дыры каскада, найденные обходом по задачам
Перечитал каскад как модель, идущая от задачи, и нашёл три места, где задача
упиралась в пустоту:

- ключ pictureParameter стоял в схеме DSL, но не был описан нигде, а задача
  «картинка в ячейке» из индекса вела в drawings.md, где её не было. Добавлен
  раздел: picIndex считается с единицы по порядку объявления в pictures,
  выравнивания и положение текста — ключи стиля, pictureParameter — ключ ячейки;
- объектная форма rowStyle с модификатором apply нигде не описана, хотя её
  пишет декомпилятор: модель, разобравшая чужой макет, встречала непонятный
  ключ. Такие формы собраны в mxl-decompile отдельной таблицей — вместе с
  controlType "none", пустым valueType, пустой привязкой к раскладке и записью
  палитры без картинки;
- область печати и повторение шапки при печати DSL не выражает — теперь это
  сказано прямо в print.md, а не выясняется опытным путём.

Индекс задач дополнен колонкой ключей: модель, увидевшая незнакомый ключ
в схеме, сразу находит нужный файл.
2026-08-17 14:23:53 +03:00
Nick Shirokov e10421d18e docs(mxl-compile): выправить примеры каскада по проверке компилятором
Прогнал каждый json-блок инструкций и спецификации через компилятор
и валидатор, фрагменты достраивая до минимального макета. Нашлось:

- пример колоночных раскладок показывал области с "rows": [] — макет
  с именованными областями поверх нуля строк, валидатор на нём ругается;
- шапка таблицы в примере SKILL.md была записана пятью объектами с col
  там, где хватает позиционного списка строк.

Оба поправлены; раундтрип примера подтверждает, что форма каноническая —
декомпилятор возвращает её же.
2026-08-17 13:01:50 +03:00
Nick Shirokov 13d100dff8 docs(mxl-compile): позиционный список ячеек работает и с rowStyle
Утверждение «короткой формой не задать height и rowStyle» неверно: проверено
компилятором, позиционный список кладётся в cells и уживается со свойствами
строки, включая маркеры > и |. Это форма записи ЯЧЕЕК, а не строки — так
и сформулировано теперь в инструкции и спецификации.

Строка итога в примере переписана короче, заодно показывает эту форму.
2026-08-17 12:56:05 +03:00
Nick Shirokov 93ced4b239 docs(mxl-compile,mxl-decompile): каскад инструкций — файл на задачу
Каскад повторял спецификацию: reference/dsl-spec.md был её копией, а styles
и format-properties — копиями двух других файлов документации. Модель, чтобы
что-то сделать, читала спеку целиком.

Теперь в SKILL.md лежит то, что нужно почти всегда: компактный пример
печатной формы, структура DSL, области, строки, ячейки с короткой формой,
rowStyle и оформление из десяти частых ключей. Остальное — по файлу на
задачу: layout, print, drawings, input-cells, groups, notes,
style-properties, каждый 26-81 строка; читать нужно только свой.

Состав ядра выбран по частоте в обычных макетах (корпус без регламентированной
отчётности, которая перекашивала статистику): шрифт 93%, выравнивание 88/87%,
размещение текста 86%, рамки 57-72%, формат 52%; поля ввода и группы, наоборот,
оказались редкими — 7%.

В mxl-decompile переехало то, что относится к чтению чужого макета, а не
к авторингу: пересборка — полная перегенерация, побайтового совпадения ждать
не всегда стоит, и перечень конструкций, которые цикл не переживают.
2026-08-17 12:51:53 +03:00
Nick Shirokov da76bb6f01 docs(mxl): свести спецификацию DSL в один файл
Оформление и полный перечень свойств стиля жили отдельными файлами, хотя
описывают тот же DSL. Теперь docs/mxl-dsl-spec.md — единственная полная
документация формата, как у остальных семейств навыков: структура,
оформление, колоночные раскладки, перечень свойств.

Ссылки в README и указателе спецификаций сведены к одной строке.
2026-08-17 11:15:43 +03:00
Nick Shirokov e048fcff70 fix(mxl-compile,mxl-decompile): пустой текст ячейки и расхождение портов на нём
Пустой текст платформа хранит только самозакрывающимся тегом: в корпусе ERP
таких 1 224 460, а из 780 934 непустых блоков нет ни одного, где пусты все
языки. Компилятор же на `text: ""` писал блок с пустым элементом языка —
запись, которой в выгрузках не существует. Теперь любая пустая форма
(строка, пустой объект, все языки пустыми) даёт один и тот же тег, а
декомпилятор возвращает её канонической пустой строкой.

Отсюда же росло расхождение портов. Свёртка текста на пустой карте языков
возвращала null, если ДРУГОГО текста в макете нет вовсе: набор языков
документа при этом пуст. Дальше py писал пропуск колонки, а ps1 наступал
на разворачивание одноэлементного массива — список из одного $null
превращался в $null, и строка целиком уходила в пустые.

Проверено на 12 корпусных макетах: 1411 пустых текстов из 1411 вернулись
на место, JSON портов совпадает.
2026-08-16 12:30:19 +03:00
Nick Shirokov fef55043cf feat(mxl-compile,mxl-decompile): параметр картинки у ячейки, прозрачность одним ключом
Ячейка получила третий параметр — pictureParameter, имя параметра, которым
подставляют картинку. Сама картинка задаётся оформлением (picIndex), а этот
тег живёт у ячейки, последним из её параметров, и уживается с текстом.
В корпусе таких ячеек 21 в 9 макетах; теперь все 21 возвращаются обратно.

Прозрачность картинки сведена к одному ключу transparent: false — фона нет,
{ x, y } — прозрачен цвет пикселя с этими координатами. Два способа записи
у платформы исключают друг друга (t принимает только false, включённую
прозрачность выражают tx/ty), так что двум ключам DSL соответствовал один
флажок диалога.

Заодно выровнен порядок ключей в проверке «объект описывает ячейку»: в
py-порте не хватало note.
2026-08-15 21:03:01 +03:00
Nick Shirokov daaa0aeaf8 docs(mxl-compile): ссылка на картинку не различает источник
Одна и та же запись `ref="v8ui:Имя"` стоит и за предопределённой картинкой
платформы, и за общей картинкой конфигурации — по макету источник не
определить. Загрузку это не ломает: платформа ссылку при загрузке не
проверяет, макет со ссылкой на отсутствующую общую картинку принимается.

Заодно снят прежний вывод «tx/ty с ref не встречаются»: на стенде такая
запись есть, координаты идут перед ссылкой. Раундтрип на ней сходится.
2026-08-15 20:42:39 +03:00
Nick Shirokov 20bb45fb9e fix(mxl-compile,mxl-decompile): прозрачность у ссылочной картинки
Атрибут прозрачности живёт не только у картинки с данными: платформа пишет
<picture t="false" ref="v8ui:Имя"/>, причём t перед ref. Компилятор в этой
ветке его терял, декомпилятор не читал — в «Бухгалтерии предприятия» на
8.3.27 таких записей 67.

Заодно уточнена картина по конфигурациям: t="false" стоит у 11 135 картинок
БП, значения true не бывает нигде — включённую прозрачность платформа
выражает координатами пикселя, и вместе с t они не встречаются.
2026-08-15 20:30:15 +03:00
Nick Shirokov ce66b03586 docs(mxl-compile): прозрачность картинки — подтверждено на стенде
Одна и та же картинка 25×30 вставлена в стенд дважды: без флажка
«прозрачный фон» платформа пишет t="false", с флажком — tx="24" ty="29",
то есть координату правого нижнего пикселя. Два способа записи исключают
друг друга. Стенд с обоими случаями положен в фикстуры.
2026-08-15 20:17:26 +03:00
Nick Shirokov 9285ed00cd feat(mxl-compile,mxl-decompile): рисунки и палитра картинок
Рисунок поверх сетки — картинка, фигура, надпись — теперь описывается в DSL
и возвращается из макета: два якоря «ячейка + смещение», тип, ссылка на
картинку, надпись, расшифровка, имя, порядок перекрытия.

Оформление рисунка разложено надвое: общее (заливка, шрифт, выравнивание)
берётся из именованного стиля, а линия и её стороны — собственные ключи
рисунка. У ячейки таких свойств не бывает: все 2 135 записей палитры с ними
принадлежат рисункам.

Палитра картинок: ссылка на библиотеку платформы, данные base64, пустая
запись «картинка не задана» (151 в корпусе) и координата пикселя прозрачного
цвета (tx/ty — проверено, это не размеры картинки).

Заодно: висячий пустой колонтитул в ps1-порте (пустой словарь в PowerShell
истинен, порты расходились на 3 макетах из 40) и проверка индекса формата
рисунка в mxl-validate.

Стенды Рисунки, Рисунки2, КартинкаВЯчейке проходят раундтрип байт в байт
в обоих портах; на 35 корпусных макетах с рисунками расхождение сократилось
у всех 35.
2026-08-15 20:07:12 +03:00
Nick ShirokovandClaude Opus 5 9a1353a1d9 fix(mxl-compile): перечни узоров и стилей линии были выборкой, а не доменом
Оба списка снимались с корпуса ERP и потому отвергали законные значения.
Узоров платформа знает семнадцать («Узор 1» … «Узор 17»), а в корпусе
встречаются только шесть номеров — Pattern4 компилятор отклонял.

Со стилями линии тоньше: палитра одна на документ, но Конфигуратор
предлагает разные наборы в разных местах. У рамки ячейки семь значений
(корпус их покрывал), у линии рисунка — шесть других, и трёх из них
(Dashed, DashDotted, DashDottedDotted) в корпусе нет ни одной. Домен —
объединение обоих наборов.

Обе поправки сняты с диалогов Конфигуратора, а не додуманы по образцу.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 18:30:41 +03:00
Nick ShirokovandClaude Opus 5 b32bb5899b feat(mxl-compile,mxl-decompile): привязка именованной области к колоночной раскладке
Именованная область ссылается на дополнительный набор колонок тегом
columnsID, и эта ссылка терялась целиком: на пилоте кампании она давала
3572 расхождения из 3572 в своей категории, на корпусе таких областей
913 483 прямоугольных, 20 063 полосы строк и 743 полосы колонок.

Ключ namedAreas[].columnSet имеет три состояния: ключа нет — привязка
выводится из накрытых строк (совпадает у 1 021 570 областей корпуса из
1 044 339), "" — привязки нет вовсе, имя набора — явная привязка.
Переопределение обязательно: у областей типа Rows 13 623 повторяют
раскладку строк, а 8 179 её не несут при тех же строках.

Декомпилятор пишет ключ, только когда он не выводится, а область, чья
привязка расходится с раскладкой её строк, уводит из блочной формы в
namedAreas — иначе блоком её не выразить.

Пустая строка тоже попадает в карту «строка → раскладка»: без этого
привязка области, накрывающей пустые строки, не выводилась.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 17:49:24 +03:00
Nick ShirokovandClaude Opus 5 702a3b6a8d feat(mxl-compile,mxl-decompile): колонтитулы и параметры печати
Ключи header и footer описывают колонтитул: три слота (left, center,
right), общий шрифт и вертикальное выравнивание, признак вывода и
страница, с которой печатать. Ключ printSettings — плоский набор
параметров печати с именами тегов платформы; порядок компилятор
выставляет сам, незнакомый ключ отвергает.

Слот устроен как ячейка: ссылка на формат плюс текст. Текст бывает двух
видов, и это не текст против шаблона, а обычная строка против
форматированной: у форматированной разметка живёт прямо в содержимом
(<b>жирный</>, <fontsize 12>, <colorstyle -16>), поэтому она возится как
есть. Признак вывода и стартовая страница лежат в формате как <height>
и <width>, но читаются только у записи с <height>: палитра
дедуплицирована, и колонтитул без своих настроек ссылается на чужую
запись, где <width> — ширина колонки (637 таких ссылок против 153 своих).

Перенос строки внутри текста колонтитула платформа хранит голым LF при
CRLF во всём остальном файле — единственное такое место в документе,
поэтому содержимое пишется одной строкой вывода.

Формат колонтитула регистрируется до отсева неиспользуемых шрифтов:
иначе шрифт, на который ссылается только колонтитул, выбрасывался бы
из палитры.

Стенд Колонтитулы собирается байт в байт обоими портами и добавлен
в платформенные фикстуры.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 17:07:54 +03:00
Nick ShirokovandClaude Opus 5 1e0b82d419 feat(mxl-compile,mxl-decompile,mxl-validate): группы строк и колонок
Документные ключи rowGroups и columnGroups описывают сворачиваемые
группы: диапазон, имя, признак свёрнутости и расположение заголовка.

Форма плоская, как в формате: вложенность выражена вхождением одного
диапазона в другой. Дерево было бы читаемее, но платформа хранит плоско,
и для правки чужого макета дерево дороже — чтобы добавить группу внутрь
существующей, пришлось бы искать родителя. Частичных пересечений
платформа не порождает (корпус: 40 620 886 пар непересекающихся,
599 958 вложенных, ни одного частичного), поэтому компилятор их
отвергает.

Число уровней вложенности не задаётся: оно совпало с <vgLevels> у всех
1797 макетов корпуса. Порядок записи компилятор приводит к
платформенному — родитель раньше вложенных, по возрастанию начала
(совпало у 1798 макетов из 1798).

Имя группы вывести нельзя, хотя 143 455 имён корпуса выглядят как
автоматические R<номер строки>: 16 666 из них протухли после вставки
строк, а 10 287 групп имени не имеют вовсе. Расположение заголовка взяло
ключ titleLocation — то же свойство и то же имя, что в DSL форм.

mxl-validate: проверка 14 — перевёрнутый диапазон, частичное пересечение,
начало группы за пределами документа.

Стенд Группировки собирается байт в байт обоими портами и добавлен
в платформенные фикстуры.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 15:50:18 +03:00
Nick ShirokovandClaude Opus 5 4005f36cce feat(mxl-compile,mxl-decompile,mxl-validate): примечания к ячейкам
Ключ ячейки note описывает всплывающую подсказку: строкой, объектом
языков или полной формой с оформлением, признаком авторазмера и
геометрией окошка.

Из четырнадцати тегов, которые платформа пишет в примечание, настоящей
информации несут пять. drawingType, pictureSize и id — константы на всём
корпусе. Якорь конца это координаты самой ячейки (1087 примечаний из
1087), якорь начала — 1/1 (1085 из 1087, три исключения в одном макете
берёт раундтрип-ключ anchor). Остаётся текст, стиль, autoSize и четыре
смещения, причём autoSize описывает не наличие геометрии, а пересчёт
размера: при true координаты всё равно записаны и осмысленны.

Стиль примечания — обычная запись палитры; без своего стиля пишем тот,
что даёт Конфигуратор (926 примечаний корпуса из 1087). Формат
примечания добавлен в сбор именованных стилей декомпилятора и в перечень
владельцев формата: иначе стиль подсказки вырезался как неиспользуемый и
ссылка оставалась висячей.

mxl-validate: индекс формата примечания проверяется наравне с ячейкой,
строкой и колонкой.

Стенд СПримечанием собирается байт в байт обоими портами и добавлен в
платформенные фикстуры.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 14:47:23 +03:00
Nick ShirokovandClaude Opus 5 faa8ab7659 docs(mxl-compile): выправить каскад инструкции после переименования ключа
В перечне свойств стиля осталось прежнее имя ключа: свойства значения
задаются через valueType и controlType, а не control.

Каскад навыка почищен от того, что нужно раундтрипу, а не автору: ключ
настроек элемента управления и значение "none" остались только в
спецификации, мотивировка «почему значение не приводится к объявленному
типу» заменена самим правилом. Из SKILL.md убрано число свойств стиля —
оно разъезжается с перечнем при каждом пополнении.

Зеркало reference/dsl-spec.md с этого момента не копия docs/: в
спецификации подробности, в инструкции навыка — применение.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 14:02:45 +03:00
Nick ShirokovandClaude Opus 5 8770ef940b test(mxl): покрыть настройки элемента управления кейсами и проверкой
Ключ control возился дословно, но проверялся только косвенно — через
платформенную фикстуру стенда. Теперь у него свои кейсы на обе стороны
(сборка блоба из DSL и раундтрип), строка в описании ячейки с пометкой
«раундтрип, не для ручного авторинга» и проверка в валидаторе: значение
и настройки элемента управления бывают только у ячейки-поля ввода —
на корпусе ни одного вхождения в обычной ячейке.

Проверки ячеек со значением заодно перестали зависеть от наличия таких
форматов в макете: раньше блок целиком пропускался, когда ни одного
формата с признаком значения нет, — то есть ровно в том случае, который
эта проверка и ловит.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 13:54:57 +03:00
Nick ShirokovandClaude Opus 5 43d2f660da feat(mxl-compile,mxl-decompile): значение ячейки-поля ввода и настройки элемента управления
Ключ ячейки value несёт значение, набранное в поле ввода, ключ control —
сериализованные настройки самого элемента управления. Оба живут у ячейки,
а не в палитре формата: у двух ячеек с одинаковым оформлением значения
разные.

Тип значения выражается литералом JSON и приведения к объявленному типу
НЕ делается. Так пишет платформа: ссылочный и составной тип она хранит
строкой, а при смене типа ячейки прежнее значение не переписывает — на
корпусе 560 ячеек объявлены числом, но несут строку. Приведение здесь
означало бы переписывать данные при пересборке. Дата литералом JSON не
выражается, поэтому едет строкой и читается как дата только у ячейки,
объявленной датой.

Настройки элемента управления возим дословно: 26 370 вхождений корпуса
дают всего 163 различных блоба, разбирать структуру незачем. Переводы
строк внутри блоба приводим к LF на чтении — XML-парсеры двух портов
нормализуют их по-разному, и без этого порты давали разный JSON на одном
файле.

Стенд СЗначениями снова собирается байт в байт обоими портами: фикстура
обновлена до версии со значениями, флажком и блобом настроек.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 13:32:40 +03:00
Nick ShirokovandClaude Opus 5 66bb285636 fix(mxl-compile,mxl-decompile): расшифровка ячейки без параметра заполнения
Ключ detail описывал ячейку только вместе с param, а платформа ставит
расшифровку самостоятельно: на корпусе ERP 20 404 ячейки несут её БЕЗ
параметра (12 653 пустых, 5 949 с текстом, 1 802 поля ввода) против
8 582 с параметром. Компилятор такую расшифровку молча выбрасывал,
декомпилятор молча не читал, а ячейку, где кроме расшифровки ничего нет,
считал заполнителем строки и терял целиком.

Заодно выправлен порядок тегов ячейки: расшифровка идёт ПОСЛЕ текста,
а не перед ним (корпус: f · parameter · tl|v · detailParameter).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 13:07:32 +03:00
Nick ShirokovandClaude Opus 5 d7c81505d8 feat(mxl-validate): предупреждать о флажке у типа, отличного от булева и числа
Конфигуратор предлагает флажок только для Булево и Числа, но ограничение
интерфейсное: макет с флажком у строки и у даты платформа принимает и
возвращает GUID дословно — проверено сборкой EPF и обратной выгрузкой
через базу. Поэтому предупреждение, а не ошибка компиляции: собрать
такую ячейку вручную нельзя, и почти наверняка это описка автора.

На корпусе ERP предупреждение не срабатывает ни разу: все семь флажков
стоят у булева.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 12:40:31 +03:00
Nick ShirokovandClaude Opus 5 aaeb752cf1 refactor(mxl-compile,mxl-decompile): ключ ячейки control → controlType
Тег ячейки <control> и тег формата <controlType> в выгрузке — разные
вещи: первый несёт сериализованные настройки элемента управления, второй
GUID его вида. Ключ DSL описывал второй, а назывался как первый, и при
поддержке настроек отображение стало бы перекрёстным.

Свести их в один ключ нельзя: <controlType> живёт в палитре и разделяется
ячейками с одинаковым оформлением, а настройки принадлежат конкретной
ячейке. Синоним не заводим — он занял бы ровно то имя, которое
освобождается.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 11:46:50 +03:00
Nick ShirokovandClaude Opus 5 7f743b18a9 feat(mxl-compile,mxl-decompile,mxl-validate): ячейки-поля ввода
Тип значения ячейки и элемент управления переживают цикл компиляции и
декомпиляции. Макет стенда с полями ввода собирается из своего же JSON
байт в байт.

DSL: два ключа ячейки — valueType (грамматика типа семьи: примитивы с
квалификаторами, ссылочные типы, категории целиком, составной через " + ")
и control (input/checkbox, синонимы, GUID для неизвестных элементов, none
для формата вовсе без тега). containsValue отдельным ключом не выражается:
он выводится из наличия типа. Текст и шаблон в такой ячейке запрещены,
параметр и расшифровка допустимы.

Умолчания голых типов взяты платформенные (строка без длины безлимитна,
число без параметров без ограничения разрядности), поэтому эмиттер типа
свой, а не копия meta-compile; resolve_type_str скопирован из эталона
семьи и объявлен в реестре дрейфа. Канон типа в ключе дедупликации
палитры развёрнут: иначе разные написания одного типа дали бы две
одинаковые записи формата и сдвинули бы все ссылки ячеек.

mxl-validate: проверка согласованности таких форматов — тип без признака
значения, ячейка с текстом и значением одновременно, посторонний тег
внутри типа, формат значения у строки или колонки, неизвестный GUID
элемента управления.

Попутно сведены два расхождения портов mxl-validate: py молчал там, где
ps1 писал строку об отсутствии шрифтов, и шапка подробного вывода
печаталась в разной форме.

Версии сведены: mxl-compile 1.41 в обоих портах (было 1.40/1.39).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 22:21:59 +03:00
Nick ShirokovandClaude Opus 5 83bb5b6fd3 fix(cfe-borrow): свойства и виды, от которых зависят стандартные поля
Сплошной прогон по типам объектов (11 минимальных объектных форм УТ, каждая
заимствована и загружена в UT_DEMO) дал три падения из одиннадцати. Все три —
один класс: в оболочке не хватает того, от чего зависит существование
стандартного поля, и платформа отвергает загрузку «Неверный путь к данным».

1. Справочник с владельцем: «Объект.Owner» не разрешается без <Owners>.
   Свойство — список <xr:Item>, а не скаляр, поэтому переносится фрагментом,
   как __TypeXml у DefinedType. Одного переноса мало: ссылка должна вести на
   объект, который в расширении есть, иначе платформа падает с access violation
   вместо сообщения. Добавлен общий проход, заимствующий владельцев (и владельцев
   владельцев) — Конфигуратор поступает так же, эталон Issue66Example7_1.

2. Регистр сведений: «Запись.Period» не разрешается без
   InformationRegisterPeriodicity. Вместе с ним переносится WriteMode, от
   которого зависит «Запись.Recorder» — Конфигуратор несёт оба, 6 эталонов из 6.

3. Задача: «Объект.Исполнитель» — это реквизит адресации, отдельный вид
   дочернего объекта. AddressingAttribute добавлен к видам, которые
   заимствуются поимённо, рядом с Dimension и Resource.

Правка сделана в Read-SourceObject и Build-BorrowedObjectXml — там, где оболочка
рождается: механизм $extraProps работает только в потоке -BorrowMainAttribute и
обычное заимствование оболочки не покрывает.

Проверено: те же 11 форм грузятся 11 из 11; ПВХ с путём «Объект.ValueType»
грузился и раньше — Конфигуратор Type и CharacteristicExtValues тоже не
переносит (эталон Issue66Example8), правило «свойство, включающее поле»
подтверждается с обеих сторон.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 19:43:23 +03:00
Nick ShirokovandClaude Opus 5 112166dc0c fix(cfe-borrow): стандартные поля и Items-пути в связях параметров выбора
Эталоны Issue66Example7 и 7_1 (три формы, оба режима) показали, что прошлая
правка была неполной в двух местах.

1. Путь на СТАНДАРТНОЕ поле объекта («Объект.Owner», «Объект.Date», «Объект.Ref»)
текстом не разрешается даже при заимствованном основном реквизите: платформа
отвергает загрузку «Неверный путь к данным». Мы такой путь оставляли текстом.
Конфигуратор оставляет ссылку на сам реквизит — «1». Теперь так же: реквизит
объекта (есть в ChildObjects) остаётся читаемым текстом, стандартное поле
сводится к id основного реквизита. Проверено загрузкой: было падение, стало
успешно; выход совпал с эталоном 7_1 дословно.

2. Пути «Items.<Элемент>.CurrentData.<Поле>» при заимствованном основном
реквизите разрешаются текстом — элементы формы на месте, а их данные доступны
через основной реквизит. Конфигуратор их и не трогает. Прошлая правка вырезала
их в обоих режимах, то есть теряла рабочие связи. Теперь вырезаются только без
заимствования основного реквизита, где они действительно не разрешаются.

Уточнение по кодировке для будущей работы: без заимствования Конфигуратор
кодирует стандартное поле как «1/-N» (Owner у справочника и Ref у документа
дали -5, Date у документа -3), а Items-путь — как
«<id элемента>:02023637-7868-4a5f-8576-835a76e0c9ba/0:<uuid поля>». Таблицу
отрицательных индексов по трём точкам не построить, поэтому в скелетном режиме
обе разновидности по-прежнему вырезаются с предупреждением.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 18:42:20 +03:00
Nick ShirokovandClaude Opus 5 ace9e29e91 test(cfe-borrow): платформенная верификация кейсов на связи параметров выбора
Прогон verify-snapshots вскрыл, что два новых кейса опирались на вход, который
платформа не принимает — падала конфигурация-источник, а не расширение.

form-choice-link-items: путь «Items.Ячейка.CurrentData.Склад» был невалиден,
потому что Ячейка — поле ввода, а не таблица. Кейс пересобран на настоящей
табличной части: путь корректен, связь по-прежнему вырезается, фикстура
грузится. Кейс продолжает падать на коде до правки.

form-choice-link-dangling: вход намеренно содержит путь на несуществующий
реквизит объекта — в валидной конфигурации такой формы не бывает, а проверяется
именно поведение навыка на таком пути. Помечен skipPlatformVerify с причиной.

После правок verify-snapshots --skill cfe-borrow: 21 прошло, 0 упало,
1 пропущен по объявленной причине. cfe-validate — 7 из 7.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 17:46:55 +03:00
Nick ShirokovandClaude Opus 5 6c491c4f2e fix(cfe-borrow): ссылки параметров выбора на реквизит формы — по id базовой формы
Третий и последний класс дефектов из ишьи #66, единственный, который валит
загрузку. Rewrite-ChoiceParameterLinks переписывал только пути от «Объект.»;
ссылка с корнем-реквизитом ФОРМЫ проходила текстом, а реквизиты формы не
заимствуются никогда — путь не разрешался:

  Неверный путь к полю - Помещение
  Неверный путь к полю - Склад
  Неверный путь к полю - ОтборСклад

Связь с постскриптумом репортёра прямая: в эталоне JR2976 ссылка
«ИспользоватьСоглашенияСКлиентами» — то самое имя из его сообщения —
закодирована как «51». В корпусе УТ таких форм 103, путей 424 из 1015.

Правило подтверждено на шести расширениях от Конфигуратора (Issue66Example4/5/6,
JR2433, JR2976, JR49904): подставляется id реквизита ИСХОДНОЙ формы. Именно
исходной — Issue66Example6 показывает, что даже с заимствованными реквизитами
формы (id 1000002/1000003) ссылки продолжают указывать на 4 и 5, то есть в
нумерацию базовой формы. Переписывать нужно в обоих режимах: реквизиты формы не
заимствуются ни при Form, ни при All, поэтому гейт «только без основного
реквизита» снят.

Путь «Объект.X» при заимствованном основном реквизите оставлен текстом:
он разрешается (проверено загрузкой и UpdateDBCfg) и читается. Конфигуратор
нормализует и его, но копировать обфускацию там, где она не нужна, незачем.
Зашитая единица в «1/0:<uuid>» заменена на реальный id основного реквизита
исходной формы.

Путь, который не сводится ни к одному известному виду, теперь вырезается с
предупреждением, а не остаётся текстом. Сюда попадают
«Items.<Элемент>.CurrentData.<Поле>» (303 в УТ): их кодировка непрозрачна и по
имеющимся эталонам не воспроизводима. Проверено — с таким путём форма не
грузилась вовсе; без связи заимствуется и работает. Связь параметров выбора —
удобство подбора, а не данные.

Проверено на UT_DEMO: расширение с обеими формами грузится и применяется к БД,
значения ссылок совпадают с эталонами (4, 5, 2) в обоих режимах и обоих портах.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 16:49:07 +03:00