mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-05-18 08:18:15 +02:00
capitalisation: intégration ~60 entrées RL799_V2 (triage 2026-05-02)
Triage du 95_a_capitaliser.md (~75 propositions) : - 60 entrées intégrées dans knowledge/ (backend, frontend, workflow) - 4 nouveaux fichiers : backend/patterns/tests.md, backend/risques/tests.md, frontend/patterns/general.md, workflow/patterns/general.md - 6 doublons rejetés - Mise à jour des READMEs index pour refléter les nouvelles entrées - 95_a_capitaliser.md restauré à sa structure initiale - 40_decisions_et_archi.md : décision mono-tenant déployable vs SaaS multi-tenant - 90_debug_et_postmortem.md : sub-agents Write indisponible, effet iceberg CI, prisma migrate diffs cosmétiques Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -382,3 +382,176 @@ followingsError: string | null; // erreur de fetchFollowings
|
||||
|
||||
- Règle : dans un store qui gère à la fois des mutations et des listes paginées, chaque opération doit avoir sa propre clé d'erreur
|
||||
- Contexte technique : React Native / Zustand — app-alexandrie review 5.3, 28-03-2026
|
||||
|
||||
---
|
||||
|
||||
<a id="risque-emit-vue-mutation-serveur-sans-listener"></a>
|
||||
## `emit` Vue annonçant une mutation serveur sans listener parent → caches stale
|
||||
|
||||
### Risques
|
||||
|
||||
- Un composant enfant émet un événement (`emit('approved')`) après une mutation côté serveur, mais aucun parent n'écoute
|
||||
- L'enfant met bien à jour son état local, mais les caches parents qui dérivent du même statut (badges accordion, verrous cascade, prop `previousAggregate` consommée par d'autres enfants) restent stale **indéfiniment**, jusqu'au prochain changement d'écran/route
|
||||
- Bug invisible dans les tests structurels (`content.includes`) et passe inaperçu en revue parce que le code enfant est "correct"
|
||||
|
||||
### Symptômes
|
||||
|
||||
- Tout autre affichage du même statut dans le parent ou dans des sous-composants frères reste "Publiée" alors que la planche est "Approuvée" en DB
|
||||
- L'UI ne se rafraîchit qu'au prochain reload manuel ou navigation
|
||||
|
||||
### Bonnes pratiques / mitigations
|
||||
|
||||
```bash
|
||||
# Repérer les emits qui annoncent une mutation serveur
|
||||
grep -rn "defineEmits" apps/frontend/src/components
|
||||
|
||||
# Pour chaque emit trouvé, chercher s'il a au moins un listener parent
|
||||
grep -rn "@<eventName>=" apps/frontend/src/pages apps/frontend/src/components
|
||||
```
|
||||
|
||||
Zéro listener = bug latent (sauf si l'emit est purement informatif — analytics, debug).
|
||||
|
||||
**Règle** : tout `emit` qui annonce une **mutation persistée serveur** (création, suppression, changement de statut, validation) doit avoir au moins un listener parent qui :
|
||||
|
||||
1. Invalide les caches locaux dérivés de la même donnée (Map de statuts, computed, props transitives)
|
||||
2. Recharge la slice agrégée si le parent passe une prop construite à partir d'un autre fetch (`previousAggregate`, dashboard data)
|
||||
3. **Ne se contente PAS de l'optimistic update** de l'enfant — la source de vérité reste serveur, le cache parent doit refléter l'état serveur post-mutation
|
||||
|
||||
```vue
|
||||
<!-- ❌ Enfant émet, parent ignore. Le badge de l'accordion reste stale -->
|
||||
<PlancheTraceeCard :tenue-id="planche.tenueId" mode="previous" />
|
||||
|
||||
<!-- ✅ Listener explicite qui invalide les caches dérivés -->
|
||||
<PlancheTraceeCard
|
||||
:tenue-id="planche.tenueId"
|
||||
mode="previous"
|
||||
@approved="onPlancheApproved(planche.tenueId)"
|
||||
/>
|
||||
```
|
||||
|
||||
**Couverture** : test de mount complet via `@vue/test-utils` qui simule l'événement et vérifie que le rendu parent change. Les tests `readFileSync + content.includes('emit')` valident que le code enfant émet, pas que le parent écoute.
|
||||
|
||||
- Contexte technique : Vue 3 — RL799_V2 29-04-2026
|
||||
|
||||
---
|
||||
|
||||
<a id="risque-templates-vue-references-orphelines"></a>
|
||||
## Templates Vue — références orphelines invisibles à `tsc --noEmit`
|
||||
|
||||
### Risques
|
||||
|
||||
- Une variable supprimée du `<script setup>` mais encore référencée dans le `<template>` ne génère pas d'erreur compile avec `tsc --noEmit` seul (Volar moins strict que `tsc` pur sur les expressions template)
|
||||
- Le composant crashe **uniquement au runtime** : `Cannot read properties of undefined` ou `[Vue warn] Property "X" was accessed during render but is not defined`
|
||||
|
||||
### Symptômes
|
||||
|
||||
- Refactor où on extrait un composable et oublie de destructurer une variable, ou on retire un import devenu (apparemment) inutilisé
|
||||
- Typecheck passe, tests structurels passent, page charge mais une section ne s'affiche pas / un bouton ne fait rien
|
||||
- `[Vue warn]` dans la console au mount
|
||||
|
||||
### Bonnes pratiques / mitigations
|
||||
|
||||
**Recommandation outillage** : migrer le `typecheck` du projet de `tsc --noEmit` vers `vue-tsc -p tsconfig.typecheck.json --noEmit`. Avec `vue-tsc`, les expressions template sont strictement typées contre le `<script setup>` exposé. Une ref orpheline → erreur de compile, pas warning runtime.
|
||||
|
||||
**Checklist étendue avant de marquer une extraction "done"** :
|
||||
|
||||
```bash
|
||||
# Pour chaque symbole supprimé/non destructuré, grep dans le template
|
||||
for symbol in normalizedQuery directoryOfficeLabel; do
|
||||
echo "=== $symbol ==="
|
||||
# Patterns template critiques
|
||||
grep -nE "v-(if|else-if|show)=\"[^\"]*\\b$symbol\\b" apps/frontend/src/pages/<Page>.vue
|
||||
grep -nE "\\{\\{[^}]*\\b$symbol\\b" apps/frontend/src/pages/<Page>.vue
|
||||
grep -nE "(:|@)[a-z-]+=\"[^\"]*\\b$symbol\\b" apps/frontend/src/pages/<Page>.vue
|
||||
done
|
||||
```
|
||||
|
||||
**QA visuel obligatoire post-refactor** : pour tout refactor qui touche une page importante, ouvrir la page en browser dev avant de pousser :
|
||||
- naviguer aux états critiques (search, modals, toggle accordion)
|
||||
- vérifier la console : zéro `[Vue warn]` ou `ReferenceError`
|
||||
- tester au moins un workflow complet par section refactorée
|
||||
|
||||
- Contexte technique : Vue 3 / Volar / `vue-tsc` — RL799_V2 29-04-2026
|
||||
|
||||
---
|
||||
|
||||
<a id="risque-symboles-orphelins-suppression-bloc"></a>
|
||||
## Symboles orphelins après suppression d'un bloc — checklist grep
|
||||
|
||||
### Risques
|
||||
|
||||
- Refactor où on supprime un bloc cohérent (state + computeds + handlers) en utilisant un Edit ciblé. Tout compile, tous les tests passent, mais au runtime : `ReferenceError: <symbole> is not defined` au mount
|
||||
- Le symbole supprimé était encore référencé dans un **lifecycle hook** (`onMounted`, `onUnmounted`), un **watcher**, ou un **handler asynchrone** non inclus dans le bloc supprimé
|
||||
|
||||
### Symptômes
|
||||
|
||||
- Page qui ne mount plus après refactor, alors que typecheck OK + tests verts
|
||||
- Erreur visible uniquement à `cmd+R` sur la page
|
||||
|
||||
### Bonnes pratiques / mitigations
|
||||
|
||||
```bash
|
||||
# Pour chaque symbole déclaré dans le bloc à supprimer
|
||||
for symbol in updatePointerType pointerMql closeInsertMenuOnOutsideClick; do
|
||||
echo "=== $symbol ==="
|
||||
grep -n "\\b$symbol\\b" apps/frontend/src/pages/<Page>.vue
|
||||
done
|
||||
```
|
||||
|
||||
Doit retourner **uniquement les lignes du bloc à supprimer**. Si une référence apparaît hors du bloc → ne pas supprimer sans traiter explicitement (déplacer, adapter, ou laisser).
|
||||
|
||||
**Lieux à vérifier en priorité** :
|
||||
|
||||
| Lieu | Fréquence du piège |
|
||||
|------|--------------------|
|
||||
| `onMounted(() => { ... })` | ⚠️⚠️⚠️ très fréquent |
|
||||
| `onUnmounted(() => { ... })` | ⚠️⚠️⚠️ très fréquent (cleanup) |
|
||||
| `watch(() => x, () => { fn() })` | ⚠️⚠️ fréquent |
|
||||
| Handler async (`.then(() => fn())`) | ⚠️ rare mais existe |
|
||||
| Computed dans une autre section du fichier | ⚠️ rare |
|
||||
| Template (`@click`, `:disabled`) | typecheck attrape via SFC plugin |
|
||||
|
||||
**Garde-fou complémentaire** : QA visuel obligatoire post-refactor (cf. `risque-templates-vue-references-orphelines`).
|
||||
|
||||
- Contexte technique : Vue 3 — RL799_V2 29-04-2026
|
||||
|
||||
---
|
||||
|
||||
<a id="risque-extraction-vue-ts-bug-typage-latent"></a>
|
||||
## Extraction `.vue` → composable `.ts` révèle des bugs de typage latents
|
||||
|
||||
### Risques
|
||||
|
||||
- Vue 3 + Volar compile les blocs `<script setup>` avec une stratégie d'inférence moins agressive que `tsc` strict pur. Un handler peut compiler dans le `.vue` si le runtime n'utilise pas le champ manquant
|
||||
- Le composable extrait est compilé par `tsc -p tsconfig.typecheck.json` **hors contexte Vue** → toutes les règles strictes s'appliquent → la divergence de type devient une erreur bloquante
|
||||
- C'est un effet de bord **positif** mais qui peut bloquer l'extraction tant qu'on n'a pas diagnostiqué la divergence
|
||||
|
||||
### Symptômes
|
||||
|
||||
```
|
||||
Type 'X' is not assignable to type 'Y'.
|
||||
The types of '<champ>.<sous-champ>' are incompatible…
|
||||
Type 'A' is missing the following properties from type 'B': …
|
||||
```
|
||||
|
||||
Cas typique : un emit Vue annonçait `Detail` (type pour la liste) alors que la fonction service renvoyait `Data` (type pour la mutation). Silencieux dans le `.vue` d'origine, devenu visible dans le `.ts` extrait.
|
||||
|
||||
### Bonnes pratiques / mitigations
|
||||
|
||||
Trois cas typiques quand l'erreur apparaît après extraction :
|
||||
|
||||
1. **Le type annoncé ne matche pas le type réellement émis** : aligner sur le type réellement émis plutôt que sur le type annoncé dans `defineEmits`. Si possible, corriger aussi la signature de l'émetteur — mais c'est un autre scope
|
||||
2. **Le `.vue` exploitait un cast implicite** : `v-if="x.foo"` réduit le union type. Dans un `.ts` extrait, narrow explicitement avec un `if` ou un type guard
|
||||
3. **Volar n'analysait pas un chemin de type complexe** : type récursif, génériques imbriqués, `Pick<...>` dans une union → extraire un alias intermédiaire propre dans `@<module>/types.ts`
|
||||
|
||||
**Quoi faire face à l'erreur** :
|
||||
|
||||
1. **NE PAS** mettre `as Foo` pour faire taire le compilateur — c'est probablement masquer le même bug sous un autre nom
|
||||
2. Identifier lequel des deux types est correct (généralement celui que la fonction service / l'API renvoie réellement)
|
||||
3. Aligner la signature du handler/composable sur ce type-là
|
||||
4. Documenter dans un commentaire au-dessus du handler que le type émis diverge du type annoncé dans `defineEmits` (si on ne corrige pas l'émetteur dans le même refactor)
|
||||
5. Ouvrir un TODO si la correction de l'émetteur est hors scope
|
||||
|
||||
**Recommandation outillage** : `vue-tsc` plutôt que `tsc` pur en typecheck (cf. `risque-templates-vue-references-orphelines`). Ce genre de divergence aurait été détecté **avant** le refactor.
|
||||
|
||||
- Contexte technique : Vue 3 / Volar / `vue-tsc` — RL799_V2 29-04-2026
|
||||
|
||||
Reference in New Issue
Block a user