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:
MaksTinyWorkshop
2026-05-02 22:12:44 +02:00
parent 02ad0de258
commit b3417ad77b
31 changed files with 5370 additions and 12 deletions

View File

@@ -365,4 +365,97 @@ it('retourne 403 si subscription inactive', async () => {
- Contexte technique : auth / cycle de vie compte — RL799_V2 17-04-2026
---
---
<a id="risque-helpers-x-actif-derivants"></a>
## Helpers "X actif" qui dérivent silencieusement
### Risques
- Plusieurs helpers répondent à la même question — *"l'entité X est-elle active / opérante ?"* — avec des filtres légèrement différents
- Un user passe la guard A mais pas la guard B sur la même ressource (ou inversement). Bugs silencieux, pas d'erreur, juste une asymétrie de comportement
### Symptômes
- Délégation `secretaireDeSeance` "active" filtrée sur `status: 'published', closedAt: null, cancelledAt: null` dans un helper, juste `cancelledAt: null` dans l'autre
- Un ex-délégué d'une soirée clôturée garde l'autorité cross-soirée indéfiniment
### Bonnes pratiques / mitigations
1. **Un seul helper canonique** par notion d'activité (ex : `isDelegationActive`, `isSoireeOpenForRappel`). Les autres l'appellent
2. Si la centralisation n'est pas faisable immédiatement (ex : helper appelé en N+1 query, perf), au moins un test qui compare leur output sur des fixtures partagées et casse à la moindre divergence
3. Au minimum : un commentaire en tête du helper "secondaire" qui pointe vers le canonique et liste explicitement les filtres à maintenir synchronisés
- Contexte technique : auth / RBAC — RL799_V2 27-04-2026
---
<a id="risque-guard-charge-objets-riches"></a>
## Guard d'autorisation qui charge des objets riches
### Risques
- Une guard d'autorisation s'exécute à CHAQUE requête sur une route protégée
- Si la guard a besoin de "trouver une candidate" (ex : "cette tenue est-elle dans les 'dernières rappelables' du grade pour une de mes délégations ?"), le repo helper utilisé doit avoir un select **minimal**, PAS le select complet utilisé par les services métier
- Pour un user avec N délégations actives, on charge N agrégats volumineux à chaque requête
### Symptômes
- Même fonction repo appelée par (1) un service qui a besoin de toutes les relations (rendu UI) et (2) une guard qui n'a besoin que de l'id
- La guard paie le coût du fetch riche inutilement
- Latence guard qui croît avec le nombre de relations chargées
### Bonnes pratiques / mitigations
Exposer **deux variantes** du repo helper :
- `findX(...)` — select riche, utilisé par les services métier
- `findXIdOnly(...)` — select `{ id: true }`, utilisé par les guards
```typescript
// Guard
export const requireXAccess = async (request, id, { roleSet }) => {
// utilise findXIdOnly (select minimal) — pas findX
const candidate = await repo.findXIdOnly({ ... });
if (!candidate || candidate.id !== id) return forbidden();
};
// Service métier
export const getXFullDetails = async (id) => {
return repo.findX({ ... }); // include riche
};
```
Coût : duplication de la clause `where` (acceptable, factorisable en constante). Bénéfice : la guard reste O(1) en payload même quand les relations grossissent.
- Contexte technique : auth / performance — RL799_V2 27-04-2026
---
<a id="risque-suppression-flag-auth-global"></a>
## Suppression d'un flag auth global (DB + DTO + tests) — cleanup atomique obligatoire
### Risques
- Un flag profondément câblé dans Prisma (ex : `mustChangePassword`, `isVerified`) ne peut pas être supprimé incrémentalement : chaque cleanup partiel produit un état non-compilable
- Les fixtures de tests qui posent `mustChangePassword: false` cassent à la compilation TS au moment du drop — bloque tout commit séparé
- Les helpers `helpers/db.ts` et les DTO partagés (`packages/shared`) sont prioritaires, sinon les imports cross-package échouent en cascade
### Symptômes
- `Property 'mustChangePassword' does not exist on type 'User'` après un drop partiel
- Tentative de découpage en sous-lots qui échoue au typecheck
### Bonnes pratiques / mitigations
Quand on prévoit de supprimer un flag auth profondément câblé :
1. **Le cleanup ne peut pas être incrémental** — soit on supprime tout dans un chantier, soit on garde le flag avec un nullable de transition
2. **Les fixtures de tests doivent être nettoyées dans le même PR** — grep systématique avant de démarrer (`grep -rn "mustChangePassword" apps/`) pour estimer l'ampleur
3. **Les helpers `helpers/db.ts`** sont prioritaires — un seul fichier touché casse tous les tests qui l'importent
4. **Les DTO partagés (`packages/shared`)** doivent être alignés en premier
5. Considérer un sous-lot dédié au cleanup si le flag est transverse — éviter de l'inclure dans un sous-lot fonctionnel
**Anti-pattern** : déprécier en douceur en gardant le flag avec un commentaire `// @deprecated` sans supprimer les usages. Le code mort s'accumule, les futurs devs hésitent à le nettoyer ("pourquoi c'est encore là ?"), la dépréciation ne se finit jamais.
- Contexte technique : auth / refactor schema — RL799_V2 28-04-2026