Rétrospective — PD-84¶
Résumé story¶
- Story : PD-84 — Encadrement contractuel offre gratuite B2C Mineurs
- Domaine : b2c-mineurs (backend)
- Date : 2026-02-24
- Durée : 5.5h (vs 14h estimé = -61%)
- Gates : G3 RESERVE (v3, 8.062/10) | G5 GO (v2, 9.562/10) | G8 GO (v1, 9.625/10)
Learnings de cette story¶
Depuis les gates¶
| Gate | Verdict | Score | Tags | Learning |
|---|---|---|---|---|
| G3 v1 | NON_CONFORME | 6.625 | #b2c, #freemium, #quotas | Transitions de plan (downgrade) et modes dégradés dépendances doivent être spécifiés dès v1 |
| G3 v2 | RESERVE | 8.188 | #b2c, #freemium, #quotas | Convergence STOP après escalade PO — 5 réserves résolues par décision produit |
| G5 v1 | RESERVE | 8.188 | #plan, #stub, #gate5 | Stubs 501 pour dépendances hors périmètre (PD-85) acceptables si explicitement documentés |
| G5 v2 | GO | 9.562 | #plan, #gate5, #code-contracts | Delta +1.374 en 1 itération — corrections chirurgicales (SLA, QuotaGuard, triple verrou) |
| G8 v1 | GO | 9.625 | #freemium, #nestjs, #security | pg_advisory_xact_lock + defense-in-depth (guard+RLS) = zéro écart sécurité |
Depuis le REX¶
| ID | Tags | Learning |
|---|---|---|
| L-84-01 | #sonar, #cpd | Extraire les duplications AVANT Gate 8 — un fichier extrait post-merge génère de nouvelles violations Sonar |
| L-84-02 | #sonar, #hash | codePointAt() + Math.trunc() obligatoires pour tout code de hashing (S7758 + S7767) |
| L-84-03 | #nestjs, #di | Checklist DI avant merge — vérifier chaque guard externe dans les imports du module |
| L-84-04 | #postgresql, #quota | Pattern pg_advisory_xact_lock + hashToInt réutilisable pour tout quota atomique sous concurrence |
| L-84-05 | #security, #nestjs | @UseGuards niveau classe (pas méthode) pour protection automatique de tout nouvel endpoint |
| L-84-06 | #sonar, #pipeline | new_violations = 0 s'applique à tout code nouveau, même extrait d'un fichier existant |
Depuis les corrections post-merge (3 fixes pipeline)¶
| Fix | Cause | Impact |
|---|---|---|
CPD hashToInt | Duplication entre 2 services | Sonar QG FAILED → extraction utils/hash.util.ts |
| Sonar S7758+S7767 | charCodeAt et \| 0 dans nouveau fichier | Sonar new_violations = 0 → codePointAt + Math.trunc |
| DI NestJS | OidcAuthModule manquant dans imports | Deploy crash → ajout import. Tests unitaires ne détectent pas DI |
Patterns récurrents (domaine b2c-mineurs + backend récent)¶
Pattern 1 — Sonar comme gardien structurel (4 stories : PD-107, PD-55, PD-63, PD-84)¶
Fréquence : #sonar dans 4 stories distinctes sur les 15 dernières.
Observations : - PD-107 : jest.doMock pattern pour singletons (Sonar flagge globals) - PD-55 : deprecated API BullMQ détectée uniquement par Sonar, pas ESLint - PD-63 : crypto.randomUUID() obligatoire (Math.random() = security hotspot) - PD-84 : codePointAt + Math.trunc + CPD duplication
Impact : 3 corrections post-merge sur PD-84 directement liées à Sonar. PD-55 avait aussi des corrections post-merge Sonar.
Recommandation : ALERTÉ FORTE — Le scan Sonar local (Phase 1.5 acceptabilité) est BLOQUANT depuis PD-55/PD-107, mais PD-84 prouve que l'extraction de code post-Gate 8 peut créer de nouvelles violations invisibles au scan initial.
Pattern 2 — Code contracts YAML accélèrent la convergence (9 stories)¶
Fréquence : #code-contracts dans 9 stories (PD-107, PD-19, PD-243, PD-245, PD-31, PD-32, PD-53, PD-81, PD-84).
Observations : - PD-84 : Gate 5 delta +1.374 en 1 correction, puis Gate 8 GO en 1 itération - PD-245 : Gate 8 GO en 1 itération avec code contracts bien définis - PD-81 : Code contracts ont structuré 19 tâches sur 7 modules
Impact : Les stories AVEC code contracts ont un Gate 8 moyen de 1.2 itérations vs 1.8 sans.
Pattern 3 — Specs ChatGPT et transitions d'état (3+ stories)¶
Fréquence : #spec-incomplete dans 3 stories (PD-19, PD-238, PD-32) + PD-84 (transitions manquantes).
Observations : - PD-84 Gate 3 : 3 itérations + escalade PO par manque de transitions retour (downgrade) - PD-19 Gate 3 : 2 itérations par critères d'acceptation non mesurables - PD-238 Gate 3 : 2 itérations par JWT/enum/error codes manquants
Impact : Chaque itération Gate 3 coûte ~1h. PD-84 a coûté 3 itérations + escalade = 4h.
Correction appliquée : Template spec v1.2.0 avec section "Transitions inverses" obligatoire (REX PD-84 §10.1).
Pattern 4 — NestJS DI invisible aux tests unitaires (3 stories : PD-30, PD-84, BATCH-RETRO)¶
Fréquence : #nestjs + #di dans 3 contextes distincts.
Observations : - PD-84 : OidcAuthModule manquant → crash deploy, invisible en tests - PD-30 : overrideGuard(Class) > overrideGuard(token) pour DI NestJS - BATCH-RETRO : Pattern transaction + advisory lock nécessite DI correcte
Impact : Erreur DI = crash en déploiement (pas en CI local). Coût : 1 pipeline supplémentaire + 30min diagnostic.
Pattern 5 — Defense-in-depth comme garantie sécurité (4 stories)¶
Fréquence : PD-79 (guard+middleware), PD-84 (guard+RLS), PD-177 (interceptor+KMS), PD-44 (sanitization+KMS).
Observations : - PD-84 : Score sécurité 10/10 grâce à double couche guard/RLS - PD-79 : Double validation taille intentionnelle (defense-in-depth) - PD-177 : SecretLeakInterceptor + S2-KMS defense-in-depth
Impact : Stories avec defense-in-depth explicite = 0 écart SEC dans les gates.
Pattern 6 — Faux positifs LLM en reviews (5+ stories)¶
Fréquence : PD-81, PD-55, PD-63, PD-79, PD-31, PD-84 — faux positifs récurrents en Phase 1 review ChatGPT.
Observations : - PD-84 Gate 5 : 2 BLQ reclassés MINEUR (conflit tests↔spec vs plan↔spec) - PD-81 Gate 8 : 5 faux positifs sur code déjà corrigé - PD-31 Gate 3 : ⅚ écarts annulés après vérification exhaustive
Impact : Phase 2 (confrontation Claude) reclasse en moyenne 50-60% des écarts ChatGPT.
Correction existante : Injection stubs/corrections dans prompts 7a/7b/7c (CLAUDE.md).
Métriques comparatives (domaine b2c-mineurs)¶
| Métrique | PD-79 | PD-84 | Delta |
|---|---|---|---|
| Durée | 8h | 5.5h | -31% |
| Gate 3 iter | 1 | 3 (+escalade) | +2 (transitions inverses) |
| Gate 5 iter | 1 | 2 | +1 |
| Gate 8 iter | 1 | 1 | = |
| Gate 8 score | 7.88 | 9.625 | +1.745 |
| Tests | 48 | 133 | +177% |
| Coverage | 94.5% | 92.5% | -2pp |
| SEC écarts | 0 | 0 | = |
| Post-merge fixes | 0 | 3 | +3 (Sonar + DI) |
Analyse : PD-84 est plus complexe que PD-79 (133 tests vs 48, 5 contrôleurs vs 1) mais les 3 itérations Gate 3 sont dues aux transitions inverses non spécifiées — maintenant corrigé dans le template spec v1.2.0. Les 3 post-merge fixes sont un angle mort structurel (scan Sonar + DI NestJS).
Recommandations¶
Priorité haute (pattern Sonar + DI = 7+ stories cumulées)¶
- Checklist DI NestJS pré-merge : Pour chaque
@UseGuards(XxxGuard)externe, vérifier que le module fournissant le guard est dans lesimports. Les tests unitaires ne détectent pas ces erreurs. → Cible : template de plan (étape 4) ou checklist pré-merge (étape 6c). - Re-scan Sonar après extraction CPD : Si une correction post-Gate 8 crée un nouveau fichier (extraction de duplication), relancer le scan Sonar local sur ce fichier avant push. Règle
new_violations = 0s'applique à tout code nouveau. → Cible : procédure post-merge dans/gov-impl.
Priorité normale¶
- Capitaliser
pg_advisory_xact_lock + hashToIntcomme pattern réutilisable pour toute story de quota atomique (INV candidat pour les futures stories b2c-mineurs et backend-core). - Triple verrou pour endpoints stub (env + header + NODE_ENV) : documenter comme pattern de référence dans CLAUDE.md pour toute story introduisant des endpoints dev/test.
- Enrichir le template spec avec "modes dégradés dépendances" : au-delà des transitions inverses (déjà ajouté v1.2.0), PD-84 a révélé que les specs omettent systématiquement le comportement quand une dépendance (PD-31 audit, PD-85 export) est indisponible.
Signal CLAUDE.md¶
Section "Règles apprises" — Nouvelle règle suggérée¶
DI NestJS — Guards externes :
### DI NestJS — Vérification imports guards externes (2026-02-24)
**Issu du REX PD-84** : `@UseGuards(XxxGuard)` avec un guard d'un autre module NÉCESSITE l'import du module fournisseur.
Les tests unitaires mockent les guards → le graphe DI NestJS n'est jamais instancié → erreur invisible en CI locale.
**Checklist pré-merge** : Pour chaque `@UseGuards()` dans le module :
1. Le guard est-il fourni par le module courant ? → OK
2. Le guard est-il externe ? → Vérifier que `imports: [XxxModule]` est présent
```typescript
// CORRECT
@Module({
imports: [OidcAuthModule], // <-- fournit OidcJwtAuthGuard
controllers: [MyController],
})
// BUG (crash deploy, invisible en tests)
@Module({
controllers: [MyController], // OidcAuthModule manquant
})
### Section "Étape 7 — Acceptabilité" — Enrichissement suggéré
Ajouter après "Scan Sonar local BLOQUANT" :
new_violations = 0 s'applique à tout code nouveau, même extrait d'un fichier existant. ``` Bilan¶
| Indicateur | Valeur |
|---|---|
| Patterns identifiés | 6 |
| Alertes haute priorité | 2 (DI NestJS, re-scan Sonar) |
| Alertes normales | 3 |
| Stories domaine analysées | 2 (PD-79, PD-84) |
| Stories global analysées | 39 |
| Tags dans ≥ 5 stories | 11 (#backend, #crypto, #auth, #security, #code-contracts, #testing, #infra, #site, #zero-knowledge, #app, #nestjs) |
| Axe fragile identifié | Gate 3 testability (PD-84: 7.25, PD-19: 6.5 v1) |