feat(corrections): Sprint 3.6a — nouveaux prompts + taxonomie erreurs + génération parallèle
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
df7ef2cc31
commit
63bc43ddcf
14 changed files with 2319 additions and 282 deletions
38
docs/CHANGELOG.md
Normal file
38
docs/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Changelog — Expria Backend
|
||||
|
||||
Toutes les modifications notables du backend sont documentées dans ce fichier.
|
||||
|
||||
Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/).
|
||||
|
||||
---
|
||||
|
||||
## [Unreleased] — 2026-04-22 — Sprint 3.6a — Qualité correction Backend
|
||||
|
||||
### Added
|
||||
- Nouveaux prompts DeepSeek spécifiés dans `docs/Prompt_maître.md` et `docs/Prompt_production_modèle.md` — builders dynamiques `buildCorrectionPrompt`, `buildModelPrompt`, `buildExercicesPrompt` dans `src/lib/deepseek.ts`.
|
||||
- `expria-frontend/docs/TAXONOMIE_ERREURS.md` — 63 codes d'erreurs TCF Canada sur 4 critères + 4 codes « autre ». Validation runtime via `src/lib/taxonomieErreurs.ts` (`isValidCode`, `isValidCritere`, `buildTaxonomyPromptSection`). Codes invalides retournés par DeepSeek sont filtrés ; le code `autre` sans description est rejeté.
|
||||
- Génération parallèle correction + modèle — option (b) : `generateProductionModele` démarre en même temps que `correctEE` avec `nclcObtenu = nclcCible - 1` comme estimation provisoire, `await` uniquement sur la correction pour répondre à la requête HTTP.
|
||||
- Exercices personnalisés fire-and-forget déclenchés après la résolution de la correction (dépendent de `rapport.erreurs_codes` et `rapport.criteres`). Format aligné sur les captures d'écran : `{difficulte, theme, diagnostic, consigne, extrait, indice, correction, explication}`.
|
||||
- Nouveaux champs dans `productions` : `revelation` (JSONB), `diagnostic` (TEXT), `conseil_nclc` (JSONB), `erreurs_codes` (JSONB), `exercices` (JSONB), `modele` (JSONB), `nclc_cible` (INTEGER), `exercices_status` / `modele_status` (TEXT, 'pending'/'ready'/'error').
|
||||
- Migration SQL `supabase/migrations/004_sprint_3_6a_qualite_correction.sql` — première migration versionnée du projet (cf. backend TD-06) ; idempotente grâce à `IF NOT EXISTS`.
|
||||
- Paramètre `nclc_cible` optionnel sur `POST /corrections/ee` (défaut 9, valeurs acceptées : 9 ou 10 ; sinon 400 VALIDATION_ERROR).
|
||||
- Index GIN sur `erreurs_codes` pour préparer l'agrégation du Sprint 3.6c (analyse patterns).
|
||||
- Nouveau fichier de tests `src/controllers/__tests__/correctionController.test.ts` — 8 tests (parallélisme option b, statuts ready/error, nclc_cible propagé, simulation introuvable, autre utilisateur).
|
||||
- 2 tests ajoutés à `simulationController.test.ts` — `getById` renvoie `nclc_cible`, `exercices`, `modele` + statuts.
|
||||
- Logs d'erreur détaillés : `callDeepSeek` classifie TIMEOUT / ABORT / JSON_PARSE / NETWORK / OTHER ; `correctionController.correctEE` logue `{simulationId, tache, nclcCible, message, stack}` avant de retourner 500.
|
||||
- FTD-23 🟡 ajoutée dans `expria-frontend/docs/TECH_DEBT.md` — `useAutosave` peut fire un PATCH `/simulations/:id/contenu` après correction, ce qui retourne 400 VALIDATION_ERROR. À corriger dans une session dédiée (préexistant au Sprint 3.6a, détecté lors des tests manuels).
|
||||
|
||||
### Changed
|
||||
- `correctEE` dans `src/lib/deepseek.ts` — nouvelle signature `correctEE(CorrectionInput)` (contenu, tache, sujet, sourceDoc1/2, nclcCible) et nouvelle forme de retour `CorrectionRapport` (revelation, diagnostic, criteres avec exemple/suggestion/astuce, conseil_nclc, erreurs_codes). `EERapport` devient alias de `CorrectionRapport`. EO inchangé.
|
||||
- `correctionController.correctEE` — charge le sujet + documents T3 depuis Supabase pour alimenter le prompt maître ; persiste les nouveaux champs (revelation, diagnostic, conseil_nclc, erreurs_codes, nclc_cible) + statuts pending initiaux ; lance `runModeleJob` en parallèle (option b) et `runExercicesJob` après correction.
|
||||
- `simulationController.getById` — retourne désormais `nclc_cible`, `exercices`, `exercices_status`, `modele`, `modele_status` en plus du `rapport` enrichi ; fallback `'pending'` si les colonnes sont absentes (compat avec productions pré-migration).
|
||||
- Timeout DeepSeek côté backend : `callDeepSeek` abort à **55 s** via `AbortSignal.timeout(55_000)` (avant : aucun timeout) ; timeout frontend corrections monte de **30 s à 60 s** — marge de 5 s entre abort backend et abort client.
|
||||
- Routes `/simulations/*` : réorganisation défensive — les `PATCH /:id/contenu` et `PATCH /:id/sujet` sont déclarées avant `GET /:id` pour éviter tout risque de masquage.
|
||||
- `deepseek.test.ts` réécrit (25 tests) — couvre correctEE nouvelle signature, generateProductionModele, generateExercices, helpers post-traitement, EO inchangé.
|
||||
|
||||
### Notes
|
||||
- **Option A retenue** pour la compatibilité frontend : backend renvoie uniquement la nouvelle forme. Le Sprint 3.6b (frontend) est immédiatement suivant et corrige l'écran blanc sur `RapportPage`.
|
||||
- **Option (b) retenue** pour le parallélisme : modèle en parallèle avec correction (nclcObtenu estimé), exercices strictement après correction.
|
||||
- Migration SQL à exécuter manuellement via `supabase db push` ou SQL Editor du dashboard (cf. Règle F) — aucune exécution automatique.
|
||||
- Tests : **174 tests verts** (+19 vs baseline 155), 18 fichiers de tests.
|
||||
- TD-15 🟡 ouvert : si le process redémarre pendant un job fire-and-forget (modèle/exercices), le statut reste `pending` indéfiniment. À traiter après observation en production.
|
||||
153
docs/Prompt_maître.md
Normal file
153
docs/Prompt_maître.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# Prompt Maître — Correction Expression Écrite TCF Canada
|
||||
|
||||
> **Source :** `app/api/corriger/route.ts` → fonction `buildPrompt()`
|
||||
> **Modèle :** DeepSeek Chat (`deepseek-chat`) · `temperature: 0.2` · `response_format: json_object`
|
||||
|
||||
---
|
||||
|
||||
## Contexte & Variables dynamiques
|
||||
|
||||
| Variable | Description | Valeur par défaut |
|
||||
|---|---|---|
|
||||
| `nclc` | Niveau NCLC cible du candidat (7 à 10) | `9` |
|
||||
| `minScore` | Score minimum requis sur 20 | `14` (NCLC 9) |
|
||||
| `taskDesc` | Description de la tâche (voir ci-dessous) | — |
|
||||
| `sujet` | Consigne ou sujet donné au candidat | `"Non précisé"` |
|
||||
| `texte` | Production écrite du candidat | — |
|
||||
| `sourceDoc1` | Document POUR (Tâche 3 uniquement) | — |
|
||||
| `sourceDoc2` | Document CONTRE (Tâche 3 uniquement) | — |
|
||||
|
||||
### Barème NCLC → score minimum
|
||||
|
||||
| NCLC | Score minimum /20 |
|
||||
|---|---|
|
||||
| 7 | 10 |
|
||||
| 8 | 12 |
|
||||
| 9 | 14 |
|
||||
| 10 | 16 |
|
||||
|
||||
---
|
||||
|
||||
## Descriptions des tâches
|
||||
|
||||
### Tâche 1 — Expression Écrite
|
||||
> Message / mail / annonce **(60-120 mots)** : décrire, raconter, expliquer à un destinataire dont le registre (formel/informel) est précisé dans la consigne.
|
||||
|
||||
### Tâche 2 — Expression Écrite
|
||||
> Article de blog / forum **(120-150 mots)** : compte rendu d'expérience ou récit, accompagné de commentaires, opinions ou arguments selon un objectif.
|
||||
|
||||
### Tâche 3 — Expression Écrite
|
||||
> Texte comparatif **(120-180 mots)** :
|
||||
> - **Partie 1** (40-60 mots) : synthèse des deux points de vue des documents sources
|
||||
> - **Partie 2** (80-120 mots) : prise de position personnelle argumentée
|
||||
|
||||
---
|
||||
|
||||
## Prompt envoyé au modèle
|
||||
|
||||
```
|
||||
Tu es un correcteur TCF Canada certifié par France Éducation International. Tu corriges avec précision et bienveillance.
|
||||
|
||||
OBJECTIF DU CANDIDAT : NCLC {nclc} — score minimum requis : {minScore}/20.
|
||||
|
||||
TÂCHE : {taskDesc}
|
||||
|
||||
[SI TÂCHE 3]
|
||||
DOCUMENTS SOURCES :
|
||||
Document 1 (point de vue POUR) : {sourceDoc1}
|
||||
Document 2 (point de vue CONTRE) : {sourceDoc2}
|
||||
[FIN SI TÂCHE 3]
|
||||
|
||||
CONSIGNE / SUJET : {sujet}
|
||||
|
||||
PRODUCTION DU CANDIDAT :
|
||||
"""
|
||||
{texte}
|
||||
"""
|
||||
|
||||
CRITÈRES OFFICIELS TCF (chacun noté de 0 à 5) :
|
||||
1. Adéquation à la tâche et au registre — respect des consignes, longueur, registre (formel/informel), pertinence du contenu.
|
||||
2. Cohérence et cohésion du discours — structure logique, connecteurs, progression thématique, lisibilité globale.
|
||||
3. Compétence lexicale — étendue du vocabulaire, précision, variété, absence de répétitions excessives.
|
||||
4. Compétence grammaticale — correction des structures, morphologie verbale, syntaxe, ponctuation.
|
||||
|
||||
RÈGLES ABSOLUES :
|
||||
- "exemple" = citation textuelle EXACTE, mot pour mot, extraite de la production du candidat. Jamais inventée.
|
||||
- "commentaire" = 2 phrases maximum, directes, sans formule introductive.
|
||||
- Interdit : "Voici", "Bien sûr", "Il convient de", toute formule introductive, tout markdown, tout backtick.
|
||||
- "score" global = somme exacte des 4 scores critères (0 à 20).
|
||||
- JSON strict sans aucun texte avant ni après.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Structure de la réponse JSON attendue
|
||||
|
||||
```json
|
||||
{
|
||||
"score": "<entier 0-20, somme des 4 critères>",
|
||||
"revelation": {
|
||||
"croyance": "<ce que le candidat croit faire bien>",
|
||||
"realite": "<ce que le correcteur observe réellement>",
|
||||
"consequence": "<impact concret sur la note>"
|
||||
},
|
||||
"diagnostic": "<phrase courte et directe identifiant le principal frein>",
|
||||
"criteres": [
|
||||
{
|
||||
"nom": "Adéquation à la tâche et au registre",
|
||||
"score": "<0-5>",
|
||||
"commentaire": "<2 phrases max>",
|
||||
"exemple": "<citation textuelle exacte extraite de la production>",
|
||||
"suggestion": "<reformulation ou correction concrète>",
|
||||
"astuce": "<conseil court et mémorisable>"
|
||||
},
|
||||
{
|
||||
"nom": "Cohérence et cohésion du discours",
|
||||
"score": "<0-5>",
|
||||
"commentaire": "<2 phrases max>",
|
||||
"exemple": "<citation textuelle exacte extraite de la production>",
|
||||
"suggestion": "<reformulation ou correction concrète>",
|
||||
"astuce": "<conseil court et mémorisable>"
|
||||
},
|
||||
{
|
||||
"nom": "Compétence lexicale",
|
||||
"score": "<0-5>",
|
||||
"commentaire": "<2 phrases max>",
|
||||
"exemple": "<citation textuelle exacte extraite de la production>",
|
||||
"suggestion": "<reformulation ou correction concrète>",
|
||||
"astuce": "<conseil court et mémorisable>"
|
||||
},
|
||||
{
|
||||
"nom": "Compétence grammaticale",
|
||||
"score": "<0-5>",
|
||||
"commentaire": "<2 phrases max>",
|
||||
"exemple": "<citation textuelle exacte extraite de la production>",
|
||||
"suggestion": "<reformulation ou correction concrète>",
|
||||
"astuce": "<conseil court et mémorisable>"
|
||||
}
|
||||
],
|
||||
"conseil_nclc": {
|
||||
"nclc_cible": "NCLC {nclc}",
|
||||
"ecart": "<manque X points / objectif atteint / X points au-dessus de l'objectif>",
|
||||
"action_prioritaire": "<conseil direct, concret et personnalisé sur quoi travailler en priorité>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Champs expliqués
|
||||
|
||||
| Champ | Rôle |
|
||||
|---|---|
|
||||
| `score` | Note globale /20 = somme stricte des 4 critères |
|
||||
| `revelation.croyance` | Perception erronée du candidat sur sa production |
|
||||
| `revelation.realite` | Constat objectif du correcteur |
|
||||
| `revelation.consequence` | Impact de cet écart sur la note finale |
|
||||
| `diagnostic` | Diagnostic court : le frein principal identifié |
|
||||
| `criteres[].commentaire` | Observation directe, 2 phrases max, sans introduction |
|
||||
| `criteres[].exemple` | Citation **mot pour mot** tirée du texte du candidat |
|
||||
| `criteres[].suggestion` | Reformulation ou correction concrète de l'exemple |
|
||||
| `criteres[].astuce` | Conseil mémorisable pour progresser sur ce critère |
|
||||
| `conseil_nclc.ecart` | Distance entre le score obtenu et l'objectif NCLC |
|
||||
| `conseil_nclc.action_prioritaire` | Plan d'action personnalisé et prioritaire |
|
||||
164
docs/Prompt_production_modèle.md
Normal file
164
docs/Prompt_production_modèle.md
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
# Prompt Maître — Génération de la Production Modèle TCF Canada
|
||||
|
||||
> **Source :** `app/api/modele/route.ts` → handler `POST` (ligne 115)
|
||||
> **Modèle :** DeepSeek Chat (`deepseek-chat`) · `temperature: 0.3` · `max_tokens: 2200`
|
||||
|
||||
---
|
||||
|
||||
## Principe de fonctionnement
|
||||
|
||||
Le prompt **réécrit la production du candidat** un niveau NCLC au-dessus de son score obtenu, en conservant intégralement ses idées et arguments. Il ne génère pas un texte de zéro.
|
||||
|
||||
```
|
||||
nclcModele = min(nclcObtenu + 1, 10)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Variables dynamiques
|
||||
|
||||
| Variable | Description | Exemple |
|
||||
|---|---|---|
|
||||
| `sujet` | Consigne ou sujet donné au candidat | `"Écrivez un mail à votre voisin..."` |
|
||||
| `taskDescription` | Description officielle de la tâche (voir ci-dessous) | — |
|
||||
| `texte` | Production originale du candidat | — |
|
||||
| `nclcObtenu` | Niveau NCLC réellement atteint par le candidat (7–10) | `8` |
|
||||
| `nclcModele` | Niveau NCLC cible de la production modèle (`nclcObtenu + 1`) | `9` |
|
||||
| `scoreModele` | Score minimum requis pour atteindre `nclcModele` | `14` |
|
||||
|
||||
### Barème NCLC → score minimum
|
||||
|
||||
| NCLC | Score minimum /20 |
|
||||
|---|---|
|
||||
| 7 | 10 |
|
||||
| 8 | 12 |
|
||||
| 9 | 14 |
|
||||
| 10 | 16 |
|
||||
|
||||
---
|
||||
|
||||
## Descriptions des tâches
|
||||
|
||||
### Tâche 1
|
||||
> Expression écrite — Tâche 1 : Message / mail / annonce **(60-120 mots)**. Respect du registre (formel ou informel), salutation, corps du message, formule de clôture et signature.
|
||||
|
||||
### Tâche 2
|
||||
> Expression écrite — Tâche 2 : Article de blog ou forum **(120-150 mots)**. Accroche, récit personnel à la 1re personne, opinion argumentée, conseil au lecteur.
|
||||
|
||||
### Tâche 3
|
||||
> Expression écrite — Tâche 3 : Texte comparatif **(120-180 mots)**. Partie 1 (40-60 mots) : présentation neutre des deux documents. Partie 2 (80-120 mots) : prise de position personnelle argumentée.
|
||||
|
||||
---
|
||||
|
||||
## Prompt envoyé au modèle
|
||||
|
||||
```
|
||||
Tu es un correcteur expert TCF Canada.
|
||||
|
||||
Le candidat a rédigé cette production sur le sujet suivant :
|
||||
|
||||
SUJET : {sujet}
|
||||
|
||||
TÂCHE : {taskDescription}
|
||||
|
||||
PRODUCTION DU CANDIDAT :
|
||||
{texte}
|
||||
|
||||
Le candidat a obtenu NCLC {nclcObtenu}. Ta mission est de lui montrer comment atteindre NCLC {nclcModele} (score minimum {scoreModele}/20).
|
||||
|
||||
Ta mission : réécrire cette production EN CONSERVANT le fond, les idées, le positionnement et les arguments du candidat — mais en appliquant parfaitement les 4 critères officiels TCF Canada :
|
||||
|
||||
1. Réalisation de la tâche — respecter le format, les limites de mots, la consigne, le registre
|
||||
2. Cohérence / Structure — paragraphes clairs, connecteurs logiques variés, progression cohérente
|
||||
3. Étendue du lexique — vocabulaire riche et précis, zéro répétition, registre adapté
|
||||
4. Maîtrise grammaticale — structures complexes, subjonctif, passif, subordination
|
||||
|
||||
RÈGLES ABSOLUES :
|
||||
- Conserver les idées et arguments du candidat — ne pas inventer
|
||||
- Respecter STRICTEMENT les limites de mots ci-dessous pour le champ production_modele_propre (ne jamais dépasser le maximum)
|
||||
- Viser exactement le niveau NCLC {nclcModele}
|
||||
- Le texte d'examen ne contient AUCUNE note : pas de [NOTE:], pas de commentaire entre parenthèses dans production_modele_propre
|
||||
- Proposer exactement 3 entrées dans notes_pedagogiques (passage court + explication)
|
||||
- Répondre en JSON valide sans markdown
|
||||
|
||||
COMPTAGE DES MOTS (TCF Canada, Expression écrite) :
|
||||
- Un mot = segment séparé par des espaces (ou fins de ligne) ; l'apostrophe (' ou ') et le tiret (-) ne créent pas un mot supplémentaire.
|
||||
- Exemples : « c'est », « l'eau », « aujourd'hui », « c'est-à-dire », « vas-y » comptent chacun pour un seul mot.
|
||||
|
||||
LONGUEUR production_modele_propre pour cette tâche (respecter min conseillé et max STRICT) :
|
||||
- Tâche 1 : 60 à 120 mots — ne pas dépasser 120 mots
|
||||
- Tâche 2 : 120 à 150 mots — ne pas dépasser 150 mots
|
||||
- Tâche 3 : 120 à 180 mots — ne pas dépasser 180 mots
|
||||
|
||||
FORMAT JSON :
|
||||
{
|
||||
"production_modele_propre": "texte final seul, prêt pour l'examen, sans aucune annotation",
|
||||
"notes_pedagogiques": [
|
||||
{"passage": "extrait court du texte modèle", "explication": "pourquoi ce passage est efficace au TCF"}
|
||||
],
|
||||
"transformations": [
|
||||
{"original": "extrait original du candidat", "ameliore": "version améliorée", "explication": "pourquoi c'est mieux"}
|
||||
],
|
||||
"message": "phrase courte encourageante sur les idées du candidat"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Structure de la réponse JSON attendue
|
||||
|
||||
```json
|
||||
{
|
||||
"production_modele_propre": "<texte final réécrit, prêt pour l'examen, sans annotation>",
|
||||
"notes_pedagogiques": [
|
||||
{
|
||||
"passage": "<extrait court du texte modèle>",
|
||||
"explication": "<pourquoi ce passage est efficace au TCF>"
|
||||
},
|
||||
{
|
||||
"passage": "<extrait court du texte modèle>",
|
||||
"explication": "<pourquoi ce passage est efficace au TCF>"
|
||||
},
|
||||
{
|
||||
"passage": "<extrait court du texte modèle>",
|
||||
"explication": "<pourquoi ce passage est efficace au TCF>"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"original": "<extrait original du candidat>",
|
||||
"ameliore": "<version améliorée>",
|
||||
"explication": "<pourquoi c'est mieux>"
|
||||
}
|
||||
],
|
||||
"message": "<phrase courte encourageante sur les idées du candidat>"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Champs expliqués
|
||||
|
||||
| Champ | Rôle |
|
||||
|---|---|
|
||||
| `production_modele_propre` | Texte final réécrit au niveau NCLC cible, sans aucune annotation, prêt pour l'examen |
|
||||
| `notes_pedagogiques` | Exactement **3** passages du texte modèle commentés pédagogiquement |
|
||||
| `notes_pedagogiques[].passage` | Extrait court tiré du texte modèle |
|
||||
| `notes_pedagogiques[].explication` | Raison pour laquelle ce passage est efficace au TCF |
|
||||
| `transformations` | Liste des améliorations appliquées sur des extraits précis |
|
||||
| `transformations[].original` | Extrait original du candidat |
|
||||
| `transformations[].ameliore` | Version améliorée de cet extrait |
|
||||
| `transformations[].explication` | Justification pédagogique de l'amélioration |
|
||||
| `message` | Message court et encourageant adressé au candidat |
|
||||
|
||||
---
|
||||
|
||||
## Post-traitement côté serveur
|
||||
|
||||
Après réception de la réponse du modèle, le serveur applique les traitements suivants :
|
||||
|
||||
1. **Nettoyage** — suppression de toutes les annotations entre crochets `[NOTE: ...]` ou parenthèses dans `production_modele_propre` via `stripModelAnnotations()`
|
||||
2. **Vérification du nombre de mots** — comptage TCF via `wordCount()` (apostrophes et tirets ne créent pas de mots supplémentaires)
|
||||
3. **Troncature automatique** — si le texte dépasse le maximum de mots autorisé, il est tronqué via `truncateToMaxWords()` et le flag `tcf_truncated: true` est retourné
|
||||
4. **Enrichissement de la réponse** — ajout des métadonnées : `nclcModele`, `nclcObtenu`, `scoreCible`, `tcf_word_count`, `tcf_word_min`, `tcf_word_max`, `tcf_truncated`
|
||||
5. **Persistance** — enregistrement dans la table `productions` avec `record_kind: "production_modele"` et lien vers le rapport parent via `parent_production_id`
|
||||
|
|
@ -98,6 +98,20 @@
|
|||
|
||||
---
|
||||
|
||||
### 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue