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
This commit is contained in:
Nick Shirokov
2026-09-13 20:00:06 +03:00
co-authored by Claude Opus 5
parent b28c044e06
commit 9d21ad3429
35 changed files with 1709 additions and 38 deletions
+10
View File
@@ -409,6 +409,16 @@ const FAMILIES = [
// Существует только в PY: PowerShell регистронезависим сам по себе (свойства PSObject, ключи
// Hashtable, -eq/-contains, имена параметров, ValidateSet), поэтому в .ps1 копии нет и быть
// не должно — ps1: null.
// Замыкание прав и фильтр значений по умолчанию: обе роли обязаны считать одинаково, иначе
// созданная и отредактированная роль разойдутся между собой и с платформой.
{
name: 'права роли: close_rights_dependencies', py: 'close_rights_dependencies', ps1: 'Close-RightsDependencies',
variants: [{ id: 'base', authority: 'role-compile', consumers: ['role-edit'] }],
},
{
name: 'права роли: get_default_right_value', py: 'get_default_right_value', ps1: 'Get-DefaultRightValue',
variants: [{ id: 'base', authority: 'role-compile', consumers: ['role-edit'] }],
},
// Значение операции может быть "@путь" — текст читается из файла. Правило поиска общее:
// абсолютный путь как есть, относительный — рядом с DSL (или с редактируемым объектом),
// затем в текущем каталоге. Разъедется — и один навык начнёт искать не там, где другой.