Aller au contenu

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

1. Résumé exécutif

Métrique Valeur
Objectif initial Détection locale de contenu sensible (IA offline) au viewer iOS
Résultat obtenu Conforme avec réserves — architecture complète, modèle ML stubé
Verdict final RESERVE (7.875/10 Gate 8)
Tests contractuels 187/187 passés (17 suites, ~96% coverage)
Code livré 23 composants, 63 fichiers, 9315 lignes
Durée totale 3.35h

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 38 min 1 +27%
1 - Spécification 2h 4 min 1 -97%
2 - Tests 1h 4 min 1 -93%
3 - Gate spec 1h 58 min 2 -3%
4 - Plan 1h 16 min 1 -73%
5 - Gate plan 1h 6 min 1 -90%
6 - Implémentation 4h 41 min 1 -83%
7 - Acceptabilité 2h 10 min 1 -92%
8 - Gate acceptabilité 1h 7 min 1 -88%
9 - REX 30 min ~30 min 1 0%
TOTAL ~14h 3.35h 11 -76%

Observation : Workflow significativement plus rapide que l'estimation standard (14h). Les étapes ChatGPT (1, 2) sont quasi-instantanées grâce à OpenCode. L'implémentation mono-agent (41 min pour 9315 lignes) est le reflet d'une story entièrement local-side (pas de backend, pas de migrations, pas de pipeline CI complexe).

2.2 Scores de convergence par gate

Gate Score v1 Score final Delta Itérations
Gate 3 4.875/10 7.75/10 +2.875 2
Gate 5 7.5/10 7.5/10 0 1
Gate 8 7.875/10 7.875/10 0 1

Analyse : Gate 3 a nécessité une correction significative (v1 NON_CONFORME → v2 RESERVE). Les Gates 5 et 8 ont convergé en une seule itération, signe que les corrections Gate 3 ont été bien propagées. Tous les verdicts sont RESERVE — aucun GO franc. Les réserves portent sur des stubs documentés et de la dette technique tracée.

2.3 Écarts par catégorie

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

Observation : Le nombre d'écarts est élevé (45 total) par rapport aux stories récentes, reflet de la complexité du domaine (IA locale + sécurité + mineurs + multi-format). Les AMB (17) dominent, liés aux nombreuses zones d'ombre d'une spec qui couvre un domaine nouveau (classification visuelle sur mobile). Les ECT (20) reflètent la v1 de Gate 3 qui était très déficiente (22 écarts, score 4.875).

3. Points fluides

  • Architecture modulaire : Le découpage en 23 composants / 8 modules a permis une implémentation propre et testable. L'interface ContentClassifier abstraite permet le swap de modèle ML sans refactoring.
  • Patterns existants réutilisés : SecureBuffer.destroy(), useAutoLock, cacheCleaner, expo-screen-capture — la base ProbatioVault-app offre des briques de sécurité matures.
  • Gate 3 v1 → v2 : Correction massive (22 écarts résolus, delta +2.875) efficace — la spec v2 est substantiellement meilleure que la v1.
  • Coverage exceptionnelle : 96% statements sur un module de 23 composants, avec tous les chemins d'erreur testés.
  • Tests contractuels exhaustifs : 34 TC spécifiés, 34 implémentés, 187 tests Jest. Mapping INV/CA complet (20/20 INV, 19/19 CA).
  • Implémentation rapide : 41 min pour 9315 lignes — bénéfice d'un périmètre bien spécifié et 100% local (pas de backend).

4. Points difficiles

  • Gate 3 v1 très déficiente : La première spécification ChatGPT était incomplète (score 4.875). Il a fallu une correction lourde (22 écarts) pour atteindre un niveau acceptable.
  • Sonar Quality Gate non exécutable : Token Sonar absent de Vault. Validation différée au pipeline CI/CD — processus dégradé.
  • Review QA LLM partielle : OpenCode n'a pas traité le code de test inliné dans le prompt. La review QA automatique est un point faible récurrent du workflow.
  • Cache non chiffré : INV-86-08 (cache local chiffré) non satisfait. AsyncStorage plain avec sandboxing iOS comme mitigation. Dette technique tracée.
  • Stubs multiples : 3 stubs (isMinor/PD-84, OnnxClassifier/H-01, VideoExtraction/H-04) laissent des fonctionnalités partiellement implémentées.

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

  • Swift/SwiftUI vs React Native : La spec mentionne "iOS (Swift/SwiftUI)" en §10 alors que le projet est React Native Expo. Écart documentaire découvert à l'étape 5 (DIV-07/ECT-03 Gate 5). La spec aurait dû refléter la stack réelle du projet dès l'étape 1.
  • Extension requiresExplicitConfirmation : Le plan étend la formule mineur de isMinor && isSensitive à isMinor && (isSensitive || isErrorStatus). Protection supplémentaire non contractualisée en spec, identifiée à l'étape 3 v2 (ECT-v2-02). Adressée dans le plan mais non retranscrite en spec.
  • Invariants hors spec dans code contracts : Les code contracts ajoutent des invariants non tracés en spec (classifieur stateless, TTL 90j, déterminisme sampling, warm-up). Découvert à l'étape 5.
  • rawScores dans blacklist logging : Le plan ajoute rawScores à la blacklist de logging — durcissement non contractualisé mais defense-in-depth cohérente. Découvert à l'étape 8.

6. Invariants complexes

  • INV-86-09 (politique prudente sur 6 codes d'erreur) — TC-86-08/09/18/19/20/26 : Invariant transversal touchant tous les chemins d'erreur. La combinaison avec suppressWarning et isMinor crée une matrice de cas complexe. L'extension requiresExplicitConfirmation aux erreurs pour mineurs (ECT-v2-02) illustre la difficulté de contractualiser exhaustivement les cas croisés.
  • INV-86-19 (idempotence + sérialisation concurrente) — TC-86-31/32 : Le mutex par (documentId, modelVersion) via Map<string, Promise> est un pattern subtil en JavaScript (pas de vrai mutex natif). Les race conditions restent un risque moyen, mitigé par le timeout aligné sur 5000ms.
  • INV-86-10 (zeroization best-effort) — TC-86-16 : Limitation fondamentale de JavaScript (GC non déterministe). SecureBuffer.destroy() est best-effort. Migration vers module natif Rust identifiée comme dette future.
  • INV-86-08 (cache local chiffré) — TC-86-06 : Non satisfait dans cette livraison. AsyncStorage plain — seul invariant non respecté. Sandboxing iOS comme mitigation.

7. Dette technique

  • Cache AsyncStorage non chiffré (INV-86-08) — impact: moyen. Migration vers expo-secure-store ou chiffrement AES-256-GCM via K_master. TODO tracé.
  • Stub isMinor (PD-84) — impact: moyen. Retourne false par défaut. Fonctionnalité mineur non testable en production tant que PD-84 non livré.
  • MockClassifier en production (H-01) — impact: élevé. L'architecture de détection est complète mais le classifieur réel (ONNX) n'est pas intégré. Scores statiques. Story séparée requise pour le modèle ML.
  • Extraction vidéo stubée (H-04) — impact: moyen. Découpage uniforme du buffer au lieu de FFmpeg/AVFoundation natif. Flux fonctionnel mais non représentatif de la performance réelle.
  • Zeroization JS best-effort — impact: faible. Limitation runtime JavaScript. Migration Rust à terme.

8. Risques résiduels

Risque Type Probabilité Impact Mitigation
Performance ML sur iPhone 12 (P95 700ms) tech moyenne élevé Benchmark device obligatoire avant mise en production (H-03/H-09)
OnnxClassifier incompatible React Native tech faible élevé Interface abstraite permet swap vers CoreML bridge natif
Cache compromis sur device jailbreaké sécu faible moyen Migration chiffrement AES-256-GCM (dette INV-86-08)
isMinor stub neutralise garde-fous mineur métier certaine moyen Dépend PD-84 — stub tracé, fallback safe (false=adulte)
Sonar QG échoue post-merge en pipeline ops faible moyen Corrections immédiates si alerté
PDF OOM sur documents volumineux (>64MB) tech moyenne moyen Guard explicite + OUT_OF_MEMORY

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

Story Direction Statut Nature de la dépendance Problème rencontré
PD-97 (crypto zero-knowledge) ← dépend de DONE SecureBuffer, zeroization, K_master RAS
PD-248 (screenshot protection) ← dépend de DONE expo-screen-capture, pattern screenshotProtection.ts RAS
PD-174 (auto-lock) ← dépend de DONE useAutoLock, cacheCleaner RAS
PD-98 (Keychain K_master) ← dépend de DONE Clé de dérivation pour chiffrement cache RAS
PD-84 (B2C-MINEURS offre) ← dépend de TODO (STUB) Flag isMinor dans profil API Stub false par défaut — fonctionnalité mineur non testable
Stories futures modèle ML → bloque TODO OnnxClassifier + benchmark modèle Architecture prête, modèle absent
Stories futures extraction vidéo native → bloque TODO FFmpeg/AVFoundation bridge Stub extraction uniforme

8ter. Bugs de tests

Pattern incorrect Pattern correct Cause Coût
Category[] dans DetectionLogger readonly Category[] Type mismatch avec Verdict type 5 min
"SENSITIVE" dans ViewerGateModal.test "SENSITIVE_DETECTED" Littéral gate reason incorrect 5 min
context recréé chaque render useMemo pour context Boucle infinie useEffect 15 min
showContent non reset entre documents Reset explicite en début d'analyse Navigation A→B garde état stale 15 min

8quater. Corrections post-Gate 8

Aucune correction post-Gate 8 nécessaire pour le moment. Sonar QG sera validé en pipeline CI/CD.

9. Patterns récurrents détectés

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

  • Gate 3 v1 systématiquement NON_CONFORME ou très bas : PD-86 (4.875), PD-81 (4.25), PD-177 (6.50), PD-84 (6.625). La première spécification ChatGPT est régulièrement insuffisante. Pattern confirmé sur 4+ stories récentes.
  • Sonar QG skippé ou problématique : PD-55, PD-107, PD-31, PD-44, PD-82, PD-248 et maintenant PD-86. Token Sonar absent de Vault local. Pattern récurrent — 7 stories impactées.
  • Stubs pour dépendances futures : PD-63 (tables inexistantes), PD-86 (isMinor PD-84). Pattern acceptable quand tracé avec TODO. Confirmé.
  • Review QA LLM partielle : PD-55, PD-79, PD-31, PD-105, PD-53 et maintenant PD-86. OpenCode n'analyse pas correctement le code inliné dans les prompts longs. Pattern confirmé.
  • AMB élevé sur stories de domaine nouveau : PD-86 (17 AMB) vs PD-264 (6 AMB) ou PD-84 (7 AMB). Les domaines nouveaux (IA locale, classification visuelle) génèrent plus de zones d'ombre.

9.2 Nouveaux patterns identifiés

  • Story 100% local-side = workflow accéléré : PD-86 est la première story sans backend, sans migration, sans pipeline CI complexe. Résultat : 3.35h au lieu de 14h estimés. Le goulot d'étranglement habituel (CI, Sonar, migrations) est absent.
  • Spec Swift/SwiftUI vs réalité React Native : Décalage entre le langage de la spec et la stack réelle. La spec doit refléter la stack du projet cible dès l'étape 1.
  • Invariants "ajoutés" dans les code contracts : Les code contracts ajoutent des invariants hors spec (TTL, déterminisme, warm-up). Cela dilue la frontière contractuelle. Pattern à surveiller.

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 instruction explicite pour que ChatGPT identifie la stack technique réelle du projet (React Native vs Swift vs NestJS) et l'utilise dans §10 haute
templates/prompts/7b Review Tests.md Ajouter une instruction pour que la review QA analyse les tests inline si fournis dans le prompt, au lieu de chercher des fichiers externes moyenne
templates/prompts/1 Specification.md Renforcer l'instruction de contractualisation des seuils numériques (bornes, timeouts, limites mémoire) dès la v1 — Gate 3 rejette systématiquement les specs sans bornes haute

10.2 Améliorations des agents

Agent Amélioration suggérée Justification
config/agents/agent-developer.md Ajouter une check-list "invariants hors spec" obligatoire dans les code contracts — les agents ajoutent des invariants non tracés ECT-04 Gate 5

10.3 Améliorations du processus

  • Sonar local : Créer un script scripts/sonar-local.sh qui exécute le scan Sonar sans dépendre du token Vault (utiliser un token projet dédié stocké localement). 7 stories impactées par ce problème récurrent.
  • Gate 3 bootstrap : Explorer l'injection d'une "pré-review" Claude avant Gate 3 v1 pour rattraper les insuffisances ChatGPT avant le scoring formel. La v1 est systématiquement basse.
  • Stack detection : Injecter le package.json ou Podfile du projet cible dans le prompt de spec pour que ChatGPT identifie automatiquement la stack.

11. Enseignements clés

  1. Les stories 100% local-side sont 4x plus rapides — PD-86 (3.35h) vs estimation standard (14h). L'absence de backend, migrations, et CI complexe supprime les principaux goulots. Ajuster les estimations pour les stories app-only.

  2. Gate 3 v1 reste le point faible structurel — La première spec ChatGPT est systématiquement insuffisante (score < 7 dans ⅘ dernières stories). Investir dans le prompt de spec (bornes numériques, stack réelle) pour réduire le coût de correction v2.

  3. L'architecture modulaire avec interface abstraite paieContentClassifier abstrait permet de livrer l'intégralité de la détection avec un MockClassifier tout en gardant la voie ouverte pour ONNX/CoreML. Pattern à reproduire pour tout composant dépendant d'un choix technologique non tranché.

  4. Les stubs documentés avec TODO tracés sont un compromis viable — PD-86 livre 3 stubs (isMinor, OnnxClassifier, VideoExtraction) sans impact bloquant. La clé : le stub doit être tracé (TODO + story de référence) et le comportement par défaut doit être safe.

  5. Le cache chiffré est un invariant critique à ne pas reporter — INV-86-08 non satisfait est le seul invariant manquant. Sur iOS le sandboxing protège, mais sur d'autres plateformes ce serait une faille. Prioriser le chiffrement cache dans les prochaines stories.

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

Métrique Cette story Moyenne projet (app) Tendance
Temps total 3.35h ~11h (estimation 2 stories app)
Itérations gates 4 ~5.5
Écarts totaux 45 ~35
Score convergence moyen 7.71/10 ~7.8/10

REX produit le 2026-02-24 par Claude (étape 9 du workflow de gouvernance ProbatioVault).