Phase C, Stage C-1 (ветка feature/custom-roles): таблица roles (name, label,
base_roles JSON, is_builtin) + засев встроенных. auth.effectiveRoles(role) —
кастомная роль наследует base_roles (какие встроенные гейты проходит); встроенные
— быстрый путь без БД. requireRole() теперь проверяет пересечение allowed с
effectiveRoles → 111 существующих гейтов не задеты (встроенные ведут себя как
прежде). Дизайн: PHASE_C_DESIGN.md. Тест effectiveRoles 5/5; полный backend pass.
ВАЖНО (обнаружено): users.role в канон-схеме имеет CHECK (admin/teacher/student/
free_student), безопасно пересобрать users (FK от многих таблиц, миграции в txn)
нельзя → присвоение кастомной роли пользователю пойдёт через users.custom_role (C-2).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
registry: поле requires (questions.delete→manage, templates.public→manage,
courses.interactive→manage, simulations.quiz→access), проброшено в byRole.
auth.requirePermission: вынесен isEnabled(); право = own AND все requires
(дочернее не работает без родителя). /me и /users/🆔 effective с учётом
requires + requires в ответе. UI permissions.js: каскад — дочернее с
невыполненной зависимостью неактивно (тумблер заблокирован + «Требует: …»).
Тест зависимости. План: plans/permissions-rework/PLAN.md. Backend 216 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>