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
Два дефекта, найденных вычиткой перед ревью.
deny-rights создавал узел объекта и, если фильтр умолчаний отбрасывал
все запреты, оставлял его пустым — в файл он попадал, когда сохранение
инициировала соседняя операция. Платформа пустых узлов не производит
(0 на 223k узлов корпуса). Теперь созданный впустую узел убирается.
Правило удаления узла было «не осталось разрешающих прав» — наследие
решения «false это шум». Замеры показали обратное: узел с одними
запретами осмыслен, так закрывают реквизит. Узел удаляется, только если
в нём не осталось прав вообще.
Там же закрыт разворот массива: `return @(...)` из функции отдаёт
единственный элемент скаляром, у которого .Count равен $null, поэтому
узел с ОДНИМ правом считался пустым и удалялся целиком. Поймал гард
минимального дифа: снятие одного права давало -11 строк вместо -4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Замер на платформе (debug/role-edit/FINDINGS.md): роль хранит только то,
что отличается от её умолчаний. При setForNewObjects=false на верхнем
уровне живут разрешения, а любой запрет выбрасывается (проверены Update,
Edit, Delete; узел из одних запретов удаляется целиком); при true —
наоборот. У реквизитных вложенных ту же роль играет
setForAttributesByDefault. Конфликт решается в пользу разрешения.
Отсюда три правки, общие для обоих навыков:
1. Прямое замыкание идёт только от РАЗРЕШЁННЫХ прав. Раньше запрет тянул
зависимости как разрешения: "Catalog.X: {Edit: false}" выдавал
Read, Update и View — навык раздавал права на основании запрета.
2. Появилось обратное замыкание: запрет уносит права, которым
запрещённое нужно. Сверено с платформой — при setForNewObjects=true
она к Update=false дописывает те же десять запретов.
3. Записи, совпавшие с умолчанием роли, не пишутся: платформа их всё
равно выбросит, а файл разошёлся бы с базой. Отброшенное
перечисляется в stderr, сообщение операции объясняет причину.
Правило применяется только там, где замерено: внешние источники данных
под него не попадают. Обе функции заведены семьями в check-inline-drift.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Симметрично role-edit: в значении пишется "@путь", текст приходит из
файла. Условия типовых занимают десятки строк с кавычками внутри, и в
JSON-строке это источник ошибок экранирования — теперь условие живёт
отдельным файлом рядом с описанием роли.
Работает в rls и в templates[].condition. Отсутствие файла — ошибка до
записи, как и прочие ошибки описания прав.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Было неинтуитивно и вдобавок молча портило файл: `@условие.txt` целиком
заменял -Value, поэтому в файле должен был лежать ещё и адрес, а
естественная форма "Catalog.Товары.Read: @условие.txt" записывала в
условие литерал "@условие.txt" — без единого предупреждения.
Теперь файл читается там, где стоит текст: условие RLS и тело шаблона —
после двоеточия, синоним и комментарий — как всё значение. Адрес
остаётся в команде. Пакет делится по ;; ДО чтения файла, поэтому ;;
внутри условия больше не разделитель — прежний костыль с отключением
пакета убран. Отсутствие файла — ошибка до записи.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Закрывает #43. Повторный role-compile перевыпускал UUID и переписывал
Rights.xml целиком, поэтому добавить права существующей роли было нечем —
оставалась ручная правка XML.
Операции: add-rights, set-rights, remove-rights, deny-rights, set-rls,
remove-rls, add/set/remove-template, modify-property, set-synonym,
set-comment. Пакет через ;;, значение из файла через @путь, отказ до
записи со всеми причинами разом, авто-вызов role-validate.
Грамматика имён и пресетов — та же, что у role-compile: копии таблиц и
валидаторов взяты побайтово, навык дописан в 16 семей реестра
check-inline-drift.
Правка держит инвариант «меняется только то, что просили»: узел встаёт на
своё место по uuid объекта, право — по канону типа, набор замыкается по
зависимостям, снятие и запрет идут каскадом в обратную сторону. Проверено
раундтрипом через платформу — выход обоих портов совпал с выгрузкой 1С
байт в байт, включая RLS с полями и двумя строками ограничений.
22 кейса на обоих портах, 17 снэпшотов приняты платформой.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Платформа при загрузке сама доводит набор до полного: выдал Edit —
получил ещё Read, Update и View, выдал View у обработки — получил Use.
Навык писал ровно заданное, поэтому файл роли и база расходились сразу
после первой загрузки.
Таблица снята с платформы: по одной роли на каждое право, 209 + 43
роли в двух прогонах, ни одного отвергнутого (debug/role-edit).
Зависимости оказались общими для типов, исключений четыре — обработка и
отчёт держатся на Use, план счетов не тянет Read под историю данных,
регистр сведений — короче на одно звено. Права, недопустимые для типа,
отсекаются, у вложенных объектов зависимостей нет.
Права конфигурации версионные: до формата 2.19 платформа взводила весь
блок режимов окна с любым правом, с 2.19 (8.3.26) перестала. Граница
измерена на шести платформах, 8.3.20 … 8.5.1.
Дописанное перечисляется в выводе — права выдаются не молча. Проверка:
выход обоих портов после загрузки и выгрузки совпал байт в байт.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Платформа нормализует и то и другое: права внутри <object> идут в
фиксированном для типа порядке, сами <object> — по uuid объекта
метаданных. Навык писал в порядке ввода, поэтому первая же выгрузка из
Конфигуратора давала диф, которого никто не делал.
Замеры (debug/role-edit/FINDINGS.md): канон прав снят с платформы и
сошёлся с топологической сортировкой по корпусу (~290k узлов <right>);
порядок узлов по uuid подтверждён на 363 ролях ACC из 363 и отдельно на
44 ролях с вложенными объектами, где ключ — uuid самого реквизита.
Порядок дерева конфигурации тут ни при чём — прямое сравнение
с ChildObjects даёт скачущие позиции. Проверка: выход обоих портов
совпал с выгрузкой 1С байт в байт.
Сортировка узлов строго ordinal: Sort-Object сравнивает по культуре и
игнорирует дефис, из-за чего порядок разошёлся бы и с платформой, и с
py-портом.
Объект, которого нет в выгрузке (uuid неизвестен), уходит в конец с
предупреждением в stderr — платформа переставит его сама.
Кейсы с несколькими объектами переведены с preRun на фикстуры: uuid,
который meta-compile генерирует заново на каждом прогоне, сделал бы
порядок узлов плавающим. Фикстуры собраны теми же навыками. Двум
фикстурам добавлен документ-регистратор: регистр расчёта без него
платформа не принимает, и verify-snapshots это поймал.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Замер: 27 объектов x 68 имён прав = 1836 ролей по одному праву, одна
загрузка и одна выгрузка. Оракул точный — неприменимое право платформа
молча выбрасывает, роль остаётся пустой и в логе ни строчки.
Лишнего в таблице не было, не хватало:
BusinessProcess (14 -> 24), Task (14 -> 24),
ChartOfCalculationTypes (15 -> 25) — InteractiveDeleteMarked и весь
блок *DataHistory*;
ChartOfAccounts (21 -> 25) — InteractiveDeleteMarked, ViewDataHistory,
EditDataHistoryVersionComment, SwitchToDataHistoryVersion;
CalculationRegister (2 -> 4) — Update, Edit.
До этого навык отвергал корректные права, а dsl-reference утверждал,
что InteractiveDeleteMarked у плана счетов не бывает.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Ревью собственной работы против плана нашло три упущения.
1. Синоним `decimal(p,s)` был в плане, но в код не попал — после включения отказа компилятора
стандартный SQL-тип стал отвергаться. Добавлен в оба навыка и покрыт кейсом.
2. Тип Null в таблице примитивных типов не описан. Голого синонима у него нет и не будет:
в DSL он задаётся как `v8:Null`. Заодно сказано прямым текстом, что имя С префиксом проходит
как есть — так задаются типы без синонима (`v8:ValueTable`, `ent:AccountType`, `v8ui:Color`).
3. Раздел 9.1 в 1c-form-spec помечен как общий справочник типов, а не «про формы»: там самые
полные перечни v8/v8ui/dcs/ent, и они относятся к любому объекту метаданных.
Проверено: meta-compile 94/94 и meta-edit 32/32 в обоих рантаймах, 11 гардов, снэпшот
sql-type-synonyms с decimal принят платформой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Неизвестное имя типа проходило насквозь молча: meta-compile писал его в XML дословно
(<v8:Type>varchar(150)</v8:Type>), meta-validate не смотрел на скалярные типы вовсе — проверка 16
разбирает только ссылочные с известным префиксом. Отказ приходил лишь от платформы при загрузке
всей конфигурации, без указания объекта и реквизита. Так вело себя и имя типа СУБД, и опечатка.
meta-validate, проверка 22 — два уровня, потому что «невалидно» и «нам незнакомо» разные вещи:
Уровень 1, грамматика (ERROR). Содержимое <v8:Type> всегда несёт префикс пространства имён;
голое имя платформа не примет никогда, независимо от её версии и состава конфигурации.
Сюда же неверная форма ссылочного типа.
Уровень 2, словарь по контексту владельца (WARN). У хранимого объекта набор типов у́же, чем
у обработки или отчёта, где доступны ТаблицаЗначений, ОписаниеТипов, Картинка. Замер по корпусу
подтверждает разделение: v8:ValueTable/ValueTree/StandardPeriod в объектных файлах acc и erp
встречаются только у обработок и отчётов, у хранимых — ноль.
Оставлено предупреждением намеренно: словарь — та часть, которая расширяется с версиями платформы,
и цена ложной ошибки выше пользы. Жёстко только там, где решает грамматика.
meta-compile отвергает голое неизвестное имя с перечнем допустимых форм. Имя с готовым префиксом
(v8:ValueTable, ent:AccountType, v8ui:Color) по-прежнему проходит как есть — это законный ввод,
и правильность имени судит валидатор, которому виден весь файл.
check-type-synonyms.mjs — новый гард: по общему ключу meta-edit обязан понимать то же, что
meta-compile (авторитет). Отсутствие ключа допустимо, разное значение — нет.
Проверено:
- корпус, 23430 объектов четырёх выгрузок: ноль ошибок и ноль предупреждений проверки 22;
на реальном справочнике разбирается 88 имён типов, то есть проверка работает, а не молчит;
- раундтрип A, 13788 объектов: побайтово как эталон, compile-fail 0 — отказ компилятора
не задел ни одного законного типа;
- meta-compile 94/94 и meta-validate 48/48 в обоих рантаймах, 11 гардов;
- снэпшот type-with-prefix-passthrough принят платформой 8.3.24.1691.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Модель, проектирующая внешний источник по схеме БД, естественно пишет типы так, как они названы
в information_schema. До сих пор такое имя молча уходило в XML дословно (<v8:Type>integer</v8:Type>),
валидатор ничего не замечал, и падало это только на загрузке в базу.
Добавлены однозначные соответствия, замеренные на стенде PostgreSQL — то же самое даёт
Конфигуратор при импорте структуры таблицы:
integer/int/int4 → Number(10,0) varchar(n)/character varying(n) → String(n)
bigint/int8 → Number(19,0) numeric(p,s) → Number(p,s)
smallint/int2 → Number(5,0) timestamp → DateTime · bytea → BinaryData
boolean НЕ включён намеренно: в DSL это уже Булево, а psqlODBC при импорте отдаёт Строка(5) —
молча выбрать один из двух смыслов нельзя. text/real/money/json не включены: замера нет.
Словарь типов meta-edit был беднее meta-compile на 25 записей (не было Time, BinaryData, UUID,
коллекций, ссылки на таблицу внешнего источника) — дополнен до набора авторитета. Конфликтов
значений не было ни одного.
Проверено: наборы обоих навыков зелёные в двух рантаймах, снэпшот sql-type-synonyms принят
платформой 8.3.24.1691, PS и PY дают идентичный XML.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Раундтрип B на стенде вскрыл пробел: у таблицы внешнего источника декомпилятор не снимал
DefaultObjectForm/DefaultRecordForm/DefaultListForm/DefaultChoiceForm, хотя DSL их поддерживает,
компилятор эмитит, а у прочих объектов они снимаются. Пересборка стенда молча теряла назначенную
форму списка.
Сами формы вне скоупа раундтрипа (это отдельные файлы, их делает навык form-add), поэтому
собранная из такого DSL конфигурация не загрузится — ровно как и у справочника с формой.
Договорённость одна и та же, записана в docs/meta-dsl-spec.md.
Раундтрип B после правки: из выгрузки стенда → DSL → meta-compile → загрузка в базу →
выгрузка платформой = побайтово то же, что собрал компилятор. Против исходной выгрузки стенда
остаются два известных расхождения: UnfilledParentValue (загрузкой XML не задаётся, замерено
тремя опытами) и регистрация <Form> (вне скоупа).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Get-ChildIndent брал отступ из первого пробельного узла контейнера. В контейнере с детьми это
отступ ПЕРЕД первым ребёнком — верно; в пустом (только что раскрытом) единственный пробельный
узел — отступ ЗАКРЫВАЮЩЕГО тега, то есть уровень самого контейнера. Первый добавленный реквизит,
команда или таблица получали отступ <ChildObjects>, а не на табуляцию глубже. То же во вложенном
ChildObjects табличной части.
Платформа такой файл принимает и при выгрузке нормализует, поэтому дефект косметический —
но он задевал любой объект, а сравнивать наш вывод с выгрузкой становилось неудобно.
Эталоны четырёх кейсов пересняты: git diff -w пуст, меняются только отступы. Все 23 снэпшота
meta-edit, доезжающие до платформы, приняты 8.3.24.1691.
Кейс eds-add-table-twice объявил skipPlatformVerify: он нарочно удаляет файл таблицы, оставляя
висячую регистрацию, — платформа отказывает по условию кейса, а не из-за дефекта навыка.
Радиус проверен: та же эвристика есть в cf-edit, form-edit, interface-edit и cfe-borrow (семья
get_child_indent из списка долга check-inline-drift, не сведена). У form-edit пустой контейнер
обрабатывается верно — проверено добавлением элемента в форму с <ChildItems/>; у cf-edit и
cfe-borrow ветка недостижима: в ChildObjects конфигурации всегда есть <Language>.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Вчерашняя правка исходила из того, что xs:base64Binary без BinaryDataQualifiers — вторая
форма ХранилищаЗначения. Спросили платформу: конфигурация с таким узлом загружается и
выгружается обратно как xs:base64Binary с Length 0 / AllowedLength Variable, то есть как
БЕЗЛИМИТНЫЕ двоичные данные. ХранилищеЗначения — отдельный узел v8:ValueStorage.
- meta-info: xs:base64Binary снова всегда ДвоичныеДанные (правка отменена, замер записан
в комментарий).
- meta-decompile: узел без квалификаторов теперь возвращается как BinaryData(0), а не
ValueStorage — раньше раундтрип молча менял тип реквизита. Проверено: пересборка из
полученного DSL даёт байт в байт то же, что выгрузила платформа.
- Кейс и фикстура переименованы по факту и стали полной конфигурацией: теперь кейс доезжает
до платформы в verify-snapshots (раньше падал с «конфигурации для загрузки нет»).
check-uuid-invariant: добавлен сценарий внешнего источника — правки идут в два файла, и
пересборка файла источника дала бы ему новый uuid, осиротив существующие таблицы. Проверка
не холостая: намеренная порча uuid источника гард роняет.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
form-add отказывал видом объекта 'Table', хотя платформа такие формы делает, а в DSL
таблицы есть слоты defaultObjectForm/defaultRecordForm/defaultListForm/defaultChoiceForm.
Инструкция при этом обещала обратное — строка исправлена.
Таблица — единственный вид, чьи ссылки трёхчастные: слот
ExternalDataSource.<И>.Table.<Т>.Form.<Ф>, главный реквизит ExternalDataSourceTableObject.<И>.<Т>,
MainTable динамического списка ExternalDataSource.<И>.Table.<Т>. Имя источника в файле таблицы
не хранится — берётся из пути ExternalDataSources/<И>/Tables/<Т>.xml, единственное место,
где навык смотрит на путь.
Набор назначений зависит от вида данных таблицы — замерено загрузками на 8.3.24.1691:
ObjectData даёт Object/List/Choice, NonobjectData — Record/List/Choice. Неверная пара
схемой формы не отвергается: она валит загрузку ВСЕЙ конфигурации «Исключением XDTO при
чтении файла» без указания причины, поэтому form-add проверяет её сам и объясняет, а без
-Purpose у таблицы с составным ключом основной становится форма записи.
form-validate: типы ExternalDataSourceTable* добавлены в список известных cfg-префиксов —
иначе корректная форма получала предупреждение «unrecognized cfg prefix».
Проверено: form-add и form-validate зелёные в обоих рантаймах, гарды прошли (в том числе
check-form-purposes: спека и оба порта сходятся), снэпшот eds-table-forms принят платформой,
формы всех четырёх назначений загружены в базу и выгружены обратно со ссылками без потерь.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Мелкие находки ревью, по одному кейсу на каждую:
- meta-compile: BinaryData(N[,fixed|variable]) — фиксированная длина больше не теряется;
голая форма по-прежнему 4294967292/Fixed, как её пишет платформа при импорте из СУБД.
Посторонний текст в скобках (BinaryData(abc)) отвергается, а не даёт тихий безлимит —
PS принимал его и молча подставлял дефолт, py такой тип не понимал вовсе.
- meta-decompile: сворачивает в голый BinaryData только точное совпадение с этим дефолтом.
- meta-info: xs:base64Binary без квалификаторов — вторая форма ХранилищаЗначения, а не
двоичные данные; раньше два навыка читали один и тот же узел по-разному.
- meta-remove (py): в ветке «файлов нет» корень реестра был жёстко Configuration, поэтому
осиротевшая таблица внешнего источника не находилась и навык падал, тогда как PS её
разрегистрировал. Сообщение в обоих портах теперь называет реальный реестр.
Проверено: наборы meta-compile/decompile/info/remove/edit/validate зелёные в обоих
рантаймах, гарды прошли, снэпшот eds-binary-data принят платформой 8.3.24.1691.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Навык регистрировал форму, макет и команду одинаково — узлом с uuid и <Properties>.
Платформа так пишет только команду: форма и макет регистрируются голым текстом
(<Form>Имя</Form>) и требуют собственных файлов. В корпусе acc_8.3.24 ни одного
<Form uuid и <Template uuid, при 81 голом <Template> и 46 <Command uuid.
Подменённая конфигурация валила загрузку с уходом 1cv8 в бесконечное выделение памяти.
Форму и макет добавляет form-add / template-add (они делают и файл, и запись),
удаляет form-remove / template-remove — meta-edit теперь отсылает к ним вместо
тихой порчи. Команда осталась за meta-edit: дописаны CommandParameterType,
ParameterUseMode, ModifiesData и OnMainServerUnavalableBehavior, плюс заготовка
Commands/<Имя>/Ext/CommandModule.bsl (в корпусе модуль есть у всех 97 команд).
Заодно: Table в childOrder, и пустой ChildObjects в PS схлопывается в
самозакрывающийся, как его пишет платформа (722 из 722 в корпусе) и py-порт.
Проверено платформой 8.3.24.1691: загрузка и обновление успешны, обратная
выгрузка даёт узел команды и модуль байт в байт (отличие только в отступе).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Get-AllChildNames собирал имена только из Properties/Name и пропускал детей, которые
регистрируются голым текстом: <Table>Имя</Table>, <Form>Имя</Form>, <Template>Имя</Template>.
Из-за этого проверка на дубликат была мёртвой: после удаления файла таблицы повторное
добавление отвечало «Added: 1» и клало в ChildObjects второй такой же <Table>.
Теперь имя берётся из текста узла, если у ребёнка нет Properties. Кейс воспроизводит
исходный сценарий: добавить таблицу, удалить её файл, добавить снова — второй записи
быть не должно.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AtG4qGvZocJSWLAsbHpBCQ
Скопированные из meta-compile Emit-FormRef и Emit-Characteristics тянули за собой хвост
хелперов (Normalize-FormRef, Normalize-CharFrom, Expand-CharField, Get-CharIntField), которых
в meta-edit нет. Добавление таблицы с ключом defaultListForm или characteristics давало в PS
CommandNotFoundException при коде возврата 0: навык отчитывался успехом, а файл таблицы
не создавался и в ChildObjects ничего не появлялось. В python то же место падало трейсбеком —
порты расходились ещё и потоком ошибки.
Причина не в копировании как таковом: копия обязана быть не только идентичной, но и
самодостаточной. Форма функции для этого не годилась, поэтому изменена в эталоне и
перекопирована — тем же приёмом, что уже применён в этой семье дважды: Emit-EdsTableProperties
принимает готовые блоки <Characteristics> и четырёх слотов <Default*Form>, рендерит их
вызывающий навык своим эмиттером. Зависимостей у тела не осталось.
meta-edit передаёт пустые блоки и отвергает ключи characteristics и default*Form с объяснением,
куда идти: форму назначает form-add, характеристики — meta-compile.
Попутно исправлено то, что вскрылось при разборе: короткое имя формы в defaultListForm
эмитилось как есть, а платформа отвечает «Неизвестный объект метаданных». Теперь оно
разворачивается в полный путь ExternalDataSource.И.Table.Т.Form.Ф — как и ссылки на поля.
check-inline-drift получил проверку РАЗРЕШИМОСТИ: всё, что зовёт скопированное тело, обязано
быть определено в том же навыке. Критерий «своей» функции — имя, определённое в навыке-эталоне,
поэтому список командлетов-исключений не нужен. Проверено обратным экспериментом: гард ловит
исходный дефект с прямой формулировкой «копия неразрешима».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Свойства таблицы ссылаются на её же поля шестичастным путём
(ExternalDataSource.И.Table.Т.Field.П). Опечатка в имени поля даёт «Неизвестный объект
метаданных» при загрузке, а глазами в таком пути её не видно. Проверка 21 разбирает форму
пути, требует, чтобы таблица в ссылке была той же самой, и чтобы поле существовало —
в сообщении перечисляются имеющиеся поля.
Отдельно: таблица без ключевых полей даёт предупреждение, а не ошибку. Загрузку XML такой
таблицы платформа принимает молча (измерено на 8.3.24.1691), Конфигуратор интерактивно
требует ключ, а рабочие конфигурации без ключей существуют — значит это повод предупредить,
а не отвергнуть.
Закрыты последние пункты плана: раздел про внешние источники в docs/meta-dsl-spec.md
(формой и связями с общими конвенциями, без дублирования таблицы свойств — она живёт
в reference навыка) и кейсы meta-validate, включая негативный.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Декомпилятор собирает DSL источника целиком: читает файл источника, подтягивает файлы таблиц
из <Источник>/Tables/ и складывает поля, ключи, ссылки и функции обратно в тот же синтаксис,
который принимает meta-compile. Ссылки на поля возвращаются короткими именами, таблица без
собственных свойств — коротким массивом полей, функция с типом по умолчанию — одной строкой.
Это замыкает дешёвый контур проверки: XML → декомпиляция → компиляция → сравнение с исходником,
без 1С и без Docker. На нём и проверено: наша выгрузка, выгрузка платформы и четыре таблицы
внешних источников из чужого рабочего проекта возвращаются байт в байт. У выгрузки платформы
остаются два известных расхождения: значение незаполненного родителя (платформа сама сбрасывает
его при любой загрузке) и формы (декомпилятор их не захватывает — так задумано).
Побочно закрыт дефект, к внешним источникам не относящийся: xs:base64Binary разбирался как
ХранилищеЗначения, хотя это ДвоичныеДанные. Различать их можно по квалификаторам —
у ХранилищеЗначения платформа пишет v8:ValueStorage, а у двоичных данных есть
BinaryDataQualifiers. Компилятор научен типу BinaryData (и BinaryData(N)) — до этого он
принимал такое имя, но эмитил его как есть, то есть невалидный XML.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Дыра в сценарии: добавить таблицу или функцию к уже существующему источнику было нечем.
Совет «пересоберите источник целиком через meta-compile» оказался вредным — повторная
компиляция заменяет файл источника, выдаёт объекту НОВЫЙ uuid и оставляет файлы не
упомянутых таблиц сиротами: в ChildObjects их уже нет, а на диске они остались.
meta-edit на файле источника принимает add.tables и add.functions. Таблица — единственная
операция навыка, создающая файл: <Источник>/Tables/<Имя>.xml плюс имя в ChildObjects.
Синтаксис тот же, что у meta-compile.
Формат файла таблицы обязан быть один и тот же, кем бы файл ни был создан, поэтому эмиттеры
скопированы из meta-compile механически и зарегистрированы в check-inline-drift.mjs — гард
теперь сверяет семь функций между двумя навыками. Чтобы копии не тянули за собой пол-navыка,
две из них развязаны от эмиттеров-специфик: Build-EdsTableXml принимает готовый XML полей,
Emit-EdsFunction — готовый XML типа. Каждый навык рендерит их своим эмиттером, вывод не изменился.
meta-compile теперь предупреждает о перезаписи существующего объекта — для всех видов, не только
внешних источников: молчаливая замена uuid ломает ссылки, а узнать об этом было неоткуда.
Раундтрип на платформе 8.3.24.1691: таблица, добавленная через meta-edit, возвращается из
выгрузки байт в байт.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
meta-edit добавляет поля в таблицу внешнего источника: тот же парсер реквизита, свой тег <Field>,
свой контекст (без индексов и полнотекстового поиска — чужой таблицей 1С не владеет) и три своих
свойства в хвосте. Сам источник точечно не правится: и таблица (отдельный файл), и функция (узел
с полным набором свойств) требуют эмиттера, который живёт в meta-compile, — дублировать его здесь
значило бы завести вторую реализацию одного и того же.
Заодно закрыт тихий отказ, существовавший независимо от внешних источников: проверка допустимых
детей смотрела на истинность списка, а не на наличие ключа, поэтому для вида с пустым списком
трактовалась как «ограничений нет» и чужой ребёнок молча записывался в объект. Теперь add-attribute
на таблице внешнего источника отвергается с предупреждением, а не пишет <Attribute> вместо <Field>.
meta-remove понимает две формы: ExternalDataSource.PG — источник целиком, и четырёхчастную
ExternalDataSource.PG.Table.products — одну таблицу. Таблица числится в ChildObjects файла
источника, а не конфигурации, поэтому дерегистрация разведена переменной реестра; поиск ссылок
дополнен путями вида ExternalDataSource.И.Table.Т и ExternalDataSourceTableRef.И.Т.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
На источнике и его таблице навык печатал только заголовок и строку поддержки: вида он не
знал, а универсальный блок «Реквизиты» ищет <Attribute>, тогда как у таблицы поля — <Field>.
Источник показывает режим блокировки, список таблиц и функции с выражением и типом результата.
Таблица — вид (таблица/выражение), имя или выражение в источнике, объектность, признак «только
чтение», ссылки на поля (ключ, представление, родитель, версия данных, ввод по строке) короткими
именами, и список полей. У поля отмечается имя колонки в источнике, если оно отличается от имени
поля, а также «только чтение» и допустимость NULL.
Заодно два типа перестали печататься сырыми: xs:base64Binary → ДвоичныеДанные (касается всех
объектов, не только внешних источников) и ExternalDataSourceTableRef → русское имя, как у прочих
ссылочных типов.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Вид ExternalDataSource навыки meta-* не поддерживали вовсе: в корпусе выгрузок его нет
ни одного, формат был неизвестен. Разведан на стенде (PostgreSQL в Docker + база 8.3.24)
и по реальной выгрузке рабочего проекта.
meta-compile собирает источник целиком одним JSON: сам источник, его таблицы с полями
и функции. Единственный вид, у которого объект складывается более чем из одного XML —
файл источника плюс по файлу на таблицу в <Источник>/Tables/.
DSL без новых конвенций: tables — dict имя → массив полей ЛИБО объект (как tabularSections),
поле — обычный реквизит плюс три своих ключа (nameInDataSource/readOnly/allowNull),
ссылки на поля короткими именами (как inputByString у справочника), functions — dict
имя → строка ИЛИ объект (как urlTemplates). Параметры функций не объекты метаданных:
они живут в самом выражении как &1, &2.
Инструкция под каскадом: в SKILL.md строка индекса, вся специфика — в
reference/external-data-source.md.
meta-validate принимает ExternalDataSource и Table, знает их наборы GeneratedType,
состав ChildObjects и шестичастную ссылку на форму таблицы. Проверено на выгрузке
платформы и на чужой рабочей выгрузке из другого проекта — обе проходят чисто.
Раундтрип на платформе 8.3.24.1691: наш эмит → загрузка → выгрузка даёт файлы,
идентичные поданным (с точностью до uuid). Два поведения платформы пришлось воспроизвести
явно, оба измерены: ввод по строке выводится из поля представления, а заданное значение
незаполненного родителя платформа при загрузке XML сбрасывает в пустую строку — задать
его можно только интерактивно.
Спека: раздел «Внешние источники данных» в 1c-config-objects-spec.md, формат ссылок,
наборы GeneratedType, строка в индексе спек.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Определение {"type": "Constant", "name": "X"} эмитило <v8:Type>Constant</v8:Type>:
в тип значения попадал ВИД объекта. Платформа отвергала всю конфигурацию целиком —
«Неизвестное имя типа - Constant», — то есть один минимальный объект ронял загрузку.
Причина: Emit-ConstantProperties передавал корневое определение в общий Build-TypeStr,
а тот читает valueType, иначе type. Для реквизита это верно, для корневого определения
нет: там type — вид объекта. Добавлен флаг ValueTypeOnly, которым корневые эмиттеры
отключают подхват; заодно убрано мёртвое условие в typeEmpty, пришедшее туда из
контекста реквизита.
Поведение теперь совпадает с документированным (reference/simple.md): Строка(10).
Проверено платформой 8.3.24.1691.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Относительный путь при копировании содержимого объекта в конфигурацию-заглушку
считался через Substring по длине переданной строки. Каталог может прийти с
коротким именем (C:\Users\NSHIRO~1\...), а Get-ChildItem отдаёт полное — разница
в один символ, и файлы уезжали в <Объект>\а\Ext\ObjectModule.bsl. Проверка при
этом молча шла по объекту без модулей и форм и всегда была зелёной. Длина
берётся у разрешённого пути; py-порт иммунен (os.path.relpath).
Дефект нашли новые кейсы stub-db-create — конвертация внешней обработки в объект
конфигурации: корневой тег и порождаемые типы, перевыпуск GUID, копирование .bsl
байт в байт. Каждый сверен возвратом дефекта в обоих портах.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MQkqXxErBepYmoUycfkbn
Сборка .epf/.erf не компилирует модули, поэтому сломанный BSL уезжал
пользователю и падал при открытии обработки. Прямой команды «проверь
внешнюю обработку» у платформы нет, но объект конфигурации она проверяет:
обработка кладётся в конфигурацию временной базы (stub-db-create
-EmbedSourceFile) и спрашивается /CheckConfig. При находках сборка
отменяется, в выводе — сообщение платформы и путь к файлу исходника.
Ключи -Checks и -Context; выключение -Checks off или externalCheck: false
в .v8-project.json. С выключенной проверкой поведение прежнее.
У копии объекта перевыпускаются все GUID: с идентификаторами исходника
платформа путает копию в базе с загружаемой внешней обработкой и через раз
отвечает «Исключение XDTO при чтении файла» на исправном исходнике.
Ручная матрица (база есть/нет × ps1/py × исправный/битый × с модулями/без)
вскрыла ещё три вещи: отказ платформы принять исходники при подготовке
проверки выдавался за сбой базы; py-порт стаба терял причину отказа
LoadConfigFromFiles (не было /Out, в PS он есть); копии общих утилит в
stub-db-create.py разошлись с эталоном db-create — у run_v8 не было
POSIX-ветки, из-за чего на darwin/Linux временная база с пробелом в пути
молча не находилась бы.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MQkqXxErBepYmoUycfkbn
Платформа отчитывается успехом и о расширении, которое не применит: отказ
всплывает лениво, при первом вызове метода, записью в журнал регистрации.
Теперь db-load-xml, db-load-git, db-load-cf и db-update после успешной операции
с расширением спрашивают платформу явно и печатают предупреждение; код возврата
операции не меняется — применение неприменимого расширения не разрушительно,
платформа просто работает по оригиналу. Поднять код возврата может -StrictLog,
он для регрессов и в инструкциях не значится.
Проверка обязана быть ОТДЕЛЬНЫМ запуском платформы: в одной командной строке
DESIGNER выполняет только последнюю пакетную команду, и дописанная проверка
отменила бы саму загрузку (замер: /LoadConfigFromFiles + /CheckCanApply… → код 0,
пустой лог, расширение в базе не изменилось). Об этом сказано в теле функции,
чтобы «оптимизация» не вернула команды в одну строку.
Выключатель: ключ -NoApplyCheck и настройка проекта extensionApplyCheck (ключ
команды сильнее). В ветке ibcmd проверка идёт соседним 1cv8; если его рядом нет —
одна строка [note], а не тишина.
Общий блок (Invoke-ApplyCheck / Invoke-ApplyCheckReport / Get-ApplyCheckEnabled и
их py-двойники) объявлен семьёй в check-inline-drift.mjs: разъехавшиеся копии
означали бы, что один навык предупреждает, а соседний по той же операции молчит.
db-load-cf получил недостающие копии Find-V8Project и ключ -StrictLog.
Раннер: у фейковой платформы появился отдельный ответ на проверку (spec.check),
выбираемый по составу аргументов, — иначе сценарий «загрузка прошла, проверка
провалилась» невыразим.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
-AdditionalV8Arguments пропускал чужие пакетные команды платформы, а в одной
командной строке DESIGNER выполняет только ПОСЛЕДНЮЮ из них, остальные молча
отбрасывает. Проверено на 8.3.24: db-load-xml с /CheckCanApplyConfigurationExtensions
в дополнительных аргументах печатал «Load completed successfully», возвращал 0 —
и не загружал ничего, что подтвердилось выгрузкой расширения из базы.
Assert-ExtraArgs отбивал только ключи, которыми навык владеет сам (V8OwnedKeys);
рядом заведён V8BatchKeys — известные пакетные команды (/CheckConfig,
/CheckModules, /CheckCanApplyConfigurationExtensions, /DumpDBCfgList, /DeleteCfg,
/UpdateCfg, /CompareCfg, /MergeCfg, /ManageCfgSupport, /RollbackCfg,
/ConvertFiles) — со своим сообщением: такая команда подменила бы операцию навыка.
Обычные опции (/UseHwLicenses+ и прочие) проходят как раньше.
Правка внесена во все 15 копий общего блока в обоих портах; тела Assert-ExtraArgs
остались побайтно одинаковыми (у стаба epf-build сохранён его поток stderr).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Документированная форма -Dynamic <+/-> работала наполовину: `-Dynamic "+"` через
powershell.exe -File связывается, а `-Dynamic "-"` парсер не связывает вовсе —
процесс выходит с кодом 2, не напечатав ни строки, и до скрипта управление не
доходит. То есть «отключить динамическое обновление» из навыка было недостижимо,
и притом молча. Склеенная форма из примера SKILL.md (-Dynamic+) — тоже ошибка
разбора. В .py-порте все формы связывались, так что расхождение было ещё и между
портами.
Каноническая форма теперь словесная: -Dynamic on / off. "+"/"-" и yes/no
принимаются для совместимости, но в инструкции не значатся. Значение
нормализуется сразу после разбора, обе ветки (1cv8 -Dynamic+/-, ibcmd
--dynamic=auto/disable) получают прежний вход.
Кейсы dynamic-on / dynamic-off фиксируют, что именно уходит платформе; на прежнем
скрипте dynamic-off падает.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Расширение можно было положить в базу, но нельзя было посмотреть, что там лежит,
в каком оно состоянии, применяется ли, и убрать лишнее — всё это делалось руками
через 1cv8 и ibcmd.
Четыре команды: list (состав и свойства подключения), check (применимость и
синтаксический контроль), set-properties (безопасный режим, активность, защита от
опасных действий, область действия, профиль, РИБ), delete.
Конструкция опирается на замеры платформы (8.3.24.1691 и 8.3.27.1859):
- /CheckModules не нужен: /CheckConfig с теми же контекстными флагами даёт
дословно тот же вывод и код возврата, но умеет вдобавок конфигурационные
проверки. Одна команда платформы вместо двух, при запросе modules+config —
один запуск;
- обе проверки БЕЗ флагов контекста рапортуют «ошибок не обнаружено» с кодом 0
на заведомо сломанном модуле, поэтому набор контекстов всегда явный;
- применимость и синтаксис друг друга не заменяют (первая слепа к синтаксису,
второй — к дрейфу контроля), отсюда умолчание apply,modules;
- коды возврата разные: применимость 1, /CheckConfig 101;
- /DeleteCfg -Extension "" возвращает 0, рапортует успех и удаляет ПЕРВОЕ
расширение из списка, поэтому пустое имя отбивается до вызова платформы, а
отсутствие имени никогда не значит «все»;
- список и удаление делает Конфигуратор (работает всегда и на серверной базе),
свойства — ibcmd, которого в установке платформы может не быть: тогда колонки
помечены прочерком с названной причиной, а set-properties отказывает внятно.
Значения флагов словесные (on/off), а не +/-: значение "-" через powershell.exe
-File парсер съедает молча — проверено, у соседнего db-update -Dynamic "-" по
этой причине не работает вовсе.
delete и set-properties проверяют результат перечитыванием состояния, а не
кодом возврата платформы.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
-Check сравнивал только тело и сигнатуру не смотрел вовсе. Если поставщик добавил
методу параметр, а тело не изменил (новый параметр часто ещё не используется),
платформа перехватчик отвергает — «Список параметров метода "X" не соответствует
методу "Y"» — а -Check говорил АКТУАЛЕН, и починка не запускалась.
Замер на стенде (8.3.24, /CheckCanApplyConfigurationExtensions + рантайм): платформа
сверяет только ЧИСЛО параметров. Имена, значения по умолчанию и лишний Экспорт не
сравнивает; дефолты копии вдобавок не действуют — берутся из оригинала. Поэтому
сверяем количество и ничего сверх того, иначе получили бы ложный дрейф там, где
платформа молчит.
Число параметров считается существующим Split-TopLevel по ParamsText обеих сторон;
при расхождении метод идёт обычной веткой дрейфа со статусом ДРЕЙФ и причиной
«список параметров: в оригинале N, в перехватчике M». -Actualize починку уже умел:
он собирает строку сигнатуры заново из оригинала — не запускался только потому, что
-Check не звал.
Кейсы: расхождение числа (ДРЕЙФ), переименованные параметры с другим дефолтом
(АКТУАЛЕН, как у платформы) и -Actualize, переписывающий список параметров.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
-Check считал дрейфом расхождение по пустым строкам (шумный ложный ДРЕЙФ, из-за
которого -Actualize предлагал косметическую перезапись тела) и НЕ считал дрейфом
расхождение по пробелам между токенами (тихий ложный АКТУАЛЕН — платформа такой
метод молча не применяет, он рвётся при первом вызове).
Правило платформы измерено на стенде: 27 вариантов косметики, платформы 8.3.24 и
8.3.27, общий модуль и модуль объекта, два независимых оракула
(/CheckCanApplyConfigurationExtensions и рантайм-вызов метода) — результат везде
одинаковый. Каждая строка Trim, строки, пустые после Trim, из сравнения выброшены,
всё остальное байт в байт и с учётом регистра: значимы пробелы между токенами,
пробелы внутри строкового литерала, комментарии целиком и регистр.
Вердикт АКТУАЛЕН считается по этому правилу (Get-ControlKey / control_key); в PS
сравнение ordinal, иначе -eq игнорирует регистр и порт расходится с Python.
Эвристики слияния (якоря, поглощение, дифф) остались на прежнем мягком normalize.
Кейсы: допуски (пустые строки, отступ, хвостовые пробелы) и два негатива —
пробелы между токенами и регистр идентификатора.
Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Настройка newObjectPosition не чинит накопленное, а собранная навыками конфигурация
могла держать группы видов не в каноне — первая же выгрузка платформы давала диф.
Теперь вызов без значения приводит в порядок и сами группы. Вызов с явным видом этого
не делает: назвали вид — операция остаётся точечной и трогает только имена внутри него.
Приём тот же, что уже работал для имён: переставляется содержимое существующих узлов, а
не сами узлы, поэтому отступы и структура файла не меняются, а диф остаётся перестановкой
строк. Отличие в том, что меняется и имя тега: в py это присваивание el.tag, в PS имя
элемента неизменяемо, поэтому узел заменяется через ReplaceChild — он сохраняет
окружающие пробельные узлы на месте (тот же приём, что в meta-edit.ps1).
Проверки: порты дают байт-в-байт одинаковый файл; повторный прогон пишет Modified: 0 и
не меняет ни байта; на боевой выгрузке УТ (13 979 строк) команда сообщает Modified: 0 —
там уже канон, и Bot остался между DefinedType и CommonCommand. Раундтрип через
платформу 8.3.24: отсортированная конфигурация вернулась побайтово той же, то есть наш
канон совпадает с платформенным и сортировка не создаёт работы следующей выгрузке.
Правило видов с осмысленным порядком продолжает действовать для ИМЁН: в раундтрипе языки
остались как были. Положение самой группы оно не регулирует — это порядок платформы.
Три прежних кейса фиксировали поведение «группы не трогаем» и переписаны по факту.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Навыки-создатели дописывали первый объект нового вида перед </ChildObjects>. Собранная
ими конфигурация выходила в неканоническом порядке видов, и первая же загрузка-выгрузка
давала диф: на стенде подали Language -> Catalog -> Document -> CommandGroup ->
CommonCommand, платформа вернула Language -> CommonCommand -> CommandGroup -> Catalog ->
Document. При этом cf-edit add-childObject и cfe-borrow канонический порядок уже держали —
очередная «одна работа, разные реализации».
Логика перенесена из cf-edit в семью Register-InChildObjects и в вариант nested-parent:
запись встаёт перед первой группой вида старше по CHILD_OBJECT_TYPES. Для этого список
канонического порядка добавлен в meta-compile, role-compile, xdto-compile и
subsystem-compile (оба порта) и заведён в check-type-maps — копий стало 9 вместо 5, зато
все под гардом.
У подсистем нашлось больше, чем планировалось: вставка шла в конец ВСЕГО блока, поэтому
подсистема покидала и собственную группу, если ниже были другие виды. Одно правило
закрывает оба случая. Во вложенном Subsystem.xml порядок видов неприменим — потомок там
всегда один, поведение не изменилось.
Сдвиг эталонов широкий, но проверяемый: 42 файла в 12 навыках, 53 добавленные строки
против 53 удалённых с совпадающим мультимножеством — чистая перестановка, все изменённые
строки вида <Тег>Имя</Тег>. Из 425 эталонов с ChildObjects неканоничными осталось 15: 11
фикстур сортировки (неканоничны по замыслу) и 4 статические фикстуры на сохранение EOL и
разбор форм, которые мы намеренно не переупорядочиваем.
Версии подняты в обоих портах и за эту правку, и за предыдущую (карты типов).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Спецификация держала Bot на позиции 11 (после CommonModule), а платформа ставит его
сразу за DefinedType. Замерено на стенде: конфигурацию с намеренно дописанными в конец
Bot и PaletteColor загрузили и выгрузили обратно —
8.5.1.1302: DefinedType -> Bot -> PaletteColor -> CommonCommand
8.3.27.1859: DefinedType -> Bot -> CommonCommand
то есть позиция не версионная, а взаимный порядок Bot и PaletteColor, которого не было
ни в одной выгрузке корпуса, теперь известен.
Последствия неверного эталона были не только косметические: cf-edit add-childObject уже
сегодня ставил новую группу Bot не туда, а планируемое выравнивание порядка групп по
такому списку передвинуло бы корректно лежащий Bot в конфигурации вроде УТ.
PaletteColor (8.5) в спецификации отсутствовал вовсе. Добавлен рядом с Bot; §7.4
поправлен: порядок типов между версиями не меняется, а набор растёт.
Синхронизированы все карты под гардом check-type-maps: порядок в cf-edit, cf-info,
cf-validate, cfe-validate, cfe-borrow и каталоги в тех же плюс cfe-diff. Проверка
эталона: последовательность групп в ut_8.3.27 и unf_8.5 теперь монотонна по списку —
раньше УТ давала False.
Кейс add-bot позицию не проверял: в фикстуре были только язык и бот, тест прошёл бы и с
неверным порядком. Добавлен add-childobject-bot-position с CommonModule, DefinedType и
CommandGroup, где место видно однозначно.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Правило «Subsystem не упорядочиваем» распространено на четыре вида и вынесено в
семью is_order_sensitive_type / Test-OrderSensitiveType: раньше литерал "Subsystem"
был размазан по четырём точкам — регистрация в Register-InChildObjects,
add-childObject, заимствование в расширение и сортировка.
CommonAttribute — исключение самого стандарта #std467: у общих реквизитов-разделителей
порядок в дереве задаёт порядок установки параметров сеанса
(https://github.com/1C-Company/v8-code-style/issues/78). Единственный из четырёх, где
сортировка ломает поведение, а не только диф.
CommandGroup и Language добавлены по замерам. В ACC 15 живых групп команд из 39 не
перечислены ни в одном GroupsOrder — как и подсистемы, они падают на порядок дерева.
Порядок языков задаёт порядок <v8:item> в мультиязычных строках по всей выгрузке.
Прежний вывод «сортировать их безопасно» опирался на стендовый прогон, где не было
ни подсистем (а значит и CommandInterface.xml), ни второго языка у объектов: ломаться
там было нечему, и отсутствие изменений ничего не доказывало.
PaletteColor сортируется — это переменные цветов.
Второе послабление стандарта (объекты «Удалить*» можно держать в конце ветки) не
реализуем: типовые им не пользуются — в ACC 360 таких объектов из 373 стоят по
алфавиту, а сортировка стандарту не противоречит. Оговорено в гайде.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Резолвер настройки искал файл от каталога конфигурации и лишь потом от cwd —
единственное место в репозитории с таким отсчётом. Остальные ищут от рабочего
каталога: support-guard (18 навыков) с фолбэком на каталог конфигурации, группа
db-* (5 навыков) вообще без фолбэка, потому что каталога конфигурации у них нет.
Обоснование прежнего порядка («cwd почти всегда чужой проект») опиралось на нашу
стендовую раскладку — вызов из репозитория навыков по выгрузке в cfsrc. У
пользователя cwd и есть каталог проекта с .v8-project.json, а скрипт навыка
зовётся по абсолютному пути и cwd не меняет, так что оба порядка эквивалентны.
Кейс cfe-borrow/newobjectposition-byname клал настройку внутрь каталога расширения
и тем закреплял несуществующую раскладку — конфиг проекта лежит в корне проекта,
расширение внутри него. Поправлен на реальную.
Из гайда убран абзац, объяснявший особый порядок: объяснять больше нечего.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
При переводе сохранения объекта на семью Detect-XmlStyle/Finalize-XmlText из блока
выпал вызов Insert-IntoOwnChildObjects: заимствованные реквизиты собираются текстом
и вставляются в собственный ChildObjects объекта, а не через InnerXml (тот ломает
пространства имён). Без вызова слияние реквизитов при заимствовании по глубокому
пути молча теряло их.
Вызов стоит до Finalize-XmlText, чтобы схлопывание пустых тегов накрывало и
вставленные реквизиты — как было в исходном порядке.
Тестами не ловится: путь Merge-AttributesIntoObject не покрыт ни одним кейсом,
поэтому удаление прошло 29/29 и верификацию платформой. Проверено изолированным
прогоном связки: реквизит вставлен, самозакрытый ChildObjects раскрыт, пустые теги
плотные, XML валиден. Покрытие этого пути — отдельная задача.
Попутно: кейс type-name-russian расширен на add-childObject — русское имя вида
проверялось только в sort и remove.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Навыки-создатели дописывали новый объект в конец группы своего вида, а стандарт
требует порядка по имени (АПК:1108). Замеры на 8 боевых выгрузках: 103 385 объектов
лежат по алфавиту, нарушают его ровно дописки в хвост. Стенд подтвердил, что
беспорядок вечен: платформа нормализует порядок ВИДОВ (возвращает канонический),
но порядок имён внутри вида не трогает.
Настройка newObjectPosition в .v8-project.json (end по умолчанию | byName) —
решение проекта, а не вызова: читают её meta-compile, role-compile, xdto-compile,
cfe-borrow и cf-edit add-childObject. Компаратор при этом константа, моделирующая
дерево Конфигуратора: ключ «ранг+символ» без культурных таблиц, одинаковый в обоих
портах на любой ОС. На корпусе он даёт 4 нарушения на 125 088 пар против 2866 у
ordinal-сравнения, которым cf-edit и cfe-borrow сортировали до сих пор.
Subsystem не упорядочивается автоматически нигде: пока подсистемы не перечислены
в <SubsystemsOrder>, порядок дерева задаёт порядок разделов в панели, а платформа
этот список сама не заводит.
Настройка не чинит накопленное, поэтому cf-edit получил операцию sort-childObjects
(вся конфигурация или названные виды). Она переставляет значения узлов, а не узлы,
поэтому диф — чистая перестановка строк. Имя вида принимается в любом регистре,
во множественном числе и по-русски; неизвестный вид — отказ со списком допустимых.
Попутно:
- PS-порты создателей переведены с DOM-сериализации на текстовую вставку: на
выгрузке не в каноне Конфигуратора они переписывали заголовок без просьбы;
- PS-сторона семьи detect_xml_style/finalize_xml_bytes закрыта в cf-edit и
cfe-borrow — правка чужого файла наследует его BOM/EOL/заголовок;
- xdto-compile присоединён к семье Register-InChildObjects вместо инлайн-копии;
- закрыт ложный успех в py subsystem-compile: <ChildObjects /> с пробелом ET
разбирает, а текстовые ветки не находили — файл писался без вставки;
- cfe-borrow py не импортировал json, из-за чего резолвер молча возвращал end;
- cf-edit add-childObject ставил объект в конец блока, за пределы своей группы,
когда видов старше в файле не было.
Проверки: 906/906 на PowerShell и 903/906 на Python, 10 гардов, верификация
эталонов платформой без падений, раундтрип отсортированной конфигурации на
8.3.24 и 8.3.27, сортировка боевой выгрузки ACC (1,4 МБ) и расширения из корпуса.
Задачу принёс PR #85; часть кода взята оттуда.
Co-Authored-By: Sergei Pleshanov <72200277+Abacadabras@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
Различение было реализовано, но не покрыто: ни один фейковый лог не
содержал строки «Объект получен из хранилища», поэтому подсказка о
перевыгрузке могла начать печататься всегда — и тесты бы это пропустили.
Логи кейсов сняты с живого стенда (FINDINGS #14).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Кейс проверял текст через stdoutContains и был зелёным на PowerShell, красным на
python: Write-Host уходит в stdout, а py-порт печатает ошибки в stderr. Это
общее для группы расхождение, поэтому давний error-partial-no-objects тоже
проверяет только код возврата. Новый кейс приведён к тому же виду.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Список без режима давал молчаливо неверный результат, причём разный. У выгрузки
умолчание Changes игнорировало -Objects и отдавало «изменённое с прошлой
выгрузки». У загрузки на движке 1cv8 умолчание Full игнорировало -Files и
заменяло всю конфигурацию базы, тогда как на ibcmd та же команда выполнялась
частично — одни и те же аргументы вели себя по-разному.
Теперь перечисленные объекты или файлы сами задают частичную операцию. Режим
разрешается до ветвления на движки, поэтому расхождение исчезает по построению;
проверено на стенде: обе ветки дают одинаковый результат.
Заданный вместе со списком -Mode Full или Changes не отбрасывается молча — о нём
сообщает [note]. Список с -Mode UpdateInfo отвергается: это другая операция,
обновление ConfigDumpInfo без выгрузки файлов, и вывод подменил бы её.
Инструкции намеренно не менялись: -Mode Partial остаётся каноничной формой,
а вывод режима — прощающим вводом, который мы не документируем.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Кейс описывал механику: сам клал fake.cmd с batch-кодом, прописывал путь к нему
и помечался osOnly win32, потому что .cmd на маке не исполнится. Тот же сценарий
на POSIX требовал второго файла с fake.sh — отсюда двойники, а забытый двойник
означал дыру: у db-repo на маке выполнялся один кейс из двенадцати, и заметили
это случайно.
Теперь кейс объявляет намерение — лог и код возврата в поле fakePlatform, — а
раннер сам кладёт .cmd или .sh под текущую ОС, пишет лог и заглушку базы и
подставляет {fakePlatform} в аргументы. Механизм включается только по объявлению
поля; остальные кейсы не затронуты.
Мигрированы db-repo (11), db-update (4), db-load-xml (4); 19 двойников удалены.
Пропусков на Windows стало 0 вместо 19 — это и были двойники.
Кейсы db-create, db-dump-cf, db-run, epf-build оставлены как есть: часть из них
однопортовая по существу (cp866 в выводе, гейт «ложного успеха» на runtimeOnly),
и каждый требует отдельного решения.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Кейсы используют фейковую платформу в виде .cmd и потому помечены osOnly win32.
На Mac mini из двенадцати выполнялся один, то есть py-порт там почти не
проверялся — ровно та ловушка, о которой предупреждает памятка по стенду.
Добавлены двойники на fake.sh для всех кейсов, кроме уже имевшегося.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Операция над всей конфигурацией перечисляет тысячи объектов, и весь список
уезжал в вывод модели. Списки обрезаются на двадцати позициях, полный уходит
в файл, путь к которому назван. Сырой лог платформы при отказе показывается
хвостом в 200 строк: итог и причину платформа пишет в конце.
Тесты на фейковой платформе — 12 кейсов, разбор лога проверяется без 1С на
логах, снятых со стенда. Ключевой кейс закрывает главное правило навыка:
платформа вернула 1, часть объектов захвачена, навык возвращает 0 с поимённым
предупреждением. Обрезка списка проверена на логе из тридцати объектов —
кейс требует и строку «и ещё 10», и отсутствие двадцать первого объекта
в выводе.
Проверено негативным контролем: намеренно сломанное ожидание роняет кейс,
то есть тесты действительно сверяют вывод, а не проходят вхолостую.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Фикстуры childobjects-missing и childobjects-selfclosed невалидны by design: первая вовсе без
<ChildObjects>, вторая с пустым. Кейсы проверяют реакцию навыка на такую конфигурацию, а платформа
её не принимает — «читаемое свойство не соответствует ожидаемому. Текущее Configuration, ожидаемое
ChildObjects» и «Неизвестный объект метаданных - Language.Русский» соответственно.
Причина пропуска записана по факту отказа платформы, а не по предположению.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
configDir приходил только из скилл-уровневого setup (empty-config) и из ветки external, поэтому
у навыков с setup: none кейс на фикстуре до платформы не доезжал — и всё равно получал PASS.
Таких кейсов девять: пять support-edit, три meta-validate и один skd-decompile. В наборе по
умолчанию дефект не проявлялся: там у всех навыков setup: empty-config, и загрузка шла.
Теперь фикстура-конфигурация грузится как external-выгрузка, а путь «грузить нечего и спец-маршрут
не подошёл» вместо молчаливого PASS требует объявить skipPlatformVerify с причиной. skd-decompile
отнесён к standalone-навыкам: его выход — JSON-описание, грузить нечего.
Отдельно — видимость: «прошло» и «проверено платформой» больше не одно и то же число. В консоли и
в отчёте печатается, сколько кейсов реально обратилось к платформе, а сколько прошло мимо и почему
(external, standalone, ожидаемый отказ навыка, недоступная платформа); в таблице отчёта появилась
колонка «Платформа». Падения в эту долю не попадают: у них обращение было и не удалось.
Девяти кейсам проставлен пропуск с проверенной причиной. Для support-edit причина выяснялась
опытом: фикстуры содержат рукотворный Ext/ParentConfigurations.bin, и платформа отвечает «Ошибка
формата потока», тогда как настоящий bin из типовой грузится, а выгрузка без bin — тоже. Попытка
пересобрать фикстуры полноценными объектами вылечила XML, но не bin, и была откачена.
Документация: в README добавлена таблица маршрутов платформенной проверки и правила появления
configDir — раньше это знание жило только в коде, и «зелёный» отчёт не отличался от «проверенного».
В docs/1c-support-state-spec.md записано, что по описанной грамматике bin можно читать, но нельзя
собрать файл, который примет платформа.
Полный прогон verify-snapshots до и после правки даёт одинаковый состав: 461 passed, 6 failed,
30 skipped из 497 — новых падений нет. Тесты: 871/879, интеграционные 13/13.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>