Commit Graph
1437 Commits
Author SHA1 Message Date
51bcc503b0 fix(meta-edit): свойства-списки у заимствованного объекта и не своего типа (#99)
У заимствованного объекта расширения в Properties только изменённые свойства.
add-registerRecord и прочие add-*/set-* над списками не находили элемент, печатали
WARN, файл не меняли и выходили с кодом 0.

- Движения заимствованного документа: отсутствующий RegisterRecords создаётся.
- Прочие списки у заимствованного объекта — отказ. Проверено на 8.3.27: Owners и
  RegisteredDocuments платформа контролирует на равенство основной конфигурации
  (расширение не применяется), BasedOn/InputByString/DataLockFields и движения
  последовательности молча выбрасывает при загрузке.
- Свойство, которого у типа объекта нет (движения у справочника), — отказ; раньше
  modify.properties дописывал его, и meta-validate это пропускал.
- У обычного объекта отсутствие элемента — отказ вместо WARN.
- reference/properties.md: типы объектов в таблице — по выгрузкам ERP, БП, УТ, УНФ.
- verify-snapshots: двухэтапная загрузка и по params.extensionPath кейса.

Co-Authored-By: Roman Syuzyov <rsyuzyov@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 16:41:14 +03:00
Nick ShirokovandClaude Opus 5.5 5d1b5cdf74 fix(meta-edit, epf-validate): у внешней обработки/отчёта нет команд объекта и модуля менеджера (#108)
Платформа выбрасывает Command из внешней обработки/отчёта при сборке
(«не является подчиненным»), а Ext/ManagerModule.bsl — вовсе без
сообщения; epf-build при этом сообщает об успехе. Цепочка молча теряла
написанное: meta-edit добавлял команду, epf-validate её пропускал.

meta-edit: внешние типы внесены в таблицу допустимых детей — команду
не добавляет, предупреждает. epf-validate: Command и ManagerModule.bsl —
ошибки с пояснением.

Refs #108

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 15:20:06 +03:00
Nick ShirokovandClaude Opus 5.5 372510bbc4 fix(meta-edit): внешнюю обработку/отчёт проверять epf-validate (#108)
Автопроверка после правки жёстко звала meta-validate, который
ExternalDataProcessor/ExternalReport не знает: корректная правка
заканчивалась «Unrecognized metadata type». Валидатор теперь выбирается
по типу объекта. В py-порту stdout сбрасывается перед запуском
валидатора — его вывод больше не обгоняет строки meta-edit.

verify-snapshots: кейс meta-edit с setup none собирается epf-build,
а не грузится пустой конфигурацией, где обработку платформа не видит.

Fixes #108

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:59:54 +03:00
Nick ShirokovandClaude Opus 5.5 68a2a11a6c fix(web-publish): сохранять все адреса Listen глобального блока
Перезапись брала адрес только из первой строки Listen: при привязке
к loopback по обоим стекам (127.0.0.1 + [::1]) повторная публикация
молча выбрасывала IPv6-строку. Теперь сохраняются все адреса блока
(повторы схлопываются), меняется только порт; URL — по первому.

Refs #107

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:36:09 +03:00
Nick ShirokovandClaude Opus 5.5 933414f2e6 fix(web-info, web-publish): Listen с адресом привязки
web-info брал первое число строки Listen: при `Listen 127.0.0.1:19081`
порт выходил 127, URL и проверка TCP были ложными. Теперь разбирается
`Listen [host:]port [proto]` (включая IPv6 в скобках), сначала в своём
глобальном блоке; явный адрес идёт в URL и в пробу порта.

web-publish при перезаписи глобального блока сохраняет адрес привязки,
вписанный вручную, — повторная публикация больше не открывает loopback
на все интерфейсы; URL в выводе строятся по нему. Стандартный Listen
комментируется целой строкой (раньше `Listen 0.0.0.0:80` превращался
в мусор, а `Listen [::]:80` оставался активным).

Refs #107

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:27:18 +03:00
Nick ShirokovandClaude Opus 5.5 614206bc5c fix(db-repo, stub-db-create): временные файлы без фиксированных имён
Списки объектов и отчёт db-repo лежали в общем временном каталоге под
постоянными именами: параллельный запуск молча подменял список, по которому
модель перевыгружает устаревшие исходники, а при отказе платформы навык мог
напечатать отчёт предыдущего запуска. Имена получают случайный суффикс —
путь и так печатается в вывод.

Логи /Out базы-заглушки переехали в каталог самой базы: он уникален на запуск
и удаляется вместе с ней, чужой или устаревший лог в вывод не попадёт.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 13:59:42 +03:00
Nick ShirokovandClaude Opus 5.5 eb1796ef94 fix(db-*, epf-*): ошибка до запуска платформы не выдаётся за успех
Временный каталог брался из $env:TEMP, которой вне Windows нет. Join-Path
падал на привязке параметра внутри try/finally без catch: try прерывался,
finally отрабатывал, и скрипт выходил с кодом 0 — платформа не запускалась,
постусловие не проверялось (#106).

- временный каталог — [IO.Path]::GetTempPath() (на Windows тот же путь);
- верхнеуровневый trap { … exit 1 } в 15 скриптах db-*/epf-*, запускающих
  платформу: любая необработанная ошибка даёт код 1 и печатает место;
- уборка временного каталога в finally не падает на пустом пути;
- гард check-ps-portability.mjs держит оба правила.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 13:53:51 +03:00
Nick ShirokovandClaude Opus 5.5 d44da2704e docs(epf-build, erf-build): проверка перед сборкой — про действия, без externalCheck
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 21:27:30 +03:00
Nick ShirokovandClaude Opus 5.5 636344666d feat(epf-build): общие модули конфигурации в проверке исходников через configSrc
Проверка исходников идёт в конфигурации-заглушке, где общих модулей не было:
обработка с кодом БСП не собиралась без -Checks off.

- stub-db-create -ConfigSrc: общие модули, к которым обращается код, получают
  пустых двойников с флагами контекста из выгрузки; неизвестные имена не
  угадываются
- epf-build -ConfigSrc, по умолчанию configSrc базы из .v8-project.json
- подсказки к «Переменная не определена (X)»: модуль не в том контексте /
  модуля нет в configSrc / выгрузка не передана (с -ConfigSrc и -Checks off)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 21:21:28 +03:00
Nick ShirokovandClaude Opus 5.5 b43d4050a6 fix(epf-bsp-add-command): Модификатор обязателен для ЗагрузкаДанныхИзФайла
Полное имя справочника-приёмника; без него команда не привязана
к справочнику в мастере загрузки.

Refs #82

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
2026-09-24 20:20:56 +03:00
Nick ShirokovandClaude Opus 5.5 16345eb83d fix(epf-bsp-add-command): типы команд и сигнатуры обработчиков по контракту БСП
- убран несуществующий ТипКомандыСценарийВБезопасномРежиме(), добавлен
  ТипКомандыЗагрузкаДанныхИзФайла() с шаблонами трёх процедур загрузки;
  список типов помечен исчерпывающим, указана применимость к видам
- СозданиеСвязанныхОбъектов: серверный ВыполнитьКоманду с СозданныеОбъекты
  (4 параметра) — также в epf-bsp-init
- клиентские обработчики: Печать для печатных форм, вариант для создания
  связанных объектов

Refs #82

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
2026-09-24 19:56:38 +03:00
Nick ShirokovandClaude Opus 5.5 647c633a9b fix(db-load-xml): -AllExtensions с загрузкой частями — отказ на обоих движках
Ветка ibcmd молча игнорировала список и загружала все расширения целиком.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
2026-09-24 18:08:20 +03:00
Nick ShirokovandClaude Opus 5.5 80618f0b93 fix(db-load-git): предупреждать об удалениях, которые загрузка частями не применит
Удалённый объект уходит из базы, только если в списке его владелец и в
составе владельца объекта уже нет; удалённый файл-часть (модуль, Ext/)
загрузка частями не удаляет никогда. Скрипт классифицирует удаления:
применимые — [note], остальные — [ВНИМАНИЕ] с причиной; если загружать
нечего, а неприменимые удаления есть — код 1. git diff с --no-renames,
чтобы старый путь переименования был виден. -AllExtensions отвергается
на обоих движках: частями грузится одно расширение за раз.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
2026-09-24 18:08:20 +03:00
Nick ShirokovandClaude Opus 5.5 f98e7e9e81 fix(db-dump-xml): Changes без тихой полной перевыгрузки, инкремент в ibcmd
- -update без -force: при другой версии формата — отказ с подсказкой
  про -Mode Full, а не молчаливая перезапись всего дерева в новом формате.
- Первая выгрузка режимом по умолчанию: в каталог без выгрузки (пусто,
  только .git/README) — полностью; выгрузка без ConfigDumpInfo.xml — ошибка.
- ibcmd: Changes через --sync; Full в непустой каталог — через временный
  каталог и копирование поверх, как пишет конфигуратор; ошибка копирования
  даёт код 1. UpdateInfo с -AllExtensions отвергается, а не выгружает всё.
- py-порт печатает вывод ibcmd, как PS.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANXwbMUhuRFSCsq2RgmTTm
2026-09-24 18:08:20 +03:00
Nick ShirokovandClaude Opus 5 9d07b2e20f fix(form-decompile): не терять заголовок-пробел у параметра динамического списка
Ветка параметра читала заголовок обычным Get-LangText, который схлопывает whitespace-only
в пустую строку; ветка реквизита давно читает его через Get-LangTextWS. Заголовок-пробел —
им гасят подпись — превращался в "" , а компилятор трактует пустую строку как отсутствие
ключа и подставляет авто-вывод из имени: " " молча становилось «Организации ВЕТИС».
Тот же класс, что и потерянный регистр, соседняя ветка той же функции.

В корпусе (13891 узел dcssch:title в четырёх типовых) платформа не пишет ни пустой,
ни whitespace-only заголовок, поэтому на разборе чужих выгрузок правка ничего не меняет —
она чинит наш собственный раундтрип compile → decompile → compile.

Кейс dl-parameter-title-case расширен вторым параметром с заголовком-пробелом; на исходной
версии падает в обоих портах. Снэпшоты приняты платформой (verify-snapshots, 3/3).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
2026-09-22 20:42:20 +03:00
Nick ShirokovandClaude Opus 5 df47d4d34f test(check-inline-drift): взять авто-вывод заголовка title_from_name под гвард
Title-FromName/title_from_name продублирован в form-compile и form-decompile: компилятор
пишет заголовок, которого нет в DSL, а декомпилятор на этом же выводе решает, можно ли
заголовок опустить. В комментарии копия заявлена «ТОЧНЫМ зеркалом», но ничем не держалась.
Разъедутся копии — заголовок либо теряется, либо дублируется, и в обоих случаях текст на
форме меняется молча, без ошибки платформы.

Эталон варианта — form-compile: компилятор авторитетен, декомпилятор его зеркалит. Тело
копии приведено к эталону без смены поведения (разбиение строк в ps1; в py — выражение
вместо индексного цикла и `not parts` вместо `len(parts) == 0`; `p.isupper()` и
`p == p.upper()` расходятся только на частях без буквенных символов, а после двух
разделяющих регулярок такая часть недостижима).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
2026-09-22 20:06:46 +03:00
cf02c69ff5 fix(form-decompile): не терять заголовок, отличающийся от авто-вывода только регистром
Заголовок реквизита формы и параметра динамического списка опускается, если равен
авто-выводу из имени: компилятор восстановит его сам. Сравнение было
регистронезависимым (в ps1 -eq/-ne, в py зеркалящий их _ps_ieq), поэтому
«Организации ВетИС» при имени ОрганизацииВЕТИС считался равным авто-выводу
«Организации ВЕТИС», выпадал из DSL, и обратная сборка молча меняла текст на форме.

Эталон сравнения — не PowerShell, а компилятор: он пишет заголовок как есть, значит
опускать можно только при побайтовом совпадении. В ps1 этого не даёт и -ceq — он
культурный, мягкий перенос и NFD-разложение для него ничего не весят, и порты
разошлись бы между собой. Сравнение идёт через [string]::Equals(..., Ordinal),
что и есть зеркало питоновского ==. _ps_ieq больше не нужен и удалён.

Два регресс-кейса: реквизит формы (регистр + невидимый мягкий перенос) и параметр
динамического списка. Оба падают на исходной версии в обоих портах.

Найдено на типовой конфигурации: Документ.ИсходящаяТранспортнаяОперацияВЕТИС.ФормаСписка.

Co-Authored-By: Шпаков Антон Александрович <shpakov.anton.job@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
2026-09-22 20:06:32 +03:00
Nick ShirokovandClaude Opus 5 38c26f319d chore(template-add): синхронизировать версию PS-порта с py
В предыдущем коммите бампнулся только py-порт: ps1 остался v1.26 при py v1.27.
Правила «версия в обоих файлах» набор тестов не проверяет — расхождение видно
только глазами в шапке.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 19:01:35 +03:00
Nick ShirokovandClaude Opus 5 dc470a3803 fix(template-add,help-add): закрыть находки ревью по коду языка и странице
help-add терял текст справки. Отказ смотрит только на Help.xml, а страница может
пережить его (удалённый дескриптор, частичная выгрузка, справка, сделанная руками):
тогда запись шла безусловно и стирала содержимое с кодом 0 и рапортом [OK].
Теперь существующая страница сохраняется, создаётся только дескриптор — та же
защита, что уже стоит в template-add. Прежняя оценка «в help-add потери данных нет»
была неверной: она опиралась на чтение кода, а не на замер.

Код языка: регулярка пропускала имена устройств Windows. При -Lang nul py-порт
молча писал <Page>nul</Page> и пустой каталог с кодом 0 (страница уходила в NUL),
а PS падал исключением — то есть порты ещё и расходились. Якоря \A…\z вместо ^…$:
последние в обоих языках допускают перевод строки в конце.

Проверка вынесена в Test-LangCode / is_valid_lang и внесена в реестр
check-inline-drift: inline-блок гард не видел, а копий у него две.

Плюс мелочи оттуда же: мёртвый дизъюнкт в $pageExists убран; полумигрированное
дерево (дескриптор есть, старый Ext/Template.html остался рядом) теперь получает
предупреждение, а не молчание; существование страницы в py сверяется без учёта
регистра — иначе на Linux -Lang RU писал бы вторую страницу мимо дескриптора.

Четыре кейса на каждый сценарий. Существующие эталоны не сдвинулись.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 18:50:58 +03:00
Nick ShirokovandClaude Opus 5 c619da4a0a fix(help-add): валидация кода языка до записи
-Lang брался как есть и шёл и в текст XML, и в имя файла страницы. Измерено на
обоих портах: `-Lang "../../beyond"` писал beyond.html двумя уровнями выше
каталога Ext/Help и клал <Page>../../beyond</Page> в дескриптор, а `-Lang ""`
давал скрытый файл «.html» и пустой <Page></Page>. Оба случая — код возврата 0
и рапорт об успехе, то есть отказ платформы был бы тихим.

Проверка — дословная копия той, что появилась в template-add: навыки автономны,
но формат «дескриптор + страница» у них общий, и расходиться копиям незачем.

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

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 18:34:39 +03:00
Nick ShirokovandClaude Opus 5 69175a7a63 fix(template-add): не терять содержимое страницы при добавлении языка
Ревью вскрыло два дефекта во вчерашней ветке добавления языка — оба
воспроизведены руками.

1. Миграция старой раскладки шла ДО проверки дубля языка. При дефолтном -Lang ru
   (самый вероятный способ повторно позвать навык на старом макете) Template.html
   уже был перенесён, затем срабатывал отказ «страница ru уже существует» — и
   дескриптор не записывался вовсе. Макет оставался разобранным: страница есть,
   дескриптора нет, платформа такую раскладку снова молча игнорирует. Печатавшийся
   при этом [WARN] «создан дескриптор» был неправдой.

2. Файл страницы писался безусловно, а проверялся только список <Page> в
   дескрипторе. При рассинхроне (страница на диске есть, в дескрипторе нет —
   в том числе после дефекта 1) содержимое затиралось пустым скелетом с кодом 0.

Теперь ветка сначала разбирает состояние целиком и только потом пишет, файл
страницы не перезаписывается никогда: существующая страница подхватывается, в
дескриптор дописывается <Page>. Старая раскладка с -Lang ru — это не коллизия,
а ровно тот случай, ради которого миграция и нужна. Состояние, где есть и
Template.html, и Template/ru.html, навык не разруливает сам: отказ до изменений.

Плюс валидация -Lang: код языка идёт и в текст XML, и в имя файла, поэтому пустое
значение давало файл «.html» с пустым <Page></Page>, а разделитель пути — запись
мимо Ext/Template. Оба отказа платформы были бы тихими.

Четыре новых кейса закрывают каждый сценарий; паритет портов сверен побайтово.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 17:41:25 +03:00
Nick ShirokovandClaude Opus 5 04563a6973 feat(template-add): добавление страницы макета на втором языке
Повторный вызов на существующем макете безусловно отказывал, поэтому второй язык
приходилось добавлять руками — дописывать <Page> в дескриптор и заводить файл
страницы. Случай не редкий: в выгрузке ERP 54 макета из 70 двуязычные.

Теперь при -TemplateType HTML существующий макет — повод добавить страницу, а не
отказать. Порядок <Page> — по коду языка (в ERP так во всех 54). Версия формата
берётся из самого дескриптора, чтобы правка чужой выгрузки не меняла формат.
Метаданные макета и ChildObjects не трогаются: перезапись сменила бы UUID.

Отказ остался там, где он по делу: тот же язык повторно, нехтмловый макет под тем же
именем, любой не-HTML тип.

Макет в старой раскладке (Ext/Template.html) мигрируется с громким [WARN]: платформа
такую раскладку игнорирует, то есть состояние и так нерабочее, а создать её могла
только версия навыка без -Lang — значит это страница на языке по умолчанию.

Проверено на 8.3.27.1859: двуязычный макет доходит до базы и возвращается обратно
байт в байт — и дескриптор, и обе страницы. Попутно измерено: страницу на языке,
не объявленном в Languages/, платформа принимает — поэтому состав языков не проверяем.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 17:19:57 +03:00
Nick ShirokovandClaude Opus 5 b8b8140e3b fix(help-add): парные <meta> и <link> в странице справки
Скелет страницы писал самозакрывающиеся <meta …/> и <link …/>. Платформа пишет их
парными: на 400 страницах справки из выгрузки acc_8.3.27 — 400 раз </meta>, нулей нет.
Расхождение безвредно для загрузки, но даёт лишний дифф при первом сохранении
страницы в Конфигураторе.

Замечено при разборе раскладки HTML-макета: template-add берёт скелет отсюда же.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 16:45:57 +03:00
1e94b9236f fix(template-add): HTML-макет в платформенной раскладке
Навык создавал Templates/<Макет>/Ext/Template.html. Платформа такой файл молча
игнорирует: загрузка проходит без ошибок и предупреждений, а макет в базе пустой.

HTML-макет платформа хранит парой, как справку: дескриптор Ext/Template.xml со
списком страниц и сама страница Ext/Template/<язык>.html (картинки — в _files/).
Раскладка снята с выгрузки acc_8.3.27: 136 HTML-макетов из 136, дескриптор во всех
байт в байт одинаков. Шапка страницы — в виде редактора платформы (одной строкой,
парный </meta>), чтобы первое сохранение в Конфигураторе не давало диффа.

Язык страницы задаётся параметром -Lang (дефолт ru) — как в help-add.
Вывод навыка теперь различает содержимое и дескриптор: «Содержимое» указывает на
страницу, иначе правка ушла бы в дескриптор.

Проверено на 8.3.27.1859: LoadConfigFromFiles + UpdateDBCfg + обратная выгрузка —
текст макета доходит до базы и возвращается байт в байт, дескриптор тоже.
Контроль — тот же макет в старой раскладке: в выгрузке из базы тела нет вовсе.
EPF: epf-build → epf-dump — раскладка возвращается с содержимым.

Диагноз и раскладка — из PR #98.

Co-Authored-By: Roman Syuzyov <rsyuzyov@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 16:45:50 +03:00
Nick ShirokovandClaude Opus 5 6eaf3211cc feat(db-load-xml,db-load-git): предупреждать о полной загрузке при Configuration.xml в списке
Список частичной загрузки задаёт объекты метаданных: вместе с объектом платформа грузит
его дочерние объекты (формы, макеты) и файлы Ext/. Configuration.xml — объект
«Конфигурация», поэтому его наличие в списке даёт полную загрузку конфигурации, а не
перечисленных объектов. В db-load-git он попадает в список из диффа сам, без участия
пользователя.

Замерено на обоих движках (1cv8 и ibcmd): правило одинаково.

- SKILL.md обоих навыков: строка про цену Configuration.xml в списке;
- хинт в выводе обеих веток движка;
- два кейса на срабатывание и на отсутствие ложного срабатывания.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 14:37:21 +03:00
Nick ShirokovandClaude Opus 5 dd0ad18076 fix(meta-compile,meta-validate): «только единственным» касается и характеристики
Правило было уже реальности: Конфигуратор не даёт включить характеристику в
составной тип вместе ни с каким другим типом — в том числе с другой
характеристикой. Предупреждение теперь ловит и DefinedType.X, и
Characteristic.X.

Корпус подтверждает даже чище, чем для определяемого типа: 1741 характеристика
единственным типом и 0 в составных (у определяемого было 6500 против 1).
Уровень прежний — предупреждение: загрузчик такое принимает, проверено
загрузкой в базу.

Голых метатипов это не касается: DocumentRef + CatalogRef — обычный составной
тип, 525 вхождений в поставляемых ERP и БП.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 19:54:29 +03:00
Nick ShirokovandClaude Opus 5 0db3656c9c fix(meta-compile,meta-validate): в типе значения ПВХ допустим только определяемый тип
Условие было шире реальности: предупреждение пропускало и Characteristic.X.
В дереве выбора типа значения ПВХ характеристики нет вовсе — тип значения
характеристики не может быть значением характеристики. Единственное множество,
которое Конфигуратор там предлагает, — ОпределяемыйТип.

Уровень прежний: платформа такую конфигурацию грузит (проверено загрузкой в
базу), поэтому предупреждение, а не отказ. В корпусе erp+acc ни один из 24 ПВХ
множеств в типе значения вообще не использует.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 19:48:06 +03:00
Nick ShirokovandClaude Opus 5 24b1aa9ee1 fix(meta-compile,meta-validate): правки по второму ревью
Три находки, все воспроизведены:

1. Счётчик членов составного типа не видел форму с локальной xmlns:
   тип из чужого пространства имён пишется как <v8:Type xmlns:mxl="…">, а
   регулярка требовала '>' сразу за именем тега. В итоге на одном и том же
   файле meta-compile молчал, а meta-validate предупреждал — навыки
   расходились в оценке одного содержимого. Радиус: оба порта.
2. Report-OK проверки 23 печатался безусловно — то есть сразу после
   собственного ERROR, и раздувал счётчик проверок в итоговой строке.
   Соседние проверки (21, 22) так не делают.
3. ToString() копировал весь буфер вывода на каждый реквизит: O(n^2) на
   крупном объекте. В файле уже есть ranged-перегрузка ровно для этого
   (Emit-TypeContent). py-порт был изначально корректен — он режет список.

Добавлен кейс на тип с локальной xmlns: без него находка 1 вернулась бы
незамеченной, потому что обычный составной тип её не показывает.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 19:28:46 +03:00
Nick ShirokovandClaude Opus 5 1481e58bfc feat(meta-compile,meta-validate): проверки на тип-множество там, где его не принимают
Запрет неочевидный, а отказ платформы приходит поздно и одной строкой из
Конфигуратора. Три уровня строгости, все замерены загрузкой в базу на
8.3.24.1691, а не выведены из документации:

- ОШИБКА — множество в составе определяемого типа. Платформа отвергает файл
  целиком («ОпределяемыйТип.<Имя> - Недопустимый тип»); проверено на
  ОпределяемыйТип, Характеристика, ЛюбаяСсылка и голых ссылках, контрольный
  вариант с обычным CatalogRef.<Имя> принят. Отсюда и ноль таких узлов в
  корпусе — это запрет, а не совпадение.
- ПРЕДУПРЕЖДЕНИЕ — голый метатип в типе значения ПВХ. Загрузка проходит, но
  Конфигуратор такой тип не предлагает: в дереве выбора СправочникСсылка и
  ДокументСсылка — папки без флажка, а ЛюбаяСсылки там нет вовсе. Плюс AnyRef
  раундтрипом возвращается как AnyIBRef, то есть молча меняется.
- ПРЕДУПРЕЖДЕНИЕ — определяемый тип одним из составного. Конфигуратор даёт
  выбрать его только единственным, но отказывать нельзя: в корпусе erp+acc
  6500 единственных против 1 составного, и этот один лежит в типовой ERP
  (Документ.НачислениеИСписаниеБонусныхБаллов.Баллы — два определяемых типа
  подряд). Навык не вправе отказаться собрать то, что поставляет 1С.

В meta-compile состав множеств берётся опросом самого эмиттера, а не второй
копией его регулярок: список видов живёт в Emit-TypeContent, и копия
разъехалась бы с ним молча.

Развёртка по 3428 объектам ERP: ровно 2 срабатывания, оба на том самом
реальном составном реквизите. Ложных нет.

Предупреждения пишутся прямо в stderr, а не через Write-Warning: в PS 5.1 тот
уходит не в тот поток (конвенция из cf-init), и до проверки они не доезжали.

Оба кейса-предупреждения проверены платформой — она эти конфигурации грузит,
что и подтверждает выбор «предупреждение, а не отказ».

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 18:45:16 +03:00
Nick ShirokovandClaude Opus 5 079a177cf4 docs(meta-compile): откат правки reference/code.md
Правка нарушала два наших правила сразу. Фраза «русские имена принимаются
наравне с английскими» описывала прощающий ввод — его документировать нельзя,
иначе модель получает развилку вместо одной каноничной формы. Замечание «в
выгрузке ERP так задана треть подписок» — про наше исследование, а не про
использование навыка: следов фикса в инструкции не держим.

Формы источника остаются описанными в docs/meta-dsl-spec.md, а круг
«вывод meta-info → вход meta-compile» держит check-typeset-coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 18:21:08 +03:00
Nick ShirokovandClaude Opus 5 e9d45984dc fix(meta-info,meta-compile,+5): правки по ревью — регистр, BOM, голый менеджер, порог
Семь находок ревью, все подтверждены воспроизведением:

1. Голый менеджер в источниках печатался по-английски: карта объектных видов
   применялась только к форме с точкой, а meta-compile пишет «cfg:DocumentManager»
   обычным v8:Type. Восемнадцать добавленных *Manager-записей были недостижимы.
   Суффикс «(все)» ему не ставится — это сам тип менеджера, а не класс объектов.
2. Новые регулярки прощающего ввода расходились между портами: -replace в
   PowerShell регистронезависим, re.sub — нет. «ДокументОбъект (Все)» проходил в
   ps1 и падал в py. Добавлен re.IGNORECASE — это задокументированная ловушка
   портирования, и она же снова сработала.
3. В шесть py-портов попал BOM (перекодировка при бампе версии), и перед
   «#!/usr/bin/env python3» он ломает shebang на POSIX. Снят там, где его не было
   в HEAD; в mxl-compile.py он был изначально и оставлен.
4. Правило со стрелкой из Resolve-TypeStr убрано. form-compile режет тип по [|+]
   ДО резолвера, поэтому строка глоссария «A -> B | C» молча превращалась в
   составной тип «A | C» вместо одного A. Половина строки, принятая за тип, —
   хуже громкого отказа. Остались суффикс «(все)» и счётчик «— типов: N».
5. py-порт meta-info не переиспользовал общий климб до корня конфигурации:
   правка тогда молча не применилась, и порты разошлись по структуре.
6. Found выставлялся до разбора состава определяемого типа: падение внутри
   давало «состав пуст» — ложь вместо «файл типа не разобран».
7. Порог сворачивания источников считался по сумме множеств и явных типов, а
   сворачивались только явные: пять множеств плюс один тип прятали этот тип, а
   шесть типов без множеств давали «и ещё» после пустоты. Порог теперь по числу
   явных типов, «и ещё» убрано как ложное.

Плюс формы источника подписки описаны в reference самого навыка: раньше они
были только в docs/meta-dsl-spec.md, а навык обязан быть самодостаточным.

Добавлен кейс на голого менеджера — без него находка 1 вернулась бы незамеченной.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 18:14:27 +03:00
Nick ShirokovandClaude Opus 5 510c9163e3 fix(meta-info): кейс на множество в определяемом типе — фикстура вместо невалидной конфигурации
verify-snapshots отверг кейс: «ОпределяемыйТип.Любой - Недопустимый тип».
Спросил платформу — v8:TypeSet внутри определяемого типа запрещён вообще, и не
только AnyRef: отвергнуты также голый CatalogRef, Characteristic.X и вложенный
DefinedType.X, при том что контрольный вариант с обычным CatalogRef.X принят.
Замерено на 8.3.24.1691 загрузкой в базу. Отсюда и ноль таких узлов в корпусе —
это не совпадение, а запрет.

Комментарий в обоих портах утверждал обратное («платформа допускает») — правда
записана, факт добавлен в docs/meta-dsl-spec.md.

Сам разбор v8:TypeSet в выводе определяемого типа оставлен: meta-info читалка,
и рукотворный файл с таким узлом она обязана показать честно, а не потерять его
молча вместе с правдивым счётчиком. Кейс переведён на рукотворную фикстуру с
объявленной причиной пропуска платформенной проверки.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 17:53:46 +03:00
Nick ShirokovandClaude Opus 5 74e88e75c2 feat(meta-compile,meta-edit,form-compile,form-edit,mxl-compile,skd-compile,skd-edit): вывод meta-info принимается на входе
Вывод читающего навыка модель несёт обратно в компилятор, а тот его не
понимал. Из 41 метки, которую печатает meta-info, в словаре typeSynonyms не
было 33: «ДокументОбъект.Заказ» из источников подписки meta-compile отвергал с
«Неизвестный тип», хотя сам же печатал эту форму через meta-info. Не
принимались и «ПВХСсылка.ВидыСубконто», и «Характеристика.Виды» — при том что
Characteristic.Xxx описан в спеке как штатная форма DSL.

Добавлены русские имена объектных типов, менеджеров и наборов записей.
Аббревиатуры (ПВХ/ПВР, РС/РН/РБ/РР) приняты наравне с полными именами: вывод
навыка сокращает долгие виды, чтобы в списке на сорок реквизитов не терялось
имя объекта, и раз он их печатает — обязан принимать.

В Resolve-TypeStr снимаются хвосты, которые дописывает глоссарий meta-info:
суффикс «(все)», счётчик «— типов: N» и раскрытие после стрелки. Срезаются
только эти известные формы — круглые скобки заняты параметризованными типами
вроде Число(15,2), слепой срез сломал бы их.

Функция под гардом check-inline-drift, поэтому правка синхронна во всех семи
навыках и обоих портах.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 17:34:49 +03:00
7cbc8e9b7f fix(meta-info): множества типов (v8:TypeSet) показываются как тип
Источники подписки на событие читались только из v8:Type. Если источник задан
множеством — определяемым типом или целым классом «все документы» — строки
«Источники» не было вовсе, и подписка выглядела так, будто ни на что не
срабатывает. В erp_8.3.24 так задана треть подписок: 151 из 492 вообще без
v8:Type, в acc_8.3.24 — 126 из 443.

Тот же дефект в типах реквизитов, и там он чаще. Вхождений
v8:TypeSet>cfg:DefinedType в Catalogs/Documents/InformationRegisters/
AccumulationRegisters ERP — 8841, из них 7575 (86%) указывают на определяемый
тип ровно с одним членом. АвансовыйОтчет печатал «Сумма
ОпределяемыйТип.ДенежнаяСуммаНеотрицательная», и было не видно, что это
Число(15,2): имя выглядит как ссылочный тип. Голый метатип-категория
(«ДокументСсылка» без точки = любой документ) отличался от конкретного
«ДокументСсылка.Заказ» только наличием точки посреди длинного имени.

Правило одно на все места: имя множества стоит в строке типа, а раскрытие —
ровно одной записью на уникальное множество, глоссарием в конце вывода.
Раскрывать в строке нельзя дважды: псевдоним повторяется в объекте десятками
раз (в АвансовомОтчете — 12), а в корпусе есть определяемые типы на 596 типов —
вывод упёрся бы в постраничник и съел хвост объекта. Выше порога
composedTypeThreshold вместо состава идут счётчик и готовая команда раскрытия.
Голый метатип получает суффикс «(все)»: разница становится словом, а не
пунктуацией. Характеристика ПВХ помечена как набор, определяемый данными, —
из выгрузки её состав не берётся в принципе.

Попутно того же класса:
- объектные типы (DocumentObject.X, *RecordSet.X, *Manager.X) печатались
  по-английски в выводе определяемого типа и по-русски в источниках подписки —
  одно понятие двумя видами; в карту добавлены недостающие виды, включая
  ConstantValueManager и ChartOfCalculationTypesObject;
- собственный вывод определяемого типа читал только v8:Type и молча терял
  v8:TypeSet — счётчик «Типы (N)» врал;
- корень конфигурации ищется климбом до Configuration.xml, общим с проверкой
  поддержки, а не фиксированным «на два уровня выше»;
- состав определяемого типа кэшируется по имени;
- нечитаемый файл типа назван нечитаемым, а не отсутствующим.

Проверено корпусом: 5129 прогонов по erp_8.3.24 и acc_8.3.24 в трёх режимах,
каждое расхождение с эталоном до правки классифицировано, необъяснённых нет.
Паритет портов — 5129 из 5129, 103912 строк совпали.

Проблему и замеры по подпискам сообщил автор PR #96.

Co-Authored-By: Шпаков Антон Александрович <shpakov.anton.job@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 17:34:27 +03:00
Nick ShirokovandClaude Opus 5 77c797e6d0 fix(db-repo,db-load-cf,epf-build): объявления цепочки v8path выше точки вызова
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>
2026-09-19 21:16:22 +03:00
Nick ShirokovandClaude Opus 5 874ae650ca feat(db-*,epf-*,erf-*,web-publish): v8path на уровне записи базы
В одном проекте базы могут жить на разных версиях платформы, а версию формата
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>
2026-09-19 20:35:20 +03:00
Nick ShirokovandClaude Opus 5 9b1bf22e8f fix(cfe-patch-method,cfe-validate): маркер возвращается той же строкой, счётчик стал регистронезависимым
Две находки ревью.

Приняв хвостовой комментарий у маркера, разбор запоминал только содержимое
блока, а -Actualize писал на его место голое ключевое слово: авторская пометка
вроде «#Вставка // проверка прав, ТЗ-142» молча исчезала при первой же
актуализации. Теперь строки открывающего и закрывающего маркера едут вместе с
блоком и возвращаются как были — с комментарием, отступом и своим написанием;
ключевое слово остаётся запасным вариантом. Кейс actualize-marker-tail-comment
эту потерю ловит: без правки падает.

Счётчик контролируемых методов в cfe-validate был единственным новым
сопоставлением без учёта регистра, хотя язык регистронезависим: на
&changeAndValidate он показывал ноль и расходился с cfe-patch-method -Check,
на который сам же и ссылается.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 18:31:33 +03:00
Nick ShirokovandClaude Opus 5 c32b82f72d fix(cfe-patch-method,cfe-diff,cfe-validate): Или в таблице ключевых слов
Пара And/И в таблице была, а Or/Или — нет: при снятии пар с таблицы строк
платформы односимвольные слова отсеял фильтр длины, и пропуск не бросался в
глаза. Сегодня кода, который эмитирует Или, нет (условия собираются только из
НЕ и И), так что дефекта это не давало, но таблица — единственное место
правды о языке, и дыра в ней ждала своего часа.

Заодно подписано, почему отрицание эмитируется как "НЕ": платформа пишет
"Не", язык регистронезависим, а смена регистра сдвинула бы все эталоны.

Val, Insert/EndInsert и Delete/EndDelete перепроверены на платформе, а не по
дампу бинарника: их английские написания в таблице строк не видны из-за
склейки одинаковых литералов. Оракул — проверка применимости расширения:
Val Item и Знач Item платформа считает одним списком параметров, блоки
#Delete/#EndDelete принимает, а подсунутый #Deletee отвергает
(«Ожидается оператор препроцессора»).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 17:59:21 +03:00
Nick ShirokovandClaude Opus 5 1b2c9388e8 feat(cfe-patch-method,cfe-diff,cfe-validate): распознавание английских ключевых слов BSL
Платформа принимает встроенный язык в двух написаниях, а навыки читали только
русское: в расширении на английском перехватчики и блоки правок не находились,
и отчёт выглядел чистым. -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>
2026-09-19 17:49:30 +03:00
Nick ShirokovandClaude Opus 5 e2c7f38518 fix(epf-build): конвертация в заглушку не калечит имена с суффиксом вида объекта
Замена 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>
2026-09-14 12:09:58 +03:00
Nick ShirokovandClaude Opus 5 56b0c0cfd7 fix(form-compile): база относительного пути не остаётся пустой
В 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
2026-09-13 21:25:55 +03:00
Nick ShirokovandClaude Opus 5 5b00410485 fix(role-compile,role-edit): шесть находок ревью
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
2026-09-13 21:20:18 +03:00
Nick ShirokovandClaude Opus 5 5ec732e12b fix(role-edit): пустой узел после отброшенного запрета и разворот массива в PS
Два дефекта, найденных вычиткой перед ревью.

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
2026-09-13 21:05:57 +03:00
Nick ShirokovandClaude Opus 5 e8c92362d5 docs(role-compile,role-edit): убрано то, что навык объясняет сам
Таблица «что хранится при каких умолчаниях роли» ушла из инструкции:
модель попадает в этот случай ровно в момент, когда навык уже печатает
«запрет совпадает с умолчанием роли и платформой не хранится». Вместо
описания механики сообщение дополнено подсказкой, где запрет имеет
смысл, — теперь вывод самодостаточен, а инструкция короче.

Там же сжаты формулировки, которые дублировали текст ошибок: про отказ
до записи, про право сервиса на корне, про ограничение RLS без права.
Назначение deny-rights в таблице операций названо прямо: закрыть
реквизит или ТЧ, которые иначе наследуют права объекта.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
2026-09-13 20:52:29 +03:00
Nick ShirokovandClaude Opus 5 9fcf7329ba docs(role-compile,role-edit): инструкции только про применение
Убран раздел про русские синонимы: прощающий ввод в инструкции даёт
модели развилку там, где есть одна каноничная форма — английская.
Убран раздел про замыкание набора при выдаче: навык перечисляет
дописанное в выводе, а в инструкции это лишняя механика. Оставлено
только то, что меняет решение ДО вызова: снятие и запрет уносят больше
перечисленного.

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

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
2026-09-13 20:23:20 +03:00
Nick ShirokovandClaude Opus 5 9d21ad3429 fix(role-compile,role-edit): замыкание прав и фильтр значений по умолчанию
Замер на платформе (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
2026-09-13 20:00:06 +03:00
Nick ShirokovandClaude Opus 5 b28c044e06 fix(role-edit): операции применяются в порядке ввода
Внутри навыка они были сгруппированы по типам, поэтому список
[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
2026-09-13 18:47:47 +03:00
Nick ShirokovandClaude Opus 5 d425833709 refactor(skd-compile,skd-edit,form-compile,role-*): одно правило поиска @файла
Чтение текста из файла жило тремя копиями под именем 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
2026-09-13 18:23:06 +03:00
Nick ShirokovandClaude Opus 5 121c9c8ad3 feat(role-compile): условие RLS и тело шаблона можно взять из файла
Симметрично 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
2026-09-13 18:02:35 +03:00
Nick ShirokovandClaude Opus 5 9c14715359 fix(role-edit): @файл читается в позиции значения, а не вместо всей строки
Было неинтуитивно и вдобавок молча портило файл: `@условие.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
2026-09-13 17:56:39 +03:00