capitalisation: triage 95_a_capitaliser + création domaine infra

Triage des 27 propositions du buffer de capitalisation (skill
capitalisation-triage), avec vérification des doublons contre la base.

Intégré dans knowledge/ (23 entrées):
- backend: redis (compensation incrBy non-atomique), nestjs (injection
  cassée sous tsx watch; guard write mode dégradé), async (test rollback
  pipeline multi-fichiers), contracts (idempotence POST), auth (disclosure
  comptes soft-deleted), prisma (index partial soft-delete), llm-providers
  (nouveau: OAuth vs API key, prompt caching).
- frontend: tests (garde-fous parking Later), navigation (fichiers
  non-route sous src/app Expo Router), general (type client vs payload
  backend), state (fallback catch-all mapping DB→UI).
- workflow: story-tracking (statut BMAD vs narratif obsolète).
- product: general (nouveau: doc feature store sans UI).
- infra: NOUVEAU DOMAINE (traefik, tailscale, docker, docker-networking,
  reverse-proxy-paths, sidecar tailscale) + 00_INDEX.md.

Autres:
- 90_debug_et_postmortem.md: post-mortem réseau Docker partagé hors compose.
- Rejeté 3 doublons (types enum contracts, getter PrismaService, $transaction).
- Buffer 95_a_capitaliser.md purgé et restauré à son état initial.
- _projects.conf: MAJ statuts epics + ajout app-rl799.
This commit is contained in:
MaksTinyWorkshop
2026-06-25 10:31:22 +02:00
parent 1c876309f1
commit ef24d85d57
31 changed files with 1042 additions and 27 deletions
+3 -3
View File
@@ -9,11 +9,11 @@ Avant toute proposition frontend, identifie le fichier dont le nom et la descrip
| Fichier | Domaine | Entrées clés |
|---------|---------|--------------|
| `auth.md` | Auth, guards de rôle, entitlements, OAuth | Auth côté client, loading infini écran gated, bouton OAuth vide, guard rôle flash UX |
| `state.md` | Zustand, state management, erreurs async, optimistic UI | Erreurs silencieuses, catch sans feedback, auto-reset état dégradé, fire-and-forget refresh, boolean UI hardcodé, flag isLoading unique, erreur sans rethrow, optimistic update sous-listes |
| `navigation.md` | Expo Router, Vue Router, deep link, useEffect fetch, contexte store | Store vide deep link/reload, guard incomplet états terminaux, collection sans clé contexte, double route racine, router-link disabled, état local query param |
| `state.md` | Zustand, state management, erreurs async, optimistic UI | Erreurs silencieuses, catch sans feedback, auto-reset état dégradé, fire-and-forget refresh, boolean UI hardcodé, flag isLoading unique, erreur sans rethrow, optimistic update sous-listes, fallback catch-all mapping statut DB → UI |
| `navigation.md` | Expo Router, Vue Router, deep link, useEffect fetch, contexte store | Store vide deep link/reload, guard incomplet états terminaux, collection sans clé contexte, double route racine, router-link disabled, état local query param, fichiers non-route sous `src/app` Expo Router |
| `design-tokens.md` | Design tokens, spacing, Tailwind, StyleSheet RN | Double système espacement, dimensions via spacing, inline styles dashboard, classes Tailwind invalides |
| `nextjs.md` | Next.js App Router, SSR, Server Actions, sécurité | useSearchParams sans Suspense, type ViewData dupliqué, composant React .ts, double validation segment, consent state ambigu, script inline XSS, window.location.reload, useTransition snapshot, window.confirm, import type server, img natif, useTransition global liste, formulaire defaultValue sans key |
| `tests.md` | Jest, ts-jest, tests React Native, Vue | Config node bloque .tsx, faux test négatif, helpers copiés, test écran indirect, test façade flux réel, tests présence textuelle |
| `performance.md` | Re-renders, memoization, useCallback, fetch | Sur-renders bundle non maîtrisé, useCallback inutile inline, fetch sans timeout |
| `react-native.md` | React Native, fetch, ScrollView, TextInput | Focus ring TextInput, contentInset iOS-only, fetch sans response.ok |
| `general.md` | Accessibilité, regex, patterns transversaux, monorepo | Accessibilité oubliée a11y, regex globale singleton lastIndex, Alert.prompt iOS-only, primitive UI couplée, migration partielle classes legacy, ARIA roles sans clavier, duplication logique métier monorepo, event listeners globaux modales, boutons imbriqués, fire-and-forget sans feedback |
| `general.md` | Accessibilité, regex, patterns transversaux, monorepo | Accessibilité oubliée a11y, regex globale singleton lastIndex, Alert.prompt iOS-only, primitive UI couplée, migration partielle classes legacy, ARIA roles sans clavier, duplication logique métier monorepo, event listeners globaux modales, boutons imbriqués, fire-and-forget sans feedback, type client non MAJ extension payload backend |
+29
View File
@@ -790,3 +790,32 @@ Garder `<fieldset>/<legend>` uniquement quand on accepte le rendu natif (groupe
- Contexte technique : HTML / a11y / CSS — RL799_V2 02-05-2026
---
<a id="risque-type-client-non-maj-extension-payload-backend"></a>
## Type client non mis à jour lors d'une extension de payload backend
### Risques
- Quand un nouveau champ est ajouté dans un objet retourné par une Server Action, le type TS côté client n'est pas toujours mis à jour en parallèle
- Si le retour de la Server Action est casté vers un type plus étroit, TypeScript accepte l'incompatibilité **silencieusement** : le champ existe au runtime mais reste invisible pour les consommateurs TS
### Symptômes
- Le payload contient un champ supplémentaire en runtime, mais le type client ne l'expose pas
- Une tâche "mettre à jour le test si le payload expose le nouveau champ" est marquée done en vérifiant le test, sans vérifier le type source qui l'alimente
- Aucune erreur de compile car le cast masque la divergence
### Bonnes pratiques / mitigations
Pour toute extension d'un payload backend (ajout de champ), appliquer une **checklist de propagation** :
1. Le type TS côté client (ex : type d'item média, type de l'entité)
2. Le contrat Zod si présent
3. Les fixtures de test qui typent le payload
4. Les composants qui déstructurent le payload (vérifier les accès au nouveau champ manquants)
- Règle : ne pas laisser cette propagation à la décision implicite du dev — l'expliciter comme checklist dans la story / la PR.
- Contexte technique : Next.js / Server Actions / TypeScript / Zod — app-template-resto 25-06-2026
---
+24
View File
@@ -296,3 +296,27 @@ const routes = [
- Contexte technique : Vue Router / navigation dynamique — RL799_V2 15-04-2026
---
<a id="risque-fichiers-non-route-sous-src-app-expo-router"></a>
## Fichiers non-route sous `src/app` avec Expo Router
### Risques
- Expo Router scanne récursivement `src/app` pour construire les routes : tout fichier TypeScript laissé dans cet arbre (`*.spec.ts`, `*.utils.ts`, `*.screen-logic.ts`) est traité comme une route potentielle, même s'il s'agit d'un test, d'un helper ou d'une logique d'écran
- Symptômes initiaux trompeurs : une succession de warnings au build puis un crash runtime qui ressemble à un problème d'app, alors que la cause est un simple fichier mal placé
### Symptômes
- Warning `Route "...spec.ts" is missing the required default export`
- Warning `Route "...screen-logic.ts" is missing the required default export`
- Erreur runtime `Property 'describe' doesn't exist` ou `Property 'jest' doesn't exist` dans Expo Go (le runtime de l'app charge un fichier de test)
### Bonnes pratiques / mitigations
- `src/app` ne doit contenir **que** des fichiers de routing Expo Router
- Déplacer helpers, utils, tests et logique d'écran ailleurs (`src/domains`, `src/features`, `src/shared`)
- **Diagnostic** : en debug mobile, si Expo Go remonte `describe` / `jest` au runtime, vérifier immédiatement qu'aucun fichier de test n'est resté sous `src/app`
- Contexte technique : Expo Router — app-alexandrie 25-06-2026
---
+38
View File
@@ -555,3 +555,41 @@ Trois cas typiques quand l'erreur apparaît après extraction :
**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
---
<a id="risque-fallback-catch-all-mapping-statut-db-ui"></a>
## Fallback catch-all dans le mapping statut DB → UI
### Risques
- Un mapping statut DB → statut UI avec un `return` catch-all final accepte implicitement **toute** nouvelle valeur d'enum DB sans alerte
- Une nouvelle valeur (ex : `pending`) atterrit alors dans la branche par défaut (ex : rendue comme `processing`) avec un libellé incorrect, violant silencieusement le contrat d'affichage
### Symptômes
- Une valeur d'enum DB non explicitement gérée affiche le mauvais état UI (ex : "En cours…" au lieu de "En attente")
- L'ajout d'une valeur d'enum côté DB ne déclenche aucune erreur de compile ni de test ; le mauvais libellé passe inaperçu
### Bonnes pratiques / mitigations
```ts
// ❌ Le catch-all masque silencieusement "pending" sous "processing"
function dbStatusToUiStatus(s: DBStatus): UIStatus {
if (s === "ready") return "ready";
if (s === "failed") return "failed";
return "processing"; // "pending" atterrit ici sans libellé correct
}
// ✅ Mapping exhaustif : un if par valeur d'enum
function dbStatusToUiStatus(s: DBStatus): UIStatus {
if (s === "ready") return "ready";
if (s === "failed") return "failed";
if (s === "pending") return "pending";
return "processing";
}
```
- Règle : chaque valeur de l'enum DB doit avoir son propre `if` dans le mapping. Éviter le `return` catch-all final qui absorbe les valeurs non prévues.
- Contexte technique : frontend / mapping statut DB → UI — app-template-resto 25-06-2026