diff --git a/docs/TECH_DEBT.md b/docs/TECH_DEBT.md index dbf4e99..edc8a4d 100644 --- a/docs/TECH_DEBT.md +++ b/docs/TECH_DEBT.md @@ -1,134 +1,242 @@ -# TECH_DEBT.md — Expria / Coach TCF Canada +# TECH_DEBT.md — Expria Frontend -> **Document de référence — Version 1.0** -> 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. +> **Document de référence — Version 1.1** +> 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 (TD-XX), une priorité, et un statut. +> 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 --- -## 1. Stubs temporaires — à compléter +## Politique de gestion de la dette -### TD-01 — src/lib/supabase.ts (backend) -**Priorité :** 🔴 Critique -**Statut :** Ouvert -**Description :** Client Supabase créé comme stub. Fonctionne en développement avec les variables d'environnement mais n'a pas de gestion d'erreur robuste si `SUPABASE_URL` ou `SUPABASE_SERVICE_ROLE_KEY` sont absentes. -**À faire :** Ajouter une validation au démarrage — si les variables manquent, le serveur refuse de démarrer avec un message clair. -**Session concernée :** Initialisation backend +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. --- -### TD-02 — src/lib/planController.ts (backend) +## 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 -**Description :** Stub créé pour permettre les tests de `updateUserPlan`. La vraie implémentation (mise à jour Supabase + gestion Stripe) n'est pas encore codée. -**À faire :** Implémenter lors de la session Stripe (POST /stripe/webhook). -**Session concernée :** Tests automatisés +**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). --- -### TD-03 — src/lib/stripe.ts (backend) -**Priorité :** 🟡 Important -**Statut :** Ouvert -**Description :** Stub créé pour permettre les tests de `verifyStripeWebhook` et `calculateProrata`. La vraie implémentation Stripe n'est pas encore codée. -**À faire :** Implémenter lors de la session Stripe. -**Session concernée :** Tests automatisés - ---- - -## 2. Décisions pragmatiques — à revisiter - -### TD-04 — Déploiement manuel (frontend + backend) +### FTD-03 — Quirk `status` dans le body des erreurs de simulations/corrections **Priorité :** 🟢 Mineur -**Statut :** Ouvert — accepté jusqu'aux premiers revenus -**Description :** Cloudflare Pages et Render ne supportent pas l'auto-deploy depuis Codeberg. Le déploiement est manuel (CLI + dashboard). -**À faire :** Migrer vers VPS Hetzner + Coolify pour restaurer l'auto-deploy. Voir ARCHITECTURE.md §9 Phase 2. -**Condition de résolution :** Quand Expria génère ses premiers revenus réguliers. +**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. --- -### TD-05 — Comptes de test avec emails @gmail.com +## 2. Dettes frontend propres + +### FTD-04 — Documents miroir sans automatisation de synchronisation **Priorité :** 🟢 Mineur -**Statut :** Ouvert -**Description :** Les comptes de test utilisent `@gmail.com` au lieu de `@expria.local` prévu dans TEST_ENVIRONMENT.md. Raison : Supabase bloque la création d'utilisateurs avec des domaines non standards via l'API admin, et le dashboard est inaccessible depuis la Russie. -**Emails actuels :** -- `test.free@gmail.com` -- `test.standard@gmail.com` -- `test.premium@gmail.com` -- `test.quota@gmail.com` -**À faire :** Mettre à jour TEST_ENVIRONMENT.md pour refléter les vrais emails. Vérifier que la validation `@expria.local` dans le middleware n'est pas implémentée (elle ne l'est pas). +**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. --- -### TD-06 — Pas de migration SQL versionnée pour les tables initiales +### FTD-05 — Ancien scaffold frontend possiblement caduc **Priorité :** 🟡 Important -**Statut :** Ouvert -**Description :** Les tables `profiles` et `productions` ont été créées directement via SQL Editor, sans fichier de migration dans `supabase/migrations/`. Viole la Règle F de DEVELOPMENT_PRINCIPLES.md. -**À faire :** Créer les fichiers de migration correspondants : -- `supabase/migrations/001_create_profiles.sql` -- `supabase/migrations/002_create_productions.sql` -- `supabase/migrations/003_create_test_accounts.sql` -**Impact :** Si la base doit être recréée (nouveau projet Supabase), les migrations permettent de tout reconstruire en une commande. +**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`). --- -### TD-07 — Ancien projet Supabase partagé -**Priorité :** 🟡 Important -**Statut :** Ouvert — accepté temporairement -**Description :** Le nouveau projet Expria V2 utilise la même base Supabase que l'ancien projet (en maintenance). Les anciennes tables ont été remplacées mais d'autres tables de l'ancien projet subsistent (`sujets`, `eo_t2_results`, `payment_transactions`, etc.). -**À faire :** Nettoyer les tables inutilisées quand V2 est stable en production. -**Tables à évaluer :** `anon_rate_limits`, `contact_submissions`, `eo_t2_results`, `events`, `payment_transactions`, `sujets`, `waitlist` -**Condition de résolution :** Après 30 jours de production stable de V2. +### 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-11 — `@theme` Tailwind 4 non défini +**Priorité :** 🟢 Mineur +**Statut :** Ouvert — à faire dans session design system +**Estimation de session :** 1 jour (palette + typo + itérations design) +**Description :** `src/index.css` a été nettoyé à l'étape 11 du Sprint 0 et réduit à la seule ligne `@import 'tailwindcss';`. L'ADR 006 (§Configuration Tailwind 4) décrit le bloc `@theme { ... }` comme le mécanisme officiel de configuration Tailwind 4 (CSS-first) : + +```css +@theme { + --color-primary: #1B4FD8; + --font-sans: 'Plus Jakarta Sans', system-ui, sans-serif; +} +``` + +La palette brand Expria et la typographie ne sont pas encore décidées, donc `@theme` est volontairement absent pour ne pas poser de valeurs placeholder qu'il faudrait repasser plus tard. + +**Impact actuel :** les composants utilisent les couleurs Tailwind par défaut (`bg-slate-*`, `text-gray-*`). Visuellement cohérent mais pas brand. + +**À faire au Sprint 1 (design system) :** +- Définir la palette brand Expria (primary, secondary, neutrals, danger, success) +- Choisir + installer la typo (Plus Jakarta Sans ou autre) via `` dans `index.html` ou `@import` dans `index.css` +- Ajouter le bloc `@theme` dans `src/index.css` +- Mettre à jour ADR 006 avec les valeurs retenues + +**Condition de résolution :** Sprint 1 — session dédiée au design system. --- ## 3. Fonctionnalités reportées -### TD-08 — Phonologie T2 EO à 0 +### 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 :** Ouvert -**Description :** L'évaluation de la phonologie pour la T2 EO live est temporairement à 0 (non évaluée). L'évaluation se fait sur 4 critères au lieu de 5. -**Raison :** La T2 live utilise un transcript texte — évaluer la phonologie nécessite l'audio brut, ce qui dépasse la limite de taille des requêtes. -**À faire :** Implémenter l'évaluation phonologique via un endpoint séparé qui traite l'audio en chunks. -**Session concernée :** T2 live (WebSocket) +**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). --- -### TD-09 — ScriptProcessorNode déprécié (T2 live) +### FTD-08 — Tests E2E non implémentés **Priorité :** 🟢 Mineur -**Statut :** Reporté à après le lancement -**Description :** Le traitement audio côté client utilise `ScriptProcessorNode` qui est déprécié. Doit être remplacé par `AudioWorklet`. -**Impact :** Fonctionne mais génère des warnings dans la console. Peut poser problème sur certains navigateurs futurs. -**À faire :** Migrer vers AudioWorklet après le lancement MVP. +**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. --- -### TD-10 — Analyse des patterns (Premium) non implémentée +## 4. Tests à renforcer + +### FTD-09 — Tests de la state machine T2 Live non implémentés **Priorité :** 🟡 Important -**Statut :** Planifié -**Description :** La feature d'analyse des patterns sur les 5 dernières productions (Premium) n'est pas encore implémentée côté backend. -**À faire :** Implémenter après les corrections EE/EO et Stripe. +**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. --- -### TD-11 — Indice de préparation non implémenté -**Priorité :** 🟢 Mineur -**Statut :** Planifié -**Description :** Le calcul de l'indice de préparation (0-100) basé sur progression + régularité n'est pas encore implémenté. -**À faire :** Implémenter en même temps que l'analyse des patterns (TD-10). +### 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. -## 4. Tests à automatiser +**À 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 -### TD-12 — Tests manuels du Golden Dataset non automatisés -**Priorité :** 🟢 Mineur -**Statut :** Accepté — par conception -**Description :** Les 41 tests du Golden Dataset sont manuels. Certains pourraient être automatisés (tests d'intégration HTTP avec Supertest). -**À faire :** Ajouter des tests d'intégration pour les routes critiques après le lancement MVP. +**Condition de résolution :** avant l'intégration des hooks TanStack Query sur des features critiques (dashboard, simulations, auth). --- @@ -137,3 +245,12 @@ | ID | Description | Résolu le | Comment | |---|---|---|---| | — | — | — | — | + +--- + +## 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 | diff --git a/src/index.css b/src/index.css index 3914fb6..d4b5078 100644 --- a/src/index.css +++ b/src/index.css @@ -1,111 +1 @@ @import 'tailwindcss'; - -:root { - --text: #6b6375; - --text-h: #08060d; - --bg: #fff; - --border: #e5e4e7; - --code-bg: #f4f3ec; - --accent: #aa3bff; - --accent-bg: rgba(170, 59, 255, 0.1); - --accent-border: rgba(170, 59, 255, 0.5); - --social-bg: rgba(244, 243, 236, 0.5); - --shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; - - --sans: system-ui, 'Segoe UI', Roboto, sans-serif; - --heading: system-ui, 'Segoe UI', Roboto, sans-serif; - --mono: ui-monospace, Consolas, monospace; - - font: 18px/145% var(--sans); - letter-spacing: 0.18px; - color-scheme: light dark; - color: var(--text); - background: var(--bg); - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - @media (max-width: 1024px) { - font-size: 16px; - } -} - -@media (prefers-color-scheme: dark) { - :root { - --text: #9ca3af; - --text-h: #f3f4f6; - --bg: #16171d; - --border: #2e303a; - --code-bg: #1f2028; - --accent: #c084fc; - --accent-bg: rgba(192, 132, 252, 0.15); - --accent-border: rgba(192, 132, 252, 0.5); - --social-bg: rgba(47, 48, 58, 0.5); - --shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; - } - - #social .button-icon { - filter: invert(1) brightness(2); - } -} - -#root { - width: 1126px; - max-width: 100%; - margin: 0 auto; - text-align: center; - border-inline: 1px solid var(--border); - min-height: 100svh; - display: flex; - flex-direction: column; - box-sizing: border-box; -} - -body { - margin: 0; -} - -h1, -h2 { - font-family: var(--heading); - font-weight: 500; - color: var(--text-h); -} - -h1 { - font-size: 56px; - letter-spacing: -1.68px; - margin: 32px 0; - @media (max-width: 1024px) { - font-size: 36px; - margin: 20px 0; - } -} -h2 { - font-size: 24px; - line-height: 118%; - letter-spacing: -0.24px; - margin: 0 0 8px; - @media (max-width: 1024px) { - font-size: 20px; - } -} -p { - margin: 0; -} - -code, -.counter { - font-family: var(--mono); - display: inline-flex; - border-radius: 4px; - color: var(--text-h); -} - -code { - font-size: 15px; - line-height: 135%; - padding: 4px 8px; - background: var(--code-bg); -}