- Shipped
- August 14, 2026 at 9:22 PM UTC
- Author
- Kamo
- Commit
- 92c8e28
Member direct rights now go through RightsHierarchy.normalizeLevel before they are persisted: a granted child pulls its ancestors up to GRANTED at the member level, a denied parent pushes NUKE down its subtree there. Server-side rather than trusting the editor, because this endpoint is reachable directly. A level stored with a child but not its parent is precisely what the resolver's closure pass strips later — the member would keep losing a right the UI showed as granted, with nothing to explain it. Collected into a map first so the hierarchy sees the whole level at once; normalising entry by entry would depend on the order the client happened to send.