Answer
Confirm the exact sequence of calls per role: Fnd_User_Role_API.Set_Role(user, role, TRUE) to grant it, Fnd_User_Role_API.Refresh__(user) to refresh that user's role cache, and Security_SYS.Refresh_Active_List__(1) to refresh the active session/security list — all three, run together, is the working pattern for programmatically granting a role and having it actually take effect for the user's next login, rather than assuming the grant alone (without both refresh calls) is sufficient.