expria-backend/docs/TECH_DEBT-backend.md
Hermann_Kitio 7cac057062 feat(eo): align correction EO on 3.6a format + Deepgram token + T1 presentation generation
Sprint 4a:
- correctEO aligned on CorrectionRapport format (revelation, diagnostic, criteres, conseil_nclc, erreurs_codes)
- nclc_cible parameter (default 9, accepts 9|10)
- Fire-and-forget modele + exercices jobs (same pattern as EE)
- EO-specific DeepSeek prompt (oral transcript tolerance, 4 TCF criteria)
- Gemini transcribeAudio: 30s timeout + 1 retry
- POST /presentations/generate: 5-field questionnaire → DeepSeek generates oral presentation (~220-260 words, NCLC 7-8)
- Migration 006_sprint_4a_eo.sql (documentation only — no audio storage)

Sprint 4b:
- POST /transcriptions/token: Deepgram temporary API key (600s TTL)
- Removed audio storage pipeline (audioStorage.ts, XOR validation, 14MB limit)
- Backend receives transcript text only, no audio files
- TD-10/TD-11 resolved (Sprint 3.6c), TD-16/17/18 resolved (4b cleanup)

Typecheck: OK · Tests: 241/241 

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 05:04:26 +03:00

13 KiB

TECH_DEBT.md — Expria / Coach TCF Canada

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.

Format : chaque entrée a un identifiant (TD-XX), une priorité, et un statut. Priorités : 🔴 Critique (bloque la production) / 🟡 Important / 🟢 Mineur


1. Stubs temporaires — à compléter

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


TD-02 — src/lib/planController.ts (backend)

Priorité : 🟡 Important Statut : Résolu — session Stripe 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


TD-03 — src/lib/stripe.ts (backend)

Priorité : 🟡 Important Statut : Résolu — session Stripe 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)

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.


TD-05 — Comptes de test avec emails @gmail.com

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).

TD-06 — Pas de migration SQL versionnée pour les tables initiales

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.

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.


TD-13 — Webhook Stripe non idempotent

Priorité : 🔴 Critique Statut : Ouvert — à faire avant mise en production Description : Stripe peut livrer un même event webhook deux fois (retries réseau, rejeu manuel depuis le dashboard). La route POST /stripe/webhook traite chaque réception sans dédoublonnage. En pratique, les opérations updateUserPlan et updateUserStripeInfo sont idempotentes par nature (même résultat en cas de double appel), mais si de la logique non idempotente est ajoutée plus tard (ex: compteur, envoi d'email, crédit utilisateur), un double traitement causerait un bug. À faire :

  • Créer une table stripe_webhook_events(id TEXT PRIMARY KEY, processed_at TIMESTAMPTZ)
  • Avant traitement, vérifier si event.id est déjà en base → si oui, retourner 200 sans rien faire
  • Après traitement, insérer l'event.id dans la table Session concernée : Stripe (POST /stripe/webhook) Condition de résolution : Avant la mise en production publique.

TD-15 — Jobs asynchrones modèle/exercices : status peut rester "pending" indéfiniment

Priorité : 🟡 Important Statut : Ouvert — introduit au Sprint 3.6a Description : Le flux POST /corrections/ee lance deux jobs DeepSeek en fire-and-forget (runModeleJob, runExercicesJob dans correctionController.ts). Si le process Node redémarre (deploy Render, crash, OOM) pendant l'exécution d'un de ces jobs, la colonne exercices_status ou modele_status reste figée à 'pending' — l'utilisateur voit un loader infini côté frontend. Impact actuel : faible en conditions normales (DeepSeek répond en ~5-15 s, Render redémarre rarement). Perceptible uniquement si un deploy a lieu pendant une correction active. À faire :

  • Option 1 (simple) : job de reprise au boot → scanner productions WHERE (exercices_status='pending' OR modele_status='pending') AND created_at < NOW() - INTERVAL '2 minutes' → relancer.
  • Option 2 (robuste) : file d'attente persistée (pg-boss, BullMQ) au lieu de fire-and-forget.
  • Option 3 (minimal) : timeout côté frontend → si pending depuis > 2 min, afficher "La génération a échoué, réessayer ?" + endpoint POST /simulations/:id/retry-jobs. Session concernée : à planifier après livraison Sprint 3.6a/3.6b en prod stable. Condition de résolution : après 7 jours d'observation en prod avec monitoring des colonnes *_status='pending' âgées.

TD-14 — Erreurs TypeScript TS2835 pré-existantes

Priorité : 🟡 Important Statut : Résolu — session correction build TypeScript Description : Erreurs TS2835 sur plusieurs fichiers de routes. Non bloquant (tests verts) mais à corriger. Gate de qualité actuel : npm run test. À faire : Ajouter les extensions .js aux imports relatifs ou ajuster moduleResolution dans tsconfig.json pour permettre npm run build de passer.


3. Fonctionnalités reportées

TD-08 — Phonologie T2 EO à 0

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)


TD-09 — ScriptProcessorNode déprécié (T2 live)

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.


TD-10 — Analyse des patterns (Premium) non implémentée

Priorité : 🟡 Important Statut : Résolu — Sprint 3.6c Description : La feature d'analyse des patterns sur les 5 dernières productions (Premium) a été livrée Sprint 3.6c (table pattern_analyses, generatePatternExercices).


TD-11 — Indice de préparation non implémenté

Priorité : 🟢 Mineur Statut : Résolu — Sprint 3.6c Description : Le calcul de l'indice de préparation (0-100) a été livré Sprint 3.6c en même temps que l'analyse des patterns (colonne preparation_index + preparation_message).


4. Tests à automatiser

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.


TD-16 — Bucket Supabase Storage audio-productions créé manuellement

Priorité : 🟡 Important Statut : Résolu — Sprint 4b Description : Décision Hermann (2026-04-25) : abandon du stockage audio backend. La transcription live passe par Deepgram en connexion directe navigateur ↔ Deepgram via token éphémère. L'audio brut est téléchargé en local par l'utilisateur. Plus aucun bucket Storage requis côté serveur.


TD-17 — Limite audioBase64 in-memory à 14 Mo (≈ 10 Mo binaire)

Priorité : 🟢 Mineur Statut : Résolu — Sprint 4b Description : Plus de payload audio reçu côté backend (POST /corrections/eo accepte uniquement transcript). La limite n'a plus lieu d'être.


TD-18 — RLS Storage audio-productions non testée en intégration

Priorité : 🟡 Important Statut : Résolu — Sprint 4b Description : Plus de bucket Storage backend à protéger. Les policies RLS de la migration 006 sont supprimées (DROP IF EXISTS) au profit d'un commentaire historique.


TD-19 — Token Deepgram non rotatif côté frontend

Priorité : 🟡 Important Statut : Ouvert — introduit au Sprint 4b Description : POST /transcriptions/token retourne un token Deepgram éphémère valide 600 s (10 min). Une session EO T1 (2 min) tient largement, mais une session T3 (4:30) ou un enchaînement de 2 tâches dépasse la fenêtre si l'utilisateur prend des pauses. Si le token expire en cours de session, la connexion Deepgram drop sans renégociation automatique. À faire (côté frontend Sprint 4c) :

  • Demander un nouveau token via /transcriptions/token à T-60 s avant expiration.
  • Reconnecter Deepgram en réutilisant la même session WebSocket si supporté. Condition de résolution : stratégie de rotation de token implémentée et testée côté frontend.

TD-20 — transcribeAudio (Gemini) sans consommateur

Priorité : 🟢 Mineur Statut : Ouvert — introduit au Sprint 4b Description : La fonction transcribeAudio dans src/lib/gemini.ts n'est plus appelée par le flux EO (Deepgram a remplacé Gemini batch). Conservée volontairement comme point d'extension futur pour TD-08 (évaluation phonologique séparée) ou un fallback si Deepgram est indisponible. À faire :

  • Si TD-08 reste fermé 30 jours après la mise en prod du Sprint 4b sans plan d'usage, supprimer transcribeAudio et gemini.ts complet. Condition de résolution : décision sur TD-08 (résolution ou abandon).

TD-21 — Pas de rate limiting sur /transcriptions/token

Priorité : 🟢 Mineur Statut : Ouvert — introduit au Sprint 4b Description : Un utilisateur authentifié peut générer un nombre illimité de tokens Deepgram. Chaque token consomme un crédit côté Deepgram (selon usage de la connexion live qui suit). Un user malveillant pourrait scripter des appels en boucle pour épuiser le quota Deepgram. À faire :

  • Ajouter un rate limit (par user, ex. 30 tokens/heure) via le middleware rateLimit.ts existant. Condition de résolution : middleware rate-limit branché sur la route et testé.

5. Historique des résolutions

ID Description Résolu le Comment
TD-02 planController.ts complété 2026-04-16 Session Stripe
TD-03 stripe.ts complété 2026-04-16 Session Stripe
TD-14 Erreurs TS2835 + TS18046 + TS7053 corrigées 2026-04-17 Session build Render
TD-10 Analyse des patterns (Premium) livrée 2026-04-25 Sprint 3.6c
TD-11 Indice de préparation livré 2026-04-25 Sprint 3.6c
TD-16 Bucket Storage abandonné 2026-04-25 Sprint 4b — Deepgram direct
TD-17 Limite audio in-memory caduque 2026-04-25 Sprint 4b
TD-18 RLS Storage caduque 2026-04-25 Sprint 4b