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
Было неинтуитивно и вдобавок молча портило файл: `@условие.txt` целиком
заменял -Value, поэтому в файле должен был лежать ещё и адрес, а
естественная форма "Catalog.Товары.Read: @условие.txt" записывала в
условие литерал "@условие.txt" — без единого предупреждения.
Теперь файл читается там, где стоит текст: условие RLS и тело шаблона —
после двоеточия, синоним и комментарий — как всё значение. Адрес
остаётся в команде. Пакет делится по ;; ДО чтения файла, поэтому ;;
внутри условия больше не разделитель — прежний костыль с отключением
пакета убран. Отсутствие файла — ошибка до записи.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
Закрывает #43. Повторный role-compile перевыпускал UUID и переписывал
Rights.xml целиком, поэтому добавить права существующей роли было нечем —
оставалась ручная правка XML.
Операции: add-rights, set-rights, remove-rights, deny-rights, set-rls,
remove-rls, add/set/remove-template, modify-property, set-synonym,
set-comment. Пакет через ;;, значение из файла через @путь, отказ до
записи со всеми причинами разом, авто-вызов role-validate.
Грамматика имён и пресетов — та же, что у role-compile: копии таблиц и
валидаторов взяты побайтово, навык дописан в 16 семей реестра
check-inline-drift.
Правка держит инвариант «меняется только то, что просили»: узел встаёт на
своё место по uuid объекта, право — по канону типа, набор замыкается по
зависимостям, снятие и запрет идут каскадом в обратную сторону. Проверено
раундтрипом через платформу — выход обоих портов совпал с выгрузкой 1С
байт в байт, включая RLS с полями и двумя строками ограничений.
22 кейса на обоих портах, 17 снэпшотов приняты платформой.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq