# Capitalisation en attente — Lead_tech Ce fichier sert de **zone tampon de capitalisation**. Les agents et les projets peuvent y déposer des propositions d'amélioration de la base de connaissance globale (`Lead_tech`). Le contenu de ce fichier **n'est pas encore validé**. Une fois relues et confirmées, les propositions doivent être **déplacées** vers les fichiers appropriés : - `knowledge/backend/patterns/.md` - `knowledge/backend/risques/.md` - `knowledge/frontend/patterns/.md` - `knowledge/frontend/risques/.md` - `knowledge/ux/patterns/.md` - `knowledge/ux/risques/.md` - `knowledge/n8n/patterns/general.md` - `knowledge/n8n/risques/general.md` - `knowledge/product/patterns/general.md` - `knowledge/product/risques/.md` - `knowledge/workflow/patterns/general.md` - `knowledge/workflow/risques/story-tracking.md` - `10_conventions_redaction.md` - `40_decisions_et_archi.md` - `90_debug_et_postmortem.md` Ce fichier ne doit donc **jamais devenir une documentation permanente**. --- # Format attendu Chaque proposition doit suivre ce format : ``` DATE — PROJET FILE_UPDATE_PROPOSAL Fichier cible : .md | knowledge/backend/risques/.md | knowledge/frontend/patterns/.md | knowledge/frontend/risques/.md | knowledge/ux/patterns/.md | knowledge/ux/risques/.md | knowledge/n8n/patterns/general.md | knowledge/n8n/risques/general.md | knowledge/product/patterns/general.md | knowledge/product/risques/.md | knowledge/workflow/patterns/general.md | knowledge/workflow/risques/story-tracking.md | 10_conventions_redaction.md | 40_decisions_et_archi.md | 90_debug_et_postmortem.md> Pourquoi : Proposition : ``` --- # Exemple ``` 2026-03-08 — portfolio FILE_UPDATE_PROPOSAL Fichier cible : knowledge/backend/patterns/prisma.md Pourquoi : Pattern réutilisable validé sur un projet réel. Proposition : ## Nom du pattern Description courte, factuelle, orientée réutilisation. ``` --- # Règles 1. Les agents peuvent **proposer librement** ici. 2. Les propositions doivent rester **courtes et factuelles**. 3. La validation et l'intégration finale dans `Lead_tech` sont faites **manuellement**. 4. Une fois intégrée, la proposition doit être **supprimée de ce fichier**. 5. La structure de ce fichier est **restaurée à son état initial** (voir `70_templates/tempate_a_capitaliser.md`). --- --- # Entrées en attente de routage projet Les propositions ci-dessous ont été jugées **trop spécifiques à un projet** lors du triage de capitalisation (2026-06-25). Elles ne vont PAS dans `knowledge/` global mais dans le `CLAUDE.md` du projet source. À router manuellement dans le repo concerné : - **RL799_V2** : « centre d'aide utilisateur in-app » + « taux de présence borné par initiatedAt » - **app-alexandrie** : « tronc commun vs extension premium » (formation Skool) Le reste du buffer (≈270 propositions, mai-juin 2026) a été intégré dans `knowledge/`, `40_decisions_et_archi.md` et `90_debug_et_postmortem.md` (commits du 2026-06-25). --- 2026-05-11 — RL799_V2 FILE_UPDATE_PROPOSAL Fichier cible : knowledge/frontend/patterns/general.md (ou créer un nouveau fichier `knowledge/frontend/patterns/doc-utilisateur-in-app.md` si le sujet mérite sa propre entrée — le pattern est dense) Pourquoi : Pattern complet et réutilisable de **centre d'aide utilisateur in-app** pour toute application Vue 3 / Vite avec un public cible exigeant (B2B niche, métiers spécialisés, organisations à valeur de confiance). Le pattern combine **5 disciplines** qui se renforcent mutuellement et constituent un système durable, contrairement aux approches "FAQ statique" ou "tour guidé Userpilot" qui dérivent silencieusement en 6 mois. Livré sur RL799_V2 en 4 sous-lots + 1 d'opportunité (~10h effort réel vs 26h estimés), validé par 948 tests verts, build OK, déployé. Bénéfice direct : réduction du coût marginal de support par loge déployée, argument commercial (signal de sérieux opérationnel attendu en B2B niche). Proposition : ## Pattern — Centre d'aide utilisateur in-app (Vue + Markdown + tests CI) ### Anti-patterns explicitement écartés - ❌ **Tour guidé pas-à-pas au premier login** (Userpilot, Intro.js) — friction pour public initié, maintenance lourde à chaque changement UI - ❌ **FAQ statique en page wiki/Notion** — dérive silencieuse, pas de revue par PR, pas de versionnement, pas de tests - ❌ **Chatbot IA** — perception négative en contexte B2B niche (méfiance), coût récurrent API LLM, pas de garantie de justesse - ❌ **Vidéos longues à la place du texte** — non-cherchable, maintenance lourde ### Architecture ``` docs/user//.md (Markdown + frontmatter YAML) ↓ import.meta.glob (Vite, lazy) ↓ frontmatter parser inline (js-yaml — JAMAIS gray-matter, cf. piège) ↓ index par audience (slug, title, audience, last_reviewed) ↓ /aide (index par onglets de rôle) /aide/:audience/:slug (rendu Markdown via marked) ↓ Bouton "?" contextuel en top-bar (route → guide canonique mappé) ``` ### Discipline 1 — Source unique de vérité Tous les guides vivent dans `docs/user/`, en Markdown, versionnés en Git. **Pas de Notion. Pas de Google Docs. Pas de wiki externe. Pas d'exception.** Bénéfices : - Versionnement gratuit (historique Git) - Revue par PR (qualité éditoriale) - Diff visible (changement traçable) - Bundling au build par Vite (lecture in-app sans fetch runtime, ~14 Ko gzippé pour 8 guides + composants) - Aucun service tiers à maintenir ### Discipline 2 — Frontmatter YAML validé Zod ```yaml --- 2026-06-08 — app-alexandrie FILE_UPDATE_PROPOSAL Fichier cible : 90_debug_et_postmortem.md (ET pointeur dans 10_backend_risques_et_vigilance.md / 10_frontend_risques_et_vigilance.md) Pourquoi : Pendant une session de rédaction de tech-specs (BMad quick-spec) sur une app de formation (modèle Skool : Pack → Module → Leçon, avec modules PREMIUM = add-ons payants en plus du pack de base), le MÊME piège a cassé 5 fois de suite, à travers 3 specs différentes (#2 leçon-preview, #6 achievements, #4 écran-de-fin) et a été attrapé uniquement par la revue adversariale itérative (parfois 4 tours pour une seule spec). Le piège est suffisamment générique pour mériter une règle. Le piège (formulation générique) : Dans tout domaine où un parcours a un « tronc commun obligatoire » + des « extensions optionnelles » (premium, bonus, modules payants à part), les notions de **séquence**, **suivant**, **fin**, **progression %**, **complétion 100%** ont DEUX définitions qui divergent silencieusement : - définition POSITIONNELLE/PHYSIQUE (toutes les unités, premium inclus, triées par position) — c'est ce que renvoient typiquement les helpers `nextAfter()`, `flattenSequence()`, `findNext()` ; - définition MÉTIER (tronc commun seul = ce qu'un acheteur de base doit/peut finir) — c'est ce que veut presque toujours la logique produit (« diplômé », « formation terminée », badge, déblocage). Un champ nommé `nextUnlockedLessonId`, `continueLessonId`, `sequence`, `completedIds` n'est PRESQUE JAMAIS filtré sur le tronc commun — il inclut le premium. Supposer le contraire produit, selon le cas : - faux NÉGATIF : la fin n'est jamais détectée (une unité premium reste « après » le tronc fini → `next !== null` éternellement) ; - faux POSITIF : « terminé » déclenché alors que le tronc ne l'est pas (finir une unité premium terminale alors que le tronc a un trou). Symptômes observés (concrets, pour reconnaissance) : - `nextLessonAfter(sequence, id)` = `idx+1 >= sequence.length ? null` → purement positionnel, `sequence` = `flattenSequence` qui inclut `isPremium`. - `completedIds` chargé AVANT l'upsert de l'unité courante → au moment d'un hook post-commit, la dernière unité validée MANQUE encore. - 3 définitions de « 100% » coexistant dans le même codebase (progress UI = COMPLETED brut sur baseSequence ; remboursement = visionnage réel ≥90% sur TOUS modules ; ce qu'on voulait = visionnage réel sur tronc) → citer l'une comme « réutilisable » envoie le dev recopier le mauvais périmètre OU la mauvaise métrique. - `baseSequence = sequence.filter(!isPremium)` existe souvent DÉJÀ mais en `const` interne non exporté → tentation de le citer comme réutilisable à tort. Proposition (règle) : « Tronc commun vs extension premium — ne jamais supposer qu'un champ fin/suivant/séquence/progression filtre le premium. » 1. Avant de spécifier/coder toute logique de complétion, fin, déblocage, progression ou « 100% » : LIRE le code réel des helpers de séquence (`buildSequence`/`flatten`/`nextAfter`/`findContinue`) et confirmer s'ils incluent ou non les unités premium/optionnelles. Ne pas déduire du nom. 2. Identifier TOUTES les définitions de complétion existantes dans le codebase (souvent ≥2 : UI progress, remboursement, déblocage). Vérifier leur PÉRIMÈTRE (tronc seul ? tous modules ?) ET leur MÉTRIQUE (état brut COMPLETED ? visionnage réel ≥seuil ?). Si aucune ne combine le périmètre + la métrique voulus, écrire une fonction dédiée et le DIRE explicitement (« ni X ni Y n'est réutilisable tel quel ») — ne pas citer une fonction proche comme réutilisable. 3. Garde-fou systématique : une condition « 100% » via `every()` sur un ensemble filtré DOIT garder le cas vide (`length === 0 → false`), sinon `every([]) === true` = faux positif massif (déblocage pour tous, y compris au backfill). 4. Préférer un BOOLÉEN SERVEUR explicite (`isFormationComplete`, `isBaseTrackDone`) porté dans le payload, et déclencher la logique mobile/UI dessus — JAMAIS sur un dérivé positionnel (`next === null`). 5. Effet de bord du fix : si on déclenche un calcul « est-ce fini ? » sur la présence d'une extension premium (`hasPremium`), ce flag est CONSTANT pour l'entité → le calcul tourne à chaque action, et tout événement post-tronc le redonne `true` → risque de re-célébration/re-déclenchement en boucle. Ajouter une garde de transition (false→true) ou une ref de session. Méta-leçon (pour 40_decisions_et_archi.md ou workflow) : Quand une MÊME zone (ici complétion/premium) casse à plusieurs tours de revue adversariale, le défaut N'EST PAS épuisé — chaque correction sur cette zone mérite une RE-vérification dédiée, car les fixes y introduisent des effets de bord non évidents (ex. un `|| hasPremium` qui corrige un faux négatif crée une re-célébration en boucle). Ne jamais clore une telle zone sur une intuition de convergence. --- 2026-06-20 — RL799_V2 / Code review v2-4-2 FILE_UPDATE_PROPOSAL Fichier cible : knowledge/backend/risques/general.md Pourquoi : `computeSeasonStats` dans `hospitalierVeilleService.ts` filtrait les tenues par grade et par saison mais omettait de les borner par `member.initiatedAt`. Un Frère initié en mars 2026 apparaissait avec un mauvais taux de présence saison 2025-2026 car le dénominateur incluait toutes les tenues depuis septembre 2025 — avant qu'il soit membre. L'AC5 et les Dev Notes le précisaient explicitement, mais le bug était invisible aux tests (le seed n'a pas de membre récemment initié avec des tenues antérieures). Proposition : **Taux de présence sur périmètre borné par `initiatedAt` : toujours filtrer `tenue.date >= member.initiatedAt`.** Quand on calcule un taux/compteur d'absences sur un périmètre temporel (saison, trimestre, etc.) pour un membre, le dénominateur doit exclure les tenues antérieures à la date d'initiation du membre — il n'était pas encore convié. Pattern : `if (tenue.date < member.initiatedAt) continue;` avant tout autre filtre de périmètre. Ce filtre est rarement couvert par les tests automatiques (le seed a des membres initiés avant la saison de test), ce qui le rend vulnérable aux oublis. Le mettre en première vérification dans la boucle le rend visible et documenté. Cas vécu : `computeSeasonStats` dans `hospitalierVeilleService.ts` — filtre ajouté en code review v2-4-2 du 2026-06-20.