diff --git a/10_backend_patterns_valides.md b/10_backend_patterns_valides.md
index 110c48c..085f1f1 100644
--- a/10_backend_patterns_valides.md
+++ b/10_backend_patterns_valides.md
@@ -8,7 +8,7 @@ Ce fichier contient **uniquement** des patterns back-end :
Objectif : éviter de réinventer la roue et réduire le temps de debug.
-Dernière mise à jour : 19-03-2026
+Dernière mise à jour : 20-03-2026
---
@@ -38,6 +38,10 @@ Dernière mise à jour : 19-03-2026
- [Guardrails multi-tenant — 403 vs 404 selon la sémantique](#pattern-guardrails-multi-tenant-403-404)
- [Repository tenant-aware — `tenantId` obligatoire dans la signature](#pattern-repository-tenant-aware)
- [Défense en profondeur — inclure `tenantId` dans les updates](#pattern-tenantid-dans-updates)
+- [Next.js server-only & Server Actions — règles d'isolation](#pattern-nextjs-server-only-isolation)
+- [Opérations auth sensibles — atomiques, idempotentes et cohérentes](#pattern-auth-operations-atomiques)
+- [Réponse HTTP 200 avec payload métier pour les états d'accès](#pattern-http-200-payload-metier)
+- [Quota journalier Redis atomique (INCR + EXPIREAT pipeline)](#pattern-quota-redis-atomique)
---
@@ -965,3 +969,183 @@ handlePackWebhookEvent(event): PackWebhookResult | null
- `tenantId` présent dans les clauses `where` des updates sensibles
- Pas de mutation tenant-scoped basée sur `id` seul
- Revue explicite des exceptions documentées
+
+---
+
+
+## Pattern : Next.js server-only & Server Actions — règles d'isolation
+
+- Objectif : permettre les tests unitaires Node tout en gardant les contraintes runtime Next.js là où elles sont nécessaires.
+- Contexte : monorepo Next.js App Router avec logique métier testée en Node runner natif.
+- Quand l'utiliser : dès qu'un module mixe logique pure et dépendances runtime Next.js.
+- Quand l'éviter : modules purement UI côté client.
+- Avantage :
+ - logique pure testable sans friction (runner Node natif)
+ - Server Action fine et lisible — orchestration uniquement
+ - `server-only` explicite et intentionnel, pas par habitude
+- Limites / vigilance :
+ - ne pas mettre `server-only` dans les repositories purs — casse les tests Node hors Next.js
+- Validé le : 16-03-2026
+- Contexte technique : Next.js App Router / Node.js test runner
+
+### Règles
+
+```txt
+- `server-only` uniquement sur les modules qui appellent des APIs Next.js runtime
+ (cookies(), headers(), redirect()) — pas sur les repositories ni la logique pure
+- Logique pure extraite dans un module injectable sans `server-only` :
+ deleteSession({ prismaClient, sessionToken })
+ → testable avec le runner Node sans friction
+- Server Action = orchestration mince, elle appelle les modules purs injectés
+ et gère les dépendances Next.js runtime uniquement
+- Logique de validation / sanitisation (safeHttpUrl, etc.) → module utilitaire séparé,
+ sans import nodemailer / server-only
+```
+
+### Checklist
+
+- [ ] `server-only` absent des repositories et modules de logique pure
+- [ ] Server Action ≤ 10 lignes, délègue au module pur injectable
+- [ ] Modules purs couverts par des tests `.spec.ts` Node sans config spéciale
+- [ ] La logique pure ne dépend pas du runtime pour être exécutée
+
+---
+
+
+## Pattern : Opérations auth sensibles — atomiques, idempotentes et cohérentes
+
+- Objectif : garantir que les opérations multi-étapes auth (reset, logout, révocation) ne laissent jamais un état incohérent.
+- Contexte : tout flux auth qui combine plusieurs writes : hash de mot de passe, invalidation de token, suppression de session.
+- Quand l'utiliser : systématiquement sur toute opération qui touche plusieurs tables auth en séquence.
+- Quand l'éviter : opérations de lecture pure.
+- Avantage :
+ - pas de token valide après reset de mot de passe si l'opération est interrompue
+ - suppression de session idempotente (P2025 absorbé silencieusement)
+ - comportement prévisible même en cas de retry ou de concurrence
+- Limites / vigilance :
+ - `$transaction` Prisma ne couvre pas les effets de bord réseau (email, cookies) — ces étapes restent hors transaction
+- Validé le : 16-03-2026
+- Contexte technique : Node.js / Prisma / auth par session ou token
+
+### Implémentation (exemple minimal)
+
+```typescript
+// consumePasswordReset — atomique dans une transaction
+await prisma.$transaction([
+ prisma.passwordResetToken.update({
+ where: { tokenHash },
+ data: { consumedAt: new Date() },
+ }),
+ prisma.user.update({
+ where: { id: userId },
+ data: { passwordHash: newHash },
+ }),
+ prisma.session.deleteMany({ where: { userId } }),
+]);
+
+// Suppression de session — idempotente (P2025 absorbé)
+try {
+ await prisma.session.delete({ where: { sessionToken } });
+} catch (err) {
+ if (err?.code !== 'P2025') throw err; // session déjà supprimée → OK
+}
+```
+
+### Checklist
+
+- [ ] Toute opération hash + update + delete dans une `$transaction`
+- [ ] `P2025` absorbé silencieusement sur les suppressions de session
+- [ ] Effets de bord hors transaction documentés (cookie, email)
+- [ ] Tests couvrant le cas d'une session déjà expirée
+
+---
+
+
+## Pattern : Réponse HTTP 200 avec payload métier pour les états d'accès
+
+- Objectif : éviter les codes 4xx pour des états métier normaux qui nécessitent un rendu côté client.
+- Contexte : endpoints dont la réponse varie selon les droits ou l'état d'abonnement, sans que l'absence de contenu soit une erreur.
+- Quand l'utiliser : paywall, trial read-only, quota soft, état d'accès partiel — quand le client doit décider du rendu.
+- Quand l'éviter : accès réellement interdit côté serveur (403), non authentifié (401), endpoint inexistant (404).
+- Avantage :
+ - pas de gestion d'exception côté client mobile pour des états courants
+ - rendu conditionnel (paywall, teaser, empty) piloté par le payload
+ - log serveur propre — 4xx réservés aux erreurs techniques/sécurité
+- Limites / vigilance :
+ - ne pas généraliser aux vraies erreurs de sécurité — 401/403/404 gardent leur sémantique HTTP
+- Validé le : 20-03-2026
+- Contexte technique : NestJS / Expo React Native — app-alexandrie story 4.1
+
+### Implémentation (exemple minimal)
+
+```typescript
+// GET /community/forums
+// Sans abonnement → 200 + { data: { forums: [], paywallRequired: true }, meta }
+// Avec abonnement → 200 + { data: { forums: [...], paywallRequired: false }, meta }
+
+// ❌ Anti-pattern
+return res.status(402).json({ error: { code: 'SUBSCRIPTION_REQUIRED' } });
+
+// ✅ Pattern correct
+return res.status(200).json({
+ data: { forums: [], paywallRequired: true },
+ meta: { total: 0 },
+});
+```
+
+### Règle
+
+- **4xx** = erreur technique ou de sécurité (401 non authentifié, 403 accès interdit, 404 introuvable)
+- **200 + flag métier** = état métier normal que le client doit interpréter pour le rendu
+
+---
+
+
+## Pattern : Quota journalier Redis atomique (INCR + EXPIREAT pipeline)
+
+- Objectif : implémenter un quota d'action journalier sans race condition ni clé TTL orpheline.
+- Contexte : quota par utilisateur sur une fenêtre calendaire UTC (posts, requêtes, actions sensibles).
+- Quand l'utiliser : toute limite d'action journalière avec Redis disponible.
+- Quand l'éviter : si Redis est down — prévoir un mode dégradé permissif (voir implémentation).
+- Avantage :
+ - atomicité garantie : `INCR + EXPIREAT` dans un pipeline `MULTI/EXEC`
+ - pas de clé sans TTL même en cas de deux requêtes simultanées (`count === 1` concurrent)
+ - mode dégradé explicite si Redis down (`count === null` → permissif)
+- Limites / vigilance :
+ - compensation `incrBy(-1)` en cas de dépassement — ne couvre pas les crashes entre INCR et la vérification
+ - la fenêtre expire à minuit UTC, pas à minuit local
+- Validé le : 20-03-2026
+- Contexte technique : Redis / NestJS / app-alexandrie story 4.2
+
+### Implémentation (exemple minimal)
+
+```typescript
+// RedisService — méthode dédiée
+async incrWithExpireAt(key: string, expireAtMs: number): Promise {
+ const pipeline = this.client.multi();
+ pipeline.incr(key);
+ pipeline.expireAt(key, Math.floor(expireAtMs / 1000));
+ const results = await pipeline.exec();
+ return results[0] as number; // valeur post-INCR
+}
+
+// Service métier
+const today = new Date().toISOString().split('T')[0]; // yyyy-mm-dd UTC
+const midnight = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
+const quotaKey = `app:quota:post:${userId}:${today}`;
+const count = await redis.incrWithExpireAt(quotaKey, midnight.getTime());
+
+if (count !== null && count > QUOTA_MAX) {
+ await redis.incrBy(quotaKey, -1); // compensation
+ throw new HttpException({ error: { code: 'QUOTA_EXCEEDED' } }, HttpStatus.TOO_MANY_REQUESTS);
+}
+// count === null → Redis down → mode dégradé permissif
+```
+
+### Checklist
+
+- [ ] Vérifier le quota AVANT la création en DB
+- [ ] `INCR + EXPIREAT` dans un pipeline atomique
+- [ ] Mode dégradé permissif si `count === null` (Redis down)
+- [ ] Clé nommée `{app}:quota:{action}:{userId}:{yyyy-mm-dd}` (date UTC)
+- [ ] Anti-pattern évité : `incrBy` + `setEx` séparés (race condition si count === 1 concurrent)
diff --git a/10_backend_risques_et_vigilance.md b/10_backend_risques_et_vigilance.md
index 882cb6b..c29f58e 100644
--- a/10_backend_risques_et_vigilance.md
+++ b/10_backend_risques_et_vigilance.md
@@ -8,7 +8,7 @@ Ce fichier recense des risques back-end susceptibles de provoquer :
- régressions coûteuses,
- incohérences de données.
-Dernière mise à jour : 16-03-2026
+Dernière mise à jour : 20-03-2026
---
@@ -46,6 +46,9 @@ Dernière mise à jour : 16-03-2026
- [`jest.clearAllMocks()` dans des `beforeEach` imbriqués avec mocks Prisma](#risque-jest-clearallmocks-imbrique)
- [Suppression du cookie après révocation DB sur logout](#risque-cookie-apres-revocation-db)
- [Repository layer non branché (dead layer)](#risque-repository-dead-layer)
+- [NestJS 11 — `TooManyRequestsException` inexistante](#risque-nestjs-toomanyrequest)
+- [`ForbiddenException` utilisé pour des erreurs de validation](#risque-forbidden-pour-validation)
+- [PrismaService — getter explicite manquant sur nouveau modèle](#risque-prismaservice-getter-manquant)
---
@@ -553,3 +556,90 @@ if (!user?.userId) {
- Rechercher explicitement les appels directs restants lors de la review
- Refuser l’introduction d’une couche repository tant que la migration effective n’est pas faite
- Contexte technique : TypeScript / Prisma / refactor d’accès aux données — 16-03-2026
+
+---
+
+
+## NestJS 11 — `TooManyRequestsException` inexistante
+
+### Risques
+
+- `TooManyRequestsException` n’est pas exportée par `@nestjs/common` en NestJS ≥ 11
+- Erreur de compilation ou 500 si utilisée directement
+
+### Symptômes
+
+- `Cannot find name ‘TooManyRequestsException’` à la compilation
+- Test qui passe sur NestJS 10 mais échoue sur 11+
+
+### Bonnes pratiques / mitigations
+
+```typescript
+// Pattern sûr pour HTTP 429
+throw new HttpException(
+ { error: { code: ‘QUOTA_EXCEEDED’, message: ‘...’ } },
+ HttpStatus.TOO_MANY_REQUESTS,
+);
+```
+
+- Contexte technique : NestJS v11+ — 20-03-2026
+
+---
+
+
+## `ForbiddenException` (403) utilisé pour des erreurs de validation
+
+### Risques
+
+- Les clients qui filtrent par HTTP 400 manquent les erreurs de validation lancées en 403
+- Sémantique API incorrecte → comportements clients imprévisibles
+
+### Symptômes
+
+- `ForbiddenException` lancée pour des tags invalides, des formats incorrects, des liens HTTP
+- Clients API qui ignorent ces erreurs ou les traitent comme des refus d’accès
+
+### Bonnes pratiques / mitigations
+
+Tableau de correspondance :
+
+| Cas | Exception correcte | Code HTTP |
+|---|---|---|
+| Tags invalides, contenu trop long, format incorrect | `BadRequestException` | 400 |
+| Accès refusé explicitement (accès forum, trial read-only) | `ForbiddenException` | 403 |
+| Quota dépassé | `HttpException(429)` via `HttpStatus.TOO_MANY_REQUESTS` | 429 |
+
+- **Règle** : HTTP 403 = "tu n’as pas le droit d’effectuer cette action". HTTP 400 = "ta requête est mal formée".
+- Contexte technique : NestJS / HTTP — 20-03-2026
+
+---
+
+
+## PrismaService — getter explicite manquant sur nouveau modèle
+
+### Risques
+
+- L’ajout d’un modèle dans `schema.prisma` sans son getter dans `PrismaService` casse le typecheck
+- Erreur silencieuse si les modules sont peu typés
+
+### Symptômes
+
+- `Property ‘forum’ does not exist on type ‘PrismaService’` à la compilation
+- Module fonctionnel sur le `PrismaClient` direct mais cassé via `PrismaService`
+
+### Bonnes pratiques / mitigations
+
+Tout ajout de modèle Prisma = **deux actions** :
+
+1. Ajouter le modèle dans `schema.prisma`
+2. Ajouter le getter dans `prisma.service.ts`
+
+```typescript
+// apps/api/src/infra/prisma/prisma.service.ts
+get forum() {
+ return this.client.forum;
+}
+```
+
+- **Checklist review** : à chaque nouvelle migration Prisma, vérifier que `prisma.service.ts` est mis à jour.
+- Contexte technique : NestJS / PrismaService encapsulé — app-alexandrie 20-03-2026
diff --git a/10_frontend_patterns_valides.md b/10_frontend_patterns_valides.md
index e9d30b0..df7823b 100644
--- a/10_frontend_patterns_valides.md
+++ b/10_frontend_patterns_valides.md
@@ -12,7 +12,7 @@ Il sert de **mémoire durable** pour éviter :
- de redélibérer éternellement sur des sujets déjà tranchés,
- de propager des “bonnes pratiques” théoriques non éprouvées.
-Dernière mise à jour : 19-03-2026
+Dernière mise à jour : 20-03-2026
---
@@ -25,6 +25,10 @@ Dernière mise à jour : 19-03-2026
- [Refresh idempotent sur store de liste paginée](#pattern-refresh-idempotent-liste-paginee)
- [UI admin légère sur domaine existant](#pattern-ui-admin-legere-domaine-existant)
- [Intégration tierce en mode link-out — préférer une page locale canonique](#pattern-link-out-page-locale-canonique)
+- [Design Tokens natifs TypeScript (Expo / React Native)](#pattern-design-tokens-expo-rn)
+- [Tests de styles React Native sans renderer JSX](#pattern-tests-styles-sans-renderer)
+- [Export des styles de composant pour réutilisation partielle](#pattern-export-styles-composant)
+- [Token typography par usage sémantique (React Native)](#pattern-token-typography-semantique)
---
@@ -427,6 +431,193 @@ const handleOAuth = async () => {
- [ ] Les garde-fous et fallbacks sont centralisés
- [ ] Les sorties directes concurrentes vers le tiers sont évitées ou justifiées
+---
+
+
+## Pattern : Design Tokens natifs TypeScript (Expo / React Native)
+
+### Synthèse
+
+- **Objectif** : centraliser les tokens de design sans librairie externe (NativeBase, Tamagui), typés et barrel-exportés.
+- **Contexte** : app Expo / React Native avec un système de design à maintenir.
+- **Quand l’utiliser** : dès le début d’un projet mobile, avant les premiers composants.
+- **Quand l’éviter** : si une librairie UI opinionée est déjà choisie et gère ses propres tokens.
+
+### Analyse
+
+- **Avantages** :
+ - aucune dépendance externe, zéro configuration magique
+ - autocomplétion TypeScript exacte via `as const` + types dérivés
+ - facile à migrer vers un design system plus élaboré ultérieurement
+- **Limites / vigilance** :
+ - les fichiers TTF doivent être présents dans `assets/fonts/` — Google Fonts ne peut pas être téléchargé automatiquement, documenter comme pré-requis dans la story
+ - ne pas réutiliser les tokens `spacing` pour les dimensions de composants (voir risques)
+
+### Validation
+
+- Validé le : 19-03-2026
+- Contexte technique : Expo SDK 52+ / React Native / TypeScript — app-alexandrie story 0.1
+
+### Implémentation (exemple minimal)
+
+```typescript
+// apps/mobile/src/theme/colors.ts
+export const colors = {
+ primary: ‘#2563EB’,
+ error: ‘#DC2626’,
+ // ...
+} as const;
+export type ColorToken = keyof typeof colors;
+
+// apps/mobile/src/theme/spacing.ts
+export const spacing = { xs: 4, sm: 8, md: 12, base: 16, lg: 24 } as const;
+export type SpacingToken = keyof typeof spacing;
+
+// apps/mobile/src/theme/index.ts (barrel export)
+export * from ‘./colors’;
+export * from ‘./spacing’;
+export * from ‘./typography’;
+export * from ‘./shadows’;
+```
+
+### Checklist
+
+- [ ] Tous les tokens `as const` pour inférence exacte
+- [ ] Pas de Context React — constantes TypeScript pures
+- [ ] Types dérivés (`ColorToken = keyof typeof colors`) pour l’autocomplétion
+- [ ] `useFonts` dans `_layout.tsx` avec guard `!fontsLoaded`
+- [ ] Fichiers TTF présents dans `assets/fonts/` et documentés dans la story
+
+---
+
+
+## Pattern : Tests de styles React Native sans renderer JSX
+
+### Synthèse
+
+- **Objectif** : tester les tokens et styles de composants React Native dans un environnement Jest `testEnvironment: node` sans renderer JSX.
+- **Contexte** : config Jest avec `transform: { ‘^.+\\.ts$’: ‘ts-jest’ }` — les `.tsx` ne sont pas transformés.
+- **Quand l’utiliser** : tokens de thème, logique pure, valeurs de style exportées.
+- **Quand l’éviter** : rendu conditionnel (styles dynamiques inline) — nécessite `@testing-library/react-native`.
+
+### Analyse
+
+- **Avantages** :
+ - teste que le composant utilise les bons tokens, pas seulement que les tokens ont des valeurs
+ - détecte les régressions de style sans renderer
+ - rapide, aucune config Jest supplémentaire
+- **Limites / vigilance** :
+ - ne teste pas le style calculé au runtime (style conditionnel dynamique)
+
+### Validation
+
+- Validé le : 19-03-2026
+- Contexte technique : React Native / Jest / ts-jest — app-alexandrie story 0.2
+
+### Implémentation
+
+```typescript
+// Button.tsx — exporter le StyleSheet avec un nom préfixé
+export const buttonStyles = StyleSheet.create({
+ base: { borderRadius: 20, height: 57 },
+ primary: { backgroundColor: colors.primary },
+});
+export function Button(...) { ... }
+
+// ui-components.spec.ts — importer et vérifier les tokens
+import { buttonStyles } from ‘./Button’;
+import { colors } from ‘@/theme’;
+
+it(‘variante primary utilise colors.primary’, () => {
+ expect(buttonStyles.primary.backgroundColor).toBe(colors.primary);
+});
+```
+
+### Deux niveaux de tests UI recommandés
+
+1. `.spec.ts` (node) : tokens, valeurs, logique pure
+2. `.spec.tsx` (config séparée avec renderer) : rendu visuel, interactions
+
+---
+
+
+## Pattern : Export des styles de composant pour réutilisation partielle (React Native)
+
+### Synthèse
+
+- **Objectif** : partager les dimensions et formes d’un composant UI vers des éléments custom qui en dérivent, sans dupliquer les valeurs.
+- **Contexte** : app React Native où des screens construisent des éléments qui doivent être "au gabarit" d’un composant existant.
+- **Quand l’utiliser** : bouton custom OAuth, container calqué sur un composant de base, etc.
+- **Quand l’éviter** : si l’écart visuel est intentionnel — dans ce cas, une constante locale est plus claire.
+
+### Analyse
+
+- **Avantages** :
+ - zéro drift silencieux : si les dimensions du composant changent, tous les éléments dérivés suivent
+ - tests de styles possibles en dehors du composant
+- **Limites / vigilance** :
+ - à n’utiliser que pour des éléments vraiment dérivés, pas comme contournement de design system
+
+### Validation
+
+- Validé le : 19-03-2026
+- Contexte technique : React Native / StyleSheet — app-alexandrie story 0.3
+
+### Implémentation
+
+```typescript
+// Button.tsx
+export const buttonStyles = StyleSheet.create({
+ base: { borderRadius: 20, height: 57 },
+ primary: { backgroundColor: colors.primary },
+});
+export function Button(...) { ... }
+
+// login.tsx — bouton OAuth au gabarit du Button
+import { buttonStyles } from ‘@/components/ui/Button’;
+
+```
+
+---
+
+
+## Pattern : Token typography par usage sémantique (React Native)
+
+### Synthèse
+
+- **Objectif** : éviter les mauvais usages de tokens typography visuellement proches mais sémantiquement distincts.
+- **Contexte** : fichier `typography.ts` dans un design system React Native.
+- **Quand l’utiliser** : dès que deux tokens partagent la même taille mais un poids différent.
+- **Quand l’éviter** : jamais — les tokens typography doivent toujours refléter l’usage, pas l’apparence.
+
+### Analyse
+
+- **Avantages** :
+ - prévient les "approximations" de tokens en code review
+ - changement de style d’usage spécifique sans régression globale
+- **Limites / vigilance** :
+ - en review : chercher les usages sans `fontWeight` explicite — c’est souvent le signe que le mauvais token a été choisi
+
+### Validation
+
+- Validé le : 19-03-2026
+- Contexte technique : React Native / TypeScript — app-alexandrie story 0.4
+
+### Implémentation
+
+```typescript
+// Bon : nommé par usage sémantique
+listItemTitle: { fontSize: 12, fontWeight: ‘600’ }, // titre d’un item de liste
+caption: { fontSize: 12, fontWeight: ‘500’ }, // info secondaire, hints
+
+// Mauvais : nommé par apparence
+mediumText12: { fontSize: 12, fontWeight: ‘500’ }, // ambigu, réutilisé à tort
+```
+
+**Règle** : `caption` (Medium) ≠ `listItemTitle` (SemiBold) même si la taille est identique. Ne jamais piocher un token "par approximation".
+
+---
+
### Principes transverses
- Un pattern = une responsabilité claire
diff --git a/10_frontend_risques_et_vigilance.md b/10_frontend_risques_et_vigilance.md
index 2c649e3..8d14451 100644
--- a/10_frontend_risques_et_vigilance.md
+++ b/10_frontend_risques_et_vigilance.md
@@ -7,7 +7,7 @@ Ce fichier recense des risques front-end susceptibles de provoquer :
- dette technique rapide,
- régressions UX/perf/a11y.
-Dernière mise à jour : 12-03-2026
+Dernière mise à jour : 20-03-2026
---
@@ -33,6 +33,12 @@ Dernière mise à jour : 12-03-2026
- [Auto-reset d’un état dégradé sur toute réponse 2xx](#risque-auto-reset-etat-degrade)
- [Refresh store en fire-and-forget après mutation](#risque-refresh-store-fire-and-forget)
- [Loading infini sur écran gated par droits distants](#risque-loading-infini-ecran-gated)
+- [Jest React Native — config node bloque les composants `.tsx`](#risque-jest-rn-config-node)
+- [Bouton OAuth présent mais handler vide après refacto UI](#risque-oauth-handler-vide)
+- [Double système d'espacement dans un monorepo Expo](#risque-double-systeme-espacement)
+- [Dimensions d'image via tokens `spacing` (React Native)](#risque-dimensions-image-via-spacing)
+- [Écran détail Expo Router — store vide en deep link / reload](#risque-store-vide-deep-link)
+- [`useEffect` fetch — guard incomplet sur les états terminaux](#risque-useeffect-guard-incomplet)
---
@@ -281,3 +287,156 @@ Dernière mise à jour : 12-03-2026
- Bloquer les retries automatiques en boucle après erreur
- Réautoriser un retry seulement via action utilisateur explicite ou nouvelle condition d’entrée
- Contexte technique : React Native / Expo / store d’entitlements — 10-03-2026
+
+---
+
+
+## Jest React Native — config node bloque les composants `.tsx`
+
+### Risques
+
+- `SyntaxError: Cannot use import statement outside a module` lors de l’import d’un barrel `.ts` qui réexporte des `.tsx`
+- Impossible d’importer des composants React Native dans les tests — JSX non transformé
+
+### Symptômes
+
+- Erreur de syntaxe inattendue au run des tests sur un fichier `.ts` qui importe un `.tsx`
+- Les tests de tokens passent mais tout test touchant un composant échoue
+
+### Bonnes pratiques / mitigations
+
+- `transform: { ‘^.+\\.ts$’: ‘ts-jest’ }` ne transforme que `.ts` — pas `.tsx`
+- **Pattern recommandé** : tester la logique pure (tokens, valeurs de style) dans `.spec.ts`, le rendu visuel dans `.spec.tsx` avec une config séparée (`@testing-library/react-native` + `babel-jest`)
+- Exporter le `StyleSheet` de chaque composant pour le tester sans JSX (voir pattern dédié dans `10_frontend_patterns_valides.md`)
+- Contexte technique : React Native / Jest / ts-jest — app-alexandrie 19-03-2026
+
+---
+
+
+## Bouton OAuth présent mais handler vide après refacto UI
+
+### Risques
+
+- L’OAuth est silencieusement cassé sur le nouvel écran — zéro erreur au démarrage, zéro crash
+- L’AC "toutes les fonctionnalités préservées" peut être coché alors que le bouton est mort
+
+### Symptômes
+
+- `