diff --git a/src/ldap/internal/tools.ts b/src/ldap/internal/tools.ts
index 4976592eb72543d174a3e7c58512634a571f730a..a00066eef55daabd77b1f23817084e8caa05d858 100644
--- a/src/ldap/internal/tools.ts
+++ b/src/ldap/internal/tools.ts
@@ -136,7 +136,7 @@ export class Tools {
         try {
             if (domain == "group")  var dirtyId = ldapConfig.group.gid;
             else                    var dirtyId = ldapConfig.user.uid;
-            dirtyId += "="+ldapEscape.filter("${txt}", { txt: id }) + "," + ldapConfig.dn[domain];
+            dirtyId += "=" + ldapEscape.filter("${txt}", { txt: id }) + "," + ldapConfig.dn[domain];
             return await Basics.searchSingle(domain, ldapConfig[domain][category], dirtyId);
         }
         catch(err) {
@@ -236,11 +236,11 @@ export class Tools {
     /**
      * @memberof LDAP
      * @summary Fonction essentielle qui permet d'ajouter un utilisateur à une catégorie d'un groupe.
-     * @desc Cette fonction fait essentiellement appel à d'autres fonctions de {@link Tools} ; {@link Tools.addIfNotPresent} et {@link Tools.manageInclusions}.
-     * Puis elle gère en interne la récursion et utilise {@link LDAP.change} pour cela.
+     * @desc Cette fonction fait essentiellement appel à d'autres fonctions de {@link Tools} ; {@link Tools.addIncluded} et {@link Tools.addIfAbsent}.
+     * Elle utilise {@link LDAP.addDFS} pour gèrer la récursion.
      * @arg {string} uid - Identifiant du futur membre
      * @arg {string} gid - Identifiant du groupe
-     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné (admin, speaker, member ou follower)
+     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné au type non contraint mais en pratique limité
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
      * @async
      * @static
@@ -261,8 +261,8 @@ export class Tools {
     
     /**
      * @memberof LDAP
-     * @summary Fonction intermédiaire naïve.
-     * @desc Cette fonction enlève 2 d'1 pour un des deux arbres si cette entrée n'était pas déjà absente.
+     * @summary Fonction intermédiaire naïve qui supprime une entrée d'une feuille si elle y existe.
+     * @desc Cette fonction enlève 2 d'1, feuille d'un des deux arbres si cette entrée y existe.
      * La symétrie entre 1 et 2 est voulue et permet de gérer indifférement l'ajout d'un individu dans un groupe ou d'un groupe à un individu.
      * @arg {string} id1 - uid/gid
      * @arg {"group"|"user"} domain1 - Arbre concerné pour l'id1
@@ -299,12 +299,12 @@ export class Tools {
 
     /**
      * @memberof LDAP
-     * @summary Fonction intermédiaire.
-     * @desc Cette fonction gère les inclusions de droits. Elle ne rajoute pas un admin pour les admins, mais elle le rajoute en tant que speaker.
-     * Cette fonction appelle {@link remove} directement pour vraiment éliminer le personnage.
+     * @summary Fonction intermédiaire de suppression des droits obtenus par inclusion.
+     * @desc Cette fonction gère les inclusions de droits, et donc les rétrogadations. Elle ne supprime pas un admin pour les admins, mais le rajoute en tant que speaker.
+     * Cette fonction appelle {@link remove} directement pour vraiment éliminer le personnage des rôles inclus et {@link add} pour vraiment le rajouter.
      * @arg {string} uid - uid de l'utilisateur à ajouter
      * @arg {string} gid - gid du groupe concerné
-     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné (admin, speaker, member ou follower)
+     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, `false` sinon
      * @async
      * @static
@@ -312,10 +312,17 @@ export class Tools {
     static async remIncluded(uid: string, gid: string, category: string): Promise<boolean> {
         switch (category) {
             case "members":
+                Tools.remove(uid, gid, "admins");
                 Tools.remove(uid, gid, "speakers");
+                break;
             case "speakers":
                 Tools.remove(uid, gid, "admins");
+                // Speaker -> member
+                Tools.add(uid, gid, "member");
+                break;
             case "admins":
+                // Admin -> speaker
+                Tools.add(uid, gid, "speaker");
             case "followers":
                 break;
         }
@@ -324,12 +331,13 @@ export class Tools {
 
     /**
      * @memberof LDAP
-     * @summary Fonction intermédiaire de récursion.
+     * @summary Fonction intermédiaire de récursion à la suppression.
      * @desc Cette fonction gère les droits par récursion par une classique Depth First Search.
      * Cette fonction agit uniquement sur l'arbre User, de façon à différencier rôles stricts de rôles hérités.
-     * @arg {string} uid - uid de l'utilisateur à ajouter
-     * @arg {string} gid - gid du groupe concerné
-     * @arg {boolean} direction - direction de la recursion
+     * Elle est sans effet sur les groupes où l'utilisateur dispose de rôles stricts.
+     * @arg {string} uid - uid de l'utilisateur à supprimer
+     * @arg {string} gid - gid du groupe à l'origine de la récursion
+     * @arg {boolean} direction - direction de la recursion (true pour ascendant)
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, `false` sinon
      * @async
      * @static
@@ -351,7 +359,7 @@ export class Tools {
         var to_visit = [gid];
         while (to_visit.length > 0) {
             let cur_gid = to_visit.pop();
-            // Si le statut de uid dans cur_gid est sun statut strict on arrête de boucler
+            // Si le statut de uid dans cur_gid est un statut strict on arrête de boucler
             for (let cat of checks) if ((await Tools.get(gid, "group", cat)).includes(uid)) continue;
             // Sinon on le tue et on boucle
             Tools.remIfPresent(uid, "user", cur_gid, "group", rol);
@@ -364,40 +372,26 @@ export class Tools {
      * @memberof LDAP
      * @summary Fonction qui permet de supprimer un membre d'une catégorie existant d'un groupe.
      * @desc Cette fonction fait essentiellement appel à d'autres fonctions de {@link Tools} passées en argument et {@link LDAP.change}.
-     * Elle essaie d'assurer les propriétés d'inclusion et de récursion du LDAP. Elle est sans effet pour un admin hérité.
+     * Elle essaie d'assurer les propriétés d'inclusion et de récursion du LDAP (voir {@link remDFS} pour la récursion). Elle est sans effet pour un admin hérité.
      * Le comportement est étrange pour la suppression d'un membre hérité ; le membre est supprimé du groupe et des groupes parents, mais pas du groupe à l'origine de l'héritage.
      * @arg {string} uid - Identifiant de l'ex-membre
      * @arg {string} gid - Identifiant du groupe
-     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné (admin, speaker, member ou follower)
+     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné au type non contraint mais en pratique limité
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, `false` sinon
      * @async
      * @static
      */
     static async remove(uid: string, gid: string, category : string): Promise<boolean> {
-        // Invulnérabilité pour les admins hérités
+        // Invulnérabilité pour les admins hérités (par définition, un admin hérité est dans User pas dans Group)
         if ((await Tools.get(gid, "group", "admins")).includes(uid) || !(await Tools.get(uid, "user", "admins")).includes(gid)) {
             Tools.remIfPresent(uid, "user", gid, "group", category);
             Tools.remIfPresent(gid, "group", uid, "user", category);
-            // Gestion des droits par inclusion
+            // Gestion des droits par inclusion (et éventuelle rétrogadation)
             Tools.remIncluded(uid, gid, category);
-            // Rétrogradation
-            switch (category) {
-                case "admins":
-                    // Gestion des droits récursive
-                    Tools.remDFS(uid, gid, true);
-                    // Admin -> speaker
-                    Tools.add(uid, gid, "speaker");
-                    break;
-                case "speakers":
-                    // Speaker -> member
-                    Tools.add(uid, gid, "members");
-                    break;
-                case "members":
-                    // Gestion des droits récursive
-                    Tools.remDFS(uid, gid, false);
-                case "followers":
-                    break;
-            }
+            // Gestion des droits récursive
+            if (category == "admins")  Tools.remDFS(uid, gid, true);
+            // Uniquement pour des membres ; sinon modification localisée au groupe
+            if (category == "members") Tools.remDFS(uid, gid, false);
         }
         return true;
     }
@@ -461,7 +455,7 @@ export class Tools {
             return Tools.ensureUnique((givenName+'.'+lastName).toLowerCase().normalize('UFD'), att, "user", (id: string, n: number) => {
                 if (n=1)        id+='.'+promotion;          // Si prénom.nom existe déjà, on rajoute la promo
                 else if (n=2)   id+='.'+(n-1).toString();   // Puis si prénom.nom.promo existe déjà on passe à nom.prenom.promo .1
-                else if (n>2)   id+=n;                      // Ensuite on continue .123, .1234, etc...
+                else if (n>2)   id+=(n-1).toString();       // Ensuite on continue .123, .1234, etc...
                 return id;
             });
         }