Aller au contenu

PD-84 — Retour d'expérience (REX)

Story : PD-84 — Définir offre gratuite B2C Mineurs Date : 2026-02-24 Durée totale : ~5.5h Résultat : GO (Gate 8 = 9.625/10)


1. Résumé exécutif

PD-84 implémente l'encadrement contractuel de l'offre gratuite B2C Mineurs dans ProbatioVault-backend (NestJS). Le module freemium (src/modules/freemium/) a été créé from scratch avec 5 contrôleurs, 4 services, 2 gardes, 1 entité, 6 enums et un système d'exceptions métier centralisé (FreemiumException, 8 codes). L'architecture repose sur pg_advisory_xact_lock pour l'atomicité des quotas sous concurrence, et une défense en profondeur guard NestJS + RLS PostgreSQL pour l'isolation multi-tenant.

La story a atteint le Gate 8 GO en 1 seule itération (9.625/10), sans BLOQUANT ni MAJEUR dans aucune des trois gates — une première dans le projet. Trois corrections post-merge ont été nécessaires sur le pipeline Sonar et l'injection de dépendances NestJS.

2. Métriques de qualité

Itérations de gates

Gate Itérations Score final Verdict
Gate 3 (spec review) 3 + escalade PO 8.062/10 GO (v2)
Gate 5 (plan review) 2 9.562/10 GO (v2)
Gate 8 (closure) 1 9.625/10 GO (v1)

Total itérations : 6 (dont 1 escalade PO en Gate 3)

Écarts par type

Type Gate 3 Gate 5 Gate 8 Total
BLOQUANT 0 0 0 0
MAJEUR 0 0 0 0
MINEUR 3 3
Suggestion 2 2
Non-écart 2 2

Métriques de tests

Métrique Valeur
Suites de tests 17
Tests totaux 133
Échecs 0
Couverture globale 92.52%
Endpoints API 8
Invariants 15
Critères d'acceptation 14

3. Architecture et design

pg_advisory_xact_lock : Verrou consultatif transactionnel sur hashToInt(userId) pour l'atomicité des quotas. Évite les deadlocks liés aux row-level locks.

Défense en profondeur : FolderOwnerGuard (NestJS) + RLS PostgreSQL — deux couches indépendantes et orthogonales.

FreemiumException : 8 codes d'erreur métier centralisés avec mapping HTTP status automatique.

PlanStubGuard : Triple verrou (env ENABLE_PLAN_STUB + header X-Stub-Secret + NODE_ENV !== production) pour l'endpoint de mutation de plan dev/test.

4. Couverture de tests

  • 17 suites, 133 tests, 0 échecs, 92.52% coverage
  • Tests paramétriques : 3 rôles × 2 plans pour les contrôles d'accès
  • Tests aux limites : ⅔ dossiers et 99/100 documents (seuils N-1/N/N+1)
  • Tests de concurrence : validation du lock pg_advisory_xact_lock

5. Sécurité

Score Gate 8 sécurité : 10/10

  • OidcJwtAuthGuard au niveau classe (pas méthode) sur 5 contrôleurs
  • FolderOwnerGuard + RLS PostgreSQL (défense en profondeur)
  • ParseUUIDPipe sur tous les path params UUID
  • class-validator avec whitelist strict sur DTOs
  • crypto.randomUUID() exclusivement (0 occurrence de Math.random())
  • TypeORM paramétrique exclusivement (0 concaténation SQL)

6. Points forts

  • Première story sans BLOQUANT ni MAJEUR sur les 3 gates
  • Gate 8 GO en 1 seule itération (9.625/10) — meilleur score de closure du projet
  • Architecture défensive cohérente (pg_advisory_xact_lock + guard/RLS)
  • Couverture de tests 92.52% avec patterns paramétriques et aux bornes
  • FreemiumException centralisée (8 codes) — extensible et cohérente

7. Points d'amélioration

  • 3 corrections post-merge nécessaires (Sonar CPD, Sonar violations, DI NestJS)
  • Absence de tests e2e (EC-T1) — chemins critiques non couverts en intégration
  • Pattern de test verbeux (EC-T2) — beforeEach non factorisés
  • AddDocumentDto inline (EC-R1) — réduit la découvrabilité

8. Écarts post-merge (pipeline)

Correction 1 — Duplication CPD hashToInt

Cause : hashToInt() copié dans folder.service.ts et folder-document.service.ts. Sonar CPD détecte la duplication, QG threshold new_duplicated_lines_density > 3% déclenché.

Fix : Extraction vers utils/hash.util.ts, imports dans les deux services.

Correction 2 — Sonar violations S7758 + S7767

Cause : Le fichier hash.util.ts nouvellement créé est analysé comme "nouveau code". Sonar remonte S7758 (charCodeAtcodePointAt) et S7767 (| 0Math.trunc). Threshold new_violations = 0 bloquant.

Fix : Math.trunc((hash << 5) + hash + (str.codePointAt(i) ?? 0))

Correction 3 — Crash DI NestJS : OidcAuthModule manquant

Cause : FreemiumModule utilise OidcJwtAuthGuard (via @UseGuards) mais n'importe pas OidcAuthModule. Les tests unitaires mockent les guards — le graphe DI NestJS n'est jamais instancié.

Fix : Ajout de OidcAuthModule dans les imports de FreemiumModule.

Règle : Tout module utilisant un guard externe DOIT importer le module fournisseur.

9. Learnings capitalisés

  1. L-84-01 #sonar #cpd : Extraire les duplications AVANT Gate 8, pas après — un fichier extrait post-merge génère de nouvelles violations Sonar
  2. L-84-02 #sonar #hash : codePointAt() + Math.trunc() obligatoires pour tout code de hashing (S7758 + S7767)
  3. L-84-03 #nestjs #di : Checklist DI avant merge — vérifier chaque guard externe dans les imports du module
  4. L-84-04 #postgresql #quota : Pattern pg_advisory_xact_lock + hashToInt réutilisable pour tout quota atomique sous concurrence
  5. L-84-05 #security #nestjs : @UseGuards niveau classe (pas méthode) pour protection automatique de tout nouvel endpoint
  6. L-84-06 #sonar #pipeline : new_violations = 0 s'applique à tout code nouveau, même extrait d'un fichier existant

10. Améliorations process

Priorité haute — Checklist DI NestJS dans le template de plan

Cible : checklist pré-merge étape 6

Ajouter : "Pour chaque @UseGuards(XxxGuard) externe, vérifier que le module fournissant XxxGuard est dans les imports." Les tests unitaires ne détectent pas les erreurs DI.

Priorité moyenne — Threshold new_violations dans la checklist d'acceptabilité

Cible : Acceptabilité étape 7

Ajouter : "Si une correction CPD crée un nouveau fichier, relancer le scan Sonar local après extraction pour valider 0 new_violations."

Priorité basse — Documentation patterns Sonar dans CLAUDE.md

Cible : CLAUDE.md section "Règles apprises"

Ajouter la règle codePointAt + Math.trunc pour les fonctions de hashing.

11. Matrice de traçabilité

  • INV : 15/15 couverts (100%)
  • CA : 14/14 couverts (100%)
  • Endpoints : 8/8 testés (100%)
  • TC contractuels : TC-01 à TC-19, TC-INV-01, TC-LIM-01 à 04, TC-AUD-01 à 04, TC-NR-01 à 04, TC-SLA-01

12. Conclusion

PD-84 est une story de référence pour la maturité du processus de gouvernance IA. Le score Gate 8 de 9.625/10 en 1 itération, 133 tests sans échec, 92.52% de couverture et sécurité 10/10 valident l'investissement dans les artefacts de spécification et les patterns architecturaux éprouvés.

Les trois corrections post-merge identifient deux angles morts structurels : (1) l'absence de scan Sonar local avant merge, (2) l'incapacité des tests unitaires NestJS à détecter les erreurs DI. Les améliorations process de priorité haute et moyenne adressent ces deux points.

Le pattern pg_advisory_xact_lock + défense en profondeur guard/RLS constitue la contribution architecturale principale, directement réutilisable pour toute future story de quota.