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_lockvia historique d'audit — découverte à l'étape 5. La colonnelegal_lockseule 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
destructionJobIntervalentre job de destruction et job de préavis crée un couplage non voulu.preNoticeJobIntervalajouté 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
clockSkewTolerancesoustractive 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_lockviahad_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 != BORDEREAUdefense-in-depth — impact: faible. La protection implicite parretentionExpiry = nullest 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¶
-
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).
-
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.
-
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.
-
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.
-
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.