@@ -75,7 +75,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
7575 ] ) ;
7676
7777 // Build the dropdown's offerable set server-side: roles that are
78- // (a) assignable on the current plan AND (b) strictly below the
78+ // (a) assignable on the current plan AND (b) at or below the
7979 // inviter's own level. The client just renders these — it doesn't
8080 // need to know about the system-role catalogue or the ladder.
8181 const assignableSet = new Set ( assignableRoleIds ) ;
@@ -84,7 +84,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
8484 . filter (
8585 ( r ) =>
8686 assignableSet . has ( r . id ) &&
87- isStrictlyBelow ( systemRoles , inviterRole ?. id ?? null , r . id )
87+ isAtOrBelow ( systemRoles , inviterRole ?. id ?? null , r . id )
8888 )
8989 . map ( ( r ) => r . id )
9090 : [ ] ;
@@ -98,7 +98,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
9898// dropdown is hidden) or as a defensive default.
9999const NO_RBAC_ROLE = "__no_rbac_role__" ;
100100
101- // An inviter can only assign a role strictly below their own. The
101+ // An inviter can only assign a role at or below their own. The
102102// plugin's systemRoles array is in canonical order (highest authority
103103// first), so array index drives the ladder — earlier index = higher
104104// rank. Plan-tier filtering happens separately via assignableRoleIds;
@@ -117,7 +117,7 @@ function buildRoleLevel(roles: ReadonlyArray<LadderRole>): Record<string, number
117117 return level ;
118118}
119119
120- function isStrictlyBelow (
120+ function isAtOrBelow (
121121 roles : ReadonlyArray < LadderRole > ,
122122 inviterRoleId : string | null ,
123123 invitedRoleId : string
@@ -133,7 +133,7 @@ function isStrictlyBelow(
133133 const invited = level [ invitedRoleId ] ;
134134 // Custom roles aren't in the level table — refuse.
135135 if ( inviter === undefined || invited === undefined ) return false ;
136- return invited < inviter ;
136+ return invited <= inviter ;
137137}
138138
139139const schema = z . object ( {
@@ -168,7 +168,7 @@ export const action: ActionFunction = async ({ request, params }) => {
168168 // Resolve the RBAC role choice. NO_RBAC_ROLE / undefined / unknown
169169 // role → don't pass one through; the runtime fallback handles it.
170170 // Validation: the chosen role must be in the org's assignable set
171- // (plan-tier) and strictly below the inviter's own level.
171+ // (plan-tier) and at or below the inviter's own level.
172172 let resolvedRbacRoleId : string | null = null ;
173173 const submittedRbacRoleId = submission . value . rbacRoleId ;
174174 if (
@@ -200,7 +200,7 @@ export const action: ActionFunction = async ({ request, params }) => {
200200 ) ;
201201 }
202202 if (
203- ! isStrictlyBelow (
203+ ! isAtOrBelow (
204204 systemRoles ,
205205 inviterRole ?. id ?? null ,
206206 submittedRbacRoleId
0 commit comments