# TECH_DEBT.md — Expria Frontend > **Document de référence — Version 1.2** > Ce document recense les décisions techniques prises par pragmatisme qui devront être revisitées, les stubs temporaires, et les fonctionnalités reportées. > À mettre à jour après chaque session de développement. > > Format : chaque entrée a un identifiant (FTD-XX pour **Frontend Tech Debt**, différent des `TD-XX` backend), une priorité, un statut. > Priorités : 🔴 Critique (bloque la production) / 🟡 Important / 🟢 Mineur --- ## Politique de gestion de la dette Pour éviter que ce document devienne un cimetière de dette ignorée (le piège classique documenté dans `ONBOARDING.md`) : - **Maximum 15 FTD actives simultanément.** Au-delà, priorisation obligatoire avant d'en ajouter une nouvelle. - **Chaque release production doit résoudre au moins 1 FTD de priorité ≥ 🟡.** - **Toute FTD 🔴 doit être résolue dans la session suivante** ou bloquer la release. - **Revue trimestrielle** du document (Hermann + futur dev). - **Chaque FTD doit avoir une estimation de session** (1h, 1 jour, 3 jours) pour permettre la priorisation. --- ## 1. Dettes héritées de l'audit backend (2026-04-17) ### FTD-01 — Inconsistance des codes de validation côté backend **Priorité :** 🟡 Important **Statut :** Ouvert — dépend du backend **Estimation de session :** 2h (backend uniquement) **Description :** Le backend utilise deux codes d'erreur pour la même classe (corps de requête invalide) : - `VALIDATION_ERROR` dans `routes/simulations.ts` et `routes/corrections.ts` - `INVALID_BODY` dans `routes/plans.ts` et `routes/stripe.ts` Le frontend gère les deux de la même manière (voir `ARCHITECTURE.md` §5), mais c'est une odeur de code qui devrait être unifiée. **Action côté frontend :** aucune — on gère les deux codes. **Action côté backend :** unifier sous `VALIDATION_ERROR` (plus explicite), session dédiée à ouvrir via **TD-15** dans `expria-backend/docs/TECH_DEBT.md`. **Condition de résolution :** après résolution de TD-15 backend, simplifier le switch dans les handlers frontend pour ne traiter que `VALIDATION_ERROR`. --- ### FTD-02 — Header `X-API-Version` envoyé mais non vérifié **Priorité :** 🟡 Important **Statut :** Ouvert **Estimation de session :** 1h (backend) + 30min (frontend) **Description :** Le frontend envoie le header `X-API-Version: 1.0` sur toutes les requêtes API (cf. `ARCHITECTURE.md` §5). Le backend ne le vérifie pas actuellement — donc en pratique, aucune incompatibilité de version ne sera détectée automatiquement. **Impact :** si le backend évolue de façon breaking (ex : format de réponse de `/plans/status` modifié), le frontend peut recevoir un payload incompatible sans message d'erreur clair. Symptôme : bugs silencieux en production après un déploiement backend. **À faire :** - Backend : ajouter un middleware qui lit `X-API-Version`, le log, et retourne `HTTP 426 Upgrade Required` avec code `API_VERSION_MISMATCH` si breaking change - Frontend : gérer `API_VERSION_MISMATCH` dans `api-client.ts` → afficher un message "Une nouvelle version est disponible, veuillez rafraîchir la page" **Condition de résolution :** avant l'arrivée d'un dev externe (qui pourrait modifier le backend sans coordination). --- ### FTD-03 — Quirk `status` dans le body des erreurs de simulations/corrections **Priorité :** 🟢 Mineur **Statut :** Ouvert — dépend du backend **Estimation de session :** 1h (backend uniquement) **Description :** Les routes `POST /simulations` et `POST /corrections/ee,eo` renvoient un champ `status` dans le body JSON d'erreur, qui duplique le code HTTP : ```json { "error": true, "code": "QUOTA_REACHED", "message": "...", "status": 403 } ``` Vient du pattern `c.json(result, result.status)` où `result` contient déjà `status`. C'est ignorable côté frontend (on ne lit pas ce champ), mais c'est du bruit. **À faire côté backend :** nettoyer les objets d'erreur retournés par `simulationController` et `correctionController` pour ne pas contenir de champ `status`. Tracé dans **TD-16** à créer dans `expria-backend/docs/TECH_DEBT.md`. **Condition de résolution :** session de nettoyage backend, non urgente. --- ### FTD-20 — `GET /simulations/:id` manquant dans le backend **Priorité :** 🔴 Critique (bloque RapportPage — Sprint 3 étape 15) **Statut :** Ouvert — session backend requise **Estimation de session :** 2h (backend) **Description :** Le backend n'expose pas de route de lecture pour récupérer une simulation et son rapport après correction. `POST /corrections/ee` retourne le rapport dans sa réponse mais celui-ci n'est pas persisté côté frontend entre les navigations. `RapportPage` appelle `GET /simulations/:id` via `getReport()` + `useRapport()` — la route retourne 404 côté backend. **Contexte frontend :** `src/entities/report/api.ts` — `getReport(id)`, `src/features/simulations/hooks/useRapport.ts`, `src/features/simulations/pages/RapportPage.tsx` sont implémentés et typechecked. Ils fonctionneront dès que le backend expose la route. **À faire (session backend) :** - Ajouter `GET /simulations/:id` dans `expria-backend/src/routes/simulations.ts` - Retourner : `id`, `tache`, `mode`, `score`, `nclc`, `feedback_court`, `criteres`, `erreurs`, `modele`, `idees`, `exercices` - Appliquer `authMiddleware` + vérifier que la simulation appartient à l'utilisateur connecté - Retourner `SIMULATION_NOT_FOUND` (404) si la simulation n'existe pas ou appartient à un autre utilisateur **Condition de résolution :** après implémentation backend + test manuel RapportPage complet (Free, Standard, Premium). --- ## 2. Dettes frontend propres ### FTD-04 — Documents miroir sans automatisation de synchronisation **Priorité :** 🟢 Mineur **Statut :** Ouvert — accepté par design (voir ADR 004) **Estimation de session :** 1 jour (mise en place monorepo) **Description :** Les documents `PLANS_TARIFAIRES.md`, `PARCOURS_UTILISATEURS.md`, et le fichier de code `src/entities/user/access.ts` existent à l'identique dans les deux dépôts (frontend et backend). La synchronisation est manuelle — si un changement est fait dans un dépôt sans être répercuté dans l'autre, divergence silencieuse. **Mitigation actuelle :** - Règle G de `DEVELOPMENT_PRINCIPLES.md` (modifications simultanées dans le même commit logique) - Commentaire `// SOURCE OF TRUTH:` en tête de `access.ts` - Tests de parité dans `src/entities/user/__tests__/access.test.ts` (calqués sur les tests backend) **À faire si la dette devient trop coûteuse :** - Migrer vers un monorepo pnpm workspaces avec package partagé `@expria/types-and-access` - OU ajouter un script CI qui vérifie que le hash SHA-256 de `access.ts` matche entre les deux dépôts **Condition de résolution :** après 3+ mois de production stable, ou si une divergence silencieuse cause un bug. --- ### FTD-05 — Ancien scaffold frontend possiblement caduc **Priorité :** 🟡 Important **Statut :** Ouvert — diagnostic en cours (session Claude Code) **Estimation de session :** variable selon diagnostic **Description :** Un scaffold frontend a été créé au démarrage du projet (fin mars 2026 ou début avril 2026), avant que les décisions architecturales récentes (entities/features/shared, auth-client/api-client découplés, pas de Zustand, etc.) ne soient prises. Le contenu actuel de `D:\expria-frontend\` peut donc contenir des fichiers qui ne matchent plus l'architecture cible. **À faire :** session Claude Code (première session frontend de la V2) qui fait un état des lieux complet et propose une stratégie (clean slate / refactor progressif / adaptation en place). Cf. prompt de session dans l'historique de conversation Claude AI du 2026-04-17. **Condition de résolution :** fin du Sprint 0 (scaffold conforme à `ARCHITECTURE.md`). --- ### FTD-10 — Semgrep non intégré en CI **Priorité :** 🟢 Mineur **Statut :** Reporté — après MVP **Estimation de session :** 2h **Description :** `ARCHITECTURE.md` §CI mentionne `semgrep scan (via plugin)` dans le workflow CI cible. L'étape 10 du Sprint 0 n'a intégré que `lint`, `format:check`, `typecheck`, `test`, `npm audit --audit-level=high` — Semgrep a été volontairement différé pour respecter la règle de scope (max 2-3 fichiers par étape). **Impact actuel :** `npm audit` couvre les vulnérabilités des dépendances npm, mais aucune analyse statique de sécurité (SAST) n'est faite sur le code custom du projet. Des patterns dangereux (`eval`, `innerHTML` sans DOMPurify, secrets en dur, etc.) passeraient inaperçus en CI. **À faire :** - Ajouter un step Semgrep au workflow `.github/workflows/ci.yml` - Utiliser les rulesets `auto` + `r2c-security-audit` + `r2c-ci` - Configurer la sortie pour bloquer sur sévérité ERROR uniquement - Documenter dans SEC-08 de `SECURITY.md` **Condition de résolution :** avant l'arrivée d'un dev externe (même raisonnement que FTD-02). --- ### FTD-14 — Anti-FOUC thème : script inline manquant dans `
` **Priorité :** 🟡 Important **Statut :** Ouvert — à faire avant déploiement production **Estimation de session :** 30 min **Description :** Le `ThemeProvider` applique la classe `.dark` sur `` après l'hydratation React (`useEffect`). Entre le premier paint du navigateur et l'exécution de React, la page s'affiche brièvement en mode clair même si l'utilisateur a choisi le mode sombre — c'est le FOUC (Flash Of Unstyled Content). **Fix :** ajouter un script inline bloquant dans le `` de `index.html` qui lit `localStorage.getItem('expria-theme')` (et `prefers-color-scheme` en fallback) et applique `.dark` sur `document.documentElement` avant le premier paint. Ce script doit être minifié et inliné (non-async, non-defer) pour garantir l'exécution avant le CSS. ```html ``` **Impact actuel :** visible uniquement pour les utilisateurs en mode sombre — bref flash de fond clair au chargement. Acceptable en dev, indésirable en production. **Condition de résolution :** avant la première mise en production (Sprint 1 ou avant). --- ### FTD-15 — Option `'system'` manquante dans ThemeProvider **Priorité :** 🟢 Mineur **Statut :** Reporté — après MVP **Estimation de session :** 2h **Description :** Le `ThemeProvider` est bi-state (`'light' | 'dark'`). L'option `'system'` (qui suit `prefers-color-scheme` en temps réel via `MediaQueryList.addEventListener`) a été volontairement différée (décision Sprint 0.5). **À faire :** - Étendre le type `Theme` à `'light' | 'dark' | 'system'` - Dans `ThemeProvider`, si `theme === 'system'` : écouter `matchMedia('(prefers-color-scheme: dark)')` et appliquer/retirer `.dark` dynamiquement - `ThemeToggle` : cycle light → dark → system (ou un sélecteur 3 états) - Mettre à jour `getInitialTheme()` pour retourner `'system'` si aucune préférence stockée **Condition de résolution :** après MVP — confort utilisateur, pas bloquant. --- > FTD-17, FTD-18, FTD-19 résolus au Sprint 3.5 (2026-04-22) — voir §5 Historique des résolutions. --- ## 3. Fonctionnalités reportées ### FTD-06 — AudioWorklet au lieu de ScriptProcessorNode (T2 Live) **Priorité :** 🟢 Mineur **Statut :** Reporté à après le lancement MVP **Estimation de session :** 1 jour **Description :** Hérité du backend (TD-09). Côté frontend, le traitement audio pour la T2 Live (capture PCM 16kHz) devra probablement utiliser `AudioWorklet` au lieu de `ScriptProcessorNode` qui est déprécié. **Impact actuel :** fonctionne avec warnings dans la console. Peut poser problème sur certains navigateurs futurs. **À faire :** session dédiée après le lancement MVP, pour migrer le pipeline audio vers AudioWorklet. **Condition de résolution :** après 30 jours de production stable. --- ### FTD-07 — Sentry non intégré **Priorité :** 🟡 Important **Statut :** Planifié — après MVP **Estimation de session :** 3h **Description :** Le monitoring frontend (erreurs JS, performances, sessions) n'est pas encore en place. Sans Sentry (ou équivalent), les bugs en production ne remontent pas — on les découvre uniquement si un utilisateur prend la peine de les signaler. **À faire :** - Créer un compte Sentry (tier gratuit suffit pour démarrer) - Ajouter `@sentry/react` au projet - Intégrer dans `src/app/providers.tsx` - Ajouter `VITE_SENTRY_DSN` dans les variables d'environnement - Configurer le filtrage des données sensibles (JWT, emails) - Mettre à jour SEC-11 dans `SECURITY.md` **Condition de résolution :** avant la première vague d'utilisateurs post-MVP (30 jours après lancement). --- ### FTD-08 — Tests E2E non implémentés **Priorité :** 🟢 Mineur **Statut :** Reporté — accepté par design **Estimation de session :** 2 jours (Playwright setup) **Description :** Actuellement, les tests de bout en bout sont manuels (via `GOLDEN_DATASET.md`). Une automatisation avec Playwright permettrait de détecter les régressions UI sans effort humain. **À faire :** session Playwright setup après MVP, pour automatiser au minimum les 10 scénarios du Groupe Z (smoke test). **Condition de résolution :** quand la maintenance manuelle du Golden Dataset devient trop chronophage. --- ### FTD-21 — Persistance session simulation **Priorité :** 🔴 Critique **Statut :** Partiellement résolu — `/simulation/ee` ✅ (2026-04-21) **Pages concernées par ordre de priorité :** ✅ **`/simulation/ee`** (résolu 2026-04-21) - Autosave contenu toutes les 30 s (`useAutosave`) - Save on `beforeunload` - Reprise au refresh via `localStorage` (`expria_simulation_id`) + `GET /simulations/:id` - `PATCH /simulations/:id/contenu` + `PATCH /simulations/:id/sujet` (Option C) - `getById` tolère `rapport=null` (Option A) - `RapportPage` redirige vers `/simulation/ee` si simulation en cours 🟡 **`/simulation/eo`** (Sprint 4 — ouvert) - Identique EE + état audio/enregistrement 🟡 **`/examen`** (Sprint 7 — ouvert) - Autosave critique — timer inarrêtable + 3 tâches - Crash pendant examen = perte totale 🟢 **`/sujets`** (inclus dans la résolution EE) - `localStorage simulation_id` suffit - Pas d'autosave (pas de données saisies) ❌ **Pas nécessaire :** `/dashboard`, `/rapport/:id`, `/historique`, `/progression` **Résolution EE livrée (2026-04-21) :** Backend : - `simulationController.create` persiste `sujet_id` à la création - `getById` retourne `SimulationState` (tolère `rapport=null` pour resume) - `autosaveContenu` + `updateSujet` controllers (refuse si `rapport !== null`) - Routes `PATCH /simulations/:id/contenu` + `PATCH /simulations/:id/sujet` - CORS : `allowMethods` étendu à PATCH/PUT/DELETE Frontend : - `useAutosave` : debounce 30 s + `beforeunload` flush + dedup par contenu - `SimulationForm` : hydrate `initialContenu`, affiche "Sauvegardé à HH:MM" - `SimulationFlowProvider` : hydratation au montage depuis `localStorage` → restaure step `task-selected` si rapport null, nettoie sinon - `getReport` délègue à `getSimulationState` et throw `REPORT_NOT_READY` si rapport null **Condition de résolution complète :** intégration EO (Sprint 4) + examen (Sprint 7). --- ## 4. Tests à renforcer ### FTD-09 — Tests de la state machine T2 Live non implémentés **Priorité :** 🟡 Important **Statut :** Planifié — à créer au Sprint 2.5 **Estimation de session :** 3h **Description :** La state machine T2 Live (`src/features/t2-live/state/t2-machine.ts`) n'existe pas encore. Quand elle sera créée, elle devra être testée de manière exhaustive (6+ tests couvrant les transitions d'états et les cas d'erreur). **À faire au Sprint 2.5 (spike T2 Live) :** - Créer `t2-machine.test.ts` avec tests des transitions : idle → connecting, connecting → listening, listening ↔ speaking, * → error, * → ended - Tests des messages d'erreur (close code 4001, 4003, autre) **Condition de résolution :** fin Sprint 2.5. --- ### FTD-12 — Tests automatisés manquants pour `api-client.ts` **Priorité :** 🟡 Important **Statut :** Ouvert — à faire avant intégration des features critiques **Estimation de session :** 3h **Description :** Le wrapper `apiFetch` dans `src/shared/lib/api-client.ts` (créé à l'étape 7b du Sprint 0) contient une logique critique non couverte par des tests automatisés : timeout via `AbortSignal.timeout`, retry avec backoff exponentiel, stratégie d'idempotence (retry `GET`/`HEAD`/`PUT`/`DELETE` uniquement), parsing des erreurs backend (`ApiError`) vs frontend (`ClientError`), construction des headers (`Authorization`, `X-API-Version`, `Content-Type`). **Impact actuel :** toute régression sur ce fichier (oubli d'un header, mauvais parsing d'une erreur, boucle de retry infinie sur un edge case) passera inaperçue jusqu'aux tests manuels ou à un bug en production. **À faire :** - Créer `src/shared/lib/__tests__/api-client.test.ts` - Mocker globalement `fetch` via `vi.fn()` - Couvrir : - Succès 2xx → retourne le payload parsé - Erreur 4xx `ApiError` bien parsée → throw conforme - Erreur 4xx body non-JSON → fallback `INTERNAL_ERROR` - Erreur 5xx avec retry → max 2 retries puis throw - Timeout → `ClientError` code `TIMEOUT` - Erreur réseau (fetch reject) → `ClientError` code `NETWORK_ERROR` + retry - Parsing succès JSON invalide → `ClientError` code `PARSE_ERROR` - Retry désactivé sur POST/PATCH par défaut (non-idempotent) - Bearer omis sur `/health` - Bearer injecté avec token valide depuis `auth-client` - Objectif : ≥ 10 tests, couverture complète des branches **Condition de résolution :** avant l'intégration des hooks TanStack Query sur des features critiques (dashboard, simulations, auth). --- ## 5. Historique des résolutions | ID | Description | Résolu le | Comment | |---|---|---|---| | FTD-11 | `@theme` Tailwind 4 non défini — palette et typographie absentes | 2026-04-18 | Résolu au Sprint 0.5 (design system). Palette Direction H complète (canvas/surface/ink/expria/deep/semantic) + typo Plus Jakarta Sans définis dans `src/index.css` via `@theme {}` et `.dark {}`. shadcn/ui remappé sur ces tokens. Règle L ajoutée dans `DEVELOPMENT_PRINCIPLES.md` pour garantir l'usage exclusif des tokens. | | FTD-13 | Incompatibilité Vitest 3 / Vite 8 (conflit de types `Plugin