# Atelier de cadrage Phase 0 — Évolution LMS EFEKTIVACADEMIE

Support de décision. Chaque fiche : constat (factuel, issu du code), options avec conséquences,
recommandation argumentée, issues du backlog impactées, et un encadré **Décision** à remplir en
séance. Les recommandations valent **décision par défaut** si le point n'est pas tranché en
atelier — pour ne jamais bloquer le démarrage des sprints.

**Références** : `01_ANALYSE_FEATURES_UX.md` (zones d'ombre Zx, conflits Cx), `03_BACKLOG_ISSUES.md` (issues EA-xxx).

---

## Organisation proposée

| Session | Durée | Thème | Fiches | Participants |
|---|---|---|---|---|
| **S-A** | 1h30 | Modèle de contenu & expérience apprenant | F1 → F6 | Produit + dev back + dev front |
| **S-B** | 1h30 | Rôles, organisation, RGPD | F7 → F11 | Produit + RH/métier + dev back |
| **S-C** | 1h | Périmètres fonctionnels | F12 → F18 | Produit + dev |
| **S-D** | hors atelier | Audit technique (schéma prod, environnements) | F19, F20 | Dev back seul, restitution 30 min |

Ordre conseillé : **S-A avant tout** (F1 conditionne les sprints 3-4 et le studio), S-B avant le
sprint 5, S-C peut se tenir en semaine 2. S-D démarre immédiatement (aucune décision produit requise).

Matériel de séance : ce document + accès à la plateforme de prod pour arbitrer sur pièces.

---

# SESSION A — Modèle de contenu & expérience apprenant

## F1 · Modèle Formation > Leçon > Support (Z1, C9) — LA décision structurante

**Question** : comment faire coïncider le vocabulaire cible du doc Features UX (une *leçon*
contient plusieurs *supports* typés : vidéo, podcast, réel, article, fiche…) avec le modèle
actuel (un *cours* contient des *sections*, une section contient des *lessons* — 1 média chacune,
type deviné par l'extension du fichier — et des *quiz*) ?

**Constat code** : `sections` > `lessons` (1 média : `video_url`/`file_url`/URL externe) + `quizzes`.
Aucun champ de type explicite ; le front devine `video|audio|pdf|image|text` par extension
(`getFileType()`, StudentCourseDetail.jsx). « Durée de la leçon = somme des supports » n'a pas de
sens dans ce modèle.

| Option | Description | Conséquences |
|---|---|---|
| **A. Renommage + typage** ✅ | Les tables restent (`sections`, `lessons`) ; on ajoute `lessons.support_type` explicite ; le vocabulaire exposé (API/UI) devient : section = **Leçon**, lesson = **Support**. | Migration légère, rétrocompatible, script de rétro-typage depuis extensions/URLs. Aucune reprise de données lourde. |
| B. Refonte des tables | Nouvelles tables `lecons`/`supports`, migration des données. | 2-3 semaines de migration + risque sur les données de prod, pour un gain nul fonctionnellement. |
| C. Statu quo vocabulaire | Garder « section/leçon » tel quel, ajouter seulement le type. | Le doc Features UX (durées, badges « par leçon », lignes de supports) devient ambigu pour toujours ; dette de vocabulaire. |

**Recommandation : A.** Le typage explicite est le vrai besoin ; le renommage est cosmétique côté
API/UI. Liste des types à valider en séance : `video`, `podcast`, `reel`, `article`, `fiche`
(+ `quiz` et, plus tard, `mise_en_situation`). Confirmer que le **quiz est un support comme un
autre** dans la liste d'une leçon (c'est déjà le cas via `items[]`).

**Issues impactées** : EA-027 (directe), EA-028/030/031/036/063/079 (dépendantes).

> **Décision** : ☐ A ☐ B ☐ C — Types retenus : ______________________ — Le quiz est un support : ☐ oui ☐ non

---

## F2 · Source de vérité des durées (Z5)

**Question** : d'où vient la durée affichée d'un support (et donc d'une leçon = somme, d'une
formation = somme) ?

**Constat code** : `item.duration` existe parfois ; sinon le front appelle l'oEmbed Vimeo **à
chaque affichage** (`<VimeoDuration>`). Rien pour les fichiers uploadés, ni pour les articles/fiches.

| Option | Description | Conséquences |
|---|---|---|
| **A. Extraction auto + override admin** ✅ | À l'upload : durée extraite (ffprobe) ; pour les URLs : oEmbed **une fois, côté serveur, stockée**. L'admin peut corriger. Articles/fiches : durée saisie par l'admin (défaut proposé : estimation au nombre de mots, 200 mots/min). | Fiable, pas d'appel réseau à l'affichage, couvre tous les types. Nécessite ffprobe sur le serveur. |
| B. Saisie admin systématique | L'admin renseigne toutes les durées. | Simple techniquement, mais pénible et vite faux (oublis, médias remplacés). |

**Recommandation : A.** Trancher aussi : la durée affichée est-elle arrondie à la minute ? (proposé : oui).

**Issues impactées** : EA-028, EA-030, EA-033, EA-060 (filtre par durée).

> **Décision** : ☐ A ☐ B — Règle articles/fiches : ______________ — Arrondi : ☐ minute ☐ seconde

---

## F3 · Formule du % d'avancement (Z6)

**Question** : comment se calcule le pourcentage d'avancement d'une formation pour un apprenant ?

**Constat code** : aucun % n'existe. Trois états dérivés en agrégat dashboard uniquement
(inscrit / ≥1 leçon complétée / cours complété). « Terminé » = 100 % strict des leçons ET quiz actifs.

| Option | Formule | Conséquences |
|---|---|---|
| **A. Supports terminés / total** ✅ | Nombre de supports complétés (quiz inclus) ÷ nombre total de supports actifs. | Lisible, explicable à l'apprenant (« 7/10 »), cohérent avec le statut Terminé existant. |
| B. Pondéré par durée | Somme des durées des supports terminés ÷ durée totale. | Plus « juste » mais opaque (finir un quiz de 2 min ne bouge presque pas la barre) et dépendant de la fiabilité des durées (F2). |
| C. Temps passé / durée totale | Basé sur le tracking. | Mesure l'exposition, pas l'accomplissement ; incompatible avec la complétion manuelle. |

**Recommandation : A**, avec les règles annexes : statut *Non débutée* = 0 support complété ;
*En cours* = ≥1 ; *Terminée* = statut de complétion existant (pas « % = 100 » recalculé, pour
rester cohérent avec les certificats déjà émis).

**Issues impactées** : EA-018 (directe), EA-033, EA-047, EA-049, EA-051.

> **Décision** : ☐ A ☐ B ☐ C — Règles annexes validées : ☐

---

## F4 · Deeplinks, minutage et URLs (Z7, Z18)

**Question** : quel format de lien partageable vers un support, et quelle politique d'URLs ?

**Constat code (branche `development`, re-vérifié 05/08)** : les slugs existent (6 entités back,
accents translittérés ; routes front `/courses/:slug`) **mais aucune route API ne les résout** —
le front garde une table slug→id en localStorage, donc **un lien partagé ne fonctionne pas dans un
autre navigateur** (bug réel à corriger, EA-038). Le token d'invitation transite par `?token=` et
survit au login (mécanisme réutilisable pour le deeplink).

> **Passif des anciennes URLs — déjà pris en charge, hors atelier.** La refonte du routage a
> renommé ou supprimé **56 chemins**, sans redirection ni écran 404 : tout ancien lien (email
> d'invitation antérieur, favori, lien partagé) affiche aujourd'hui une **page blanche**.
> Traité par [EFEKTIVACADEMIE-FRONT#18](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/issues/18)
> (écran 404 de repli + redirections ancien → nouveau). Aucune décision d'atelier requise.

**Proposition à valider** :
- Format : `/courses/{slug}?support={supportId}&t={secondes}` — le slug existe déjà côté back
  (accents translittérés) ; l'identifiant numérique reste accepté (aucune migration).
- Le deeplink **survit à l'authentification** (même mécanique que le token d'invitation).
- Toute URL inconnue tombe sur un écran 404, jamais sur une page blanche (cf. encadré ci-dessus).
- Le minutage `t` s'applique aux supports temporels (vidéo/podcast/réel) ; ignoré sinon.
- Bouton « copier le lien » sur chaque ligne de support ; le lien avec minutage se copie depuis le
  player (« copier le lien à cet instant »).
- **Résolution du slug côté API** (EA-038) : aujourd'hui `/courses/:slug` ne se résout que via une
  table `slug → id` gardée en `localStorage` — un lien partagé ne fonctionne donc pas dans un autre
  navigateur. C'est le vrai sujet à trancher ici (le passif des anciennes URLs, lui, est couvert).

**Issues impactées** : EA-038 (directe), EA-080 (remédiation minutée réutilise ce format).

> **Décision** : format validé : ☐ oui ☐ amendé : ______________________

---

## F5 · Badge de leçon vs certificat de formation (Z8, C8)

**Question** : le doc demande un « badge cliquable à chaque leçon terminée permettant de
télécharger le certificat » — existe-t-il un certificat *par leçon* ?

**Constat code** : le certificat n'existe qu'au niveau **formation** (généré automatiquement à
100 %, DomPDF). Les badges de gamification existent (`badges`/`user_badges`) mais ne sont pas liés
aux leçons.

| Option | Description | Conséquences |
|---|---|---|
| **A. Certificat = formation ; badge de leçon = jalon visuel** ✅ | Chaque leçon terminée affiche un badge (coche/médaille). Tant que la formation n'est pas finie : état « en préparation » au survol. Une fois la formation terminée : le badge devient cliquable → certificat de la formation. | Aucun nouveau document légal ; réutilise la gamification ; répond à l'esprit de la demande (visibilité de l'accomplissement). |
| B. Certificat par leçon | Nouveau template, nouvelle table, N certificats par formation. | Inflation documentaire ; dilue la valeur du certificat QUALIOPI ; à ne retenir que si un besoin client explicite existe. |

**Recommandation : A.**

**Issues impactées** : EA-039 (directe), EA-033.

> **Décision** : ☐ A ☐ B

---

## F6 · Définition de « Tuto » (Z17)

**Question** : l'écran Cours cible liste Parcours / Cours / **Tuto** — qu'est-ce qu'un Tuto ?

**Constat code** : `course_types` = Self-paced / Structured / Scheduled ; `learning_paths` = parcours.
Aucune notion de tuto.

**Proposition à valider** : un Tuto = un **cours allégé mono-leçon** (un ou quelques supports, pas
de quiz obligatoire, pas de certificat), techniquement un `course_type` supplémentaire — ce qui
évite toute nouvelle table et le fait bénéficier de tout l'existant (inscription, suivi, chat).
Alternative : simple **catégorie/étiquette** éditoriale (encore plus léger, mais pas de règles
propres — un « tuto » pourrait alors exiger un quiz).

**Issues impactées** : EA-058, EA-062.

> **Décision** : Tuto = ☐ course_type dédié ☐ étiquette éditoriale — Certificat sur tuto : ☐ non ☐ oui

---

# SESSION B — Rôles, organisation, RGPD

## F7 · Matrice des rôles cible (Z2, C6) — à valider ligne par ligne

**Question** : quels rôles, quel périmètre de données, quels écrans pour chacun ?

**Constat code** : 7 rôles Spatie (`learner`, `teacher`, `manager`, `admin`, `sub_manager`,
`manager_general`, `director`) dont **2 non seedés** (`director`, `manager_general`) ; pas de rôle
RH ; 5 blocs de routes copiés-collés ; le rôle `manager` actuel a une portée **globale** sur le
dashboard (comportement d'admin fonctionnel).

**Proposition de matrice cible** (à corriger en séance) :

| Rôle code | Libellé métier | Voit | Écrans pilotage | Crée des users |
|---|---|---|---|---|
| `learner` | Apprenant | soi | — | non |
| `teacher` | Formateur | apprenants de SES formations | Liste apprenants + cockpit (restreint) + chat | non |
| `sub_manager` | **Manager** | rattachés directs | Dashboard, Invitation*, Mes collaborateurs | non |
| `manager_general` | Manager général | hiérarchie directe + indirecte | + Mes équipes | non |
| `director` | Directeur | hiérarchie directe + indirecte | + Mes équipes | non |
| `manager_rh` | Manager RH (**à créer**) | tous les utilisateurs | + Tous les collaborateurs | non |
| `admin` | Administrateur | tout | Admin complet | **oui (seul)** |
| `manager` | ⚠️ à statuer | aujourd'hui : portée globale | — | aujourd'hui : oui |

\* Invitation = inscription d'utilisateurs **existants** à des formations (cf. F9).

**Points à trancher explicitement** :
1. Le « Manager » du doc Features UX = `sub_manager` du code ? (probable, à confirmer)
2. Sort du rôle `manager` actuel (admin fonctionnel) : ☐ fusionner dans `admin` ☐ garder comme « admin délégué » ☐ requalifier
3. `manager_general` et `director` : mêmes droits (le doc les traite ensemble) ? Si oui, les
   distinguer seulement par libellé.
4. Règle générique validée : l'onglet **Mes équipes** s'affiche dès qu'il existe >1 niveau
   hiérarchique sous l'utilisateur (piloté par la donnée, pas par le rôle).
5. Le formateur voit-il les données de suivi détaillées (temps, connexions) ou seulement les
   résultats pédagogiques ? (lié F11)

**Issues impactées** : EA-003, EA-008, EA-043, EA-046, EA-048…056.

> **Décisions** : mapping validé : ☐ — sort de `manager` : ______________ — MG=Directeur : ☐ oui ☐ non — règle profondeur : ☐ validée — périmètre formateur : ______________

---

## F8 · Société : référentiel simple ou multi-tenancy (Z3)

**Question** : le champ « société » (Aïkan, ACS…) est-il un simple attribut d'affichage/filtre, ou
le début d'un cloisonnement des données par entreprise ?

**Constat code** : aucune table société. Le cloisonnement actuel est purement hiérarchique (`parent_id`).

| Option | Description | Conséquences |
|---|---|---|
| **A. Référentiel simple** ✅ | Table `companies` (nom, statut) gérée dans Paramètres ; `users.company_id` ; colonne + filtre + hover card. Aucune règle d'accès basée sur la société. | 2-3 jours. Couvre 100 % du doc Features UX (la société n'y est qu'une colonne). |
| B. Multi-tenancy | La société devient un périmètre d'accès (un manager ne voit que sa société, catalogues distincts…). | Chantier transverse majeur (toutes les requêtes, tous les écrans) ; recoupe la vision SaaS mais n'est PAS demandé ici. |

**Recommandation : A**, en nommant explicitement la contrainte d'avenir : ne rien coder qui
*empêche* un cloisonnement futur (le `company_id` posé proprement aujourd'hui est la fondation du
multi-tenant de demain).

**Issues impactées** : EA-041 (directe), EA-046, EA-049, EA-052.

> **Décision** : ☐ A ☐ B — Liste initiale des sociétés : ______________________

---

## F9 · Création d'utilisateurs vs invitations (Z13, C3, C4)

**Question** : « seuls les admins créent des utilisateurs » — que devient l'invitation par email,
qui aujourd'hui **crée des comptes à la volée** (mot de passe aléatoire envoyé en clair) et force
l'inscription en `Approved` ?

**Proposition à valider** :
1. **Création de compte = admin uniquement** (retrait des routes des 4 autres rôles).
2. **Invitation par les managers = inscription d'utilisateurs existants** à des formations
   (sélection dans leur périmètre) + relance des non-inscrits. L'entrée « inviter un email inconnu »
   disparaît pour eux — remplacée par « demander la création à l'admin » (pré-rempli, cf. tickets/chat).
3. **1 email par utilisateur et par envoi**, listant toutes les formations (C4).
4. Nouveau compte : **mail d'onboarding** avec lien sécurisé « définir mon mot de passe » +
   complétion du profil à la 1ʳᵉ connexion — plus aucun mot de passe en clair.

Variante si le flux « inviter un inconnu » doit rester : l'invitation crée un compte **inactif**
qui ne devient réel qu'à l'acceptation (lien à expiration), et l'acte reste réservé à l'admin.

**Issues impactées** : EA-003, EA-044, EA-045.

> **Décision** : points 1-4 : ☐ validés ☐ amendés : ______________ — Flux « email inconnu » : ☐ supprimé ☐ variante compte inactif

---

## F10 · Menu manager « Rôle » (Z10)

**Question** : le doc réduit le pilotage à « Dashboard / Invitation / Utilisateurs / **Rôle** » —
que recouvre « Rôle » pour un manager qui ne crée plus d'utilisateurs ?

**Hypothèses** : (a) consultation de la matrice des rôles (qui a quel droit) ; (b) demande de
changement de rôle d'un collaborateur (workflow vers l'admin) ; (c) faute de frappe pour « Rôles »
au sens annuaire d'équipe ; (d) l'écran n'a pas lieu d'être.

**Recommandation** : trancher en séance avec le demandeur du doc ; par défaut **(a) consultation
seule**, le changement de rôle restant un acte admin.

**Issues impactées** : EA-056.

> **Décision** : ☐ a ☐ b ☐ c ☐ d — précision : ______________________

---

## F11 · Exports & données de suivi : périmètre RGPD par rôle (Z16 + volet tracking)

**Question** : « pouvoir extraire au format Excel tous les tableaux » — qui peut exporter quoi ?
Et qui voit les données de suivi individuelles (temps passé, connexions, téléchargements) ?

**Constat** : le tracking à venir (CH1) crée des données nominatives sensibles au sens RGPD
(mesure d'activité des salariés). Le doc demande leur affichage dans le cockpit managérial.

**Proposition à valider** :
| Rôle | Export | Données de suivi individuelles |
|---|---|---|
| Manager (sub_manager) | ses collaborateurs directs | oui, périmètre direct |
| MG / Directeur | sa hiérarchie | oui, sa hiérarchie |
| Manager RH | tous | oui, tous |
| Formateur | ses formations, **résultats pédagogiques seulement** | non (pas temps/connexions) |
| Admin | tout | oui |

+ trois garde-fous : mention d'information des utilisateurs (écran + politique), durée de rétention
des événements bruts (proposé : **24 mois** puis agrégation), et pas d'export des données de suivi
par des rôles non managériaux.

**Complément (spec Pilotage & Analytics du 05/08, `05_SPEC_PILOTAGE_ANALYTICS.md`)** — la spec
fige le schéma de tracking et les KPI ; il ne reste que 5 paramètres à valider ici :
1. Fenêtre anti-bruit des vues de leçon : [30] secondes.
2. Seuils des profils d'usage : One-shot = 1 jour actif, Occasionnel = 2-3, Récurrent = ≥ 4.
3. Rétention des événements bruts : [24] mois.
4. Liste des comptes « test » à exclure des compteurs : à fournir.
5. Volumétrie cible de dimensionnement : [500 – 5 000] apprenants.

**Issues impactées** : EA-017, EA-022, EA-051, EA-054, EA-070, EA-082.

> **Décision** : matrice : ☐ validée ☐ amendée — rétention : ______ mois — mention d'info : ☐ à rédiger (qui : ______) — anti-bruit : ______ s — seuils profils : ☐ validés ☐ amendés : ______ — comptes test : ______ — volumétrie : ______

---

# SESSION C — Périmètres fonctionnels

## F12 · SCORM : ingestion du manifeste pipeline ou vrai player (Z4)

**Constat** : le LMS n'a **rien** (zéro occurrence SCORM/xAPI). Le pipeline sait produire du SCORM
et dispose d'un contrat de manifeste JSON v1 déjà spécifié.

| Option | Description | Conséquences |
|---|---|---|
| **A. Ingestion manifeste JSON pipeline** ✅ | Import d'un paquet pipeline → création auto de la structure formation/leçons/supports/quiz en brouillon. | Léger (quelques jours), idempotent, couvre le besoin réel (« intégrateur automatique » de NOS contenus). |
| B. Player SCORM générique | Import et lecture de paquets SCORM tiers (runtime JS, suivi cmi). | Plusieurs semaines ; n'a de valeur que pour ingérer du contenu **externe** au pipeline. À chiffrer séparément (étude E2) si le besoin client existe. |

**Recommandation : A maintenant, B en étude optionnelle.**

**Issues impactées** : EA-081 (directe), P1.7.

> **Décision** : ☐ A seul ☐ A + étude B — besoin de contenus tiers identifié : ☐ non ☐ oui : ______

---

## F13 · Tickets vs « Demande de support » (Z9, Z15)

**Question** : le doc dit « ouvrir un ticket par un manager uniquement », mais le chat apprenant
propose « Parler à un admin ou Demande de support ». Les demandes apprenants sont-elles des tickets ?

**Proposition à valider** :
- **Tickets formels** (numéro, statuts, priorité) : réservés aux **managers** (demandes sur
  users/formations), conformément au doc.
- **Demande de support apprenant** = conversation de chat **étiquetée « support »**, routée vers
  les admins, sans numéro de ticket. L'admin peut **convertir** une demande en ticket s'il veut la
  tracer (bouton « créer un ticket depuis cette conversation »).
- Priorité : ☐ deux niveaux (normal / urgent) — recommandé — ☐ pas de priorité.

**Issues impactées** : EA-071, EA-073.

> **Décision** : modèle validé : ☐ — conversion chat→ticket : ☐ oui ☐ non — priorité : ______

---

## F14 · Notifications : canaux et principes (Z11 — détail en P1.8)

**Question de cadrage** (le détail événement par événement se fait en conception P1.8) :
- Canaux au lancement : **in-app (cloche) + email** — recommandé ; push web = phase ultérieure ?
- L'utilisateur peut-il régler ses préférences (couper les emails, garder l'in-app) ? — recommandé : oui.
- Fréquence email : immédiat par événement, ou digest quotidien pour les événements de faible
  priorité (recommandé : immédiat pour chat/annonces/tickets, digest pour « cours ajouté »).

**Issues impactées** : EA-024, EA-025, P1.8.

> **Décision** : canaux : ______________ — préférences user : ☐ oui ☐ non — digest : ☐ oui ☐ non

---

## F15 · Visio : retrait (Z12, C7)

**Constat** : la visio ZegoCloud embarque un **secret d'API en clair dans le front** (mode test) ;
le doc demande de la « dégager sur Admin ».

**Recommandation** : **retrait complet** (tous rôles) + révocation du secret côté ZegoCloud.
L'historique des messages `video_call` reste lisible. Si un besoin visio réel émerge, ce sera une
feature refaite proprement (hors périmètre).

**Issues impactées** : EA-007.

> **Décision** : ☐ retrait complet ☐ garder côté apprenants (nécessite refonte sécurisée immédiate — à chiffrer)

---

## F16 · Politique de passage des quiz : défauts et acquis (Z14, C1)

**Constat critique** : `passing_percentage` (défaut 80) n'est **jamais évalué** — aujourd'hui un
quiz est validé quel que soit le score. Appliquer un seuil est donc un durcissement rétroactif
potentiel.

**Proposition de valeurs par défaut** (modifiables par quiz) :
| Paramètre | Défaut proposé |
|---|---|
| Seuil de réussite | 70 % (aligné sur l'usage actuel annoncé) |
| Tentatives max | illimitées |
| Délai entre tentatives | 0 |
| Limite de temps | aucune |
| Feedback | en fin de quiz |
| Mélange questions/options | activé |

**Sort des acquis** : les quiz déjà « validés » et les certificats émis **restent acquis**
(aucune dé-certification rétroactive). Le seuil ne s'applique qu'aux tentatives postérieures à la
mise en service.

**Issues impactées** : EA-065, EA-066, EA-013.

> **Décision** : défauts : ☐ validés ☐ amendés : ______________ — acquis préservés : ☐ oui

---

## F17 · Chatbot (Z19) — cadrage de l'étude E1

Hors sprints de dev. À décider seulement : ☐ lancer l'étude (POC chatnode ou équivalent, sur quelle
base de connaissance : contenus de formation ? FAQ support ?) ☐ reporter.

> **Décision** : ☐ étude lancée (responsable : ______, échéance : ______) ☐ reportée

---

## F18 · Gamification de la banque de questions (Z20) — cadrage de l'étude E3

« Hall of fame : qui sera le maître de la DO ? » — les tables `point_master`/`badges` existent.
À décider seulement : ☐ inclure un classement par thème dans le périmètre S8 (léger) ☐ étude
séparée ☐ reporter.

> **Décision** : ______________________

---

# SESSION D — Audit technique (hors atelier, dev back)

## F19 · Environnements et stratégie de portage (Z21)

**Constat** : la référence est `EFEKTIVACADEMIE-FRONT/BACK` (acté 05/08), branche active/déployée :
`development`. Les clones EACLAUDE ne sont PAS la référence (retard constaté : ≈ branche `main`).
Les issues seront suivies là où tu l'as décidé (backlog markdown pour l'instant).

**À formaliser** :
1. Stratégie de branches sur `EFEKTIVACADEMIE-*` : branche par sprint ? par chantier ? PR vers `development` ?
2. Stratégie de portage vers la prod : PR par sprint ? par chantier ? gel de la prod pendant la migration tracking ?
3. Environnement de recette (staging) avec base iso-prod anonymisée : existe-t-il ? à créer ?
4. Fenêtre de mise en service du tracking (CH1) : plus tôt = mieux (données).

> **Décision** : ______________________

## F20 · Audit du schéma prod (Z22) — kit prêt à exécuter

**Objectif** : lister les écarts entre les migrations du repo et la base de production avant tout
chiffrage ferme (des colonnes utilisées par le code n'existent dans aucune migration).

**Colonnes suspectes identifiées** (code ↔ migrations) :
`users.is_active`, `enrollments.is_complete`, `lessons.order_no`, `quizzes.order_no`,
`courses.category_id`, `sections.description`.

**Kit** (lecture seule, via le pack SQL déjà transmis ou un accès MySQL prod) :

```sql
-- 1. Schéma réel des tables sensibles
SHOW COLUMNS FROM users;    SHOW COLUMNS FROM enrollments;  SHOW COLUMNS FROM lessons;
SHOW COLUMNS FROM quizzes;  SHOW COLUMNS FROM courses;      SHOW COLUMNS FROM sections;
SHOW COLUMNS FROM completed_courses;  SHOW COLUMNS FROM quiz_scores;  SHOW COLUMNS FROM course_files;

-- 2. Migrations réellement appliquées en prod (à diff-er avec database/migrations/ du repo)
SELECT migration FROM migrations ORDER BY id;

-- 3. Volumétries (dimensionnement du tracking et des reprises de données)
SELECT (SELECT COUNT(*) FROM users)            AS users,
       (SELECT COUNT(*) FROM enrollments)      AS enrollments,
       (SELECT COUNT(*) FROM learner_lessons)  AS learner_lessons,
       (SELECT COUNT(*) FROM quiz_scores)      AS quiz_scores,
       (SELECT COUNT(*) FROM completed_courses) AS completed,
       (SELECT COUNT(*) FROM course_files)     AS course_files;

-- 4. Qualité de données avant contraintes (EA-015 : unicité completed_courses)
SELECT learner_id, course_id, COUNT(*) c FROM completed_courses
GROUP BY learner_id, course_id HAVING c > 1;

-- 5. Rôles réellement présents (director / manager_general non seedés)
SELECT r.name, COUNT(mhr.model_id) AS users FROM roles r
LEFT JOIN model_has_roles mhr ON mhr.role_id = r.id GROUP BY r.name;
```

**Livrable attendu** : tableau des écarts + migrations de rattrapage (EA-001) + liste des données
à nettoyer avant contraintes (doublons `completed_courses`, comptes sans rôle…).

---

# Récapitulatif des décisions par défaut (si non tranché en atelier)

| Fiche | Défaut appliqué |
|---|---|
| F1 | Renommage + typage (option A) |
| F2 | Extraction auto + override admin |
| F3 | % = supports terminés / total |
| F4 | `/cours/{id}-{slug}?support=&t=` + redirections |
| F5 | Certificat formation ; badge leçon = jalon |
| F6 | Tuto = course_type dédié, sans certificat |
| F7 | Matrice proposée ; Mes équipes piloté par la profondeur |
| F8 | Référentiel sociétés simple |
| F9 | Création admin-only ; invitation = users existants ; 1 mail/user ; onboarding sécurisé |
| F10 | « Rôle » = consultation seule |
| F11 | Matrice d'accès proposée ; rétention 24 mois |
| F12 | Ingestion manifeste pipeline (player SCORM = étude) |
| F13 | Tickets managers + demandes support en chat convertibles |
| F14 | In-app + email, préférences user, digest partiel |
| F15 | Retrait complet de la visio |
| F16 | Défauts du tableau ; acquis préservés |
| F17/F18 | Reportés (études) |
