PD-103 — Retour d'expérience (REX)
1. Résumé exécutif
| Métrique | Valeur |
| Objectif initial | Capture probatoire d'écran iOS avec scellement crypto complet (SHA3-256, AES-256-GCM, RSA-OAEP, Merkle/TSA/HSM/blockchain) |
| Résultat obtenu | Conforme — 15 modules livrés (app + backend + infra), 39 invariants couverts, pipeline scellement en STUB (PD-55/56/41) |
| Verdict final | GO (Gate 8 v2 : 8.25/10) |
| Tests contractuels | Sonar QG OK — coverage 80.2%, duplication 0.95%, ESLint 0 erreur, TypeScript 0 erreur |
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 | 2 min | 1 | -93% |
| 1 - Spécification | 2h | 22 min | 1 | -82% |
| 2 - Tests | 1h | 8 min | 1 | -87% |
| 3 - Gate spec | 1h | 63 min | 3 | +5% |
| 4 - Plan | 1h | 13 min | 1 | -78% |
| 5 - Gate plan | 1h | 27 min | 2 | -55% |
| 6 - Implémentation | 4h | 9h 48min | 4 waves, 16 agents | +145% |
| 7 - Acceptabilité | 2h | 29 min | 1 | -76% |
| 8 - Gate acceptabilité | 1h | 52 min | 2 | -13% |
| 9 - REX | 30 min | ~30 min | 1 | 0% |
| TOTAL | ~14h | ~13.9h | 7 gate iter. | ~0% |
2.2 Scores de convergence par gate
| Gate | Score v1 | Score final | Delta | Itérations |
| Gate 3 | 5.75/10 | 8.0/10 | +2.25 | 3 |
| Gate 5 | 7.625/10 | 8.25/10 | +0.625 | 2 |
| Gate 8 | 7.875/10 | 8.25/10 | +0.375 | 2 |
2.3 Écarts par catégorie
| Catégorie d'écart | Gate 3 | Gate 5 | Gate 8 | Total |
| ECT (complétude/testabilité) | 17 | 3 | 4 | 24 |
| DIV (divergence spec/impl) | 11 | 3 | 8 | 22 |
| AMB (ambiguïté) | 0 | 3 | 1 | 4 |
| SEC (sécurité) | 0 | 0 | 5 | 5 |
| PERF (performance) | 0 | 0 | 0 | 0 |
| TOTAL écarts | 28 | 9 | 18 | 55 |
3. Points fluides
Ce qui a bien fonctionné :
- Injection learnings proactive : 4 learnings injectés (PD-101 upload iOS, PD-248 screenshot, PD-251 PENDING_SIGNATURE guard, PD-79 B2C mineurs) ont permis de contractualiser la garde
SEALED dès le besoin v1. - Spec v3 exhaustive : 39 invariants, machine à états à 8 états avec transitions exhaustives autorisées/interdites, payload canonique d'idempotence normé — qualité de spécification élevée.
- Implémentation multi-agents en 4 waves : 16 agents, 0 erreur TypeScript post-génération, architecture modulaire propre (M1-M15 bien découplés).
- Convergence Gate 3→Gate 8 décroissante : delta d'amélioration 2.25 → 0.625 → 0.375, montrant que chaque gate corrige de moins en moins (convergence saine).
- Sonar QG OK d'emblée : coverage 80.2% sans retouche, pas de blocage phase 1.5.
4. Points difficiles
Obstacles rencontrés (sans justification) :
- Gate 3 NON_CONFORME en v1 (5.75/10) : 3 BLOQUANTS sur le key exchange DEK/KEK non contractualisé, la reprise différée sans redemande URL, et l'absence de multipart. Score le plus bas du workflow, nécessitant 3 itérations.
- Step 6 à 9h48 (145% de dépassement) : 16 agents séquencés en 4 waves, complexité réelle du module
capture sous-estimée (crypto pipeline + upload single/multipart + FSM + reconciliation + idempotence + keyring). - Gate 8 : 3 BLOQUANTS pré-correction : E-01 (notification SEALED manquante), E-02 (schema migration↔entités incompatible), E-03 (enum DB incomplète). Corrigés avant soumission Gate 8 mais révèlent un gap entre step 6 output et la spec v3.
- 8 MAJEURS résiduels Gate 8 : Dont E-04 (
@HttpCode forçant 202), E-06 (incohérence multipart object_key), S-02 (fuite ocr_text en clair dans AsyncStorage), T-01/T-02 (tests auth manquants).
5. Hypothèses révélées tardivement
- SHA3-256 non natif React Native — découverte à l'étape 1 (spec). Polyfill
@noble/hashes/sha3 ou react-native-quick-crypto requis, non anticipé dans le besoin initial. react-native-quick-crypto potentiellement incompatible SHA3 — découverte à l'étape 4 (plan). Fallback @noble/hashes documenté comme dérogation avec benchmark P95 obligatoire. - Limitation zéroïsage mémoire JS — découverte à l'étape 3 (Gate 3 v2, ECT-05-v2).
TypedArray.fill(0x00) n'offre aucune garantie d'effacement physique (GC non déterministe V8/Hermes). upload_object_key absent du payload canonique initial — découverte à l'étape 3 (Gate 3 v3, ECT-01-v3). Nécessaire pour discriminer des uploads S3 différents du même fichier.
6. Invariants complexes
Invariants difficiles à implémenter ou sensibles aux régressions :
- INV-103-37 (idempotency-canonical-fingerprint) — TC-INV-06 : sérialisation JSON déterministe avec clés triées, normalisation lowercase, exclusion OCR optionnel. 4 variantes de test (A/B/C/D). Risque de régression sur toute modification du payload.
- INV-103-34 (kek-keyring-rotation) — TC-INV-13, TC-NOM-16 : le keyring doit couvrir
deferredUploadTtl + dedupWindow (24h + 24h min). Profondeur insuffisante = 422 sur captures différées après rotation. - INV-103-32 (dek-zeroization) — TC-INV-12 : limitation fondamentale du runtime JS. Test
buffer === 0x00 vérifie le buffer applicatif mais pas la mémoire physique. - INV-103-33 (s3-orphan-gc) — TC-INV-11 : scan S3 par prefix dépend du bucket config Terraform (M15). Orphelin non détecté si prefix mismatch.
- INV-103-08 (sealed-guard) — TC-NOM-08, TC-ERR-08 : double condition
signature_status='SIGNED' AND hsm_signature_ref IS NOT NULL. Fenêtre PENDING_SIGNATURE critique.
7. Dette technique
Compromis acceptés et non bloquants :
- Pipeline scellement en STUB — impact: élevé. STUB: PD-55 (TSA), PD-56 (Merkle), PD-41 (blockchain). Les transitions
PENDING_SEAL → SEALED → ANCHOR_CONFIRMED non testables E2E. - Fichiers manquants (E-09) — impact: moyen.
CaptureProgress.tsx, tests app côté mobile, module infra S3 (M15) non générés par les agents step 6. @HttpCode(202) conditionnel (E-04) — impact: faible. Workaround pour supporter 200 idempotent + 202 nouveau. Pattern non standard NestJS. ocr_text en clair dans AsyncStorage (S-02) — impact: moyen. Les captures différées stockent ocr_text non chiffré dans AsyncStorage. À corriger dans une story de hardening. - Tests auth 401/403 absents (T-01/T-02) — impact: faible. Guard
@Roles et auth 401 non couverts par tests explicites PD-103.
8. Risques résiduels
| Risque | Type | Probabilité | Impact | Mitigation |
| Transcodage implicite PNG après upgrade Expo SDK | tech | moyen | élevé | TC-NR-01 (hash triplet) à exécuter après chaque upgrade |
| SHA3-256 pure JS trop lent sur images >100MB | perf | faible | élevé | Benchmark P95 obligatoire ; fallback module natif JSI |
| Keyring KEK trop court après rotation fréquente | sec | faible | élevé | Profondeur configurable (3 par défaut), rétention >= deferredUploadTtl + dedupWindow |
| Orphelins S3 non détectés si prefix mismatch | ops | faible | moyen | Prefix dédié captures/ dans config Terraform M15 |
| Watchdog TTL différé ne s'exécute pas en background iOS | tech | moyen | moyen | purgeStale() au prochain lancement couvre le cas |
Fuite ocr_text clair dans AsyncStorage | sec | élevé | moyen | Story de hardening à planifier (chiffrement AsyncStorage) |
8bis. Matrice de délégation inter-PD
| Story | Direction | Statut | Nature de la dépendance | Problème rencontré |
| PD-55 | <- dépend de | STUB | Pipeline TSA/scellement backend | Transitions PENDING_SEAL->SEALED non testables E2E |
| PD-56 | <- dépend de | DONE | Merkle proof generation | Livré 2026-04-01. Intégration leaf hash via BullMQ post-commit |
| PD-41 | <- dépend de | STUB | Ancrage blockchain | Transition SEALED->ANCHOR_CONFIRMED non testable E2E |
| PD-101 | <- réutilise | DONE | Pattern upload avec progress | Réutilisé pour M4 upload-service, pattern Zustand persist |
| PD-248 | <- learning | DONE | Screenshot iOS capture pattern | Learning injecté step 0 |
| PD-251 | <- learning | DONE | PENDING_SIGNATURE guard | Learning injecté step 0, INV-103-08 directement inspiré |
| PD-284 | -> bloque | TODO | UX scellement urgent SSE | Nécessite les notifications SEALED de PD-103 |
8ter. Bugs de tests
| Pattern incorrect | Pattern correct | Cause | Coût |
@HttpCode(202) global | @HttpCode conditionnel 200/202 | Idempotence requiert 200 pour replay | ~30 min |
| Entity noms divergents vs migration | Alignement noms table/colonnes | Schema migration généré séparément des entités | ~45 min |
Enum DB sans SEAL_DELAYED + états mobiles | Enum complète avec tous les états | Agent migration (Wave 1) n'avait pas la spec v3 complète | ~20 min |
8quater. Corrections post-Gate 8
| Correction | Fichier | Nature | Pipeline |
| Notification SEALED ajoutée | notifications.ts | Fix fonctionnel (E-01 BLOQUANT) | commit 5abf199 (app) |
| Schema migration/entités aligné | migrations + entities | Fix schema (E-02 BLOQUANT) | commit 5228173 (backend) |
| SEAL_DELAYED + 4 états ajoutés enum DB | migration | Fix enum (E-03 BLOQUANT) | commit 5228173 (backend) |
| @HttpCode conditionnel 200/202 | capture.controller.ts | Fix idempotence (E-04 MAJEUR) | commit 5228173 (backend) |
9. Patterns récurrents détectés
9.1 Patterns confirmés (déjà vus dans d'autres stories)
- Key exchange non contractualisé en v1 — aussi dans PD-282 (ProofEnvelope HSM), PD-277 (anti-rejeu PKI). Les flows crypto côté client requièrent une contractualisation DEK/KEK dès la spécification initiale, pas en correctif Gate 3.
- Gate 3 NON_CONFORME en v1 sur stories crypto — aussi dans PD-56 (6.5/10), PD-282 (6.56/10), PD-276 (6.0/10). Les stories avec crypto complexe sous-performent systématiquement en Gate 3 v1.
- Schema migration/entités divergent — aussi dans PD-282, PD-279. Quand migration et entités sont générés par des agents différents (ou à des moments différents), les noms divergent.
purgeStale() au démarrage du flux — pattern confirmé et validé depuis PD-283, PD-262. Systématiquement exigé pour toute story manipulant des artefacts temporaires sensibles. - Limitation zéroïsage mémoire JS — aussi dans PD-97, PD-98. Pattern documenté et accepté comme limitation fondamentale du runtime.
9.2 Nouveaux patterns identifiés
- Payload canonique d'idempotence avec fingerprint SHA-256 — pattern nouveau pour PD-103. Sérialisation JSON déterministe (clés triées, normalisation lowercase, exclusion champs optionnels) comme contrat d'idempotence. À surveiller pour réutilisation dans d'autres APIs backend.
- KEK keyring avec
kek_id pour rotation transparente — pattern nouveau. Permet de déchiffrer les uploads différés après rotation de clé. Rétention minimum deferredUploadTtl + dedupWindow. - GC orphelins S3 par réconciliation cron — pattern nouveau. Scan S3 vs DB pour détecter et supprimer les objets sans enregistrement backend correspondant.
- Step 6 à 16 agents en 4 waves — plus grande implémentation multi-agents à date. Temps réel 9h48 pour 15 modules. Facteur déterminant du temps total.
10. Améliorations du workflow
10.1 Améliorations des prompts/templates
| Fichier | Amélioration suggérée | Priorité |
templates/prompts/step1-spec.md | Ajouter une section obligatoire "Contrat crypto" pour toute story impliquant chiffrement client-side (DEK/KEK, key exchange, zeroization) | haute |
templates/prompts/step6b-agent.md | Injecter la migration DDL complète (pas seulement le contrat du module) dans le contexte de chaque agent backend pour éviter les divergences schema/entités | haute |
templates/prompts/step7-review-code.md | Ajouter un check explicite "enum DB vs code usage" dans la checklist de review | moyenne |
10.2 Améliorations des agents
| Agent | Amélioration suggérée | Justification |
| Agent migration (step 6b) | Générer l'entité TypeORM ET la migration dans le même agent | 3 BLOQUANTS Gate 8 liés à divergence migration/entité |
| Agent tests-integration | Inclure tests auth 401/403 dans le template minimal | T-01/T-02 résiduels Gate 8 |
10.3 Améliorations du processus
- Estimation step 6 pour stories >10 modules : le ratio estimé/réel est de 1:2.5 (4h estimé vs 9h48 réel). Pour les stories avec >10 modules, multiplier l'estimation par 2.5.
- Vérification TypeScript incrémentale post-wave : confirmé fonctionnel (0 erreur TS). Le pattern Wave 1->Wave 4 avec
npx tsc --noEmit entre chaque wave est validé. - Pre-check enum DB avant Gate 8 : ajouter une vérification automatique que tous les états utilisés dans le code existent dans les migrations DDL.
11. Enseignements clés
-
Contractualiser le key exchange crypto dès le besoin — Pour toute story avec chiffrement client-side, le protocole DEK/KEK (algorithme, wrapping, zeroization, rotation) doit être dans le besoin v1. L'ajout en correctif Gate 3 coûte 2 itérations supplémentaires et cascade sur tous les documents.
-
L'idempotence déterministe nécessite un contrat de sérialisation normé — Un simple capture_id comme clé d'idempotence ne suffit pas. Le fingerprint canonique (JSON clés triées, normalisation, exclusion champs optionnels) est le seul contrat testable pour distinguer 200 vs 409.
-
Les stories >10 modules nécessitent un budget step 6 de 2.5x l'estimation standard — PD-103 (15 modules, 4 waves, 16 agents) a pris 9h48 vs 4h estimé. Le parallélisme inter-waves ne compense pas la complexité séquentielle intra-wave.
-
Migration et entités doivent être générées par le même agent — La séparation en agents distincts (migration vs service) produit des divergences de nommage (table, colonnes, types enum) qui deviennent des BLOQUANTS Gate 8.
-
Le pattern purgeStale() + suppression immédiate post-UPLOADED est le standard sécurité mobile — Purge au démarrage (résilience crash) + purge immédiate après ACK (réduction surface) + TTL watchdog (garde ultime). Trois niveaux de défense couvrant tous les scénarios de rétention locale.
12. Métriques cumulatives (auto-calculées)
| Métrique | Cette story | Moyenne projet | Tendance |
| Temps total | 13.9h | 6.47h | ↑ (story complexe, 2x la moyenne) |
| Itérations gates | 7 | 5.3 | ↑ (Gate 3 à 3 iter. — crypto) |
| Écarts totaux | 55 | 15.1 | ↑ (39 invariants, story la plus spécifiée) |
| Score convergence moyen | 8.17/10 | 8.45/10 | ↓ (Gate 3 v1 NON_CONFORME tire la moyenne) |