- Shipped
- August 14, 2026 at 9:22 PM UTC
- Author
- Kamo
- Commit
- ffe66fb
MemberRightsAppliedService now denies any right whose parent chain is not granted. Every row written before the hierarchy existed knows nothing about ancestors, so a role holding MANAGE_SUBSCRIPTION_PLANS without ACCESS_COMMERCE — entirely normal until now — would lose it on the next recalculation. Across 13 live organizations that is an outage, not a migration. Brings all four levels (org roles, departments, job titles, member overrides) up to the invariant the write paths now maintain, using the same rule: a granted descendant pulls its ancestors up to GRANTED. Step 2 is the one that actually prevents the outage — it flips an explicitly DENIED ancestor to granted, because rows that exist survive step 1 untouched and closure would then revoke every descendant beneath them. Security model templates are deliberately NOT touched: a FORCE_NUKE on a parent is an administrator switching a subtree off on purpose, and closure honouring that is the feature. Step 3 is a GATE, not a log line: it re-checks the invariant and fails the run if any level still holds a granted descendant under a non-granted ancestor. A migration that reported success with a violation left behind would hand the problem to the resolver, where it surfaces as a member quietly losing a permission with nothing to explain it.