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
ContentClassifierabstraite 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
suppressWarningetisMinorcrée une matrice de cas complexe. L'extensionrequiresExplicitConfirmationaux 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)viaMap<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
falsepar 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.shqui 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.jsonouPodfiledu projet cible dans le prompt de spec pour que ChatGPT identifie automatiquement la stack.
11. Enseignements clés¶
-
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.
-
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.
-
L'architecture modulaire avec interface abstraite paie —
ContentClassifierabstrait permet de livrer l'intégralité de la détection avec unMockClassifiertout en gardant la voie ouverte pour ONNX/CoreML. Pattern à reproduire pour tout composant dépendant d'un choix technologique non tranché. -
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.
-
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).