416 lines
28 KiB
Markdown
416 lines
28 KiB
Markdown
# TECH_DEBT.md — Expria Frontend
|
||
|
||
> **Document de référence — Version 1.16**
|
||
> 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.
|
||
|
||
---
|
||
|
||
## 2. Dettes frontend propres
|
||
|
||
### 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 `<head>`
|
||
**Priorité :** 🟡 Important
|
||
**Statut :** Ouvert — à faire avant déploiement production
|
||
**Estimation de session :** 30 min
|
||
**Description :** Le `ThemeProvider` applique la classe `.dark` sur `<html>` 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 `<head>` 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
|
||
<script>
|
||
(function(){var t=localStorage.getItem('expria-theme');
|
||
if(t==='dark'||(t!=='light'&&matchMedia('(prefers-color-scheme:dark)').matches))
|
||
document.documentElement.classList.add('dark')})()
|
||
</script>
|
||
```
|
||
|
||
**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-17, FTD-18, FTD-19 résolus au Sprint 3.5 (2026-04-22) — voir §5 Historique des résolutions.
|
||
|
||
---
|
||
|
||
### FTD-24 — Pas de polling automatique pour exercices / modèle `pending`
|
||
**Priorité :** 🟡 Important
|
||
**Statut :** Ouvert — accepté au Sprint 3.6b, refresh manuel requis entre-temps
|
||
**Estimation de session :** 2h
|
||
**Description :** Après soumission d'une correction EE, le backend génère la correction en bloquant (jusqu'à 45 s), puis retourne 200 dès que la correction est prête. Les jobs `modele` et `exercices` (fire-and-forget côté backend) peuvent mettre 10-30 s supplémentaires après la réponse HTTP. Pendant ce temps, `exercices_status` et `modele_status` valent `'pending'` côté `GET /simulations/:id`. Côté frontend, `RapportPage` affiche un `JobStatusFallback` invitant l'utilisateur à **rafraîchir manuellement** la page pour voir les résultats.
|
||
|
||
**Impact UX :** l'utilisateur voit le rapport principal immédiatement, mais doit recharger pour voir ses exercices + production modèle. Expérience acceptable en MVP mais sous-optimale.
|
||
|
||
**À faire :**
|
||
- Hook `useRapport` : déclencher un polling automatique via TanStack Query `refetchInterval: 3000` si `exercices_status === 'pending' || modele_status === 'pending'`.
|
||
- Arrêt du polling dès que les deux statuts sortent de `'pending'` (ready ou error).
|
||
- Afficher un indicateur visuel discret pendant le polling actif (petit spinner dans JobStatusFallback).
|
||
- Timeout de polling : max 2 minutes → message "La génération prend plus de temps que prévu" + bouton Réessayer.
|
||
|
||
**Lien avec TD-15 backend :** si le process backend redémarre pendant un job, le statut reste indéfiniment `'pending'`. Le timeout frontend atténue ce problème côté UX (on arrête de poller après 2 min).
|
||
|
||
**Condition de résolution :** après Sprint 3.6c (patterns) si la patience utilisateur devient un frein.
|
||
|
||
---
|
||
|
||
### FTD-23 — `useAutosave` continue après correction → 400 VALIDATION_ERROR
|
||
**Priorité :** 🟡 Important
|
||
**Statut :** Ouvert — pré-existant au Sprint 3.6a, détecté lors des tests manuels 3.6a
|
||
**Estimation de session :** 30 min
|
||
**Description :** Le hook `useAutosave` (cf. `src/features/simulations/hooks/useAutosave.ts`) peut déclencher un `PATCH /simulations/:id/contenu` après que la correction a été persistée (colonne `rapport !== null`). Le backend refuse alors avec `400 VALIDATION_ERROR` message « Cette simulation a déjà été corrigée. » (cf. `simulationController.autosaveContenu` backend lignes 248-255).
|
||
|
||
**Scénario déclencheur :**
|
||
1. L'utilisateur soumet sa production → `rapport` persisté côté backend.
|
||
2. `SimulationForm` passe `step` à `'done'`, mais :
|
||
- Le timer d'autosave debouncé (30 s) peut encore fire après cette transition si le debounce n'est pas clear.
|
||
- Un `beforeunload` handler peut déclencher un `flush()` final même une fois la correction reçue.
|
||
3. `useAutosave.enabled` est calculé comme `!isSubmitting` dans `SimulationForm` — il redevient `true` après la correction (quand `isSubmitting` repasse à `false`).
|
||
|
||
**À faire :**
|
||
- Propager `enabled = !isSubmitting && step !== 'done' && step !== 'correcting'` depuis `SimulationForm`
|
||
- OU : au montage, quand `rapport` devient non null après correction, clear le timeout debouncé et retirer le handler `beforeunload` immédiatement.
|
||
- Ajouter un test regression dans `useAutosave.test.ts` qui vérifie qu'aucun `autosaveContenu` n'est appelé après `step='done'`.
|
||
|
||
**Impact actuel :** erreur 400 dans les DevTools Network uniquement (pas d'impact UX — le texte est déjà corrigé, la sauvegarde n'est plus nécessaire). Pollue les logs frontend et backend.
|
||
|
||
**Condition de résolution :** session dédiée — ne bloque pas le Sprint 3.6b.
|
||
|
||
---
|
||
|
||
### FTD-25 — Mise à jour ARCHITECTURE.md §3 (arborescence réelle)
|
||
**Priorité :** 🟢 Mineur
|
||
**Statut :** Ouvert
|
||
**Estimation de session :** 1h
|
||
**Description :** ARCHITECTURE.md §3 ne liste pas `entities/patterns`, `features/historique`, `features/progression`, `features/design-system` (ajoutés aux Sprints 3.6c et 3.7). Les composants layout (`AppLayout`, `Sidebar`, `MobileHeader`, `BottomNav`, `MaintenancePage`) sont dans `app/` alors que §3 ne prévoit que `providers`, `router`, `main` dans ce dossier.
|
||
|
||
**À faire :**
|
||
- Mettre à jour ARCHITECTURE.md §3 pour refléter l'arborescence réelle.
|
||
- Formaliser `app/` comme contenant entry points + composants layout de la coquille OU déplacer vers `shared/components/layout/`.
|
||
|
||
**Condition de résolution :** ARCHITECTURE.md §3 reflète l'arborescence réelle.
|
||
|
||
---
|
||
|
||
### FTD-26 — Clarifier cohabitation `shared/ui/` vs `shared/components/ui/`
|
||
**Priorité :** 🟡 Important
|
||
**Statut :** Ouvert
|
||
**Estimation de session :** 2h
|
||
**Description :** Deux conventions UI cohabitent sans documentation :
|
||
- `src/shared/ui/{Button,Card,Badge}.tsx` (PascalCase) — wrappers Expria, 40+ imports dans les features.
|
||
- `src/shared/components/ui/{button,dialog,input,…}.tsx` (kebab-case) — primitives shadcn/ui, 7 fichiers consommateurs.
|
||
|
||
Risque : confusion pour un futur dev sur quel composant utiliser.
|
||
|
||
**À faire :** documenter la convention dans ARCHITECTURE.md (distinction wrappers Expria / primitives shadcn) **OU** regrouper sous un seul dossier (ex. `shared/components/ui/primitives/` + `shared/components/ui/expria/`).
|
||
|
||
**Condition de résolution :** un seul pattern documenté et appliqué.
|
||
|
||
---
|
||
|
||
### FTD-27 — CI GitHub Actions pour `expria-backend`
|
||
**Priorité :** 🔴 Critique
|
||
**Statut :** Ouvert
|
||
**Estimation de session :** 2h
|
||
**Description :** Le backend n'a aucune CI. Aucun typecheck, test, ni audit ne s'exécute automatiquement. Le backend gère Stripe, JWT, permissions — c'est le dépôt le plus sensible.
|
||
|
||
**À faire :** créer `.github/workflows/ci.yml` dans `expria-backend` avec jobs :
|
||
- `typecheck` (`tsc --noEmit`)
|
||
- `test` (`vitest run`)
|
||
- `audit` (`npm audit --audit-level=high`)
|
||
|
||
**Condition de résolution :** CI verte sur le prochain push backend.
|
||
|
||
---
|
||
|
||
### FTD-28 — Semgrep dans CI frontend + backend
|
||
**Priorité :** 🔴 Critique
|
||
**Statut :** Ouvert
|
||
**Estimation de session :** 1h
|
||
**Description :** `SECURITY.md` §4.3 exige `semgrep scan` en CI. Absent des deux dépôts. SEC-05 (XSS rapports IA) classé critique nécessite un scan SAST.
|
||
|
||
**À faire :** ajouter un step `semgrep scan --config=auto` dans les CI des deux dépôts. Bloquer sur sévérité ERROR uniquement.
|
||
|
||
**Condition de résolution :** Semgrep exécuté et vert sur les deux CI.
|
||
|
||
---
|
||
|
||
## 3. Fonctionnalités reportées
|
||
|
||
### 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-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).
|
||
|
||
---
|
||
|
||
## 3bis. Backlog gelé — post-MVP
|
||
|
||
> Ces FTDs sont volontairement gelées : elles concernent des fonctionnalités non encore livrées (T2 Live, tests E2E) ou du confort utilisateur non bloquant (option `'system'` thème). Elles **ne comptent pas dans le cap de 15 FTD actives** et seront réactivées quand leur sprint arrive ou quand la condition de déblocage (post-MVP) est atteinte.
|
||
|
||
### FTD-06 — AudioWorklet au lieu de ScriptProcessorNode (T2 Live)
|
||
**Priorité :** 🟢 Mineur
|
||
**Statut :** Gelé — post-MVP (T2 Live non encore implémenté)
|
||
**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-08 — Tests E2E non implémentés
|
||
**Priorité :** 🟢 Mineur
|
||
**Statut :** Gelé — post-MVP (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-15 — Option `'system'` manquante dans ThemeProvider
|
||
**Priorité :** 🟢 Mineur
|
||
**Statut :** Gelé — post-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.
|
||
|
||
---
|
||
|
||
## 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<any>` entre le Vite 8 top-level avec Rolldown et le Vite 7 pinné de Vitest 3.2.4 ; `npm run build` cassé) | 2026-04-17 | Résolu par upgrade Vitest `3.2.4 → 4.1.4` (et `@vitest/coverage-v8` idem) à l'étape 12-bis du Sprint 0. Vitest 4.x supporte nativement Vite 8 Rolldown. Correctif complémentaire : script `typecheck` passé de `tsc --noEmit -p tsconfig.app.json` à `tsc -b --noEmit` pour couvrir aussi `tsconfig.node.json` (d'où `vite.config.ts`) et éviter qu'un bug similaire échappe à la CI. |
|
||
| FTD-16 | `VITE_MAINTENANCE_MODE` non lu dans le code — la variable d'env était dans `env.ts` mais jamais consommée | 2026-04-18 | Résolu au Sprint 1 étape 6. Ajout de `isMaintenanceMode` dans `src/shared/config/env.ts` et garde dans `src/app/main.tsx` : `isMaintenanceMode ? <MaintenancePage /> : <Providers />`. `MaintenancePage` est statique (aucun provider requis), tokens Direction H exclusivement. |
|
||
| FTD-22 | Code orphelin suite à la refonte UX `/sujets` (2026-04-21) — composant `SujetSelector` et helper `selectSujet` plus référencés après bascule dropdown → page dédiée | 2026-04-23 | Résolution complète. `SujetSelector` + `selectSujet` supprimés. Éléments conservés (`choosing-subject`, `goToSubjectPicker`) sont activement utilisés par `SimulationFlowProvider` et `SimulationForm` — ce n'est plus de la dette. |
|
||
| FTD-20 | `GET /simulations/:id` manquant dans le backend | 2026-04-22 | Implémenté au Sprint 3.6a (backend) — route complète avec auth, owner check, `REPORT_NOT_READY`. Consommé par `RapportPage` et `useAutosave`. |
|
||
| FTD-04 | Documents miroir sans automatisation de synchronisation | 2026-04-23 | Risque accepté par design (ADR 004). Mitigation en place (Règle G, commentaire `SOURCE OF TRUTH`, tests de parité). Condition de ré-ouverture : si une divergence silencieuse cause 2+ bugs en production. |
|
||
| FTD-05 | Ancien scaffold frontend possiblement caduc | 2026-04-23 | Audit Claude Code complet — aucun résidu scaffold Vite, aucun fichier orphelin, règles critiques (D, E, F, G, J + ADR 003/005) respectées. Désalignements documentaires traités via FTD-25 et FTD-26. |
|
||
| FTD-29 | `.github/dependabot.yml` dans les 2 dépôts | 2026-04-23 | Fichier créé dans expria-frontend et expria-backend. Ecosystem npm, weekly, limit 10 PRs. Dependabot alerts + security updates activés via UI GitHub. |
|
||
| FTD-17 | Clé `['plan']` dupliquée entre features (`usePlan`, `SimulationPage`, `RapportPage`) | 2026-04-22 | Résolu au Sprint 3.5. Création de `src/entities/user/query-keys.ts` (constantes pures, aucun import runtime) exportant `PLAN_QUERY_KEY = ['plan'] as const`. `features/dashboard/hooks/usePlan.ts` l'importe et le re-exporte pour conserver la rétro-compatibilité de l'import `PLAN_QUERY_KEY`. `SimulationPage.tsx` et `RapportPage.tsx` remplacent leur `useQuery` inline par le hook `usePlan()` — dédup totale de la clé et de la config staleTime. |
|
||
| FTD-18 | SimulationForm utilise encore le shadcn Button au lieu de la primitive `@/shared/ui/Button` | 2026-04-22 | Résolu au Sprint 3.5. Remplacement de l'import `@/shared/components/ui/button` par `@/shared/ui/Button` dans `SimulationForm.tsx`. Aucun variant à adapter (usage du Button sans prop `variant` → `primary` par défaut dans les deux implémentations). Les 7 autres consommateurs shadcn (`Login/RegisterPage`, `PaywallBanner`, `DesignSystemPage`, `ThemeToggle`, `dialog.tsx`) restent hors scope de cette FTD. |
|
||
| FTD-19 | Token `--shadow-focus` absent de `src/index.css` | 2026-04-22 | Résolu au Sprint 3.5. Ajout de `--shadow-focus: 0 0 0 3px rgba(27, 79, 216, 0.18)` dans `@theme {}` (valeur conforme à `DESIGN_SYSTEM.md §2`) et `--shadow-focus: 0 0 0 3px rgba(91, 127, 255, 0.32)` dans `.dark {}` (recalculé sur la teinte expria dark `#5B7FFF`). Tailwind 4 génère automatiquement l'utility `shadow-focus`. Migration de 5 occurrences `ring-2 ring-expria/20` → `shadow-focus` dans `Button.tsx`, `Card.tsx`, `SimulationForm.tsx` (×3), `SpecialCharsKeyboard.tsx`. Factorisation bonus : className dupliquée des boutons secondaires de `SimulationForm` extraite en const `secondaryActionBtn`. |
|
||
|
||
---
|
||
|
||
## 6. Historique de ce document
|
||
|
||
| Version | Date | Changements |
|
||
|---|---|---|
|
||
| 1.0 | 2026-04-17 | Création initiale avec 9 FTD identifiées depuis l'audit backend et les décisions d'architecture |
|
||
| 1.1 | 2026-04-17 | Ajout FTD-10 (Semgrep CI), FTD-11 (`@theme` Tailwind 4), FTD-12 (tests `api-client`) suite à l'étape 11 du Sprint 0 |
|
||
| 1.2 | 2026-04-17 | Ajout FTD-13 résolu (incompatibilité Vitest 3 / Vite 8) suite à l'étape 12-bis du Sprint 0 |
|
||
| 1.3 | 2026-04-18 | FTD-11 résolu (design system Sprint 0.5) ; ajout FTD-14 (anti-FOUC), FTD-15 (option 'system' thème) |
|
||
| 1.4 | 2026-04-18 | FTD-16 résolu (VITE_MAINTENANCE_MODE implémenté — Sprint 1 étape 6) |
|
||
| 1.5 | 2026-04-19 | Ajout FTD-17 (clé ['plan'] dupliquée entre features — Sprint 3 étape 14) |
|
||
| 1.6 | 2026-04-20 | Ajout FTD-18 (SimulationForm shadcn Button — Sprint 0.5 bis D2) ; ajout FTD-19 (token --shadow-focus manquant — Sprint 0.5 bis D2) |
|
||
| 1.7 | 2026-04-20 | Ajout FTD-20 🔴 (GET /simulations/:id manquant backend — bloque RapportPage Sprint 3 étape 15) |
|
||
| 1.8 | 2026-04-20 | Ajout FTD-21 🔴 (persistance session simulation — prod + sujet perdus au refresh, session dédiée après G1-G5) |
|
||
| 1.9 | 2026-04-21 | FTD-22 résolu partiellement (nettoyage code orphelin refonte `/sujets` — `SujetSelector` + `selectSujet` supprimés ; `choosing-subject` + `goToSubjectPicker` conservés) |
|
||
| 1.10 | 2026-04-21 | FTD-21 résolu partiellement pour `/simulation/ee` (autosave 30 s + `beforeunload` + reprise via `localStorage` + `PATCH /:id/contenu` + `PATCH /:id/sujet` + `getById` tolère `rapport=null`) ; EO + examen restent ouverts |
|
||
| 1.11 | 2026-04-22 | Sprint 3.5 Clean — FTD-17, FTD-18, FTD-19 résolus. 15 FTD actives restantes (cap de 15 respecté) |
|
||
| 1.12 | 2026-04-22 | Sprint 3.6a — Ajout FTD-23 🟡 (useAutosave fire après correction). 16 FTD actives → cap de 15 dépassé temporairement, à revoir au prochain clean. |
|
||
| 1.13 | 2026-04-22 | Sprint 3.6b — Ajout FTD-24 🟡 (polling auto exercices/modèle pending). 17 FTD actives → cap dépassé, un clean 3.6.5 devra résoudre FTD-23/24 ensemble. |
|
||
| 1.14 | 2026-04-23 | Triage : FTD-04, FTD-05, FTD-20, FTD-22 fermées. FTD-25, FTD-26 ajoutées. 15 FTD actives (cap respecté). |
|
||
| 1.15 | 2026-04-23 | Réorg sécurité : FTD-06, FTD-08, FTD-15 gelées (backlog post-MVP). FTD-27 🔴, FTD-28 🔴, FTD-29 🟡 ajoutées (sécurité). 15 FTD actives (cap respecté). |
|
||
| 1.16 | 2026-04-23 | FTD-29 fermée (Dependabot config). 14 FTD actives. |
|