Aller au contenu

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)

  1. Audit fail-closed : vérifier le catch, pas juste le tryawait auditLog() dans try ne garantit rien si le catch absorbe l'erreur
  2. Joi valide la config, le code valide le runtime — Doubler validation Joi avec Math.min() pour bornes de sécurité
  3. Anti-énumération 404 — Messages distincts ("not found" vs "not accessible") permettent le profilage
  4. La capitalisation REX accélère le workflow — Anticipation du piège createVerify vs crypto.verify(null) grâce au REX PD-282
  5. 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 --coverage immé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