Rétrospective — PD-85¶
Résumé story¶
- Story : PD-85 — Créer export dossier complet pour plainte
- Domaine : b2c-mineurs
- Date : 2026-03-08
- Gates : G3 RESERVE (v2, 8.0) | G5 RESERVE (v1, 8.0) | G8 RESERVE (v1, 8.0)
- Temps total : 4.2h (vs 14h estimé, -70%)
Learnings de cette story¶
Depuis les gates¶
| Gate | Verdict | Score | Tags | Note |
|---|---|---|---|---|
| G3 | RESERVE | 8.0 (v2) | #export, #spec-correction | Correction spec v2 sans MAJ tests cause 6 désynchronisations |
| G5 | RESERVE | 8.0 (v1) | #audit-worm, #nestjs | ExceptionFilter global pour audit WORM pré-service |
| G8 | RESERVE | 8.0 (v1) | #fail-closed, #security, #stubs | 3 patterns sécurité + 4 stubs V1 tracés |
Depuis le REX (5 enseignements)¶
- Audit fail-closed : vérifier le catch, pas juste le try —
await auditLog()dans try ne garantit rien si le catch absorbe l'erreur - Joi valide la config, le code valide le runtime — Doubler validation Joi avec
Math.min()pour bornes de sécurité - Anti-énumération 404 — Messages distincts ("not found" vs "not accessible") permettent le profilage
- La capitalisation REX accélère le workflow — Anticipation du piège
createVerifyvscrypto.verify(null)grâce au REX PD-282 - Stubs documentés avec story destination passent les gates — 3ème story consécutive (PD-251, PD-72, PD-85)
Patterns récurrents (domaine b2c-mineurs)¶
4 stories analysées : PD-79, PD-84, PD-85, PD-86¶
| Pattern | Stories | Impact | Recommandation |
|---|---|---|---|
| Stubs V1 tracés acceptés en gates | PD-72, PD-85, PD-251 (+ PD-84) | GO/RESERVE sans pénalité MAJEUR | Confirmer comme bonne pratique standard |
| Audit fail-closed insuffisant | PD-63, PD-85, PD-250, PD-262, PD-265 (5 stories) | BLOQUANT/MAJEUR récurrent | Alerte forte — contractualiser dans code-contracts |
| Coverage sous seuil 85% | PD-32, PD-44, PD-79, PD-85 (4 stories) | RESERVE sur test_coverage | Ajouter pre-check coverage en fin step 6 |
| Spec v2 sans MAJ tests | PD-85 (nouveau) | 6 désynchronisations en confrontation | Toujours corriger spec ET tests ensemble |
Patterns cross-domaines (alertes fortes ≥ 5 stories)¶
| Tag | Stories | Action |
|---|---|---|
#fail-closed | 5 stories (PD-63, PD-85, PD-250, PD-262, PD-265) | Alerte forte : Ajouter invariant systématique dans code-contracts |
#stubs | 4 stories (PD-72, PD-85, PD-86, PD-251) | Pattern confirmé : stubs + story destination = acceptable |
#audit | 8 stories | Le pattern audit est omniprésent mais les erreurs de catch persistent |
#nestjs | 8 stories | ExceptionFilter/Guards pattern récurrent, stabilisé |
Recommandations¶
Priorité haute (≥ 5 stories ou pattern bloquant)¶
- Contractualiser "audit fail-closed" dans les code-contracts : ajouter un invariant explicite
INV-AUDIT-FAILCLOSED: Le catch block NE DOIT PAS absorber les erreurs d'audit — l'audit DOIT propager en 500 si le service est indisponible. Injecter dans le template code-contracts step 4. - Ajouter vérification anti-catch-absorb dans le prompt step 7a (review code) :
"Vérifier que TOUT catch block qui appelle auditLog propage l'erreur d'audit, pas juste l'erreur métier".
Priorité normale¶
- Pre-check coverage en fin step 6 : Lancer
npx jest --coverageimmédiatement après l'implémentation, avant l'acceptabilité, pour corriger les lacunes plus tôt (pattern PD-85: 82% → 3 suites ajoutées en step 7). - Anti-énumération dans prompt step 6b : Injecter la règle "uniformiser les messages d'erreur 404 pour les lookups par ID externe" dans le prompt agent developer.
- Correction spec+tests en bloc : En boucle de correction Gate 3, TOUJOURS mettre à jour spec ET tests ensemble pour éviter les désynchronisations (6 divergences détectées en PD-85 G3 v2).
- Joi + clamp runtime : Injecter dans le prompt step 6b "pour chaque borne de configuration validée par Joi, ajouter un Math.max/Math.min dans le code métier".
Signal CLAUDE.md¶
Section .claude/rules/learnings.md¶
Suggestion d'ajout :
## Anti-catch-absorb pour audit fail-closed (2026-03-08)
**Issu des REX PD-85, PD-63, PD-250, PD-262, PD-265** (5 occurrences).
**Pattern** : Un `await auditLog()` dans un `try` block ne garantit PAS le fail-closed si le `catch` absorbe l'erreur d'audit et retourne une exception métier à la place.
**Règle** : Tout `catch` block qui appelle auditLog DOIT propager l'erreur d'audit. Le pattern correct est :
- Option A : `finally { await audit() }` — audit toujours émis
- Option B : `catch { await audit(); throw; }` — re-throw obligatoire après audit
**INTERDIT** : `.catch(() => logger.error(...))` sur un appel audit (absorbe silencieusement).
Section .claude/rules/learnings.md¶
Suggestion d'ajout :
## Anti-énumération sur messages d'erreur 404 (2026-03-08)
**Issu du REX PD-85** : Messages distincts ("not found" vs "not accessible") permettent le profilage d'existence.
**Règle** : Utiliser un message d'erreur UNIQUE pour tout lookup par ID externe (ex: "Resource not found"). Ne JAMAIS distinguer "n'existe pas" de "pas autorisé" dans le message d'erreur HTTP.
Section .claude/rules/procedures.md — Étape 7¶
Suggestion d'ajout dans le paragraphe acceptabilité :
**Pre-check coverage en fin step 6** (issu du REX PD-85) : Lancer `npx jest --coverage` immédiatement après l'implémentation (step 6c) pour identifier les lacunes de couverture AVANT l'étape 7. Évite de créer 3 suites de tests supplémentaires en step 7.
Artefact : PD-85-retrospective.md Patterns identifiés : 4 domaine + 4 cross-domaines Alertes haute priorité : 2 (audit fail-closed, anti-catch-absorb) Action humaine : Réviser CLAUDE.md si pertinent