Files
_Assistant_Lead_Tech/95_a_capitaliser.md
T
MaksTinyWorkshop 88f2eab058 docs(capitalisation): purge 95_ après intégration du triage local (mai-juin 2026)
Le buffer 95_a_capitaliser.md contenait ~273 propositions accumulées localement
(RL799_V2 + app-alexandrie, mai-juin 2026). Toutes triées et intégrées :
- backend → knowledge/backend/ (f1b7834)
- frontend → knowledge/frontend/ (5f5c872)
- workflow + ux → knowledge/ (81fde91)
- post-mortems + ADR → 90_/40_ (2a06429)

Le buffer est purgé. Restent 3 entrées jugées trop spécifiques au projet lors du
triage, conservées avec une note de routage pour intégration manuelle dans les
CLAUDE.md des projets concernés :
- RL799_V2 : centre d'aide in-app + taux de présence borné par initiatedAt
- app-alexandrie : tronc commun vs extension premium (formation Skool)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 00:56:46 +02:00

12 KiB

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/<thème>.md
  • knowledge/backend/risques/<thème>.md
  • knowledge/frontend/patterns/<thème>.md
  • knowledge/frontend/risques/<thème>.md
  • knowledge/ux/patterns/<thème>.md
  • knowledge/ux/risques/<thème>.md
  • knowledge/n8n/patterns/general.md
  • knowledge/n8n/risques/general.md
  • knowledge/product/patterns/general.md
  • knowledge/product/risques/<thème>.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 : <knowledge/backend/patterns/<thème>.md | knowledge/backend/risques/<thème>.md | knowledge/frontend/patterns/<thème>.md | knowledge/frontend/risques/<thème>.md | knowledge/ux/patterns/<thème>.md | knowledge/ux/risques/<thème>.md | knowledge/n8n/patterns/general.md | knowledge/n8n/risques/general.md | knowledge/product/patterns/general.md | knowledge/product/risques/<thème>.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 :
<raison pour laquelle ce savoir mérite d'être capitalisé>

Proposition :
<contenu suggéré à intégrer dans le fichier cible>

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/<audience>/<slug>.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


---

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.