PowerShell ищет функцию в момент вызова и видит только уже выполнившиеся
объявления. В трёх навыках блок Find-V8Project/Test-SamePath/Find-ProjectDatabase
оказался ниже строки, где отрабатывает Find-ProjectV8Path: CommandNotFoundException
уходил в stderr, результат становился $null, и выбор платформы по записи базы молча
откатывался на корневой v8path. Порты .py не задеты — Python связывает имя при
вызове, так что расхождение PS↔PY было бы тихим.
Гард check-ps-define-before-call.mjs проверяет порядок объявлений во всех .ps1,
где отрабатывает эта цепочка (15 навыков), и на прежнем состоянии даёт ровно те
три диагностики. Кейс db-repo/v8path-from-database ловит тот же дефект поведением.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
В одном проекте базы могут жить на разных версиях платформы, а версию формата
XML-выгрузки задаёт та платформа, которая выгружает: без выбора платформы по базе
срабатывает автопоиск и берёт самую старшую установленную — то есть самый новый
формат, который коллега на младшей платформе уже не загрузит.
Теперь порядок такой: явный -V8Path -> v8path записи базы -> корневой v8path ->
автопоиск. Запись базы ищет сам скрипт тем же матчем, которым добывал реквизиты
хранилища (Find-ProjectDatabase): файловую по path, серверную по server + ref.
- в 9 навыков, где матча не было, скопирован блок из авторитета db-repo;
- списки потребителей трёх семей в check-inline-drift расширены; заведена семья
platform: resolve_v8path — обёртка меняла сигнатуру, а под гардом не была;
- раннер подставляет {workDir}/{fakePlatform} и в содержимое preRun.writeFile,
иначе кейс про выбор платформы нельзя написать кроссплатформенно;
- в SKILL.md платформа теперь берётся ПОСЛЕ разрешения базы, строка про
автоопределение через Program Files убрана как деталь реализации.
Живой прогон: две базы в одном проекте при корневом v8path = 8.5 дали формат
2.17 (8.3.24) и 2.20 (8.3.27), оба порта.
Closes#93
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Оба кейса не проходили проверку эталонов платформой, и по разным причинам.
region-reuse клал в расширение декоративный перехватчик &Перед("МетодА"),
чтобы навыку было куда встроиться, но МетодА в конфигурации не было — только
МетодБ. Раннер этого не видел: он сверяет выход навыка, а навык отрабатывал
верно. Платформа же расширение не принимала: «Не найден метод "МетодА",
указанный в аннотации». Метод добавлен в модуль конфигурации; выход навыка не
изменился, сдвинулся только эталон исходного модуля.
resync-conflict моделирует неразрешённый конфликт: навык намеренно паркует
блок под меткой // [РЕСИНК-КОНФЛИКТ] и оставляет его человеку, так что текст
метода заведомо расходится с оригиналом и расширение неприменимо by design.
Это штатный случай для skipPlatformVerify — объявлен с причиной.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Две находки ревью.
Приняв хвостовой комментарий у маркера, разбор запоминал только содержимое
блока, а -Actualize писал на его место голое ключевое слово: авторская пометка
вроде «#Вставка // проверка прав, ТЗ-142» молча исчезала при первой же
актуализации. Теперь строки открывающего и закрывающего маркера едут вместе с
блоком и возвращаются как были — с комментарием, отступом и своим написанием;
ключевое слово остаётся запасным вариантом. Кейс actualize-marker-tail-comment
эту потерю ловит: без правки падает.
Счётчик контролируемых методов в cfe-validate был единственным новым
сопоставлением без учёта регистра, хотя язык регистронезависим: на
&changeAndValidate он показывал ноль и расходился с cfe-patch-method -Check,
на который сам же и ссылается.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Пара And/И в таблице была, а Or/Или — нет: при снятии пар с таблицы строк
платформы односимвольные слова отсеял фильтр длины, и пропуск не бросался в
глаза. Сегодня кода, который эмитирует Или, нет (условия собираются только из
НЕ и И), так что дефекта это не давало, но таблица — единственное место
правды о языке, и дыра в ней ждала своего часа.
Заодно подписано, почему отрицание эмитируется как "НЕ": платформа пишет
"Не", язык регистронезависим, а смена регистра сдвинула бы все эталоны.
Val, Insert/EndInsert и Delete/EndDelete перепроверены на платформе, а не по
дампу бинарника: их английские написания в таблице строк не видны из-за
склейки одинаковых литералов. Оракул — проверка применимости расширения:
Val Item и Знач Item платформа считает одним списком параметров, блоки
#Delete/#EndDelete принимает, а подсунутый #Deletee отвергает
(«Ожидается оператор препроцессора»).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Шесть кейсов: дрейф и актуализация английского перехватчика, генерация
ИзменениеИКонтроль и Instead из английского источника, отчёт cfe-diff,
счётчик cfe-validate, плюс русский кейс с хвостовым комментарием у маркера.
Эталоны фиксируют главное: сгенерированный код повторяет язык источника,
а -Actualize оставляет Procedure и #Insert английскими.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Платформа принимает встроенный язык в двух написаниях, а навыки читали только
русское: в расширении на английском перехватчики и блоки правок не находились,
и отчёт выглядел чистым. -Check молчал, cfe-diff не показывал вставки,
счётчик контролируемых методов в cfe-validate давал ноль.
Пары ключевых слов сверены по таблицам строк платформы (backbas.dll, bsl.dll),
а не по памяти. Разбор стал двуязычным везде, где навык читает модуль:
аннотации, объявления и Конец*, Знач, директивы контекста, препроцессор в
цепочке обрамления, поиск региона при переиспользовании.
Маркеры правок распознаются по началу строки, а не сравнением целой строки, —
заодно перестал теряться хвостовой комментарий (#Вставка // старая логика).
Эмиссия подчиняется одному правилу: отдаём тем языком, который прочитали.
Генерация берёт язык метода-источника, -Actualize — язык переписываемого
перехватчика, чтобы актуализация не превращала чужие Procedure и #Insert в
русские. Комментарии и вывод в консоль остаются русскими.
Таблица ключевых слов и разбор аннотаций скопированы в три навыка и заведены
семьями в реестре анти-дрейфа: разъехавшаяся пара слов дала бы ровно тот же
тихий ложно-чистый отчёт.
Проверено платформой: расширение с &ChangeAndValidate принимается
(/CheckCanApplyConfigurationExtensions), после дрейфа оригинала отвергается,
после -Actualize принимается снова.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Замена ExternalReport. -> Report. шла по всему тексту XML и попадала внутрь
имени объекта, если оно оканчивается так же (ПриёмкаExternalReport). Имя берётся
из <Name> до замены и остаётся авторским везде — в имени файла, GeneratedType и
составе конфигурации, — а ссылка в скопированном XML расходилась с ним, и
заглушка не грузилась: «Неизвестный объект метаданных - Report.ПриёмкаReport…».
Заменяется только префикс вида объекта в начале квалифицированного имени.
Регрессии строят фикстуру навыками (erf-init --WithSKD, epf-init, form-add) —
так в кейс попадает и настоящая ссылка вида cfg:ExternalReportObject.<Имя>.
Соседние кейсы каталога переведены на {fakePlatform} и прямые слэши.
Closes#90
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
В py-порте QUERY_BASE_DIR инициализировался None и заполнялся только в
ветке с -JsonPath: без него os.path.join(None, ...) упал бы. Путь
недостижим — запросы приходят только из JSON, — но PS-порт в этой ветке
берёт текущий каталог, и расхождение стоит закрыть до того, как оно
станет достижимым.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
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
Таблица «что хранится при каких умолчаниях роли» ушла из инструкции:
модель попадает в этот случай ровно в момент, когда навык уже печатает
«запрет совпадает с умолчанием роли и платформой не хранится». Вместо
описания механики сообщение дополнено подсказкой, где запрет имеет
смысл, — теперь вывод самодостаточен, а инструкция короче.
Там же сжаты формулировки, которые дублировали текст ошибок: про отказ
до записи, про право сервиса на корне, про ограничение RLS без права.
Назначение deny-rights в таблице операций названо прямо: закрыть
реквизит или ТЧ, которые иначе наследуют права объекта.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Убран раздел про русские синонимы: прощающий ввод в инструкции даёт
модели развилку там, где есть одна каноничная форма — английская.
Убран раздел про замыкание набора при выдаче: навык перечисляет
дописанное в выводе, а в инструкции это лишняя механика. Оставлено
только то, что меняет решение ДО вызова: снятие и запрет уносят больше
перечисленного.
В примере объектной формы запрет переехал с объекта верхнего уровня на
реквизит: на верхнем уровне такая запись при умолчаниях роли не
хранится, и пример учил бесполезному.
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
Внутри навыка они были сгруппированы по типам, поэтому список
[remove-rights, add-rights] для одного права выполнялся наоборот: право
сначала добавлялось, потом снималось. В инструкции об этом не было ни
слова, а порядок ввода — то, чего ожидает читающий.
Заодно: условие RLS со ссылкой на шаблон, которого в роли нет, больше не
проходит молча — предупреждение в stderr (отказывать нельзя, шаблон
могут добавить следующей операцией).
Вычитка инструкций обоих навыков: сказано, что -Operation это одна
операция, а несколько разных задают списком в файле (с примером);
перечислены все три глобальных флага; про значение из файла вынесено в
свой раздел; добавлено, что права реквизитам поштучно платформа не
хранит — ограничивают их запретом. Из role-compile убрано объяснение,
зачем навык замыкает набор: инструкция про применение.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Чтение текста из файла жило тремя копиями под именем Resolve-QueryValue
и вне реестра гарда, а role-* завели четвёртое правило — только от
рабочего каталога. Теперь функция одна на пять навыков:
Resolve-TextFromFile / resolve_text_from_file, заведена семья в
check-inline-drift, эталон — skd-edit.
Правило поиска общее: абсолютный путь как есть, относительный — рядом с
DSL (у edit-навыков — рядом с редактируемым объектом), затем в текущем
каталоге, не нашли — ошибка со списком проверенных мест. Имя без "query":
в ролях из файла приходит условие RLS, а не запрос. Текст ошибки
приведён к языку остальных сообщений.
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
Снапшоты фиксируют итоговый файл целиком, поэтому лишняя перестановка
узлов или переписанный соседний блок уехали бы в эталон как норма.
Гард считает diff к исходному файлу: операция обязана дать ровно свои
строки и ни одной чужой.
Измерено заодно на живой роли типовой (БазовыеПраваБП, 4357 строк,
105 узлов): add-rights нового объекта — +11/-0, права в существующий
узел — +4/-0, set-rls — +3/-0, remove-rights — +0/-4, запрет на
реквизит — +11/-0. Удалённых строк нигде, кроме снятия.
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
child-operations.md был на 191 строку и держал в одном файле реквизиты, поля внешнего
источника, таблицы, функции, ТЧ, предопределённые и значения перечисления: модель, добавляющая
реквизит, читала заодно про функции внешнего источника. Ось резки была механизмом (add/remove/
modify), а нужна сущность — как в meta-compile.
SKILL.md: частое (команда, сводная таблица операций, быстрые примеры) плюс индекс
«что правишь → файл». Остальное — reference/: attributes, tabular-sections, predefined,
external-data-source, other-children, properties (бывший properties-reference), json-dsl.
Попутно: везде «навык form-add», а не голое имя — иначе читается как команда оболочки или
значение -Operation. Тексты предупреждений в обоих портах тоже.
scripts/switch.py собирал .md только из корня навыка, подкаталоги не видел: команды запуска
в reference/*.md молча остались бы на старом рантайме. Обход стал рекурсивным.
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
Раздел 18 был указателем на reference навыка — это ломало принятое разделение: спецификация
в docs описывает DSL полно (для нас), инструкция навыка — только как применять (для модели).
Указатель экономил дублирование ценой того, что полного описания не оказалось нигде.
Теперь §18 описывает источник, таблицы, поля и функции целиком: ключи, XML-теги, умолчания,
конвенции ссылок на поля, presence-aware inputByString, различение BinaryData и ValueStorage,
запрет составного типа, и границы — почему в DSL нет значения незаполненного родителя (платформа
сбрасывает его при любой загрузке XML) и почему нет кубов OLAP.
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
Свойство теряется не «в DSL», а при любой загрузке XML — платформа сбрасывает его даже при
загрузке собственной выгрузки. Проверено изолированно: dump-1 без правок загружен и выгружен
обратно, узел UnfilledParentValue — единственное расхождение во всём файле.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
Перечитал reference глазами модели, которая будет им пользоваться, и убрал то, что отвечает
на вопрос «как сделано», а не «как применять»: развёртывание коротких имён полей в полный путь,
«компилятор отказывает сразу», обоснование через поведение Конфигуратора, отдельный раздел про
пустой ключ (ужат до строки в таблице свойств).
Исправлено по существу:
- `unfilledParentValue` был описан как рабочий ключ, хотя платформа сбрасывает его при загрузке
XML — теперь сказано прямо, что через DSL его не задать;
- добавлена строка `readOnly` у таблицы с подсказкой ставить его представлениям и таблицам вида
Expression (загрузка этого не требует — проверено, — но писать в них нельзя);
- `inputByString` документирует вывод из поля представления;
- убран дубль строки `readOnly` в таблице свойств.
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
В корпусе выгрузок внешних источников нет ни одного, поэтому позиция вида в порядке
ChildObjects была неизвестна, а check-type-maps держал ExternalDataSource исключением
с причиной «позиция в порядке не измерена».
Измерено на платформе 8.3.24.1691: в конфигурацию с объектами-маркерами семи видов
(CommonModule … Task) и настоящим сервисом интеграции загружен внешний источник,
выгрузка показала порядок Task → ExternalDataSource → IntegrationService. Независимо
подтверждено рабочей конфигурацией с внешними источниками.
Спека: ExternalDataSource = 46, IntegrationService сдвинут на 47; добавлены наборы
GeneratedType источника (3 категории) и его таблицы (8 категорий, имя элемента
трёхчастное: префикс.Источник.Таблица).
Вид внесён в 52 карты одиннадцати навыков — без этого cf-validate считал бы каталог
ExternalDataSources/ неизвестным, а порядок в ChildObjects навыки-создатели ставили бы
в конец блока, откуда платформа переставила бы группу при первой же выгрузке.
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
Кейс с expectError и fileContains/fileNotContains/filesEqual/preserves проходил
при любом содержимом файла — проверки лежали под !expectError вместе со снэпшотом.
Под !expectError остались только снэпшот и идемпотентность: эталон с аварийного
состояния снимать нельзя, а файлы, написанные до отказа, проверять нужно.
Заодно во втором кодовом пути раннера не было filesEqual — пути выровнены.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MQkqXxErBepYmoUycfkbn
Относительный путь при копировании содержимого объекта в конфигурацию-заглушку
считался через 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-build (epf-build + stub-db-create),
и в невидимой копии прожили три расхождения с эталонами, включая потерянную
POSIX-ветку run_v8. Теперь сверяются все копии, в сообщении указывается файл.
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
В комментарии семьи is_order_sensitive_type и в гайде причина исключения Language была
подана как установленная: «порядок языков задаёт порядок <v8:item> в мультиязычных
строках по всей выгрузке». Это гипотеза, высказанная при обсуждении, и мы её не
проверяли. Основания у четырёх исключений разные, и теперь это видно: CommonAttribute —
исключение самого стандарта, Subsystem и CommandGroup — замер по корпусу (в ACC 15 живых
групп команд из 39 вне GroupsOrder), Language — осторожность без замера.
Правка задевает тело семьи, поэтому идёт по всем пяти навыкам в обоих портах.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
В справочнике операции при каждом исключённом виде стояла причина: «порядок
разделителей задаёт порядок параметров сеанса», «порядок в панели», «порядок
мультиязычных строк». Позицию вставки выбирает проект, а не модель, — в инструкции
навыка этому места нет. К тому же документальное основание есть только у
CommonAttribute (исключение самого стандарта), остальные три — наш вывод из замеров,
и подавать его как факт тем более не следовало. Осталось перечисление видов.
Заодно сняты два утверждения, которые предыдущая правка сделала ложными: «взаимный
порядок групп видов навыки не меняют вовсе» в гайде и та же фраза в докстрингах обоих
портов — рядом с реализацией, которая ровно это и делает. Формулировка про четыре вида
в гайде уточнена: не сортируются их ИМЕНА, положение самой группы канонично всегда.
Обоснования остаются в docs/v8-project-guide.md, где решение принимает человек.
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
В инструкции стояло «локальная конфигурация изменена: из хранилища
получено N», скрипт печатает «…изменена, получено объектов из
хранилища: N». Модель искала в выводе фразу, которой там нет.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Различение было реализовано, но не покрыто: ни один фейковый лог не
содержал строки «Объект получен из хранилища», поэтому подсказка о
перевыгрузке могла начать печататься всегда — и тесты бы это пропустили.
Логи кейсов сняты с живого стенда (FINDINGS #14).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Справочник Номенклатура объявлял реквизиты типа EnumRef.* до того,
как сами перечисления попадали в конфигурацию. Валидатор, ужесточённый
в 7b40eb4c, проверяет разрешимость ссылочных типов и отвергал такое
промежуточное состояние.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Правило требует синхронного бампа обоих портов, а я поднял только py — дважды
за сегодня, в правке кавычек на POSIX и в выравнивании потоков. Версия
описывает состояние навыка, а не отдельного файла, поэтому пара обязана
двигаться вместе.
Двадцать один PS-порт выровнен по своему py.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Двадцать один py-порт печатал ошибки в stderr, тогда как их PS-мастера пишут
через Write-Host в stdout. Счётчики совпадали один в один (14↔14, 19↔19,
13↔13) — сообщения были те же, разъехался только поток. Это нарушало
соответствие из docs/python-porting-guide.md, где Write-Host сопоставлен
обычному print.
Это не косметика. Харнесс не чередует потоки, а группирует: сначала весь
stderr, потом весь stdout. Из-за этого в py-порте вердикт «Error dumping
configuration (code: 1)» печатался ПЕРЕД строками, которые его объясняют, а
причина из лога платформы оказывалась в самом низу — причинный порядок вывода
переворачивался. Порт, работающий на macOS, читался хуже того, что работает на
Windows.
Тесты этого не ловили по построению: текст ошибки сверяют только кейсы со
строковым expectError, а он смотрит в stderr — потому такие кейсы есть лишь у
семейства, где потоки сходятся, а в db-* их ноль.
Добавлен гард check-error-streams.mjs: нет записи в stderr в PS-порте — не
должно быть и в py, и симметрично. Он сразу нашёл пять навыков сверх тех, что
я насчитал вручную, и отсеял два ложных срабатывания (в meta-remove слово
Write-Error стоит в комментарии «почему НЕ Write-Error»).
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>
В каскаде стояло «Операция необратима». Это неверно: переподключение работает,
просто требует обоих флагов connect, о чём сказано абзацем выше. Ложная
безвозвратность отпугивала бы от законного действия.
Убрано и «пропускает диалог аутентификации»: в пакетном режиме с реквизитами
диалога нет — это теория из документации, а не действие.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Инструкция велела захватывать «объект и корень». Объекта ещё нет ни в базе, ни
в хранилище, поэтому список отвергается целиком — «Загруженный список объектов
пуст» — и корень тоже не достаётся: модель осталась бы вовсе без захвата.
Верный порядок такой же, как у новой формы или макета: захватываем то, что
существует (корень), частичная загрузка создаёт объект, и при помещении он
называется вместе с корнем. Пример в SKILL.md разделён на два шага.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Раннер получил новое поле — формат кейса обязан его описывать, иначе следующий
автор снова напишет .cmd руками и пометит кейс osOnly.
Отдельно сказано, что таким кейсам osOnly не нужен, и почему: именно забытый
-posix двойник давал дыру, из-за которой на маке выполнялся один кейс из
двенадцати.
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>
Проверка прямым вызовом платформы на darwin: /ConfigurationRepositoryF"путь с
пробелом" отвергается, тот же ключ без кавычек проходит. Значит на POSIX лишние
и обрамляющие кавычки значений, и кавычки внутри склеенных ключей — они нужны
только для склейки команды на Windows.
File="…" не трогаем: там кавычки часть синтаксиса строки соединения, и с ними
на POSIX всё работает (проверено db-create).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Воспроизведено на darwin: db-load-xml с путём, содержащим пробел, падает с
«Неопределена информационная база». На POSIX аргументы уходят списком, и
кавычки, нужные для склейки команды на Windows, становятся частью значения.
Тот же механизм ранее молча терял многословный -comment в db-repo.
Правка в общей run_v8 (семья platform: run_v8) — чинит все двенадцать
потребителей разом. Снимается ОДИН слой обрамляющих кавычек, поэтому склеенные
ключи (/N"user", File="…", /ConfigurationRepositoryF"путь") не задеты: у них
кавычки внутри токена, там их ждёт разборщик 1С.
PS1 не затронут: там команда всегда склеивается в строку. Точечный arg_value(),
добавленный в db-repo при разборе дефекта, откачен — семейная правка делает его
лишним.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Живой прогон на darwin показал: многословный -comment теряется целиком, а
однословный доходит. На POSIX аргументы уходят списком, и наши кавычки
становятся частью значения; склейка в одну строку нужна только на Windows,
там же нужны и кавычки.
Ключи вида /F"путь" и /N"имя" не трогаем: там кавычки внутри токена требует
сам разборщик 1С, и они нужны на обеих ОС.
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>
В history.md лежала цепочка «выгрузить CF версии → создать временную базу →
загрузить → выгрузить XML → сравнить». Это один из способов, поданный как
единственный: сравнить можно и средствами конфигуратора, а по задаче #76
источником сравнения может быть прямо версия хранилища, без временной базы —
тогда рецепт станет ещё и неверным.
Работа навыка заканчивается на dump-cfg; что делать с полученным CF, решает
пользователь.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Перечитал изменённые инструкции свежим взглядом и убрал всё, что описывает
поведение навыка вместо способа им пользоваться: образец вывода отчёта (модель
увидит настоящий), фразу «навык подсказывает оба флага», объяснение, почему
длинный отчёт не печатается, и заверение, что неверная пара путь+расширение
ничего не ломает. Осталось действие: как назвать, что указать, чего ждать от
кода возврата.
Столбец «Что теряется» переименован в «Почему»: у lock -All не теряется ничего,
и строка «Ничего, но…» в таком столбце читалась криво.
Подсказка о недоступном хранилище разделена по схеме адреса: tcp ведёт к
серверу хранилища и порту, http — к веб-серверу и публикации. Прежний общий
текст советовал бы проверять сервер хранилища и порт 1542 для http-адреса, где
это неверно.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
В SKILL.md место только тому, как навык применять. Оговорка о том, что форма
адреса не описана в известной документации и проверена на стенде, — это история
работы, модели она ничего не даёт. Осталось применимое: вид адреса, порт по
умолчанию и то, что недоступный сервер отвечает тем же сообщением, что и
отсутствие реквизитов.
В справочнике проекта оговорка тоже переписана на факты: требование совпадения
версий сервера и платформы вместо рассказа о том, где мы это выясняли.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Установлен crserver 8.3.23, поднят на порту 1542 — сетевое хранилище перестало
быть непроверенным местом. Адрес tcp://<хост>[:<порт>]/<имя>, порт по умолчанию
1542; хранилище создаётся сервером по требованию. Весь цикл, файл списка
объектов и администрирование работают так же, как на файловом, и одинаково в
обоих портах. Отдельного поведения у сетевого хранилища не обнаружено.
Нашлась двусмысленность: недоступный сервер даёт «Соединение с хранилищем
конфигурации не установлено» — ровно то же сообщение, что и отсутствие
реквизитов. Прежняя подсказка «добавьте repository в реестр» в сетевом случае
уводила не туда. Теперь db-repo различает по схеме адреса и советует проверить
сервер и порт, а подсказка db-load-xml и db-load-git называет обе причины.
Документация больше не оговаривается «не проверено»: форма адреса подтверждена
на стенде.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Форма tcp://srv01/MyApp пришла из примера в ишью #77, а мы повторили её в
справочнике реестра и в db-list как факт. Прицельный поиск показал, что в
доступной документации её нет: глава 7.4.15 описывает только каталог хранилища,
а в обоих «Приложение 4» (8.3.24 и 8.3.27) не встречаются ни crserver, ни
tcp:// — совпадения по слову «хранилище» относятся к лицензированию.
Теперь сказано «каталог хранилища или адрес сервера хранилища» и добавлена
оговорка: путь передаётся платформе как есть, конкретный синтаксис в известной
нам документации не описан, на серверном хранилище навык не проверялся.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Пункт плана, остававшийся открытым: каталог исходников расширения модель
указывала руками, хотя он объявлен в записи базы. Каталог выбирает модель, а не
скрипт, поэтому это строка в инструкции — рядом с такой же про configSrc.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Печать отчёта целиком стоит модели контекста, но опаснее другое: обрезанный
отчёт по версиям читается как полный ответ. По куску легко заключить, что
объект не менялся, — в отличие от списка объектов, где неполнота очевидна.
Короткий отчёт (до ста строк) печатается целиком, длинный не печатается
совсем: называется путь к файлу и способы сузить выборку. Порог не
теоретический — даже стенд из девяти версий даёт 124 строки.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Три места, где модель делала работу за навык.
report требовал -OutputFile, хотя отчёт всё равно печатается в вывод: путь
приходилось придумывать ради файла, который никто не читает. Теперь без
параметра отчёт уходит во временный файл, а путь называется — сохранить
осознанно по-прежнему можно.
create и connect с явным путём хранилища теперь печатают готовый блок
repository для .v8-project.json. Без записи в реестре реквизиты придётся
передавать в каждом вызове, а update откажется работать вовсе — вспомнить
об этом модель не может, а подставить готовое мы можем.
Подсказка про -ForceReplaceCfg сразу называет и второй флаг: при
переподключении платформа отвергает дважды подряд, сообщая о причинах по
одной, и модель упиралась бы два раза.
Проверено, что печатаемый блок — валидный JSON: строка из вывода разбирается
парсером, путь читается обратно. В PS обратный слэш в строке замены -replace
не спецсимвол, из-за чего слэши сперва удвоились дважды.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Административные и сервисные команды собирались по документации, а вживую
гонялись сырыми вызовами платформы — сборка аргументов навыком не проверялась.
Прогон через навык показал, что работают dump-cfg (включая -Version), set-label,
add-user, clear-cache во всех областях, optimize, copy-users, create -NoBind.
Нашлись два тупика подряд при переподключении базы: платформа сначала отвергает
непустую конфигурацию, а после -ForceReplaceCfg — то, что за пользователем
хранилища уже числится эта база. Оба раза действие не называется. Добавлены
подсказки на оба сообщения, сценарий описан в references/connect.md.
Заодно постусловие update сработало на настоящей аварии: после неудачного
переподключения база осталась отключённой, и команда отказалась вместо тихой
замены конфигурации.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Файлы references/ несли сокращённую запись «... db-repo.ps1 -Command …», и
scripts/switch.py её не видел: его регулярное выражение требует полной формы
powershell.exe -NoProfile -File <path>.ps1. В python-дистрибуции каскад остался
бы с .ps1, то есть на macOS указывал бы на неисполнимый файл, а гард
переносимости этого не ловит — он следит за плейсхолдером ${CLAUDE_SKILL_DIR},
которого в сокращённой записи тоже не было.
Проверено прогоном switch_runtime_content по всем четырём файлам: до правки
переключатель находил 0 вызовов при трёх упоминаниях .ps1, после — все
конвертируются, .ps1 не остаётся.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
db-list/SKILL.md — фактическая спека .v8-project.json, которую читает модель,
и без repository она давала неполную картину: база под хранилищем не принимает
ни одной операции конфигуратора без реквизитов доступа, причём это касается не
только db-repo, но и всей группы загрузки-выгрузки.
Добавлены repository и extensions[] в пример и таблицу полей, раздел с
описанием обоих (включая то, что у расширения своё хранилище со своим путём,
а пользователь хранилища не наследуется от пользователя базы), пункт в
интерактивный сценарий добавления базы и форма ключей доступа в разделе про
строку подключения.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Операция над всей конфигурацией перечисляет тысячи объектов, и весь список
уезжал в вывод модели. Списки обрезаются на двадцати позициях, полный уходит
в файл, путь к которому назван. Сырой лог платформы при отказе показывается
хвостом в 200 строк: итог и причину платформа пишет в конце.
Тесты на фейковой платформе — 12 кейсов, разбор лога проверяется без 1С на
логах, снятых со стенда. Ключевой кейс закрывает главное правило навыка:
платформа вернула 1, часть объектов захвачена, навык возвращает 0 с поимённым
предупреждением. Обрезка списка проверена на логе из тридцати объектов —
кейс требует и строку «и ещё 10», и отсутствие двадцать первого объекта
в выводе.
Проверено негативным контролем: намеренно сломанное ожидание роняет кейс,
то есть тесты действительно сверяют вывод, а не проходят вхолостую.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Прогон всех веток вывода подряд показал, что длинный совет про перевыгрузку
вставал МЕЖДУ фактами и вердиктом: строка «захват не выполнен» терялась в
середине. Совет теперь печатается последним во всех ветках — сначала факты,
затем вердикт.
Убрана мёртвая подсказка про -Yes: флага больше нет. Снято утверждение «по
убыванию частоты» у причин отказа commit — проверить его нечем. Сняты повтор
слова «проверьте» и склонение «объект(ов)». Заголовок «Не были захвачены»
дополнен пояснением, что снимать нечего.
Примеры в шапке скрипта приведены к именованной подкоманде.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Порт .py зеркалит .ps1 по порядку функций, именам и комментариям — расхождения
только там, где их диктует рантайм. Общие функции взяты из соседних портов
дословно.
Копии зарегистрированы в check-inline-drift.mjs: db-repo присоединён к шести
платформенным семьям, четыре функции блока реквизитов хранилища заведены новыми
семьями, разбор сообщений хранилища — семьёй с эталоном db-load-xml. Гард сразу
нашёл настоящее расхождение: копии Get-RepositoryArgs в четырёх соседях остались
со старым comma-return, эталон правился позже. Ресинхронизировано.
Разбор сообщений хранилища добавлен и в db-load-git: он тоже грузит в базу
частично и получает те же три отказа платформы.
Подкоманда стала именованной (-Command): в группе скрипты принимают только
именованные параметры, слэш-форму в них переводит модель. Позиционный параметр
у пишущего навыка запрещён гардом check-positional-binding. Mandatory при этом
не ставим — обязательный параметр PowerShell запрашивает интерактивно, а в
пакетном запуске это зависание.
Все девять гардов и функциональные тесты db-* зелёные в обоих рантаймах.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Захват всей конфигурации на большой базе идёт долго и блокирует работу всей
команде, а получался он от одного забытого -Objects: «вся конфигурация» была
умолчанием. Теперь для lock, unlock и commit это отдельный флаг -All, который
нельзя совместить с -Objects. Захват корня с -WithChildren, означающий то же
самое, отсылает к нему же.
update без -Objects остаётся обычным вызовом: получение всех изменений — это
нормальный ежедневный сценарий, а не тяжёлая операция.
Флаг отдельный, а не -Force: у -Force уже есть платформенное значение, разное
по подкомандам, и третье значение сделало бы его нечитаемым.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Модель может попросить захватить реквизит, табличную часть, измерение или
ресурс. Платформа на это отвечает «Загруженный список объектов пуст» — без
имени объекта и без причины, отладить такой ответ нечем.
Навык распознаёт вложенные виды подчинённых и подменяет их объектом-владельцем,
сообщая о подмене. Это не меняет смысл запроса: владелец — минимально возможная
единица захвата для того, что просили, потому что своей сущности у части объекта
нет. Отказ стоил бы лишнего круга без пользы.
Заодно убран comma-return у двух функций: в связке с @() у вызывающего он даёт
вложенный массив, из-за чего список объектов склеивался в один fullName.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверка навыка субагентом на сквозном сценарии вскрыла фактическую ошибку
в инструкции: реквизиты и табличные части перечислялись наравне с формами как
захватываемые объекты. Платформа их объектами не считает — список объектов
получается пустым, причём без секции «отсутствующие в конфигурации». Правишь
реквизит, табличную часть, измерение, ресурс или модуль — захватывай владельца;
формы, макеты и команды захватываются отдельно.
Сообщение «объект не найден» имеет три разные причины: опечатка, отставание
базы от хранилища и попытка захватить то, что объектом не является. Теперь
перечислены все три.
В цикл добавлен нулевой шаг — получение актуального состояния перед началом
работы: правки должны опираться на актуальные версии в том числе тех объектов,
которые не меняются, но используются. Загрузка и обновление БД слиты в один
шаг через -UpdateDB, поэтому цикл не удлинился.
Захват корня конфигурации с -WithChildren отклоняется: это захват всей
конфигурации, а выглядит как захват корня. Для всей конфигурации есть
однозначная форма — вызов без -Objects.
Текстовый отчёт печатается, а не только сохраняется в файл.
Схема repository и extensions[] описана в docs/v8-project-guide.md,
цикл под хранилищем — в docs/db-guide.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Новый навык /db-repo с подкомандами: рабочий цикл (lock, unlock, commit,
update), подключение базы (connect, disconnect), история (report, dump-cfg),
администрирование (create, add-user, copy-users) и сервис (set-label,
optimize, clear-cache). Каскад инструкций в references/.
Ядро навыка — разбор вывода платформы: код возврата о фактическом результате
не говорит. Захват и помещение не атомарны (код 1 при реально захваченном
объекте), а все no-op'ы дают код 0. Вердикт строится по строкам лога и
отражает достижение запрошенного состояния, а не факт изменения.
Захват, обновление и unlock -Force молча подтягивают свежие версии в локальную
конфигурацию. Навык называет полученные объекты и печатает готовую команду
перевыгрузки: без неё частичная загрузка старых исходников молча откатывает
чужие изменения.
Соседние навыки группы получили общий блок разрешения реквизитов хранилища из
.v8-project.json — база под хранилищем не принимает без них ни одной операции
конфигуратора. db-load-xml дополнительно подсказывает действие по трём
сообщениям платформы, db-dump-xml получил -ObjectsFile для передачи списка
объектов между навыками без перепечатывания.
Порты .py следуют отдельно.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
На Windows fs.rmSync и fs.cpSync молча не делают ничего, когда не-ASCII символы
есть в самом аргументе пути (nodejs/node#61067). Для аудитории проекта это боевой
сценарий: кириллическое имя пользователя даёт кириллический %TEMP%, где раннер
создаёт воркспейсы, плюс кириллические имена объектов 1С в путях внутри кейсов.
Замерено на восьми сборках. Затронуты 22.18.0–22.23.2 (последняя LTS Jod) и
24.12.0–24.14.0; исправны 22.15.1, 24.15.0+, 25.x, 26.x. Фикс приехал в fs-слой
Node 24.15 и в ветку 22.x не бэкпортирован, поэтому «обновить Node» вопрос не
закрывает. Зависимость не монотонна по версиям (24.14 чинит rmSync, но не
cpSync) — гард по номеру версии невозможен, решает только сам путь.
Симптомы: rmSync молча ничего не удаляет; cpSync с не-ASCII приёмником молча
ничего не копирует; cpSync с не-ASCII источником валит процесс нативно
(0xC0000409) мимо try/catch. Не затронуты mkdirSync, readdirSync, lstatSync,
copyFileSync (включая перезапись), unlinkSync, rmdirSync — на них стоит обход.
Что ломалось: под кириллическим %TEMP% фикстуры не доезжали до воркспейсов
(meta-info — 15 ложных падений из 26, один кейс ложно-зелёный на пустом
воркспейсе); cfe-validate/module-state-flag-without-file был красным даже при
ASCII %TEMP% (кириллица в самом deletePath); --update-snapshots молча не сносил
старый эталон, то есть портил коммитимые артефакты.
Реализация — единый fsutil в двух побайтно одинаковых копиях (tests/common/ и
внутри автономного навыка web-test), раскатанный на все 42 точки вызова в 11
файлах: tests/skills/*, tests/web-test/*, hooks/test/run.mjs, движок web-test.
Предикат судит по resolve(p), а не по строке аргумента: относительный
ASCII-аргумент при не-ASCII cwd платформа роняет так же молча. Ретраи доживают
до ручного обхода, перезапись как у cpSync с force: true, настоящие ошибки не
глотаются, выживший после удаления путь — громкая ошибка.
Гард tests/skills/check-nonascii-fs.mjs в check-all.mjs проверяет хелпер, а не
платформу (поэтому зелёный и на исправной Node), сверяет хеши обеих копий и
печатает справкой состояние текущей сборки.
Co-Authored-By: androman.pro <5669019+andromanpro@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>