Unified self-service role toggle RPC action.
One static request_response action — self_service_role_set — that
takes {role, enabled} and toggles a global role_grant on the caller for an
allowlisted role. Idempotent in both directions: re-enabling an
already-held role returns changed: false; disabling a role the caller
doesn't hold returns changed: false.
Eligibility is derived by default from RoleSpec.grant_paths —
every role whose grant_paths includes 'self_service'
(GRANT_PATH_SELF_SERVICE) is eligible. The factory accepts an
optional eligible_roles override (validated against the supplied
roles.role_specs at factory time so typos surface at startup
instead of at first call) for deployments that want to lock the
surface down further than the role spec declares. Roles outside
the eligible set are rejected with forbidden + reason
role_not_self_service_eligible.
Audit metadata carries self_service: true so admin reviewers can
distinguish self-toggled role_grants from admin grants/offers. The
role_grant_create / role_grant_revoke metadata schemas declare
self_service: z.boolean().optional() explicitly, so the field is
part of the documented schema surface and is round-trip-validated by
query_audit_log.
Static method name — role lives in the input, not the method name —
so the spec is codegen-compatible (satisfies RequestResponseActionSpec)
and the surface stays constant as consumers add eligible roles. Mirrors
the existing role_grant_offer_create({role}) precedent rather than
generating per-role methods.
Specs and schemas live in auth/self_service_role_action_specs.ts so
client-side codegen can import the surface without dragging in the
query layer.