Ce fichier est **le seul du document rédigé à la main**. Il est recopié tel quel
en fin de `MODELE_DONNEES.md` par `php artisan modele:documenter`. On y met ce
qu'aucune introspection ne peut donner : une intention, une règle, un piège.

**Règle d'entretien** : n'écrire ici que ce qui est SOURCÉ (une issue, un test,
une décision datée). Une affirmation invérifiable dans cette annexe redonne au
document le défaut qu'il combat.

---

### Les valeurs qui ne se lisent pas dans le type

- **`courses.status` — `enum('0','1')`, et `'1'` veut dire PUBLIÉ.** Le défaut de
  la colonne valait `'1'` : un cours créé sans que le code nomme `status`
  naissait donc *publié*. Corrigé par BACK#553 ; le test
  `tests/Feature/Back553CoursNaitBrouillonTest.php` garde l'amont. Le même
  encodage `'0'`/`'1'` se retrouve sur `lessons.status` et `quizzes.status`.
- **`is_required` — `enum('yes','no')`, pas un booléen.** Sur `lessons` comme sur
  `quizzes`. Une comparaison à `true` y est toujours fausse.

### `activity_log` — ce que le schéma ne dit pas (BACK#389)

La table du journal d'audit est **celle de `spatie/laravel-activitylog`, à la
colonne près**. Trois choses ne se lisent pas dans son schéma :

- **`description` ne contient pas une phrase, mais une CLÉ canonique** —
  `compte.desactive`, `droit.role_modifie`, `donnee.export_nominatif`. Le
  registre est `App\Support\Audit\TypeAction` ; le libellé lisible vit dans
  `config/audit.php` et n'est jamais stocké. Une phrase figerait la formulation
  du jour dans les lignes du jour.
- **`log_name` porte le DOMAINE métier**, pas le nom d'une table : `users`
  alimente `comptes` quand une identité change et `droits` quand un rôle change.
- **Il n'y a AUCUNE colonne de sensibilité, et c'est une décision** (#186,
  09/08). La sensibilité est un attribut du *type d'action*, lue à l'affichage.
  Figée à l'écriture, un reclassement ne vaudrait que pour l'avenir et la vue
  « simplifiée » mentirait sur l'historique.
  `tests/Feature/Back389ClassificationLueALAffichageTest.php` reclasse un type
  après coup et vérifie que les entrées déjà écrites basculent avec lui.

Deux règles d'écriture ne se déduisent pas non plus du schéma, et leur oubli
rendrait le journal inutilisable :

- **Une opération en masse produit UNE entrée, pas une par ligne.** `Enrollment`
  ne porte donc pas le trait d'audit : inscrire 260 apprenants à 10 formations
  écrirait 2 600 entrées pour une seule action humaine.
- **Aucune table de tracking n'est dupliquée à l'écriture** (`login_events`,
  `lesson_views`, `quiz_attempts`, `media_progress`, `resource_downloads`,
  `completed_courses`). Les croiser au reporting reste libre.

Les deux interdits sont tenus par
`tests/Feature/Back389TrackingNonDupliqueTest.php`, qui les vérifie **par
introspection des traits** autant que par comptage : il échoue au moment où
quelqu'un pose le trait, pas seulement quand la panne se voit.

⚠️ `config/permission.php` → `events_enabled` est passé à `true` pour ce
chantier. Le rôle ne vit dans aucune colonne de `users` : sans ces événements,
un changement de rôle n'est observable nulle part.

### Les deux incidents qui ont fait naître ce document

- **BACK#558 — le champ fantôme.** `QuizResource` exposait `type` alors qu'aucune
  colonne `quizzes.type` n'existait : la clé sortait à `null` depuis des mois,
  sans erreur, et une décision produit est restée inapplicable sans que rien ne
  le signale. La colonne existe depuis. La section « Base ↔ API » du document
  généré détecte désormais ce motif automatiquement.
- **BACK#567 — la collision de clé.** Une fois `quizzes.type` créée, la même clé
  `type` a porté deux notions : *quelle sorte d'item est-ce* (leçon ou quiz, ce
  que lit le sommaire) et *quel type pédagogique de quiz*. Aucune erreur non
  plus : juste un sommaire qui ne reconnaît plus rien. **Leçon à retenir : une
  clé de charge utile porte UNE notion, et le nom d'une clé n'est pas libre dès
  lors qu'un écran s'en sert pour discriminer.**

### Ce qui reste hors du document

- Les **rôles et le vocabulaire** (le piège `manager` / `sub_manager`) : voir
  `docs/roadmap/` et les issues de la file « rôles ».
- Les **durées** (`duration_seconds` / `pedagogical_duration_seconds` /
  `duration_source`) : le modèle est décrit dans BACK#148 et BACK#139.
- Les **règles de périmètre** (qui voit quoi) : elles vivent dans les politiques
  et les portes d'accès, pas dans le schéma.
