Aller au contenu

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

1. Résumé exécutif

Métrique Valeur
Objectif initial Automatiser la destruction définitive des documents expirés avec bordereau probatoire signé/horodaté, conforme ISO 14641 / NF Z42-013 / eIDAS / RGPD
Résultat obtenu Conforme — module destruction/ complet (23 fichiers source, 17 fichiers tests, 475 tests passés, coverage 92.79%)
Verdict final RESERVE (accepté par PO) — Gate 8 : 7.875/10
Tests contractuels 475/475 passés
Coverage 92.79% global, 98.98% services, 99.37% processors

2. Métriques de convergence

2.1 Temps et itérations

Étape Durée estimée Durée réelle Itérations Écart
0 - Besoin 30 min 30 min 1 0%
1 - Spécification 2h 3h 4 (v1→v4) +50%
2 - Tests 1h 1.5h 4 (couplés spec) +50%
3 - Gate spec 1h 4h 4 (v1→v4, ESCALADE) +300%
4 - Plan 1h 2h 1 +100%
5 - Gate plan 1h 1h 1 0%
6 - Implémentation 4h 8h 1 +100%
7 - Acceptabilité 2h 2h 1 0%
8 - Gate acceptabilité 1h 1h 1 0%
9 - REX 30 min 30 min 1 0%
TOTAL ~14h ~23.5h 15 +68%

2.2 Scores de convergence par gate

Gate Score v1 Score final Delta Itérations
Gate 3 5.75/10 8.625/10 +2.875 4
Gate 5 7.875/10 7.875/10 0 1
Gate 8 7.875/10 7.875/10 0 1

2.3 Écarts par catégorie

Catégorie d'écart Gate 3 Gate 5 Gate 8 Total
ECT (complétude/testabilité) 2 3 6 11
DIV (divergence spec/impl) 2 2 4 8
AMB (ambiguïté) 3 1 0 4
SEC (sécurité) 1 1 1 3
PERF (performance) 0 0 0 0
TOTAL écarts 8 7 11 26

3. Points fluides

Ce qui a bien fonctionné :

  • Injection des learnings PD-81/PD-55/PD-264 : les invariants issus de stories précédentes (BullMQ v5 API, noms de queue sans :, atomicité DB synchrone vs BullMQ asynchrone) ont été intégrés directement dans la spécification et n'ont généré aucun écart en gates.
  • Architecture modulaire : le découpage en 16 tâches avec DAG de dépendances et parallélisation par niveau a permis une implémentation structurée. L'arborescence finale correspond exactement au plan.
  • Validation croisée inter-LLM : la confrontation ChatGPT/Claude a identifié des divergences réelles (destructionExecutionSla FAILED vs PARTIAL_FAILED, RECONCILIATION_FAILED non couvert par INV-250-11) qui auraient causé des bugs en production.
  • Gate 5 en 1 itération : le plan a passé Gate 5 AMBIGUITY avec RESERVE au premier passage (7.875/10), les réserves étant des enrichissements (audit log, intégration test, variable dédiée préavis) et non des corrections.
  • Coverage services 99% : la couverture des services critiques (éligibilité, bordereau, exécution, réconciliation) est quasi-complète.
  • 10 hypothèses d'implémentation toutes validées : aucune hypothèse n'a été invalidée en cours d'implémentation (pdf-lib CJS, EventEmitter2 @OnEvent, Guard réutilisable, etc.).

4. Points difficiles

Obstacles rencontrés (sans justification) :

  • Gate 3 : 4 itérations avec ESCALADE — la spécification initiale (v1) a obtenu 5.75/10 avec clarity 4.0. Les itérations v2 et v3 ont stagné à clarity 3.0 (convergence delta=0 → STOP). L'escalade humaine a été nécessaire pour débloquer avec correction directe Claude sur les ambiguïtés terminologiques.
  • Complexité normative — la conformité simultanée ISO 14641, NF Z42-013, eIDAS et RGPD a généré des tensions de conception (granularité bordereau vs minimisation RGPD, zeroization sélective vs suppression universelle).
  • RECONCILIATION_FAILED comme état formel — cet état de document (pas de batch) n'était pas modélisé dans la v1 de la spécification. Sa formalisation complète (transitions interdites, complétude audit, escalade obligatoire) a nécessité 3 itérations de Gate 3.
  • Phase 1.5 Sonar non exécutée — Docker indisponible localement, sonar-scanner non installé. Dérogation formalisée avec couverture de substitution (ESLint strict + TypeScript strict + 0 erreur).
  • Preuve contractuelle TC-250-20 insuffisante — le test du rejet 403 de l'endpoint admin bordereaux vérifiait le mécanisme (guard + filter) mais pas le contrat complet (HTTP 403 + audit ACCESS_DENIED).

5. Hypothèses révélées tardivement

Hypothèses non explicites découvertes en cours de workflow :

  • Backfill had_legal_lock via historique d'audit — découverte à l'étape 5. La colonne legal_lock seule ne suffit pas si un legal_lock a été retiré (pas expiré). L'historique d'audit (legal_lock.applied, legal_lock.activated) doit être consulté pour le backfill complet.
  • Variable de configuration dédiée pour le préavis — découverte à l'étape 5. Le partage de destructionJobInterval entre job de destruction et job de préavis crée un couplage non voulu. preNoticeJobInterval ajouté comme variable distincte.
  • Test d'intégration PostgreSQL requis pour audit ACID — découverte à l'étape 5. La mesurabilité de l'audit (non-perte, monotonie, complétude) n'est pas démontrable en test mocké. Un test d'intégration réel (TC-250-16) est nécessaire.

6. Invariants complexes

Invariants difficiles à implémenter ou sensibles aux régressions :

  • INV-250-05 (audit mesurable) — TC-250-09, TC-250-16. La triple propriété (non-perte, ordre causal, complétude) nécessite une séquence PostgreSQL dédiée (audit_destruction_seq), un audit transactionnel dans la même TX ACID que la finalisation DESTROYED, et un test d'intégration réel pour la preuve de crash recovery.
  • INV-250-01 (triple condition WORM) — TC-250-01. La convergence DB + S3 Object Lock + legal_lock avec clockSkewTolerance soustractive sur les 3 conditions crée une query complexe. Les faux positifs d'éligibilité sont le risque principal.
  • INV-250-12 (zeroization flux legal_lock) — TC-250-17. La détection du flux legal_lock via had_legal_lock (historique, pas seulement état courant) et l'ordonnancement strict (zeroization → S3 → DB) avec fail-closed sont sensibles aux régressions.
  • INV-250-11 + MAJ-28 (transitions interdites) — TC-250-15. 5 transitions interdites incluant RECONCILIATION_FAILED → *. L'ajout tardif de MAJ-28 (Gate 3 v4) montre que le modèle d'états initial était incomplet.

7. Dette technique

Compromis acceptés et non bloquants :

  • Stubs TSA/S3 (TODO(PD-250)) — impact: moyen. Les adapters TSA (signature + horodatage) et S3 (suppression) sont stubés avec fail-closed. À remplacer par les implémentations réelles en intégration.
  • Sonar Quality Gate non exécutée — impact: moyen. Dérogation formalisée : ESLint strict + TypeScript strict + 0 erreur comme couverture de substitution. À vérifier post-merge via pipeline CI/CD GitLab.
  • Filtre entity_type != BORDEREAU defense-in-depth — impact: faible. La protection implicite par retentionExpiry = null est suffisante mais le filtre explicite dans la query d'éligibilité est souhaitable.
  • trackedDefault() non implémenté — impact: faible. Les defaults numériques sont directs dans la config Joi. L'observabilité des defaults appliqués est réduite.
  • Matrice combinatoire 9 cas WORM — impact: faible. La couverture est partielle (3 cas principaux testés). Les 6 combinaisons restantes sont couvertes implicitement par la query SQL mais sans test explicite.
  • 4 tests globaux pré-existants en échec — impact: nul. Confirmés identiques sur main (deposit.controller, delete-account-rate-limit, reauth-token-blacklist, session-invalidation). Hors périmètre PD-250.

8. Risques résiduels

Risque Type Probabilité Impact Mitigation
Migration ALTER TYPE bloque en prod (lock exclusif) tech moyenne élevé Migration hors heures, test en staging avec volume réaliste
TSA externe indisponible en production ops faible élevé Fail-closed + retry + alerte, stub actuel à remplacer
S3 eventual consistency (objet lisible après DELETE 204) tech certaine faible Risque accepté, zeroization couvre flux legal_lock
Backfill had_legal_lock incomplet (legal_lock retiré sans audit) métier faible moyen Vérification historique audit ajoutée (MAJ-04)
Sonar découvre des issues post-merge tech moyenne moyen Pipeline CI/CD vérifie automatiquement

8bis. Matrice de délégation inter-PD

Story Direction Statut Nature de la dépendance Problème rencontré
PD-21 ← dépend de DONE BullMQ infrastructure, BaseProcessor, queue naming RAS — pattern réutilisé
PD-63 ← dépend de DONE DocumentSecure entity, DocumentStatus enum, legal_lock fields RAS — entity extensible
PD-81 ← dépend de DONE LegalDestructionService (pattern zeroization) RAS — pattern adapté
PD-39 ← dépend de DONE (STUB) TSA module RFC 3161 (signature + horodatage) Stub TODO(PD-250) — adapter créé
PD-37 ← dépend de DONE AuditLogService, AuditActionType enum RAS — interface étendue avec actions destruction
PD-36 ← dépend de DONE (STUB) HSM PKCS#11 client (signature qualifiée) Stub TODO(PD-250) — adapter créé
PD-55 ← dépend de DONE BullMQ v5 API (getJobSchedulers) RAS — learning intégré
PD-240 ← dépend de DONE Atomicité suppression/purge RGPD RAS — pattern réutilisé
PD-264 ← dépend de DONE Transaction DB synchrone vs BullMQ asynchrone RAS — learning intégré

8ter. Bugs de tests

Pattern incorrect Pattern correct Cause Coût
TC-250-20 : expect(response.statusCode).toBeDefined() expect(response.statusCode).toBe(403) + expect(auditService.logBatchEvent).toHaveBeenCalledWith(DOCUMENT_DESTROY_ACCESS_DENIED) Assertion trop faible sur preuve contractuelle INV-250-15 30 min (correction post-review)

8quater. Corrections post-Gate 8

Correction Fichier Nature Pipeline
Renforcer TC-250-20 (403 + audit) bordereau.controller.spec.ts +assertions contractuelles post-merge CI/CD
Sonar Quality Gate vérification pipeline CI/CD post-merge CI/CD

9. Patterns récurrents détectés

9.1 Patterns confirmés (déjà vus dans d'autres stories)

  • Clarity oscillante en Gate 3 pré-ESCALADE — aussi dans PD-81 (clarity 4.25 v1 → 9.25 v3, 3 itérations), PD-84 (3 itérations). Les spécifications normatives (ISO, eIDAS) génèrent systématiquement des ambiguïtés terminologiques qui résistent aux corrections ChatGPT. La correction directe par Claude après escalade converge plus vite.
  • BullMQ v5 API deprecated — pattern intégré dans l'invariant INV-250-14 (issu de PD-55). Zero écart en gates sur ce point. Le learning-as-invariant fonctionne.
  • Sonar non exécutable localement — aussi dans PD-55, PD-107, PD-31, PD-44, PD-82, PD-248, PD-177. Pattern récurrent : Docker indisponible ou sonar-scanner non installé. Les corrections post-Gate 8 causées par Sonar QG FAILED sont un coût récurrent.
  • Test 403/audit insuffisant pour endpoints admin — pattern similaire dans PD-63 (endpoint download, assertion triviale). Les tests de contrôle d'accès vérifient le mécanisme (guard) mais pas le contrat (HTTP code + audit trail).
  • Stubs de services externes (TSA, HSM, S3) — aussi dans PD-63 (S3 adapter stub), PD-81 (HSM stub). Les adapters sont créés avec fail-closed mais les tests d'intégration réelle sont reportés.

9.2 Nouveaux patterns identifiés

  • ESCALADE comme accélérateur de convergence Gate 3 — première fois observé formellement : les itérations v2/v3 ont stagné (delta=0) avant que l'escalade humaine permette une correction directe par Claude (clarity 3.0 → 7.75 en une itération). Ce pattern suggère que le plafond de 3 itérations est pertinent pour déclencher l'escalade.
  • Historique d'audit comme source de vérité pour backfill — première fois qu'un backfill de migration nécessite la consultation des logs d'audit (événements legal_lock.*) en plus des colonnes DB courantes. Ce pattern est spécifique aux champs qui peuvent être retirés (pas seulement expirés).
  • Test d'intégration PostgreSQL bloquant pour audit ACID — premier cas (BLQ-01 Gate 5) où un invariant (INV-250-05 mesurabilité) nécessite explicitement un test d'intégration réel, pas un mock. Ce pattern s'applique à tout invariant impliquant des propriétés ACID transactionnelles.

10. Améliorations du workflow

10.1 Améliorations des prompts/templates

Fichier Amélioration suggérée Priorité
templates/prompts/1-specification.md Ajouter une checklist obligatoire : pour chaque état de la machine à états, lister les transitions sortantes autorisées ET interdites explicitement. Évite le pattern MAJ-28 (RECONCILIATION_FAILED non couvert par INV). haute
templates/prompts/7a-review-code.md Ajouter un axe de review : « Preuve contractuelle des guards d'accès — vérifier que chaque @Roles() a un test assertant le HTTP status code exact ET l'audit trail ». haute
templates/prompts/3-specification-review.md Renforcer le scoring clarity : les ambiguïtés terminologiques (ex: « Async post-validation » vs await séquentiel) doivent être détectées comme des contradictions internes, pas comme des manques de détail. moyenne

10.2 Améliorations des agents

Agent Amélioration suggérée Justification
agent-developer Pour les modules avec machine à états formelle, générer un tableau exhaustif des transitions (autorisées + interdites) dans le code contract, pas seulement les transitions autorisées. Écart MAJ-28 — l'agent n'a pas pensé à interdire RECONCILIATION_FAILED → *
agent-qa-unit-integration Pour les endpoints protégés par @Roles, le test DOIT asserter : (1) HTTP status exact, (2) absence de contenu dans le body, (3) appel audit trail. Pattern minimal non négociable. Écart DIV-02 — assertion triviale dans TC-250-20

10.3 Améliorations du processus

  • Sonar local : installer sonar-scanner sans Docker — Le pattern récurrent (6+ stories) de Sonar non exécutable localement justifie l'installation d'un sonar-scanner CLI natif (npx sonar-scanner) ou d'un scan Sonar via l'API REST du serveur GitLab. Cela éliminerait les dérogations Phase 1.5 et les corrections post-Gate 8.
  • Gate 3 : seuil d'escalade automatique à delta=0 — Quand 2 itérations consécutives de Gate 3 ont le même score (delta=0), déclencher automatiquement l'escalade au lieu d'attendre le plafond de 3 itérations. PD-250 aurait économisé 1 itération (v2 et v3 identiques).
  • Backfill migration : checklist « sources de vérité complémentaires » — Pour tout backfill de colonne booléenne dérivée d'un état historique, exiger une vérification explicite des sources : (1) colonnes DB courantes, (2) historique d'audit, (3) event store si applicable.

11. Enseignements clés

  1. Les learnings-as-invariants évitent des itérations de gate — Les 3 invariants issus de PD-55/PD-264 (BullMQ v5, noms de queue, atomicité DB/BullMQ) ont généré 0 écart en 3 gates. ROI : ~4.5 itérations évitées (1.5 par invariant).

  2. L'escalade humaine est un accélérateur, pas un échec — Gate 3 v2/v3 ont stagné à clarity 3.0 (delta=0). L'escalade + correction directe Claude a résolu en 1 itération ce que 2 itérations ChatGPT n'avaient pas pu corriger. Le plafond de 3 itérations est un garde-fou pertinent.

  3. La preuve contractuelle des guards d'accès nécessite des assertions complètes — Vérifier que le guard existe (mécanisme) ne suffit pas. Le test DOIT asserter le HTTP status exact + l'absence de contenu + l'audit trail. Ce pattern est applicable à tous les endpoints protégés.

  4. Les propriétés ACID transactionnelles exigent des tests d'intégration réels — Les mocks ne peuvent pas prouver la non-perte, la monotonie et la complétude d'audit après crash recovery. Premier cas formalisé comme BLQ en Gate 5.

  5. La complexité normative multi-standard multiplie le temps de spécification — La conformité ISO 14641 + NF Z42-013 + eIDAS + RGPD a triplé le temps de Gate 3 (4h vs 1h estimé). Pour les stories normatives, prévoir un budget Gate 3 de 3-4h minimum.

12. Métriques cumulatives (auto-calculées)

Métrique Cette story Moyenne projet (42 stories) Tendance
Temps total 23.5h 11.5h ↑ (+104%)
Itérations gates 6 4.6 ↑ (+30%)
Écarts totaux 26 6.8 ↑ (+282%)
Score convergence moyen 8.125/10 8.35/10 ↓ (-3%)

PD-250 est la story la plus complexe (normative multi-standard) du backlog. Les métriques élevées reflètent la richesse de la spécification (16 invariants, 16 CA, 8 ERR, 16 paramètres configurables) et non une dégradation du processus.