edtech / tasks /lessons.md
CognxSafeTrack
fix: resolve all Railway deployment crashes (tsx, database build, Fastify v5 migration)
b75a181

🎓 Infrastructure Lessons (La Plomberie)

Ce fichier archive les échecs et solutions liées à la stabilité technique du système. Les règles ici doivent être respectées lors de toute modification structurelle.

  • [ARCHI] Détachement Gateway : Ne JAMAIS laisser une gateway (Hugging Face / Meta) attendre > 20s. Répondre 200 OK immédiatement et déléguer au worker.

  • [DB] Séquençage des Opérations : Ne JAMAIS envoyer de message WhatsApp avant d'avoir persisté la progression dans Prisma. La DB est la seule source de vérité.

  • [CACHING] Verrous d'Idempotence : Utiliser des locks Redis avec un TTL minimal de 300s (5 min) pour protéger les appels LLM longs et éviter les retries en boucle.

  • [MEDIA] Vision Base64 : Gemini échoue par URL ; toujours convertir les photos d'utilisateurs en Base64.

  • [STT] Whisper Auto-Validation : Si l'indice de confiance est <= 80%, intercepter le flux et basculer en PENDING_REVIEW pour validation humaine sans crash.

  • [NON-BLOQUANT] Processus FFMPEG : Les conversions audio doivent tourner dans des sous-processus.

  • [AI] OpenAI Zod Schemas : Pour les Structured Outputs, OpenAI exige que TOUS les champs d'un schéma soient obligatoires OU explicitement .nullable().optional(). Les .optional() seuls sur les sous-objets provoquent une erreur.

  • [QUEUES] Payload Sync : S'assurer que les noms de champs dans whatsappQueue.add (producteur) matchent exactement le destructuring dans le worker (consommateur). Préférer currentDay partout pour la consistance.

  • [DB] Enrollment ID : Toujours passer l'ID d'inscription (enrollmentId) au job de feedback pour permettre la persistance des réponses sans crash Prisma.

  • [JSON] Null Safety & Expansion : Toujours vérifier la présence de données (Null Safety) lors de l'extension des schémas JSON, sous peine de briser le pipeline des jours précédents (Utiliser des guards currentDay >= X).

  • [FLOW] Re-validation Window : Ne jamais bloquer une analyse entrante sous prétexte que le statut est COMPLETED si le délai depuis la dernière validation est inférieur à 5 minutes (cas de correction immédiate par l'utilisateur) ou si un nouvel élément multimédia est présent.

  • [FLOW] Day 7 Card Blockage : Risque de blocage lors du choix de carte. Toujours valider que l'entrée textuelle courte (ex: "WhatsApp") déclenche la même logique que l'audio en baissant le seuil de minWordCount.

  • [UX] Card Interaction (Hook) : L'interaction par cartes/boutons ne doit jamais être bloquante. Elle doit servir de "hook" : le Coach valide le choix et relance immédiatement sur le "pourquoi" pour enrichir l'analyse.

  • [UX] Anti-False Validation (Card Click) : Un clic sur une carte/bouton ne doit JAMAIS valider une étape (COMPLETED). Il doit servir de point d'entrée ("hook") pour une explication riche. Forcer le statut à PENDING après un choix par bouton pour obliger l'utilisateur à donner son explication audio/texte.

  • [FLOW] Validation Subordonnée : Le passage en COMPLETED est strictement réservé au feedback positif de l'IA sur une réponse de fond. L'UI d'attente ("Analyse de votre réponse...") doit être envoyée, mais le statut ne doit bouger que sur confirmation IA.

🛡️ Règle d'Or de l'Intégrité

Les correctifs techniques ne doivent JAMAIS impacter la logique de personnalisation du prompt generatePersonalizedLesson ou des feedbacks. Toute simplification technique qui réduit la spécificité de l'IA est un échec.

📅 Historique des Apprentissages

  • [24/03/2026] | Gemini 404 | Utiliser gemini-1.5-pro-latest au lieu de gemini-1.5-pro sur l'API v1beta.
  • [24/03/2026] | Missing dayNumber | currentDay vs dayNumber mismatch dans le job payload. Toujours vérifier le destructuring.
  • [30/04/2026] | tsx en production | Ne jamais utiliser --import tsx en PM2 pour production. tsx est une devDependency. Compiler tous les packages/apps avec tsc et pointer PM2 vers dist/index.js.
  • [30/04/2026] | @repo/database non compilé | Tout package workspace consommé en production doit : (1) avoir noEmit: false dans son tsconfig, (2) pointer main vers ./dist/index.js, (3) être compilé dans le Dockerfile avant les apps qui en dépendent.
  • [30/04/2026] | Imports .js dans tsconfig node | Les imports './foo.js' dans un fichier .ts requièrent moduleResolution: node16. Avec moduleResolution: node, utiliser des imports sans extension : './foo'.
  • [30/04/2026] | Fastify plugin versioning | Avant de modifier la version de Fastify ou d'un plugin @fastify, auditer TOUS les plugins avec grep "fastify:.*\.x" dans leur index.js. Le numéro de version majeur varie par plugin — ne pas supposer que X.x = Fastify v4 ou v5.
  • [30/04/2026] | addContentTypeParser Fastify v5 | La signature callback (req, body, done) => void est remplacée par async (req: FastifyRequest, body: Buffer) => unknown. Retourner la valeur parsée, ne pas appeler done().
  • [30/04/2026] | Fire-and-forget Fastify v5 | Pattern correct : setImmediate(async () => { ... }) D'ABORD, puis return reply.code(200).send(...). Ne jamais laisser reply.send() sans return dans un handler async Fastify v5.