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
OidcJwtAuthGuardau niveau classe (pas méthode) sur 5 contrôleursFolderOwnerGuard+ RLS PostgreSQL (défense en profondeur)ParseUUIDPipesur tous les path params UUIDclass-validatoravec whitelist strict sur DTOscrypto.randomUUID()exclusivement (0 occurrence deMath.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) —
beforeEachnon factorisés AddDocumentDtoinline (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 (charCodeAt → codePointAt) et S7767 (| 0 → Math.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¶
- 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 - 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 lesimportsdu module - L-84-04
#postgresql #quota: Patternpg_advisory_xact_lock + hashToIntréutilisable pour tout quota atomique sous concurrence - L-84-05
#security #nestjs:@UseGuardsniveau classe (pas méthode) pour protection automatique de tout nouvel endpoint - L-84-06
#sonar #pipeline:new_violations = 0s'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.