Pratiques de Programmation
Comment coder — Guide du développeur
---
1.1 Vue d'Ensemble Technique
Stack Technique
- Framework : Next.js 16, TypeScript, React
- Tests : Jest (TDD), Cucumber.js (BDD), Playwright (E2E)
- Hébergement : Vercel
- Dépôt : GitHub `web-malain-et-possible`
Architecture Hexagonale appliquée à Next.js
Principe fondamental : Séparation claire entre :
- Backend Pur (`utils/`) : Logique métier isolée, testable en CLI, sans dépendance React/Next.js
- Backend Next.js (`app/`, `components/`) : Génération HTML, Server/Client Components
Avantages :
- Testabilité maximale (tests sans mocks complexes)
- Réutilisabilité du code (CLI, tests, composants, API)
- Séparation des préoccupations claire
Convention : Les fichiers du backend pur commencent par un commentaire explicite :
/**
* Backend pur : [Description]
* Cette logique est réutilisable et testable en ligne de commande
*/---
1.2 Workflow et Agents
Commandes Lead Dev
Deux commandes uniquement :
- GO US : Commencer une nouvelle User Story (invoque l'agent @US)
- GO NEXT : Avancer d'un cran (revue → corrections/passage à l'étape suivante)
Tunnel Obligatoire
US → BDD → TDD-back-end → TDD-front-end → doneAgents Disponibles
| Agent | Rôle | Périmètre | |-------|------|-----------| | Lead Dev | Orchestration, revue | Ne code pas directement | | US | Reformule en User Story | Critères d'acceptation | | BDD | Écrit scénarios Gherkin | Fichiers `.feature` dans `tests/bdd/` | | TDD-back-end | Logique métier | `utils/` uniquement | | TDD-front-end | Interface utilisateur | `app/`, `components/` | | Designer | CSS uniquement | Hors tunnel, à la demande |
---
1.3 TDD Strict
Cycle Obligatoire
RED → GREEN → REFACTOR, un test à la fois, toujours le plus simple possible.
Structure des Tests avec Progression Visible
describe('nomFonction - Approche TDD (simple → complexe)', () => {
describe('ITÉRATION 1 : Le cas le plus simple', () => { /* ... */ });
describe('ITÉRATION 2 : Ajouter une deuxième fonctionnalité', () => { /* ... */ });
// ...
});Validation JSON avec Détection de Types Inconnus
Les tests d'intégration détectent les types présents dans les JSON mais non gérés dans le code. Le test échoue avec un message actionnable : implémenter le type ou le supprimer.
Tests d'Intégration qui Modifient le Code
Approche originale : Les TI ne se contentent pas de vérifier — ils :
- Génèrent automatiquement les e2eID manquants dans les fichiers JSON/React
- Mettent à jour `_Pages-Liens-Et-Menus.json` en préservant les métadonnées
- Forcent la décision du développeur (arbitrage obligatoire)
---
1.4 Pre-commit Hooks
Régénération automatique des artefacts avant chaque commit :
- Scénario E2E (`parcours-complet-liens.spec.ts`)
- Plan du site (`_Pages-Liens-Et-Menus.json`)
- Métriques
---
1.5 Bonnes Pratiques de Code
- Lire avant d'écrire : Toujours utiliser `Read` avant `StrReplace`
- Tests d'abord : Cycle RED → GREEN → REFACTOR strict
- Journal immédiat : Mise à jour du journal de bord après chaque modification
- Commit descriptif : Message expliquant le "pourquoi"
- Lint après édition : Vérifier les erreurs sur les fichiers modifiés
- Build avant clôture : Lancer build et tests complets à l'étape `done`
- Parallélisation : Appels d'outils indépendants en parallèle
---
1.6 Points de Vigilance Développeur
Tests Préexistants en Échec
Certains tests peuvent échouer pour des raisons antérieures à la session courante. Toujours distinguer les régressions introduites des problèmes préexistants.
Sécurité
- Hash SHA-256 pour les mots de passe (migration depuis MD5)
- Headers CSP, HSTS, X-Content-Type-Options configurés dans `next.config.ts`
- XSS corrigées (plus de `dangerouslySetInnerHTML` pour le contenu utilisateur)
- Validation Path Traversal obligatoire dans `aboutSiteReader.ts` et `api/images/`
---
Dernière mise à jour : 2 février 2026
Sécurité et validation des entrées
Règles appliquées dans le backend et les routes API.
---
1. Path traversal (chemins de fichiers)
Règle : Tout chemin dérivé d’un paramètre utilisateur ou d’une config doit être validé pour ne pas sortir du répertoire autorisé.
Implémentation :
- `utils/shared/resolveDataPath.ts` : `resolveDataPath(relativePath)` résout un chemin relatif sous `data/` et lève une erreur si le chemin résolu sort du répertoire `data/`. Utilisé par `utils/vitrine/pageReader.ts` (readPageData, readIndexData, readDomaineData, etc.) et par les routes API qui construisent un chemin vers un fichier dans `data/` (ex. `/api/vitrine/pages/[slug]`, `/api/vitrine/profils/[slug]`).
- `utils/projet/aProposReader.ts` : `validateAndResolvePath(relativePath, allowedBase)` résout le chemin et vérifie qu’il reste sous `allowedBase` (défaut `data`). En cas de sortie, une `Error` est levée. Utilisé par `readPathContentAtRoot`, `readChapitreByPath`, etc.
- `app/api/images/[type]/[filename]` : validation explicite que le chemin résolu reste dans le répertoire autorisé avant lecture.
- `app/api/a-propos-chapitre` : le paramètre `path` est passé à `readChapitreByPath`, qui s’appuie sur `validateAndResolvePath` côté `utils`.
À respecter : pour toute nouvelle route ou reader qui lit des fichiers à partir d’un paramètre (query, path, config), utiliser `resolveDataPath` (pour chemins sous `data/`) ou un équivalent (ex. `validateAndResolvePath`) avant toute lecture.
---
2. Routes API Vitrine (query / path)
Règle : Les paramètres de requête et de chemin sont validés avant utilisation (lecture fichier, logique métier).
Implémentation :
- Paramètre `mode` : `utils/vitrine/apiHelpers.ts` — `validateModeParameter(mode)` accepte uniquement `refs` ou `full`. Retourne une erreur 400 si absent ou invalide. Utilisé par les routes `/api/vitrine/pages`, `/api/vitrine/profils`, `/api/vitrine/domaines`, `/api/vitrine/competences`.
- Paramètre `slug` : Règle « slug = safe pour path » — Les slugs utilisés pour construire un chemin fichier (ex. `profil-${slug}.json`, `${slug}.json`) doivent être validés : caractères autorisés uniquement (minuscules, chiffres, tirets), pas de `..`, `/`, ni caractères spéciaux. Utiliser `isValidSlugForPath(slug)` (`utils/shared/slugFromTitle.ts`) avant toute construction de chemin ; en cas d’échec, retourner 400. Appliqué dans `/api/vitrine/pages/[slug]` et `/api/vitrine/profils/[slug]`. La résolution du chemin fichier s’appuie ensuite sur `resolveDataPath` (voir §1).
- Paramètre `path` (a-propos-chapitre) : passé à `readChapitreByPath` qui applique la validation path traversal (voir §1).
À respecter : pour toute nouvelle route, valider et sanitiser les query/path params (whitelist, type, longueur si besoin) avant toute lecture fichier ou logique métier.
---
3. Recommandations optionnelles
- Rate limit sur `/api/verify-password` : en cas d’exposition publique, limiter le nombre de requêtes par IP (ex. 5 requêtes/minute) pour réduire le risque de brute force. Non implémenté par défaut (contexte projet personnel, risque proportionné).
---
Document créé dans le cadre du plan 10/10 principes backend (Phase 5–7).
Fonctionnement du Site Vitrine
Comment utiliser — Manuel du rédacteur
---
2.1 Identité du Site
- Nom : Web Malain et Possible
- Type : Site vitrine professionnel / Portfolio multi-profils
- Profils : CPO, COO, Coach Agile, CTO
- Propriétaire : Alain Meunier
---
2.2 Système CMS basé sur JSON
Tout le contenu du site est défini dans des fichiers JSON situés dans `data/` :
| Fichier | Rôle | |---------|------| | `index.json` | Page d'accueil | | `mes-profils.json` | Liste des profils | | `profil-*.json` | Détail de chaque profil | | `_site-identity.json` | Identité SEO (Person, WebSite pour JSON-LD) | | `_Pages-Liens-Et-Menus.json` | Plan du site et liens | | `_footerButtons.json` | Boutons du footer | | `_temoignages.json` | Témoignages | | `_bibliotheque.json` | Ressources documentaires |
---
2.3 Types de Contenu (Liste Complète)
Chaque élément dans les JSON a un type qui détermine son rendu. Voici la liste exhaustive :
| Type | Classe CSS | Description | |------|------------|-------------| | `hero` | `.hero` | Bandeau d'accueil (titre, sous-titre, description, CTA, vidéo) | | `titre` | `.titre` | Titre de section (bande bleue) | | `titreDePage` | `.titreDePage` | Titre de la page (affiché dans le header) | | `texteLarge` | `.texteLarge` | Bloc de texte avec paragraphes | | `video` | `.video` | Incrustation YouTube | | `domaineDeCompetence` | `.domaineDeCompetence` | Bloc domaine (titre, contenu, compétences, expériences) | | `competence` | `.competence` | Carte compétence (titre, description, auteur, bouton, image) | | `experienceEtApprentissage` | `.experienceEtApprentissage` | Ligne d'expérience (catégorie, description, période) | | `callToAction` | `.callToAction` | Bouton d'action principal | | `groupeDeBoutons` | `.groupeBoutons` | Groupe de boutons (icônes, liens) | | `listeDesPages` | `.listeDesPages` | Plan du site (liens par zone) | | `listeDeProfils` | `.listeDeProfils-cont` | Grille de profils professionnels | | `profil` | `.profil` | Carte profil (titre, job titles, CV, lien) | | `temoignages` | `.temoignages` | Liste de témoignages | | `temoignage` | `.temoignage` | Carte témoignage (photo, nom, fonction, texte) | | `listeDeDetournementsVideo` | `.videoDetournement` | Liste de détournements vidéo | | `detournementVideo` | `.detournementVideo` | Carte détournement (titre, pitch, date, vidéos) |
Règle : Le type doit être implémenté dans le code. Sinon, le build échoue avec un message explicite.
---
2.4 Types Hiérarchiques (Liste Complète)
Tailles de Police (Tokens)
| Token | Valeur | Usage | |-------|--------|-------| | `--enorme` | 2rem | h1 | | `--grande` | 1.5rem | h2 | | `--moyenne` | 1.25rem | (réservé) | | `--normale` | 1rem | h3, p, listes, liens | | `--petite` | 0.8rem | h4, notes, sous-listes |
Spec Canonique (Source de Vérité)
Le fichier `constants/canonicalSpec.ts` définit le lien entre chaque propriété des types de contenu et son type hiérarchique. Extrait :
| Nom canonique | Type hiérarchique | Description | |---------------|-------------------|-------------| | `hero.titre` | `--h1` | Titre du hero | | `hero.sousTitre` | `--h2` | Sous-titre du hero | | `hero.description` | `--p` | Description (texte) | | `hero.callToAction` | `--lk` | Lien CTA (aspect bouton) | | `hero.ensavoirplus` | `--lk` | Lien "En savoir plus" | | `titre.texte` | `--h2` | Titre de section | | `profil.titre` | `--h2` | Titre du profil | | `profil.jobTitles` | `--n` | Job titles (note) | | `profil.route` | `--b` | Lien vers profil (bouton) | | `competence.titre` | `--h3` | Titre compétence | | `competence.description` | `--p` | Description compétence | | `temoignage.nom` | `--h3` | Nom du témoin | | `temoignage.fonction` | `--n` | Fonction (note) | | `temoignage.temoignage` | `--p` | Texte témoignage | | `experienceEtApprentissage.description` | `--n` | Description (note) | | `detournementVideo.titre` | `--h2` | Titre détournement | | `detournementVideo.date` | `--n` | Date (note) |
Fonctions utilitaires :
- `getSpecEntriesForType(type)` : retourne les entrées de la spec pour un type de contenu
- `isMarkdownContent(nomCanonique)` : indique si la propriété contient du Markdown
- `typeHierarchiqueToDisplay(type)` : convertit `--h1` → `h1`, `--p` → `p`, etc.
Propriétés JSON avec Markdown
Certaines propriétés des JSON contiennent du texte Markdown qui est parsé et rendu en HTML :
| Type de contenu | Propriété | Markdown supporté | Type hiérarchique | |-----------------|-----------|-------------------|-------------------| | `hero` | `description` | `gras`, `\n` | `--p` | | `texteLarge` | `texte` | `gras`, `\n\n` | `--p` | | `competence` | `description` | `gras`, `lien` | `--p` | | `domaineDeCompetence` | `contenu` | `gras`, `\n` | `--p` | | `temoignage` | `temoignage` | `\n\n` | `--p` | | `experienceEtApprentissage` | `description` | `gras` | `--n` | | `detournementVideo` | `pitch` | `gras` | `--p` |
Syntaxe Markdown supportée :
- `texte` → `<strong>texte</strong>` (gras)
- `texte` → `<em>texte</em>` (italique)
- `texte` → `<a href="url">texte</a>` (lien)
- `\n` → `<br />` (saut de ligne)
- `\n\n` → nouveau `<p>` (paragraphe)
---
2.5 Noms Canoniques
Convention de Nommage
- camelCase : un seul mot quand c'est possible (`hero`, `titre`, `video`), sinon plusieurs mots en camelCase (`domaineDeCompetence`, `listeDesPages`, `texteLarge`)
- Préfixe `ui-` : Pour les éléments de structure (layout, wrapper) non définis dans les JSON
- Containers implicites : Chaque propriété a un container `[nomCanonique].cont`
Layouts (data-layout)
Les conteneurs de listes exposent un attribut data-layout :
| Conteneur | data-layout | Rôle | |-----------|-------------|------| | listeDeProfils | `4 columns x 1 row` | Grille 4 colonnes | | temoignages | `2 columns x N rows` | Grille 2 colonnes | | competences | `3 columns x 1 row` | Grille 3 compétences | | experiencesEtApprentissages | `accordeon, X rows` | Accordéon | | listeDeDetournementsVideo | `X rows` | Liste verticale | | listeDesPages | `draw with page properties` | Rendu dynamique | | droits d'auteur | `tooltip, droits d'auteur` | Popup/tooltip |
---
2.6 Hiérarchie du Contenu
Pour les Pages du Site
Structure implicite des pages générées :
- Page = Composant React
- Sections = Blocs de contenu (hero, texte, callToAction...)
- Éléments = Composants individuels
Pour le Contenu Markdown ("A propos du site", Journaux, Cours)
Hiérarchie unifiée :
| Niveau | Markdown | Représentation | |--------|----------|----------------| | H1 | `#` | Chapitre (dossier) | | H2 | `##` | Section (fichier MD) | | H3 | `###` | Partie (dans le fichier) | | H4 | `####` | Sous-partie | | H5 | `#####` | Bloc |
Spécificités via attributs : `typeDeContenu`, `estPrompt`, `estResultatTechnique`
---
2.7 Parsing Markdown Unifié
Un seul parser (`parseMarkdownContent()`) pour tous les types de fichiers MD :
- Journaux de bord
- "A propos de ce site"
- Cours
- Documentation
L'approche permet une cohérence de rendu sur tout le site.
---
2.8 Mode Lecture
Composant `ModeLectureRenderer` affichant la hiérarchie du contenu avec types sémantiques visibles (H1→H5, containers, répétiteurs). Utile pour comprendre la structure réelle des données.
---
2.9 Metadata SEO
Metadata Pages
Les metadata SEO sont définies dans les JSON de contenu, pas dans les pages. Le helper `metadataBuilder.ts` génère les metadata Next.js.
JSON-LD
Les données structurées (Schema.org) sont lues depuis `data/_site-identity.json` :
- `Person` : Identité du propriétaire
- `WebSite` : Description du site
- `BreadcrumbList` : Fil d'Ariane (généré dynamiquement)
---
Dernière mise à jour : 2 février 2026
Outils de Suivi du Projet
Maintenance et monitoring
---
3.1 Section "A propos du site"
Accessible via `/a-propos-du-site`, cette section expose :
- Sprints : Organisation du travail (un dossier par sprint)
- Definition of Done : Règles qualité par agent
- Journaux : Historique des décisions et travaux
- Cours : Documentation technique
Le contenu est stocké dans `data/A propos de ce site/` en Markdown.
---
3.2 Journaux de Bord
Structure
data/journaux/
├── 1. Journal de bord/ # Entrées quotidiennes par conversation
├── 2. Cours/ # Documentation technique
├── 3. Questions et arbitrages/ # Décisions architecturales
└── 4. Architecture/ # Pratiques originales documentéesFormat des entrées
- Fichier par conversation : `YYYY-MM-DD-NomConversation.md`
- Niveaux H3 → H5 (jamais H1/H2 pour éviter conflits avec la hiérarchie globale)
---
3.3 Système de Métriques
Collecte Automatique
Script `collect-metrics-simple.ts` collectant :
| Catégorie | Métriques | |-----------|-----------| | Tests | Unitaires, Intégration, BDD, E2E (avec durées) | | Couverture | Lines, Statements, Functions, Branches | | Qualité | ESLint errors, Type Coverage (NC actuellement) | | Taille | Fichiers, Lignes, Composants, Pages | | Dépendances | Total, Vulnérabilités | | Performance | Build time, Scores Web (Lighthouse via PageSpeed Insights) |
Scores Web (Lighthouse) : les 4 jauges (Performance, Accessibilité, Bonnes pratiques, SEO) sont remplies par l’API PageSpeed Insights. Sans clé API, le quota est très limité et les scores restent « NC » (jauge à 0). Pour afficher des scores réels : créer une clé sur PageSpeed Insights, puis selon le contexte : en local : définir `PAGESPEED_API_KEY` dans `.env.local` (voir `.env.example`) ; sur Vercel (build = publication) : ajouter la même variable dans le projet Vercel (Settings → Environment Variables), pour que `metrics:collect` (lancé pendant le build) puisse appeler l’API. Mise à jour automatique tous les 7 jours une fois les scores obtenus.
Historique et Tendances
- 100 snapshots maximum (`public/metrics/history.json`)
- Tendances calculées : ↗️ up, ↘️ down, → stable
- Dashboard visuel sur `/metrics`
En local : pour afficher des valeurs à jour (Tests, Couverture, Score Web, Autres indicateurs), exécuter `npm run metrics:collect` avant d’ouvrir la page (ou lancer la commande qui l’inclut, ex. `publie`). Sur Vercel, la collecte est lancée automatiquement avant chaque build.
Métriques "NC"
Les métriques non calculables affichent "NC" au lieu de valeurs fictives.
---
3.4 Stratégie E2E
Ce que compte la métrique « E2E »
Les scénarios E2E affichés sur la page métriques = nombre de blocs `test('...')` dans `tests/end-to-end/*.spec.ts` (un scénario = une page ouverte + un lien cliqué + retour). En local un seul navigateur (Chromium) est utilisé ; en CI, trois (Chromium, Firefox, WebKit). Le rapport Playwright compte alors des runs (scénarios × navigateurs) : la métrique est normalisée pour afficher le nombre de scénarios (ex. 54), pas le nombre de runs (ex. 162).
Lister les scénarios E2E : `npm run test:e2e:list` (ou `npx playwright test -c playwright.e2e-only.config.ts --list`). Les specs sont dans `tests/end-to-end/` (une par page : accueil, plan-du-site, mes-profils, profils CTO/CPO/COO/Agile, etc.).
Objectif
Couverture complète de tous les liens cliquables du site.
Phase 1 : Détection et Qualification
- Audit automatique du site
- Génération des e2eID manquants
- Arbitrage obligatoire (testé ou exclu explicitement)
Phase 2 : Construction du Scénario
- Algorithme glouton optimisé (chaque page visitée une seule fois)
- Enrichissement avec les éléments interactifs
- Mécanismes de fallback (plan du site, logo header)
Plages Réservées pour les e2eID
Pour éviter les conflits entre système déterministe (liens plan du site) et séquentiel :
- Plage 1 : `l600-l749` (150 numéros)
- Plage 2 : `l800-l949` (150 numéros)
---
3.5 Plan du Site
Génération Automatique
- Fichier : `_Pages-Liens-Et-Menus.json`
- Régénéré à chaque commit (pre-commit hook)
- Régénéré au build (pour éviter plan vide sur Vercel)
Contenu
- Toutes les pages du site
- Tous les liens cliquables avec leurs e2eID
- Coordonnées et zones pour les tests E2E
---
3.6 Expérience Parallèle
Ce projet existe en deux versions :
- Version itérative (ce dépôt) : TDD/BDD strict, 400+ tests, 91%+ couverture
- Version expérimentale (clone) : Code généré directement depuis les US
L'objectif est de comparer les deux approches en termes de qualité, maintenabilité et vélocité.
---
Dernière mise à jour : 2 février 2026
Organisation du DOM
*Guide pour le Designer : structure HTML, containers, attributs data-**
---
4.1 Principe Général
Le site utilise une architecture DOM déclarative : chaque concept métier est identifiable par sa classe racine et ses attributs data-*. Le Designer cible ces éléments dans le CSS sans modifier le code React.
Source de vérité : `constants/canonicalSpec.ts` définit :
- Les noms canoniques (ex. `hero.titre`, `competence.description`)
- Les types hiérarchiques (ex. `--h1`, `--p`, `--n`)
- Les containers parents et layouts
---
4.2 Classes Racine (Types de Contenu)
Convention de Nommage
- camelCase : un mot si possible (`hero`, `titre`), sinon plusieurs (`domaineDeCompetence`)
- Sous-éléments : reprennent le nom du concept → `.hero .titre`, `.competence .description`
- Préfixe `ui-` : éléments de structure non métier (ex. `.ui-heroCtas`, `.ui-grid`)
---
4.3 Types de Contenu et leurs Propriétés
titreDePage
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `texte` | `--h1` | Titre de la page (dans le header bleu) |
hero
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `titre` | `--h1` | Titre principal | | `sousTitre` | `--h2` | Sous-titre | | `description` | `--p` | Description (Markdown: gras, \n) | | `callToAction` | `--lk` | Lien CTA (aspect bouton) | | `ensavoirplus` | `--lk` | Lien "En savoir plus" | | `video` | `--v` | Vidéo YouTube (dans hero.droite) |
titre
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `texte` | `--h2` | Titre de section (bande bleue) |
texteLarge
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `texte` | `--p` | Bloc de texte (Markdown: gras, \n\n) |
video
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `urlYouTube` | `--v` | URL de la vidéo YouTube |
profil
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `titre` | `--h2` | Titre du profil | | `jobTitles` | `--n` | Liste des job titles (note) | | `jobTitle` | `--l1` | Job title individuel (liste) | | `slug` | `--m` | Identifiant (métadonnée, non affiché) | | `route` | `--b` | Lien vers le profil (bouton) | | `cvPath` | `--lk` | Lien vers le CV (PDF) |
temoignage
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `photo` | `--photo` | Photo du témoin | | `nom` | `--h3` | Nom du témoin | | `fonction` | `--n` | Fonction (note) | | `temoignage` | `--p` | Texte du témoignage (Markdown: \n\n) |
domaineDeCompetence
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `titre` | `--h2` | Titre du domaine | | `contenu` | `--p` | Description (Markdown: gras, \n) | | `auteur` | `--a` | Auteur de la citation (italique) |
competence
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `titre` | `--h3` | Titre de la compétence | | `description` | `--p` | Description (Markdown: gras, lien) | | `auteur` | `--a` | Auteur de la citation (italique) | | `image.src` | `--img` | Image illustrative | | `image.alt` | `--n` | Texte alternatif (note) | | `bouton.action` | `--lk` | Lien optionnel |
experienceEtApprentissage
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `categorie` | `--m` | Catégorie (métadonnée) | | `description` | `--n` | Description (Markdown: gras) | | `periode` | `--m` | Période (métadonnée) |
callToAction
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `action` | `--lk` | Lien (aspect bouton) |
bouton (dans groupeDeBoutons)
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `command` | `--lk` | Lien du bouton |
detournementVideo
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `titre` | `--h2` | Titre du détournement | | `pitch` | `--p` | Pitch (Markdown: gras) | | `date` | `--n` | Date (note) | | `titreVideoDetournee` | `--h3` | Titre vidéo détournée | | `videoDetournee` | `--v` | Vidéo détournée | | `droitsAuteur` | `--n` | Droits d'auteur (tooltip) | | `linkedin` | `--n` | Lien LinkedIn (note) | | `titreVideoOriginale` | `--h3` | Titre vidéo originale | | `videoOriginale` | `--v` | Vidéo originale |
listeDesPages
| Propriété | Type hiérarchique | Description | |-----------|-------------------|-------------| | `page` | `--l1` | Élément de liste (lien vers page) |
---
4.4 Containers Implicites
Chaque propriété (non container) reçoit un container implicite `[nomCanonique].cont` :
hero
├── hero.cont (container)
│ ├── hero.gauche.cont
│ │ ├── hero.titre.cont → hero.titre (--h1)
│ │ ├── hero.sousTitre.cont → hero.sousTitre (--h2)
│ │ ├── hero.description.cont → hero.description (--p)
│ │ ├── hero.callToAction.cont → hero.callToAction (--lk)
│ │ └── hero.ensavoirplus.cont → hero.ensavoirplus (--lk)
│ └── hero.droite.cont
│ └── hero.video.cont → hero.video (--v)Utilité : Permet de styler le container (padding, margin, background) indépendamment du contenu.
---
4.5 Attributs data-*
data-layout
Indique la mise en page attendue pour les listes et containers :
| Conteneur | data-layout | Comportement | |-----------|-------------|--------------| | `listeDeProfils` | `4 columns x 1 row` | Grille 4 colonnes | | `temoignages` | `2 columns x N rows` | Grille 2 colonnes | | `domaineDeCompetence .competences` | `3 columns x 1 row` | 3 compétences en ligne | | `listeDesExperiencesEtApprentissages` | `accordeon, X rows` | Accordéon (fermé par défaut) | | `listeDeDetournementsVideo` | `X rows` | Liste verticale | | `listeDesPages` | `draw with page properties` | Rendu dynamique par zone | | `droitsAuteur` | `tooltip, droits d'auteur` | Popup au clic |
Usage CSS :
[data-layout="4 columns x 1 row"] {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
[data-layout^="accordeon"] {
/* styles accordéon */
}data-container-id
Identifie le container pour le mode lecture et le debug :
<div class="hero" data-container-id="hero.cont">
<div class="heroGauche" data-container-id="hero.gauche.cont">
...
</div>
</div>---
4.6 Modificateurs Visuels
Classes additionnelles pour les effets visuels :
| Classe | Effet | |--------|-------| | `.light` | Fond clair (alternance de blocs) | | `.tailleGrande` | Boutons plus grands | | `.video.light` | Vidéo avec fond clair |
Usage :
.domaineDeCompetence.light {
background: var(--CouleurClaire);
}---
4.7 Consignes pour le Designer
Fichiers à modifier
| Fichier | Usage | |---------|-------| | `app/content-styles.css` | Styles des types de contenu | | `app/globals.css` | Tokens CSS (variables) |
Fichiers à NE PAS modifier
- Composants React (`components/*.tsx`)
- Utils (`utils/*.ts`)
- Pages (`app/(main)/*.tsx`)
---
4.8 Référence Rapide : Types Hiérarchiques
| Type | Token | Taille | Usage | |------|-------|--------|-------| | `--h1` | `--enorme` | 2rem | hero.titre, titreDePage.texte | | `--h2` | `--grande` | 1.5rem | hero.sousTitre, titre.texte, profil.titre | | `--h3` | `--normale` | 1rem | competence.titre, temoignage.nom | | `--p` | `--normale` | 1rem | description, contenu, temoignage | | `--n` | `--petite` | 0.8rem | jobTitles, fonction, date, experienceEtApprentissage.description | | `--lk` | `--normale` | 1rem | callToAction, ensavoirplus, bouton.command | | `--a` | `--normale` | 1rem | auteur (italique) |
---
Dernière mise à jour : 2 février 2026
Hiérarchie du site (ASCII Art)
Un dossier par container, une ligne par propriété. Chaque propriété a un container implicite `[nomCanonique].cont` (spec canonique). Source : spec canonique (`constants/canonicalSpec.ts`). Généré par `utils/siteHierarchyGenerator.ts` (sans IA).
body/
└── .main-content-cont/ (page)
├── header/
│ └── titreDePage.texte/
│ └── titreDePage.texte
├── listeDesPages/
│ └── listeDesPages.page/
│ └── listeDesPages.page
├── hero/
│ ├── hero.gauche/
│ │ ├── hero.titre/
│ │ │ └── hero.titre
│ │ ├── hero.sousTitre/
│ │ │ └── hero.sousTitre
│ │ ├── hero.description/
│ │ │ └── hero.description
│ │ ├── hero.callToAction/
│ │ │ └── hero.callToAction
│ │ └── hero.ensavoirplus/
│ │ └── hero.ensavoirplus
│ └── hero.droite/
│ └── hero.video/
│ └── hero.video
├── listeDeProfils/
│ └── profil/
│ ├── profil.titre/
│ │ └── profil.titre
│ ├── profil.jobTitles/
│ │ └── profil.jobTitles
│ ├── profil.jobTitle/
│ │ └── profil.jobTitle
│ ├── profil.slug/
│ │ └── profil.slug
│ ├── profil.route/
│ │ └── profil.route
│ └── profil.cvPath/
│ └── profil.cvPath
├── titre.texte/
│ └── titre.texte
├── video.urlYouTube/
│ └── video.urlYouTube
├── listeDeTemoignages/
│ └── temoignage/
│ ├── temoignage.temoin/
│ │ ├── temoignage.temoin.photo/
│ │ │ └── temoignage.photo/
│ │ │ └── temoignage.photo
│ │ └── temoignage.temoin.texte/
│ │ ├── temoignage.nom/
│ │ │ └── temoignage.nom
│ │ └── temoignage.fonction/
│ │ └── temoignage.fonction
│ └── temoignage.temoignage/
│ └── temoignage.temoignage
├── domaineDeCompetence/
│ ├── domaineDeCompetence.header/
│ │ ├── domaineDeCompetence.titre/
│ │ │ └── domaineDeCompetence.titre
│ │ ├── domaineDeCompetence.contenu/
│ │ │ └── domaineDeCompetence.contenu
│ │ └── domaineDeCompetence.auteur/
│ │ └── domaineDeCompetence.auteur
│ └── domaineDeCompetence.competences/
│ └── competence/
│ ├── competence.titre/
│ │ └── competence.titre
│ ├── competence.description/
│ │ └── competence.description
│ ├── competence.auteur/
│ │ └── competence.auteur
│ ├── competence.image.src/
│ │ └── competence.image.src
│ ├── competence.image.alt/
│ │ └── competence.image.alt
│ └── competence.bouton.action/
│ └── competence.bouton.action
├── listeDesExperiencesEtApprentissages/
│ ├── listeDesExperiencesEtApprentissages.titre/
│ │ └── listeDesExperiencesEtApprentissages.titre
│ └── experienceEtApprentissage/
│ ├── experienceEtApprentissage.categorie/
│ │ └── experienceEtApprentissage.categorie
│ ├── experienceEtApprentissage.description/
│ │ └── experienceEtApprentissage.description
│ └── experienceEtApprentissage.periode/
│ └── experienceEtApprentissage.periode
├── texteLarge.texte/
│ └── texteLarge.texte
├── callToAction.action/
│ └── callToAction.action
├── groupeDeBoutons/
│ └── bouton/
│ └── bouton.command/
│ └── bouton.command
└── listeDeDetournementsVideo/
└── detournementVideo/
├── detournementVideo.header/
│ ├── detournementVideo.titre/
│ │ └── detournementVideo.titre
│ ├── detournementVideo.pitch/
│ │ └── detournementVideo.pitch
│ └── detournementVideo.date/
│ └── detournementVideo.date
└── detournementVideo.videos/
├── detournementVideo.videoDetournee/
│ ├── detournementVideo.titreVideoDetournee/
│ │ └── detournementVideo.titreVideoDetournee
│ ├── detournementVideo.videoDetournee/
│ ├── detournementVideo.videoDetournee
│ ├── detournementVideo.droitsAuteur/
│ │ └── detournementVideo.droitsAuteur
│ └── detournementVideo.linkedin/
│ └── detournementVideo.linkedin
└── detournementVideo.videoOriginale/
├── detournementVideo.titreVideoOriginale/
│ └── detournementVideo.titreVideoOriginale
├── detournementVideo.videoOriginale/
└── detournementVideo.videoOriginaleLégende
| Symbole | Signification | |--------|----------------| | `xxx/` | Container (dossier) — élément de type `--c` avec enfants ; inclut les containers implicites `[nom].cont` | | `xxx.yyy` | Propriété (ligne) — champ affiché (h1, h2, p, lien, etc.), enfant de son container `xxx.yyy.cont` | | `└──` | Dernier enfant du parent | | `├──` | Enfant suivi d'autres frères | | `│` | Suite du parent (enfants imbriqués) |
Racine
- body : racine de la page (layout).
- .main-content-cont : zone d'affichage entre header et footer (DOM : `main.main-content-cont`).
Problèmes Rencontrés et Résolus
Catalogue des problèmes majeurs rencontrés et leurs solutions, pour éviter de les reproduire.
---
1. La Bataille des Tooltips (24 janvier 2026)
Symptôme
Les info-bulles de la page Metrics apparaissaient sous d'autres éléments au lieu de flotter au-dessus.
Cause Racine
Les tooltips étaient rendues DANS la carte parent, héritant de son contexte d'empilement (stacking context). Un `z-index` même très élevé ne peut pas sortir de ce contexte.
Solution
React Portal vers `document.body` :
import { createPortal } from 'react-dom';
// ...
return createPortal(
<div className="tooltip" style={{ position: 'fixed', zIndex: 999999 }}>
{content}
</div>,
document.body
);Leçon Apprise
- Une tooltip doit toujours utiliser un Portal
- Le mot "dessous" est ambigu (position Y vs z-index) — toujours préciser
---
2. Confusion "dessous" Vertical vs Z-index
Symptôme
Multiples corrections inefficaces car l'IA confondait :
- Position verticale (Y, "bottom")
- Z-index (couches d'empilement)
Cause
Le terme "dessous" a deux sens en CSS/UI.
Solution
Être explicite dans les prompts :
- ❌ "L'info bulle est sous les icônes"
- ✅ "L'info bulle est sous les icônes EN TERMES DE Z-INDEX (couches)"
Leçon Apprise
Toujours préciser le concept technique quand un mot a plusieurs sens.
---
3. Tests Désynchronisés après Refactoring
Symptômes
- Tests qui attendent `h1` alors que le composant utilise `h2`
- Chemins d'import obsolètes (`../../app/maintenance/page` vs `../../app/(main)/maintenance/page`)
- Props testées qui n'existent plus
Cause
Les tests n'avaient pas été mis à jour lors des refactorings précédents.
Solutions
- Chemins : Mettre à jour les imports après déplacement de fichiers
- Assertions : Vérifier ce que le composant rend réellement, pas ce qu'il rendait avant
- Props : Supprimer les tests de props obsolètes
Leçon Apprise
Après un refactoring structurel, toujours lancer la suite de tests complète.
---
4. E2eID Conflictuels
Symptôme
Le test E2E échouait car un e2eID généré par le système déterministe (plan du site) était identique à un e2eID du système séquentiel.
Cause
Les deux systèmes utilisaient la même plage de numéros (0-999).
Solution
2 plages réservées pour le système déterministe :
- `l600-l749` (150 numéros)
- `l800-l949` (150 numéros)
Le système séquentiel saute automatiquement ces plages.
Leçon Apprise
Quand deux systèmes génèrent des identifiants, définir des plages disjointes.
---
5. Métriques Hardcodées Affichées comme Valeurs Réelles
Symptôme
"Complexité Cyclomatique : 5" affichée alors que la valeur n'est pas calculée.
Cause
Valeurs fictives hardcodées dans le code pour faire joli pendant le développement.
Solution
Remplacer par `"NC"` (Non Calculé) et masquer les cartes dont la valeur est `"NC"`.
Leçon Apprise
Jamais de valeurs fictives en dur — utiliser un placeholder explicite ("NC", "N/A").
---
6. Chronométrage BDD/E2E Mélangé
Symptômes
- v1 : Durées BDD et E2E identiques (une seule mesure)
- v2 : Durée BDD à zéro
Cause
Le fichier `data.json` de Playwright contenait les résultats combinés. La durée était lue depuis ce fichier au lieu d'être mesurée indépendamment.
Solution
Mesure séparée avec `Date.now()` :
const startBdd = Date.now();
await runBddTests();
const bddDuration = Date.now() - startBdd;
const startE2e = Date.now();
await runE2eTests();
const e2eDuration = Date.now() - startE2e;Stockage dans `durations.json`, pas dans `data.json`.
Leçon Apprise
Toujours mesurer les durées explicitement, ne pas les déduire de fichiers intermédiaires.
---
7. Plan du Site Vide sur Vercel
Symptôme
La page plan-du-site affichait un contenu vide sur Vercel mais fonctionnait en local.
Causes
- Le fichier `_Pages-Liens-Et-Menus.json` n'était pas régénéré avant le build
- Les pages n'avaient pas de propriété `zone` (filtrage échouait)
Solutions
- Génération automatique du plan du site dans le script de build
- Assignation automatique des zones selon l'URL dans `detecterPages()`
Leçon Apprise
Tout fichier généré doit être régénéré automatiquement lors du build.
---
8. Marge Droite Résiduelle dans Tooltip
Symptôme
Espace à droite dans la tooltip même après suppression de l'ascenseur.
Cause
Règles CSS résiduelles de l'ascenseur (`overflow-y: auto`, `max-height`).
Solution
Supprimer complètement les styles d'ascenseur :
overflow-y: visible;
max-height: none;Leçon Apprise
Quand on supprime une fonctionnalité CSS, supprimer TOUTES les règles associées.
---
9. path Traversal dans les Lecteurs de Fichiers
Symptôme
Vulnérabilité de sécurité permettant d'accéder à des fichiers hors du répertoire autorisé.
Cause
Concaténation directe du chemin utilisateur sans validation :
// ❌ Vulnérable
const filePath = path.join(process.cwd(), userPath);Solution
Validation avec `path.resolve()` :
function validateAndResolvePath(relativePath: string, allowedBase: string): string {
const resolvedPath = path.resolve(process.cwd(), relativePath);
const allowedDir = path.resolve(process.cwd(), allowedBase);
if (!resolvedPath.startsWith(allowedDir + path.sep)) {
throw new Error(`Accès interdit: chemin hors répertoire autorisé`);
}
return resolvedPath;
}Leçon Apprise
Toujours valider les chemins avant d'accéder aux fichiers.
---
10. Titres Redondants dans Tooltips
Symptôme
Le titre de la métrique apparaissait deux fois : dans le bloc bleu ET dans la tooltip.
Cause
Le composant `MetricTooltip` incluait un `<h4>` avec le titre.
Solution
Supprimer le titre de la tooltip — il est déjà visible juste en dessous sur le bloc bleu.
Leçon Apprise
Éviter la redondance d'information dans l'UI.
---
11. Auteurs de Citations Non Affichés
Symptôme
Les citations dans les compétences n'affichaient plus l'auteur.
Causes
- Le champ `auteur` n'était pas défini dans l'interface TypeScript
- Le champ n'était pas copié lors de la résolution des références (bibliothèque)
- La classe CSS utilisait `styles.competenceAuteur` au lieu de la classe globale
Solutions
- Ajouter `auteur?: string` à l'interface `Competence`
- Copier `auteur: competence.auteur` dans `profilBuilder.ts`
- Utiliser la classe globale `competenceAuteur`
Leçon Apprise
Vérifier les 3 couches : interface TypeScript, logique de résolution, CSS.
---
12. Imports CSS Modules Vides
Symptôme
Erreur "Failed to fetch" après refactorisation CSS.
Cause
Composants important des CSS modules vides (contenant seulement un commentaire de déplacement) sans les utiliser.
Solution
Supprimer tous les imports CSS modules non utilisés.
Leçon Apprise
Après migration CSS, supprimer les imports obsolètes, pas seulement vider les fichiers.
---
13. Algorithme E2E Visitant 63 Pages au lieu de 13
Symptôme
Le scénario E2E visitait 63 pages alors que le site n'en a que 13.
Cause
L'algorithme glouton repassait plusieurs fois par les mêmes pages pour suivre différents liens.
Solution
Refactorisation : visiter chaque page une seule fois, marquer tous ses liens comme testés.
Leçon Apprise
Un algorithme glouton naïf peut être très inefficace — optimiser dès que le résultat est anormal.
---
14. Commande Jest Incorrecte
Symptôme
Tests Jest ne s'exécutent pas correctement.
Cause
Utilisation de `--testPathPattern` au lieu de `--testPathPatterns` (avec 's').
Solution
npm test -- --testPathPatterns="tests/unit"Leçon Apprise
Vérifier la documentation pour les options CLI exactes.
---
15. Agents Manquants sur Vercel
Symptôme
Le board KanBan n'affichait que 2 colonnes (A faire, Fait) sur Vercel.
Cause
Le dossier `.cursor/agents` n'était pas poussé sur GitHub (`.gitignore`).
Solution
Créer `data/A propos/agents.json` comme source de vérité, synchronisé depuis `.cursor/agents` en dev.
Leçon Apprise
Pour les données utilisées en production, avoir une source dans `data/` (pas dans `.cursor/`).
---
16. Conflit lock Next.js lors de npm run publie (février 2026)
Symptôme
`npm run publie` échoue pendant les tests BDD puis E2E avec :
- `Port 3000 is in use by process X, using available port 3001 instead`
- `Unable to acquire lock at .next/dev/lock, is another instance of next dev running?`
- `Process from config.webServer was not able to start`
Cause
Un `next dev` tourne déjà (ex. dans un autre terminal). Playwright, avec `reuseExistingServer: true`, doit détecter que `http://localhost:3000` répond pour réutiliser ce serveur. Si la vérification échoue (timeout ou réponse lente), il lance un second `npm run dev`, qui tente le port 3001 puis échoue sur le lock partagé `.next/dev/lock`.
Solution
Dans `scripts/collect-metrics-simple.ts`, avant l’exécution des tests BDD, une attente explicite (`waitForServerReady`) vérifie que `http://localhost:3000` répond (2xx/3xx/4xx) pendant jusqu’à 15 s en local. Ainsi Playwright considère le serveur existant comme prêt et le réutilise au lieu d’en démarrer un second.
En pratique : soit ne pas lancer `npm run dev` avant `npm run publie` (Playwright démarrera le serveur), soit le lancer et attendre que Next affiche « Ready » avant de lancer `publie`.
Leçon Apprise
En local, s’assurer que la sonde de réutilisation (URL prête) réussit avant que Playwright tente de lancer le webServer.
---
Ce catalogue sera enrichi au fil des problèmes rencontrés.
Guide pour des Prompts Efficaces
Synthèse des patterns efficaces et des pièges à éviter, basée sur l'analyse de nos échanges.
---
Règle d'Or
Un prompt efficace = Action + Contexte + Critères + Exemple
---
1. Patterns Efficaces ✅
1.1 Demandes Techniques Précises avec Contexte
Efficace :
Dans 'index.json' remonte 'Développement informatique' sous 'Engager les équipes'
et avant 'Interactions humaines'Pourquoi : Action claire, fichier identifié, résultat attendu explicite.
1.2 Demandes avec Exemples Concrets
Efficace :
Sur le metric 'Complexité cyclomatique' ajoute une info bulle avec ce tableau :
Complexité | Interprétation
1–10 | Excellente : Code simple...Pourquoi : Format attendu montré, pas d'ambiguïté.
1.3 Corrections Ciblées avec Localisation
Efficace :
Les 5 blocs supérieurs de metrics ne sont pas responsives.
Si SmartPhone, les empiler les uns sous les autres.Pourquoi : Problème localisé, solution attendue claire.
1.4 Lettres pour les Options
Efficace :
Propose moi des options avec une lettre :
A) Corriger le test
B) Supprimer le test
C) Refactoriser le composantPourquoi : Réponse rapide avec "A", "B" ou "C".
---
2. Pièges à Éviter ❌
2.1 Ambiguïté Terminologique
Problématique :
L'info bulle est sous les icônesConfusion : Position verticale (Y) ou z-index (couches) ?
Mieux :
L'info bulle est sous les icônes EN TERMES DE Z-INDEX (couches),
pas en position verticale2.2 Scope Implicite
Problématique :
Sur le metric 'Complexité cyclomatique' ajoute une info bulleConfusion : Juste cette métrique ou les 19 ?
Mieux :
Sur TOUTES les 19 métriques de la page, ajoute une info bulle
avec le contenu approprié2.3 Critères de Qualité Non Définis
Problématique :
C'est illisible. Vas tu pouvoir corriger ?Confusion : Qu'est-ce qui est lisible pour vous ?
Mieux :
Le fichier est illisible. Je veux un format chronologique :
- Une demande de ma part
- L'image illustrative juste au dessus ou en dessous
- Un résumé de ta réponse
Format factuel, pas de titres excessifs2.4 Stratégie de Correction Non Précisée
Problématique :
Le fichier contient un titre H2. Les fichiers MD doivent commencer au H3.Confusion : Remplacer tous les H2 ? Dans quel ordre ?
Mieux :
Corrige en remontant progressivement depuis le bas :
1. H6 → H5
2. H5 → H4
3. H4 → H3
4. Garde les 3 H2 originaux (lignes 231, 389, 471) en H32.5 Contexte Manquant
Problématique :
Les images n'apparaissent pasManque : Le dossier images a été déplacé.
Mieux :
Les images n'apparaissent pas.
Contexte : Le dossier images a été déplacé de
'data/A propos de ce site/images/' vers 'data/images/' à la racine.---
3. Formats Recommandés
3.1 Correction Technique
Dans [fichier], [problème identifié].
[Explication de la cause si connue].
Solution attendue : [description claire].Exemple :
Dans 'content-styles.css', les boutons débordent de leur conteneur.
Solution attendue : Ajouter max-width: 100% et box-sizing: border-box.3.2 Nouvelle Fonctionnalité
[Contexte : où, quand, pourquoi]
[Action souhaitée]
[Comportement attendu avec exemples]
[Contraintes ou exceptions]Exemple :
Contexte : Page plan-du-site, besoin d'organiser visuellement.
Action : Grouper les pages par zones (HomePage, Profils, Autres, Footer).
Comportement :
- Ligne 1 : HomePage (centré)
- Ligne 2 : Profils (tous côte à côte)
- Ligne 3 gauche : Autres (colonne)
- Ligne 3 droite : Footer (colonne)
Contrainte : Boutons de 270x60px, centrés dans leur zone.3.3 Demande Multi-étapes
Plusieurs actions à faire :
1. [Première action]
2. [Deuxième action]
3. [Troisième action]
Fais-les dans cet ordre et valide chaque étape.3.4 Bug à Investiguer
Symptôme : [ce qui se passe]
Attendu : [ce qui devrait se passer]
Contexte : [changements récents, fichiers concernés]
Pistes : [hypothèses si vous en avez]---
4. Commandes Rapides
| Commande | Signification | |----------|---------------| | `GO US` | Lancer une nouvelle User Story | | `GO NEXT` | Passer à l'étape suivante (revue Lead Dev) | | `A` | Approuver l'action proposée | | `B` | Demander plus d'explications | | `0` | Passer/Ignorer cette action | | `C puis A` | Commit d'abord, puis faire l'action |
---
5. Checklist Avant d'Envoyer
Avant d'envoyer un prompt, vérifiez :
- [ ] Action claire : Qu'est-ce que je veux exactement ?
- [ ] Scope défini : Un seul élément ou tous ?
- [ ] Contexte fourni : Y a-t-il des changements récents à mentionner ?
- [ ] Critères précisés : Comment juger que c'est bon ?
- [ ] Termes désambiguïsés : Les mots techniques ont-ils plusieurs sens ?
- [ ] Stratégie indiquée : Comment procéder (ordre, méthode) ?
- [ ] Exemple fourni : Format attendu montré ?
---
6. Économiser des Tokens
6.1 Éviter les Allers-Retours
Coûteux :
Prompt 1: "Corrige le problème"
IA: "Quel problème ?"
Prompt 2: "L'info bulle"
IA: "Quelle info bulle ?"
Prompt 3: "Celle de Complexité"Économique :
Corrige l'info bulle "Complexité Cyclomatique" qui ne flotte pas au-dessus
des cartes bleues (problème de z-index, pas de position verticale).6.2 Grouper les Demandes Liées
Coûteux : 5 prompts pour 5 corrections CSS indépendantes.
Économique :
5 corrections CSS à faire :
1. Bouton trop large → max-width: 270px
2. Texte sur 2 lignes → white-space: nowrap
3. Pas de rollover → :hover { background: var(--BleuClair) }
4. Titre en noir → color: var(--BleuFonce)
5. Marge droite → padding-right: 06.3 Donner le Contexte une Fois
Coûteux : Répéter le contexte à chaque prompt.
Économique :
Contexte pour cette session :
- On travaille sur la page Metrics
- Les tooltips utilisent React Portal
- Le thème des boutons est "Fond BleuFoncé / Texte blanc"
---
[Puis vos demandes sans répéter le contexte]6.4 Utiliser les Références
Coûteux : Redonner tout le contenu d'un fichier.
Économique :
Dans le fichier qu'on vient de créer (_site-identity.json),
ajoute le champ "phone".---
7. Quand l'IA Se Trompe
7.1 Feedback Constructif
Peu efficace :
Tu fais n'importe quoi !Efficace :
C'est quoi cette stratégie ? Si tu corriges d'abord les H2 en H3,
comment vas-tu corriger les H3 en H4 ? Il faut commencer par le niveau le plus bas.7.2 Réorienter vers la Documentation
Peu efficace :
Tu aurais dû savoir que le hook pre-commit régénère automatiquementEfficace :
Ce que tu viens de faire manuellement aurait dû être fait par le TI.
Va lire le fichier 'data/journaux/4. Architecture/Pre-commit hooks...'
pour comprendre la stratégie automatique.7.3 Demander une Analyse Systémique
Quand : Après plusieurs corrections inefficaces.
Comment :
Prend de la hauteur. Oublie tout et repars des fondamentaux.
N'essaie pas d'appliquer un correctif mais fais une analyse
plus systémique de la construction de la page et du CSS associé.---
8. Les 3 Règles d'Or
1. Spécificité > Généralité
- Préciser plutôt que généraliser
- Montrer plutôt que décrire
- Exemple plutôt qu'abstraction
2. Contexte > Assumptions
- Mentionner les changements récents
- Fournir les informations contextuelles
- Diriger vers la documentation si nécessaire
3. Structure > Flux de conscience
- Lister les actions
- Ordonner les étapes
- Définir les critères de succès
---
Ce guide est basé sur l'analyse de notre historique de collaboration, notamment la "bataille des tooltips" qui illustre parfaitement les patterns de friction et d'efficacité.
Analyse de Profil : Forces, Faiblesses et Positionnement
Basée sur l'observation des échanges IA/Utilisateur et la lecture de 67 fichiers de journaux (février 2026)
---
1. Forces Identifiées
Vision Produit & UX
| Observation | Exemple | |-------------|---------| | Précision visuelle | "Les 3 compétences doivent être horizontalement sur PC, verticalement sur mobile" | | Détection des incohérences | Repérer les auteurs manquants, la marge résiduelle, les 4 tooltips oubliées | | Exigence qualité | Refus des valeurs hardcodées → "NC" plutôt que mentir | | Sensibilité UX | "L'info bulle ne tient pas dans l'écran", "Texte sur 2 lignes" |
Méthodologie & Organisation
| Observation | Exemple | |-------------|---------| | Structuration naturelle | US → BDD → TDD, sprints, Definition of Done | | Workflow clair | "GO US", "GO NEXT" — commandes simples et efficaces | | Traçabilité | Journal de bord, historique des décisions | | Comparaison scientifique | Deux projets parallèles pour évaluer les approches |
Posture de Leadership
| Observation | Exemple | |-------------|---------| | Challenger sans casser | "Prends de la hauteur, repars des fondamentaux" | | Délégation claire | Agents spécialisés avec périmètres définis | | Feedback constructif | Expliquer pourquoi c'est faux, pas juste critiquer | | Savoir dire non | "Je refuse que l'UX-UI designer soit sollicité" après échecs répétés |
Compréhension Architecturale
| Observation | Exemple | |-------------|---------| | Concepts maîtrisés | Architecture hexagonale, TDD, BDD, Clean Code | | Vision système | Comprendre que le problème vient des icônes, pas de la tooltip | | Pragmatisme technique | "C'est moche d'utiliser `any` alors que j'ai choisi TypeScript" |
---
2. Zones de Délégation
Vocabulaire Technique CSS/JS
| Pattern observé | Traduction technique | |-----------------|---------------------| | "flotter dessous" | z-index / stacking context | | "assensseur" | scrollbar / overflow | | "tetière" | header | | "rollover" | hover state |
Conséquence : Quelques allers-retours pour clarifier, mais le concept est toujours là.
Détails d'Implémentation
| Pattern observé | Ce que ça signifie | |-----------------|-------------------| | Pas de proposition de code | Décrit le quoi, pas le comment | | Questions sur les mécanismes | "Comment est calculée la couverture ?" |
Conséquence : Besoin d'un binôme technique pour l'exécution.
Configuration des Outils
| Pattern observé | Ce que ça signifie | |-----------------|-------------------| | Surprises sur Jest/Playwright | Options CLI, fichiers de config | | Dépendance à l'IA pour les commandes | `--testPathPatterns` vs `--testPathPattern` |
Conséquence : Pas un problème avec un dev ou une IA en support.
---
3. Positionnement Recommandé
Le Profil qui Émerge
┌─────────────────────────────────────────────────────────────┐
│ │
│ CPO / Product Owner │
│ ════════════════════ │
│ • Définit le QUOI avec précision │
│ • Exigence qualité élevée │
│ • Feedback UX détaillé │
│ │
│ + │
│ │
│ Coach Agile / Facilitateur │
│ ══════════════════════════ │
│ • Structure méthodologique (US, BDD, TDD, DoD) │
│ • Capacité à challenger constructivement │
│ • Vision processus et amélioration continue │
│ │
│ + │
│ │
│ Architecte Fonctionnel │
│ ══════════════════════ │
│ • Comprend les patterns (hexagonal, Clean Code) │
│ • Fait le pont entre métier et technique │
│ • Valide les choix sans coder │
│ │
└─────────────────────────────────────────────────────────────┘Où la Valeur Ajoutée est Maximale
| Contexte | Contribution | |----------|-------------| | Projet sans PO clair | Structurer les US, définir les critères d'acceptation | | Équipe technique sans méthodo | Installer TDD/BDD, DoD, revues | | Produit avec dette UX | Identifier les incohérences, prioriser les corrections | | Transformation digitale | Faire le pont métier-tech, challenger les deux côtés | | Audit qualité | Évaluer un projet (comme l'audit 17 critères réalisé) |
Où la Valeur Ajoutée est Limitée
| Contexte | Raison | |----------|--------| | Développeur au quotidien | Profil qui décrit, pas qui code | | Ops / DevOps | Configuration, infrastructure | | Architecte technique pur | Choix de frameworks, optimisation performance |
---
4. Zones d'Apprentissage Potentielles
Pour Renforcer l'Autonomie Technique
| Zone | Investissement | ROI | |------|----------------|-----| | Vocabulaire CSS | Faible (glossaire) | Moyen — moins d'allers-retours | | Bases Git avancées | Moyen | Élevé — autonomie sur les branches | | Lecture de code TypeScript | Moyen | Élevé — comprendre les PR |
Pour Rester sur la Zone de Force
| Zone | Investissement | ROI | |------|----------------|-----| | Outils IA (Cursor, Copilot) | Faible | Élevé — déjà très bien maîtrisé | | Frameworks BDD | Faible (déjà maîtrisé) | — | | Product Discovery | Moyen | Élevé — compléter le profil CPO |
Recommandation
Ne pas chercher à devenir développeur. La valeur est dans la capacité à :
- Définir précisément ce qu'on veut
- Structurer le travail méthodologiquement
- Challenger avec bienveillance
- Faire le pont entre vision et exécution
C'est un profil rare et recherché — beaucoup de devs, peu de gens qui savent définir clairement le besoin.
---
5. Argumentaire Recruteur
> "25 ans d'expérience à transformer des idées en produits logiciels. Profil hybride CPO/Coach Agile capable de définir des User Stories précises, installer une culture TDD/BDD, et challenger les équipes techniques sur la qualité. Pas développeur au quotidien, mais comprend l'architecture (hexagonale, Clean Code) et sait utiliser les outils IA pour prototyper. Valeur ajoutée : faire le pont entre la vision métier et l'exécution technique, avec une exigence qualité élevée."
---
6. Patterns de Prompts Observés
Ce qui Fonctionne Bien
"Dans 'index.json' remonte 'Développement informatique'
sous 'Engager les équipes' et avant 'Interactions humaines'"→ Action claire, fichier identifié, résultat attendu explicite.
Ce qui Nécessite des Clarifications
"L'info bulle est sous les icônes"→ Ambiguïté : position verticale ou z-index ?
Évolution Recommandée
| Avant | Après | |-------|-------| | "L'info bulle est sous les icônes" | "L'info bulle est sous les icônes EN TERMES DE Z-INDEX (couches)" | | "C'est illisible" | "Format attendu : chronologique, une demande → une réponse" | | "Corrige les H2" | "Corrige en remontant depuis le bas : H6→H5, H5→H4..." |
---
Document généré le 2 février 2026 à partir de l'analyse des échanges projet.
Hiérarchie containers par page (ASCII Art)
Containers uniquement — pour le mode GO CSS de l'agent Designer. Source : JSON de chaque page (`readPageData` + `contenuToAsciiArt`). Généré par `npm run css:hierarchy`.
Usage : copier-coller le bloc ASCII de la page souhaitée dans la commande `GO CSS`.
---
Home (`/`)
body/
└── .main-content-cont/ (page)
└── hero/ (data-layout="2 columns")
├── hero.gauche/
│ ├── hero.titre/
│ ├── hero.sousTitre/
│ ├── hero.description/
│ ├── hero.callToAction/
│ └── hero.ensavoirplus/
└── hero.droite/
└── hero.video/---
Mes Profils (`/mes-profils`)
body/
└── .main-content-cont/ (page)
├── header/
│ └── titreDePage.texte/
├── listeDeProfils/ (data-layout="4 columns x 1 row")
│ └── profil/
│ ├── profil.titre/
│ ├── profil.jobTitles/
│ ├── profil.jobTitle/
│ ├── profil.slug/
│ ├── profil.route/
│ └── profil.cvPath/
├── callToAction.action/
├── texteLarge.texte/
└── listeDeTemoignages/ (data-layout="2 columns x N rows")
└── temoignage/
├── temoignage.temoin/
│ ├── temoignage.temoin.photo/
│ │ └── temoignage.photo/
│ └── temoignage.temoin.texte/
│ ├── temoignage.nom/
│ └── temoignage.fonction/
└── temoignage.temoignage/---
Produit logiciel (`/profil/cpo`)
body/
└── .main-content-cont/ (page)
├── video.urlYouTube/
├── header/
│ └── titreDePage.texte/
├── domaineDeCompetence/
│ ├── domaineDeCompetence.header/
│ │ ├── domaineDeCompetence.titre/
│ │ ├── domaineDeCompetence.contenu/
│ │ └── domaineDeCompetence.auteur/
│ ├── domaineDeCompetence.competences/ (data-layout="3 columns x 1 row")
│ │ └── competence/
│ │ ├── competence.titre/
│ │ ├── competence.image.src/
│ │ ├── competence.image.alt/
│ │ ├── competence.description/
│ │ ├── competence.auteur/
│ │ └── competence.bouton.action/
│ └── experiencesEtApprentissages/ (data-layout="accordeon, X rows")
│ ├── experiencesToggle/
│ │ └── experienceEtApprentissage.titre/
│ └── experiencesContent/
│ └── experienceEtApprentissage/ (li)
└── callToAction.action/---
Opérations (`/profil/coo`)
body/
└── .main-content-cont/ (page)
├── video.urlYouTube/
├── header/
│ └── titreDePage.texte/
├── domaineDeCompetence/
│ ├── domaineDeCompetence.header/
│ │ ├── domaineDeCompetence.titre/
│ │ ├── domaineDeCompetence.contenu/
│ │ └── domaineDeCompetence.auteur/
│ ├── domaineDeCompetence.competences/ (data-layout="3 columns x 1 row")
│ │ └── competence/
│ │ ├── competence.titre/
│ │ ├── competence.image.src/
│ │ ├── competence.image.alt/
│ │ ├── competence.description/
│ │ ├── competence.auteur/
│ │ └── competence.bouton.action/
│ └── experiencesEtApprentissages/ (data-layout="accordeon, X rows")
│ ├── experiencesToggle/
│ │ └── experienceEtApprentissage.titre/
│ └── experiencesContent/
│ └── experienceEtApprentissage/ (li)
└── callToAction.action/---
Transformation Agile (`/profil/agile`)
body/
└── .main-content-cont/ (page)
├── video.urlYouTube/
├── header/
│ └── titreDePage.texte/
├── domaineDeCompetence/
│ ├── domaineDeCompetence.header/
│ │ ├── domaineDeCompetence.titre/
│ │ ├── domaineDeCompetence.contenu/
│ │ └── domaineDeCompetence.auteur/
│ ├── domaineDeCompetence.competences/ (data-layout="3 columns x 1 row")
│ │ └── competence/
│ │ ├── competence.titre/
│ │ ├── competence.image.src/
│ │ ├── competence.image.alt/
│ │ ├── competence.description/
│ │ ├── competence.auteur/
│ │ └── competence.bouton.action/
│ └── experiencesEtApprentissages/ (data-layout="accordeon, X rows")
│ ├── experiencesToggle/
│ │ └── experienceEtApprentissage.titre/
│ └── experiencesContent/
│ └── experienceEtApprentissage/ (li)
└── callToAction.action/---
Technologie (`/profil/cto`)
body/
└── .main-content-cont/ (page)
├── video.urlYouTube/
├── header/
│ └── titreDePage.texte/
├── domaineDeCompetence/
│ ├── domaineDeCompetence.header/
│ │ ├── domaineDeCompetence.titre/
│ │ ├── domaineDeCompetence.contenu/
│ │ └── domaineDeCompetence.auteur/
│ ├── domaineDeCompetence.competences/ (data-layout="3 columns x 1 row")
│ │ └── competence/
│ │ ├── competence.titre/
│ │ ├── competence.image.src/
│ │ ├── competence.image.alt/
│ │ ├── competence.description/
│ │ ├── competence.auteur/
│ │ └── competence.bouton.action/
│ └── experiencesEtApprentissages/ (data-layout="accordeon, X rows")
│ ├── experiencesToggle/
│ │ └── experienceEtApprentissage.titre/
│ └── experiencesContent/
│ └── experienceEtApprentissage/ (li)
└── callToAction.action/---
Détournement de scènes cultes du cinéma (`/detournement-video`)
body/
└── .main-content-cont/ (page)
├── video.urlYouTube/
├── header/
│ └── titreDePage.texte/
├── domaineDeCompetence/
│ ├── domaineDeCompetence.header/
│ │ ├── domaineDeCompetence.titre/
│ │ ├── domaineDeCompetence.contenu/
│ │ └── domaineDeCompetence.auteur/
│ ├── domaineDeCompetence.competences/ (data-layout="3 columns x 1 row")
│ │ └── competence/
│ │ ├── competence.titre/
│ │ ├── competence.image.src/
│ │ ├── competence.image.alt/
│ │ ├── competence.description/
│ │ ├── competence.auteur/
│ │ └── competence.bouton.action/
│ └── experiencesEtApprentissages/ (data-layout="accordeon, X rows")
│ ├── experiencesToggle/
│ │ └── experienceEtApprentissage.titre/
│ └── experiencesContent/
│ └── experienceEtApprentissage/ (li)
└── callToAction.action/---
Portfolio de detournements vidéos (`/portfolio-detournements`)
body/
└── .main-content-cont/ (page)
├── header/
│ └── titreDePage.texte/
├── listeDeDetournementsVideo/ (data-layout="X rows")
│ └── detournementVideo/
│ ├── detournementVideo.header/
│ │ ├── detournementVideo.titre/
│ │ ├── detournementVideo.pitch/
│ │ └── detournementVideo.date/
│ └── detournementVideo.videos/
└── callToAction.action/---
Pour aller plus loin, je vous propose une expérience... (`/pour-aller-plus-loin`)
body/
└── .main-content-cont/ (page)
├── header/
│ └── titreDePage.texte/
├── texteLarge.texte/
├── video.urlYouTube/
└── callToAction.action/---
Faisons connaissance (`/faisons-connaissance`)
body/
└── .main-content-cont/ (page)
├── header/
│ └── titreDePage.texte/
└── groupeDeBoutons/ (data-layout="1 column, centered")
└── bouton/
└── bouton.command/Dictionnaire du vocabulaire du projet
Document d’onboarding : définitions et usages des termes du domaine (données, code, doc). Un seul terme par concept, pour parler le même langage.
Le dictionnaire est organisé en trois parties selon le contexte limité (bounded context) :
- Partie I — Vitrine : site public, pages CMS (accueil, profils, témoignages, détournements, plan du site, etc.).
- Partie II — A propos : contenu éditorial (dossiers, menu, documentation technique), journal de bord, sprints et tableau Kanban.
- Partie III — Maintenance : page réservée (développement / authentifiée), easter egg.
---
Partie I — Vitrine
1. Types de contenu (TypeDeContenu)
Ce site fonctionne comme un CMS : chaque page est construite à partir d’une liste d’éléments de contenu (propriété `contenu` dans les JSON de page). Chaque élément a un typeDeContenu — identifiant métier en camelCase, propriété `type` dans le JSON, type TypeScript `TypeDeContenu` dans `pageReader.ts`. Le rendu et le style s’appuient sur des containers (suffixe `.cont`, classe CSS `typeContenu-cont`). Source unique des types : `utils/vitrine/pageReader.ts` (`TypeDeContenu`). Ordre d’affichage : selon l’énumération des noms canoniques dans `constants/canonicalSpec.ts`.
Les typeDeContenu détaillés dans les sections 2 à 5 ne sont pas redécrits ici (voir les sections indiquées). Les autres sont définis dans le tableau ci‑dessous.
| typeDeContenu | Container (.cont) | Détail | |---------------|-------------------|--------| | titreDePage | — | Titre principal de la page. JSON. | | listeDesPages | listeDesPages.cont | Affiche le plan du site (liens par zone). Données injectées par le script du plan. plan-du-site.json, ListeDesPages, canonicalSpec. | | hero | hero.cont | Voir section 2 (Hero / Profils professionnels). | | listeDeProfils | listeDeProfils.cont | Voir section 2. | | profil | profil.cont | Voir section 2. | | titre | — | Titre de section (texte). JSON, rendu titre. | | video | — | Vidéo YouTube (url, lancement auto). JSON, pageReader. | | listeDeTemoignages | listeDeTemoignages.cont | Voir section 4 (Témoignages). | | temoignage | temoignage.cont | Voir section 4. | | domaineDeCompetence | domaineDeCompetence.cont | Voir section 3 (Domaines de compétence et expériences). | | competence | competence.cont | Voir section 3. | | listeDesExperiencesEtApprentissage | listeDesExperiencesEtApprentissages.cont | Voir section 3. | | experienceEtApprentissage | experienceEtApprentissage.cont | Voir section 3. | | texteLarge | — | Paragraphe de texte large. JSON. | | callToAction | — | Bouton d’action principal (texte, action). JSON, hero, pages. | | groupeDeBoutons | groupeDeBoutons.cont | Groupe de boutons (taille petite/grande). JSON, canonicalSpec. | | bouton | bouton.cont | Item d’un groupe de boutons (icône, texte, url/command). groupeDeBoutons.boutons, canonicalSpec. | | listeDeDetournementsVideo | listeDeDetournementsVideo.cont | Voir section 5 (Détournements vidéo). | | detournementVideo | detournementVideo.cont | Voir section 5. |
---
2. Hero / Profils professionnels
| Terme | Définition | Où | |-------|------------|-----| | hero | Bannière d’accueil (typeDeContenu). Titre, sous-titre, description, CTA, vidéo, lien « En savoir plus ». | Page d’accueil, canonicalSpec | | profil | Profil métier (CPO, COO, Agile, CTO). typeDeContenu (item dans hero ou listeDeProfils) ; aussi catégorie cliquable dans les liens (`Profil` en PascalCase). | JSON (type `profil`), `pageReader`, `planDuSiteTypes` (TypeElementCliquable) | | listeDeProfils | typeDeContenu : liste de profils (cartes). Données : tableau `profils`. Container `listeDeProfils.cont`. | `mes-profils.json`, `pageReader`, composant ListeDeProfils, canonicalSpec | | profil (container) | Pour chaque carte profil, container profil.cont (classe `profil-cont`). Rester sur container et le suffixe `.cont`. | `canonicalSpec.ts`, ProfilContainer, CSS | | slug | Identifiant court d’un profil (ex. `cpo`, `coo`, `agile`, `cto`). | `mes-profils.json` (profil.slug) |
---
3. Domaines de compétence et expériences
| Terme | Définition | Où | |-------|------------|-----| | domaineDeCompetence | typeDeContenu : domaine avec titre, texte, liste de compétences, expériences. Container `domaineDeCompetence.cont`. | Profils (profil-*.json), profilBuilder, canonicalSpec | | competence | typeDeContenu : carte compétence dans un domaine (titre, description, bouton). En liens du plan : Competence (PascalCase). | domaineDeCompetence.items, canonicalSpec, planDuSiteTypes | | listeDesExperiencesEtApprentissage | typeDeContenu : conteneur logique pour la liste d’expériences d’un domaine. | pageReader (lecture), canonicalSpec | | experienceEtApprentissage | typeDeContenu : item expérience dans un domaine (categorie, description, periode). | domaineDeCompetence.experiences, canonicalSpec |
---
4. Témoignages
| Terme | Définition | Où | |-------|------------|-----| | listeDeTemoignages | typeDeContenu : liste de témoignages. Container `listeDeTemoignages.cont`. | JSON, canonicalSpec | | temoignage | typeDeContenu : item témoignage (nom, fonction, photo, texte). Container `temoignage.cont`. | listeDeTemoignages.items, JSON, pageReader |
---
5. Détournements vidéo
| Terme | Définition | Où | |-------|------------|-----| | listeDeDetournementsVideo | typeDeContenu : liste de détournements vidéo. Container `listeDeDetournementsVideo.cont`. | JSON, canonicalSpec | | detournementVideo | typeDeContenu : item (vidéo détournée + vidéo originale, titre, pitch, date). Container `detournementVideo.cont`. | listeDeDetournementsVideo.items, canonicalSpec | | detournement | Sans accent : URLs, chemins, clés techniques (ex. `/detournement-video`, types `detournementVideo`, `listeDeDetournementsVideo`). | URLs, JSON, `pageReader` | | détournement | Avec accent : libellés affichés à l’utilisateur (titres, descriptions). | Textes des pages, métadonnées affichées |
---
6. Bibliothèque
Concept général (données réutilisables : compétences, domaines, témoignages, etc.), distinct des typeDeContenu.
| Terme | Définition | Où | |-------|------------|-----| | bibliotheque | Sans accent : noms de fichiers, chemins, identifiants techniques (dossier `data/bibliotheque/`, types, imports). | `bibliothequeReader`, chemins, types | | bibliothèque | Avec accent : acceptable en français dans la documentation et les libellés affichés. | Descriptions OpenAPI, textes utilisateur |
---
7. Contenu de page
Règle DOM/CSS : à chaque typeDeContenu correspond un container avec le suffixe `.cont` (nom canonique) ; en classe CSS : `typeContenu-cont`. Ne pas utiliser le vocable « bloc » : rester sur container et typeDeContenu. Liste des typeDeContenu en section 1 ; détails dans les sections 2 à 5.
| Terme | Définition | Où | |-------|------------|-----| | contenu | Liste ordonnée d’éléments de la page. Chaque élément a une propriété `type` (son typeDeContenu). | Propriété `contenu` des JSON de page, `pageReader.ts` | | content | Réservé aux usages techniques (ex. segments markdown, champs internes). À ne pas utiliser pour la liste d’éléments de page. | `markdownInlineParserPure`, `aboutSiteReader` (champs internes) | | typeDeContenu | Identifiant métier d’un élément de contenu (camelCase). Propriété `type` dans le JSON ; type TypeScript `TypeDeContenu` dans `pageReader.ts`. À ne pas confondre avec le mot-clé `type` de TypeScript. | Tous les JSON de page (`element.type`), `TypeDeContenu` dans `pageReader.ts` | | typeElement | Catégorie cliquable d’un lien du plan, PascalCase (ex. `Competence`, `Profil`, `LienPage`). Distinct du typeDeContenu (contenu de page). | Liens du plan, `TypeElementCliquable` dans `planDuSiteTypes.ts` |
---
8. Conventions A (concept métier) et B (syntaxe code)
- A — Concept métier : en doc, discussions et libellés utilisateur, on écrit en français, avec espaces et accents (ex. compétence, détournement, bibliothèque).
- B — Syntaxe dans le code : selon le type d’élément, contraintes techniques à respecter.
| Type d’élément | Règle B | Exemples | |----------------|---------|----------| | Fichier / dossier | Pas d’accent, pas d’espace | `bibliotheque/`, `planDuSiteGenerator.ts` | | Clé JSON métier | camelCase, pas d’accent | `type`, `listeDeProfils`, `domaineDeCompetence` | | typeDeContenu (contenu de page) | camelCase | `domaineDeCompetence`, `listeDeTemoignages` | | typeElement (liens du plan) | PascalCase | `Competence`, `Profil`, `LienPage` | | Classe CSS | Pas d’accent, convention projet | `listeDeProfils-cont`, `profil-cont` | | URL / route | Pas d’accent | `/detournement-video`, `/mes-profils` |
Libellés affichés à l’utilisateur (titres, descriptions dans les JSON) : français avec accents (A). Identifiants techniques (clés, chemins, types) : pas d’accent (B).
---
9. Titre, label, title (texte affiché)
Trois termes pour éviter toute confusion.
| Terme | Usage | Propriétés / où | Règle | |-------|--------|-----------------|--------| | titre | Libellé affiché pour une page, un élément de contenu ou une carte (section, domaine, profil, témoignage, etc.). Pas pour le texte d’un lien. | `titre` dans les JSON de page (PlanPage, éléments), `profil.titre`, `domaineDeCompetence.titre`, hero.titre, etc. Affiché dans le header (titre de page), dans les cartes, sections. | Toujours titre (propriété `titre`) pour tout libellé de page ou d’élément ; jamais `titre` pour un lien cliquable (voir label). | | label | Texte cliquable d’un lien ou d’une entrée de menu. | `label` dans les liens du plan (_Pages-Liens-Et-Menus.json), `label` dans _Menus-header.json (entrées de menu). | Réservé aux liens et menus ; ne pas exposer un libellé de lien sous la propriété `titre`. | | title | Technique SEO uniquement : balise `<title>`, partage réseaux. | `metadata.title` dans les JSON de page. En doc : dire « titre SEO » ou « metadata.title ». | Ne jamais utiliser « title » pour le titre affiché à l’utilisateur ; on dit titre. |
---
10. Pages et navigation
| Terme | Définition | Où | |-------|------------|-----| | url | Lien pouvant pointer vers l’extérieur du site (https, mailto, etc.) ou être une adresse générique. | Boutons, liens externes, champs de type « lien » | | route | Chemin interne au site (ex. `/`, `/mes-profils`, `/profil/cpo`). Un seul terme pour tout lien interne. | Plan du site (pages, liens), `mes-profils.json` (profil.route), `_Menus-header.json` (entrée.pageUrl) | | titre | Voir section 9. Libellé affiché pour une page ou un élément (pas pour un lien). | Propriété `titre` des pages et éléments | | metadata.title | Voir section 9. Titre SEO (technique). | Fichiers JSON de page : `metadata.title` | | label | Voir section 9. Texte cliquable d’un lien ou d’une entrée de menu. | Liens du plan (`label`), _Menus-header.json (`label`) | | zone | Zone d’affichage d’une page dans le plan du site : `HomePage`, `Profils`, `Autres`, `Footer`, `Masqué`. | _Pages-Liens-Et-Menus.json, type `PlanPage.zone` | | dessiner | Indique si la page doit apparaître dans le plan affiché : "Oui" ou "Non" (majuscule). | _Pages-Liens-Et-Menus.json, type `PlanPage.dessiner` |
---
11. Menu et header
| Terme | Définition | Où | |-------|------------|-----| | menu header / header | Menu principal du site (navigation en haut). | Layout, `headerMenuReader`, _Menus-header.json (source éditable) | | menus | Nom de la section JSON dans _Pages-Liens-Et-Menus.json qui contient les entrées du menu (fusion depuis _Menus-header.json). | _Pages-Liens-Et-Menus.json (clé `menus`) | | entrée | Une ligne du menu : objet avec `id`, `pageUrl`, `label`, optionnellement `sousmenuPageUrls`. | _Menus-header.json, _Pages-Liens-Et-Menus.json |
---
12. Plan du site et structure
| Terme | Définition | Où | |-------|------------|-----| | \_Pages-Liens-Et-Menus.json | Fichier JSON regroupant la liste des pages, des liens entre pages et la section menus. Source de vérité pour le plan du site et le menu header. | `data/`, lu par le générateur de plan, `headerMenuReader`, `pageReader` | | plan du site / planDuSite | Données (pages + liens) issues de _Pages-Liens-Et-Menus.json. À utiliser exclusivement en français en doc et en camelCase en code (planDuSite). Ne pas utiliser « site map » ni « sitemap » pour ces données. | Types `PlanPage`, `PlanLien`, `PlanSite` dans `utils/shared/planDuSiteTypes.ts` | | sitemap (exception) | Uniquement l’artefact SEO sitemap.xml et la route Next.js qui le génère (`app/sitemap.ts`). À ne pas confondre avec le plan du site. | `app/sitemap.ts` génère sitemap.xml à partir du plan du site | | page (plan) | Objet décrivant une page du site : `url` (contient une route), `titre`, `zone`, `dessiner`, `x`, `y`, optionnellement `e2eID`. | _Pages-Liens-Et-Menus.json, interface `PlanPage` | | lien (plan) | Objet décrivant un lien cliquable entre deux pages : `source`, `destination` (des routes), `label`, `e2eID`, `typeElement`. | _Pages-Liens-Et-Menus.json, interface `PlanLien` |
---
13. Identifiants E2E
| Terme | Définition | Où | |-------|------------|-----| | e2eID | Identifiant unique pour les tests E2E (camelCase). Un élément interactif = un e2eID. Une page du plan = au plus un e2eID. | Éléments de contenu (video, callToAction, etc.), liens du plan, pages du plan, règle `.cursor/rules/e2eid-convention.mdc` |
---
Partie II — A propos
14. Périmètre et données A propos
| Terme | Définition | Où | |-------|------------|-----| | A propos | Contexte limité regroupant le contenu éditorial du site (documentation, sprints, configuration, métriques). En doc : « A propos » (avec espace). En code : a-propos (route `/a-propos`). | `constants/routes.ts`, `app/(main)/a-propos/` | | dossier à propos | Vocable unifié pour l’arbre de dossiers « A propos ». Deux usages : (1) Racine = dossier racine, chemin `data/<A_PROPOS_DATA_DIR>` (ex. `data/A propos`), contient `menu.json`, `agents.json`, sous-dossiers (Sprints, Documentation technique, etc.). (2) Chemin = chemin relatif vers la racine ou un sous-dossier (ex. `data/A propos/Documentation technique`) ; une entrée de menu de type Path a Parametre = ce chemin ; la page `/a-propos/[dossier]` affiche son contenu. | Racine : `aProposReader.ts`, `menuReader.ts`, `sprintBoardReader.ts`. Chemin : `menu.json` (Parametre), `readPathContentAtRoot`, `AProposDossierPage` | | A_PROPOS_DATA_DIR | Constante donnant le nom du segment du chemin de la racine du dossier à propos (valeur `'A propos'`). Chemin racine = `data/` + A_PROPOS_DATA_DIR. Utilisée partout où on construit un chemin vers la racine ou un sous-dossier. | `constants/routes.ts`, `constants/aProposPath.ts`, `aProposReader.ts`, `menuReader.ts`, `sprintBoardReader.ts`, tests |
15. Structure des contenus A propos (chapitre, section, partie, bloc)
Hiérarchie markdown lue par `aProposReader` : dossier → chapitre, fichier MD → section, titres ### / #### / ##### → partie, sous-partie, bloc.
| Terme | Définition | Où | |-------|------------|-----| | chapitre | Dossier dans un Path ; contient des sections (fichiers .md). En affichage : titre H1, liste des sections ou accordéon. | `Chapitre`, `DossierRacine`, `aProposReader.ts` | | section | Fichier markdown dans un chapitre ; contient des parties (###). Propriétés : `nom` (sans .md), `contenu`, `parties`, `niveauBase`. | `Section`, `SectionContent`, `aProposReader.ts` | | partie | Bloc de contenu délimité par un titre ### dans le MD. Contient des sous-parties (####) et du contenu parsé (`contenuParse`). | `Partie`, `aProposReader.ts` | | sous-partie | Bloc délimité par ####. Contient des blocs (#####) et du contenu parsé. | `SousPartie`, `aProposReader.ts` | | bloc | Bloc délimité par ##### (ex. « Prompt », « Résultat technique »). Contient `contenu`, `contenuParse`, optionnellement `typeDeContenu`. | `Bloc`, `aProposReader.ts` | | PathContentAtRoot | Contenu à la racine d’un Path : `fichiers` (Section[]) et `dossiers` (DossierRacine[]). Fichiers MD = H1 directs ; dossiers = H1 avec accordéon (sauf affichage direct si un seul fichier). | `readPathContentAtRoot`, `AProposContent`, `aProposReader.ts` | | DossierRacine | Dossier à la racine d’un Path : `nom`, `path` (chemin pour déplier), optionnellement `chapitre` (si un seul fichier MD = affichage direct sans accordéon). | `PathContentAtRoot.dossiers`, `aProposReader.ts` | | AProposStructure | Structure complète « A propos » pour un chemin donné : `chapitres` (tableau de Chapitre). Utilisée quand on lit un dossier entier (ex. pour export ou tableau de bord). | `AProposStructure`, `aProposReader.ts`, `AProposContent` | | ContenuElement | Élément parsé dans un bloc (paragraphe, liste, code, image). Partagé avec le domaine Vitrine (rendu markdown). Propriété contenu (pas `content`) pour le texte. | `aProposReader.ts`, `AProposContentRenderer`, section 7 | | accordéon | Affichage des dossiers à la racine d’un Path : un clic déplie/replie le contenu du dossier. Si le dossier ne contient qu’un seul fichier MD, affichage direct sans accordéon (US-10.3). | Composants A propos, `data-layout="accordeon"` (DOM), doc technique |
16. Menu A propos
Menu latéral de la zone « A propos » : entrées lues depuis `data/A propos/menu.json`, triées par Numéro.
| Terme | Définition | Où | |-------|------------|-----| | LigneDeMenu | Une entrée du menu A propos : `Titre`, `Numéro`, `Type` (Path ou container), `Parametre`, optionnellement `e2eID`. | `menu.json`, `menuReader.ts`, type `LigneDeMenu` | | Titre (menu) | Libellé affiché pour l’entrée de menu (ex. « A propos du projet », « Sprint en cours »). Propriété Titre (PascalCase) dans le JSON. | `menu.json`, bande horizontale A propos | | Type (menu) | Path = lien vers un dossier (Parametre = chemin relatif) ; container = vue spéciale (ex. `sprintEnCours`, `metrics`, `openapi`, `charte`). | `menu.json`, `LigneDeMenu.Type` | | Parametre | Valeur associée à l’entrée : pour Type=Path, chemin relatif (ex. `data/A propos/Documentation technique`) ; pour Type=container, identifiant (ex. `sprintEnCours`, `metrics`). | `menu.json`, passé à la page ou au layout pour afficher le bon contenu | | menu.json | Fichier source du menu A propos. Tableau d’objets (Titre, Numéro, Type, Parametre, e2eID). Tri par Numéro à la lecture. | `data/A propos/menu.json`, `readMenu()` |
17. Journal de bord
Contenu markdown hors « A propos » : fichiers journaux datés dans JOURNAL_DE_BORD.
| Terme | Définition | Où | |-------|------------|-----| | journal de bord | Ensemble des fichiers markdown du JOURNAL_DE_BORD : nommage `YYYY-MM-DD.md`, tri par date. Chaque fichier = JournalFile (filename, date, title, contenu). | `journalReader.ts`, dossier `JOURNAL_DE_BORD` (racine projet) | | JournalFile | Fichier journal lu : `filename`, `date` (YYYY-MM-DD), `title` (première ligne ou extrait), `contenu` (brut MD). | `readJournalFiles()`, `journalReader.ts`, composants liste journal | | JOURNAL_DE_BORD | Dossier racine (cwd) contenant les journaux. Ne pas confondre avec `data/A propos` (sprints, doc). | `journalReader.ts` |
18. Sprint et tableau Kanban
Suivi Agile : sprint en cours, post-its US, colonnes (A faire, agents, Fait), détail US en modal.
| Terme | Définition | Où | |-------|------------|-----| | sprint | Période de développement ; dossier sous `data/A propos/Sprints` (ex. `2026-02-04 - Amélioration du message délivré`). Contient des fichiers US (`US-X.Y - Titre.md`), Sprint Goal (`00 - Sprint goal et contexte.md`), et optionnellement US en cours (`US en cours.md`). | `sprintBoardReader.ts`, `Sprints/` | | sprint en cours | Dernier sprint connu contenant au moins une US. Détermine le tableau affiché (menu « Sprint en cours »). | `getSprintFolderContainingUs`, `readSprintBoardData` | | Sprint Goal | Texte lu après la ligne `# Sprint Goal` dans `00 - Sprint goal et contexte.md` du dossier sprint. | `readSprintGoal`, `DonneesTableauKanban.goal` | | US en cours | Fichier métadonnée `US en cours.md` à la racine de `data/A propos/Sprints` : indique l’US et l’étape courantes (ex. « US-8.6 - TDD-front-end »). Non affiché dans la liste des documents A propos. | `UsEnCours`, `readUsEnCours`, `sprintBoardReader.ts` | | tableau Kanban / DonneesTableauKanban | Données du tableau Kanban : `goal`, `columns` (A faire, colonnes agent, Fait), `cards` (PostItUS[]). | `sprintBoardReader.ts`, `readSprintBoardData`, composant SprintBoardKanban | | Post-it d'US / PostItUS | Carte US sur le tableau : `id`, `titre`, `filename`, `state` (a_faire, en_cours, fait), `agentColumn`, `rotation`, `enRevue`. | `PostItUS`, `ColonneTableauKanban`, `sprintBoardReader.ts` | | Colonne de tableau Kanban / ColonneTableauKanban | Colonne du tableau : `id`, `label`, `type` (a_faire, agent, fait), `count`, optionnellement `wipLimit`. | `DonneesTableauKanban.columns` | | agents | Colonnes « agent » du tableau (ex. US, BDD, TDD-back-end). Liste lue depuis `data/A propos/agents.json` ou `.cursor/agents` (fichiers `N. Nom.md`). | `readAgentsFromCursorAgents`, `readAgentsFromConfig`, `sprintBoardReader.ts` | | contenu d'une US / ContenuUS | Contenu complet d’une US pour la modal détail : `id`, `titre`, `contenu` (markdown brut du fichier .md). | `readUsContent`, `UsDetailModal`, API `/api/sprint-board/us/[usId]` |
---
Partie III — Maintenance
19. Page maintenance
| Terme | Définition | Où | |-------|------------|-----| | maintenance / page maintenance | Route /maintenance. Accessible uniquement en mode développement ou lorsque l’utilisateur est authentifié (EditingContext). Sinon redirection vers `/`. Contenu : message easter egg et un CallToAction (bouton « Faisons connaissance... »). | `app/(main)/maintenance/page.tsx`, `ROUTES.MAINTENANCE` | | easter egg | Contenu caché (Bravo, message ludique, œuf) affiché sur la page maintenance. Utilise le même composant CallToAction que la Vitrine (type `callToAction`). | `maintenance/page.tsx`, styles `maintenance.module.css` |
---
Dette vocabulaire (incohérences à résorber)
Liste des écarts par rapport au langage ubiquitaire. À traiter par itération ; chaque point est une cible de résorption possible. Aucune ligne de dette ouverte restante pour les quatre points du plan. À mettre à jour si de nouveaux écarts sont identifiés.
Plan : passer la couverture des branches à 80 %
État actuel : 76,89 % (2453 / 3190 branches couvertes) Objectif : 80 % → il faut couvrir environ 99 branches de plus.
---
1. Cibles prioritaires (impact / effort)
| Fichier | Branches total | Couvertes | Non couvertes | % actuel | Effort estimé | |--------|----------------|-----------|----------------|----------|----------------| | utils/shared/planDuSiteTypes.ts | 16 | 2 | 14 | 12,5 % | Faible – fichier petit, logique pure | | utils/backoffice/integrity/e2eIdGenerator.ts | 90 | 53 | 37 | 58,9 % | Moyen – déjà un test, ajouter cas limites | | utils/shared/e2eIdMapping.ts | 61 | 26 | 35 | 42,6 % | Moyen – pas de test dédié, à créer | | utils/vitrine/apiTreeBuilder.ts | 44 | 0 | 44 | 0 % | À évaluer – peut être utilisé seulement côté API | | components/MetricsCompact.tsx | 128 | 94 | 34 | 73,4 % | Moyen – composant, ajouter scénarios (états vides, erreurs) | | components/AProposContentRenderer.tsx | 147 | 67 | 80 | 45,6 % | Élevé – beaucoup de branches, composant complexe | | utils/backoffice/generators/planDuSiteGenerator.ts | 374 | 263 | 111 | 70,3 % | Élevé – gros fichier, tests d’intégration existants |
---
2. Plan d’action proposé (par ordre de priorité)
Phase 1 – Gains rapides (objectif : ~30–50 branches)
- Tester les fonctions non couvertes : `getLiensAParcourirInitial`, `getPagesAccessiblesDepuis`, `retirerLienUtilise`, `pageAccueil`.
- Couvrir les branches `plan?.liens ?? []`, `(liens ?? []).filter`, et les cas limites de `retirerLienUtilise`.
- Gain estimé : ~14 branches.
- planDuSiteTypes.ts
- Ouvrir `tests/unit/e2eIdGenerator.test.ts` et le rapport HTML de couverture (`coverage/index.html`) pour ce fichier.
- Ajouter des cas pour les branches non couvertes (chemins alternatifs, valeurs nulles/undefined, cas limites).
- Gain estimé : 15–25 branches.
- e2eIdGenerator.ts
- Ajouter des tests pour les branches autour des lignes 102–104 (états vides, métriques manquantes, formats de durée).
- Gain estimé : ~10–15 branches.
- MetricsCompact.tsx
Phase 2 – Fichiers à fort impact (si besoin après Phase 1)
- Créer `tests/unit/e2eIdMapping.test.ts` (ou équivalent) et couvrir les chemins principaux et les cas d’erreur / limites.
- Gain estimé : 20–35 branches.
- e2eIdMapping.ts
- Vérifier où il est utilisé (API, build, etc.). Si couvert par les tests d’intégration, envisager des tests unitaires ciblés.
- Gain estimé : jusqu’à 44 branches si tout est couvert.
- apiTreeBuilder.ts
Phase 3 – Ajustements fins (si on reste juste sous 80 %)
- Consulter le rapport HTML `coverage/index.html` pour voir les branches exactes (lignes jaunes/orange) et ajouter un ou deux tests ciblés par fichier.
- ListeDesPages.tsx (47 branches, 33 couvertes), MenuAPropos.tsx (34, 24), PageContentRenderer.tsx (37, 25), HeroSection.tsx (20, 13), MarkdownRenderer.tsx (16, 12), etc.
---
3. Méthode de travail
- Lancer la couverture avec rapport HTML
`npm run test -- --coverage` Puis ouvrir `coverage/index.html` et cliquer sur un fichier pour voir les branches non couvertes (couleur dans la marge).
- Vérifier après chaque lot de tests
`npm run test -- --coverage --coverageReporters=text` Regarder la ligne « All files » et la colonne « % Branch ».
- Mettre à jour le seuil Jest
Quand la couverture branches ≥ 80 % : dans `jest.config.js`, passer `branches: 65` à `branches: 80`. Faire de même dans `scripts/collect-metrics-simple.ts` et `scripts/publie.ts` (seuils et messages).
---
4. Résumé
- Objectif : branches à 80 % (environ +99 branches couvertes).
- Priorité 1 : planDuSiteTypes, e2eIdGenerator, MetricsCompact.
- Priorité 2 : e2eIdMapping, apiTreeBuilder (si pertinent).
- Priorité 3 : composants et générateurs restants selon le rapport HTML.
- Toujours vérifier avec `npm run test -- --coverage` et, une fois à 80 %, mettre à jour les seuils (Jest + scripts de métriques/publication).
Plan de résorption de la dette vocabulaire
Ordre de traitement et décisions d’arbitrage pour la résorption.
---
Décisions prises
| # | Sujet | Décision | |---|--------|----------| | 1 | e2eID vs e2eIDs | Option A : une page n’a qu’un seul e2eID. Garder uniquement e2eID (singulier), supprimer e2eIDs du type et des JSON. | | 2 | Périmètre contenu vs content | Tous les contenus (JSON ou MD) sont des contenus (terme métier français). content réservé au strict technique (ex. champs internes TS, API du langage). | | 3 | Ordre des chantiers | OK : Phase 1 (doc) → Phase 2 (e2eID) → Phase 3 (contenu/content) → Phase 4 (casse B) → Phase 5 (retrait lignes dette). |
---
Ordre des chantiers
| Phase | Chantier | Dépendances | Effort estimé | |-------|----------|--------------|---------------| | 1 | Documenter A vs B (casse, accents) + titre / label / title | Aucune | Doc uniquement | | 2 | e2eID uniquement : supprimer e2eIDs (type, JSON, générateur, tests) | Décision 1 | Code + données | | 3 | Contenu vs content : audit et corrections (JSON + MD = contenu) | Aucune | Code + doc | | 4 | Casse / accents : corrections code (B) si écarts | Doc phase 1 | Code | | 5 | Retirer les lignes résorbées de la section Dette | Après 1–4 | Dictionnaire |
Pourquoi cet ordre
- Phase 1 : Pure doc. Fixe les règles (A/B, titre/label/title).
- Phase 2 : Une page = un e2eID ; suppression de e2eIDs partout (types, _Pages-Liens-Et-Menus.json, planDuSiteGenerator, tests).
- Phase 3 : Tout contenu métier (JSON, MD) = contenu ; content uniquement pour le technique (parsers, champs internes TS).
- Phase 4 : Appliquer les règles B (syntaxe code) après doc.
- Phase 5 : Retrait des lignes résorbées dans le dictionnaire.
---
Détail par chantier
Phase 1 — Documentation (A/B, titre, label, title)
1.1 Règles A (concept métier) et B (syntaxe code)
- Ajouter au dictionnaire (ou à un fichier « Conventions A/B » référencé) :
- A : concept métier = français, espaces, accents autorisés (doc, libellés, discussions).
- B : tableau par type d’élément :
- Fichier / dossier : pas d’accent, pas d’espace (ex. `bibliotheque`, `planDuSiteGenerator.ts`).
- Clé JSON métier : camelCase, pas d’accent (ex. `type`, `listeDeProfils`, `domaineDeCompetence`).
- Type TS (typeDeContenu, typeElement) : camelCase / PascalCase selon usage existant.
- Classe CSS : pas d’accent, convention du projet (ex. `listeDeProfils-cont`).
- URL / route : pas d’accent (ex. `/detournement-video`).
- Préciser pour typeElement (liens du plan) : PascalCase volontaire (Competence, Profil, LienPage) vs typeDeContenu en camelCase.
1.2 titre
- Dans le dictionnaire, une entrée dédiée titre qui liste :
- Tous les cas d’usage (titre de page, titre d’élément de contenu, titre de section, titre de carte).
- Les propriétés concernées : `titre` (JSON de page), `metadata.title` (SEO), affichage header.
- La règle : « titre » = libellé affiché pour une page ou un élément ; ne pas utiliser « titre » pour le texte d’un lien (voir label).
1.3 label
- Entrée label : réservé au texte cliquable (lien du plan, entrée de menu). Propriétés : `label` (plan, _Menus-header.json). Règle : aucun libellé de lien ou de menu ne doit être exposé sous le nom de propriété `titre` dans les données.
1.4 title
- Entrée title : réservé au technique SEO (`metadata.title`, balise `<title>`, partage). Règle : en doc, dire « titre SEO » ou « metadata.title » ; ne jamais utiliser « title » pour le titre affiché (on dit titre).
---
Phase 2 — e2eID uniquement (suppression de e2eIDs)
- Objectif : une page = un seul e2eID. Supprimer e2eIDs partout.
- État actuel : `PlanPage` a `e2eID?: string` et `e2eIDs?: string[]`. _Pages-Liens-Et-Menus.json peut contenir `e2eIDs` (tableau). plan-du-site.json / `PagePlanDuSite` utilisent `e2eID` (singulier).
- Implémentation :
- Retirer `e2eIDs` du type `PlanPage` dans `utils/shared/planDuSiteTypes.ts`.
- Migrer les données : pour toute page ayant `e2eIDs`, choisir une règle (ex. prendre le premier élément du tableau comme `e2eID`, ou laisser vide si plusieurs étaient utilisés pour autre chose) ; supprimer la clé `e2eIDs` des JSON.
- Mettre à jour `planDuSiteGenerator.ts` (ne plus lire ni écrire `e2eIDs`).
- Mettre à jour les tests (BDD, intégration) et les steps qui mentionnent `e2eIDs`.
- Documenter dans le dictionnaire : une page du plan a au plus un e2eID.
---
Phase 3 — contenu vs content
- Objectif : tous les contenus (JSON ou MD) = contenu (terme métier). content réservé au strict technique (champs internes TS, API du langage).
- Actions :
- Audit : repérer tous les usages de `content` dans les types, JSON et MD qui portent du contenu métier (données de page, blocs markdown métier, etc.).
- Renommer en contenu (ou terme français adapté) partout où c’est du contenu métier (y compris dans les structures issues de MD).
- Garder content uniquement là où c’est technique (ex. segment de markdown parsé, propriété interne d’un type TS sans sémantique métier).
- Documenter dans le dictionnaire : « contenu = tout contenu métier (JSON, MD) ; content = technique uniquement (parsers, champs internes) ».
---
Phase 4 — Casse / accents (corrections code selon B)
- Après phase 1, vérifier :
- Libellés affichés (titres, descriptions) : bien en français avec accents quand c’est pour l’utilisateur.
- Identifiants techniques (clés JSON, routes, noms de fichiers, classes CSS) : pas d’accent, conformes à B.
- Corriger les écarts (ex. libellé JSON « detournement » affiché à l’utilisateur → « détournement » si c’est du métier affiché).
---
Suite
- Détail des tâches techniques (fichiers, tests, dictionnaire) à préciser au moment d’exécuter chaque phase.
- Après chaque phase (ou sous‑étape résorbée), retirer la ligne correspondante de la section « Dette vocabulaire » du dictionnaire.
