Aller au contenu

PD-86-GOV-PROMPT-STEP2

2/ Tests & Validation

Tu es Architecte QA senior / auditeur qualité indépendant, orienté testabilité, conformité et non-régression. À partir de la SPÉCIFICATION CANONIQUE CONTRACTUELLE ci-dessous, rédige les SCÉNARIOS DE TEST DE RÉFÉRENCE permettant de démontrer objectivement la conformité de toute implémentation.

Tu ne modifies PAS la spécification. Tu ne proposes AUCUNE implémentation.

Règles impératives

  • Chaque invariant (INV-86-01 à INV-86-15) DOIT avoir au moins un scénario de test associé.
  • Chaque critère d'acceptation (CA-86-01 à CA-86-12) DOIT être couvert par ≥1 test.
  • Tout scénario doit être déterministe, reproductible et vérifiable.
  • Les comportements ambigus doivent être signalés comme non testables.
  • Aucune hypothèse implicite : tout prérequis doit être explicité.
  • Les tests décrivent le quoi vérifier, jamais le comment coder.
  • Les scénarios doivent être automatisables, même s'ils ne sont pas encore automatisés.
  • Toute règle non testable → marquée explicitement NON TESTABLE avec justification.

Structure de sortie obligatoire

  1. Matrice de couverture : tableau INV/CA → scénario(s) de test
  2. Scénarios de test : ID (TC-86-XX), description, prérequis, étapes, résultat attendu, INV/CA couverts
  3. Tests de sécurité : scénarios spécifiques zero-knowledge, zeroization, fichiers temporaires
  4. Tests de performance : scénarios budget temps, warm-up, dégradation gracieuse
  5. Tests de gestion d'erreurs : MODEL_UNAVAILABLE, CORRUPTED_INPUT, UNSUPPORTED_FORMAT, TIMEOUT, INTERNAL_ERROR
  6. Tests UX/consentement : modal, confirmation mineur, préférence document, désactivation globale
  7. Signalement : invariants/CA potentiellement non testables ou ambigus

Références

  • Spécification : PD-86-specification.md
  • Epic : PD-185 — B2C-MINEURS
  • Projet : ProbatioVault-app (iOS)

Contexte technique

  • Domaine : app iOS (Swift/SwiftUI), traitement visuel local
  • 4 entités locales : SensitiveDetectionSettings, DocumentSensitiveVerdict, DocumentSensitivePreference, ViewerGateDecision
  • 4 catégories : VIOLENCE, NUDITE, SEXUEL, CHOQUANT
  • Seuils par défaut : VIOLENCE 0.75, NUDITE 0.70, SEXUEL 0.70, CHOQUANT 0.75
  • Vidéo : 12 frames, max(score)
  • Cache chiffré, pas de fichiers temporaires

Voici la spécification :

# PD-86-specification.md

# 1. Metadata

- **Story ID** : PD-86
- **Epic** : PD-185 — B2C-MINEURS
- **Titre** : Détection de contenu sensible (IA locale)
- **Projet** : ProbatioVault-app (iOS)
- **Date** : 2026-02-24
- **Version** : 1.0.0

# 2. Périmètre

## 2.1 Couvert

- Détection locale de sensibilité visuelle à la **consultation** (viewer) pour :
  - Images
  - Vidéos (échantillonnage de frames)
  - PDF (rendu de pages représentatives)
- Catégories détectées (whitelist contractuelle) :
  - `VIOLENCE`
  - `NUDITE`
  - `SEXUEL`
  - `CHOQUANT`
- Affichage d'un écran intermédiaire de consentement avant rendu du contenu quand un risque sensible est détecté.
- Gestion d'une préférence locale "ne plus me prévenir pour ce document".
- Cache local minimal des verdicts de classification, non ré-identifiant du contenu visuel.

## 2.2 Non couvert

- Aucune modération automatique (suppression, blocage, signalement).
- Aucune qualification légale/morale du contenu.
- Aucune analyse côté serveur / API externe.
- Aucune analyse à l'upload.
- Aucun contrôle parental (parental gate).
- Aucune classification de texte (OCR/NLP).
- Aucune persistance serveur des verdicts.

## 2.3 Clarifications contractuelles (Q1–Q10)

- **Q1 (modèle précis)** : Décision d'implémentation. Exigence contractuelle : modèle hors ligne avec benchmark documenté (précision/latence/taille) avant mise en production.
- **Q2 (ONNX vs CoreML)** : Tranché. Référence de livraison iOS : moteur natif iOS hors ligne ; le pipeline de conversion éventuel est hors périmètre de cette spec.
- **Q3 (agrégation vidéo)** : Tranché. Agrégation contractuelle = `max(score_categorie)` sur frames échantillonnées.
- **Q4 (nombre/stratégie frames)** : Tranché. 12 frames par vidéo, stratégie uniforme + inclusion obligatoire des 2 premières secondes.
- **Q5 (seuils)** : Tranché. Seuil par catégorie (configurable utilisateur) avec valeurs par défaut prudentes :
  - `VIOLENCE: 0.75`
  - `NUDITE: 0.70`
  - `SEXUEL: 0.70`
  - `CHOQUANT: 0.75`
- **Q6 (cache chiffré)** : Tranché. Cache local chiffré obligatoire.
- **Q7 (timeout inférence)** : Tranché. Comportement prudent obligatoire : modal "contenu non analysé, potentiellement sensible".
- **Q8 (thumbnails PDF)** : Décision d'implémentation. Exigence contractuelle : rendu local en mémoire uniquement, sans persistance disque.
- **Q9 (warm-up)** : Tranché. Warm-up déclenché à l'ouverture du coffre, puis lazy fallback au premier document si non prêt.
- **Q10 (désactivation utilisateur)** : Tranché. Désactivation globale autorisée (pas par catégorie dans PD-86).

# 3. Modèle de données

## 3.1 Entités locales

| Entité | Champs | Contraintes |
|---|---|---|
| `SensitiveDetectionSettings` | `enabled: Bool`, `thresholds: Map<Category, Float>`, `updatedAt: LocalTimestamp` | `threshold ∈ [0.50, 0.95]` |
| `DocumentSensitiveVerdict` | `documentId: String`, `modelVersion: String`, `isSensitive: Bool`, `detectedCategories: Category[]`, `roundedScores: Map<Category, Float(2d)>`, `analyzedAt: LocalTimestamp`, `analysisStatus: Enum` | Unicité `(documentId, modelVersion)` |
| `DocumentSensitivePreference` | `documentId: String`, `suppressWarning: Bool`, `updatedAt: LocalTimestamp` | Portée strictement locale |
| `ViewerGateDecision` (événement en mémoire) | `documentId`, `decision: SHOW_ANYWAY|KEEP_HIDDEN|CANCEL`, `requiresExplicitConfirmation: Bool` | Non persistant |

## 3.2 Types

- `Category` : `VIOLENCE | NUDITE | SEXUEL | CHOQUANT`
- `analysisStatus` : `SUCCESS | TIMEOUT | MODEL_UNAVAILABLE | UNSUPPORTED_FORMAT | CORRUPTED_INPUT | INTERNAL_ERROR`
- `isSensitive = true` si au moins un score catégorie >= seuil catégorie.

## 3.3 Relations

- `SensitiveDetectionSettings` (1) influence l'évaluation de `DocumentSensitiveVerdict` (N).
- `DocumentSensitivePreference` (0..1) par `documentId` surcharge l'affichage d'alerte pour ce document.
- Aucun lien serveur, aucune réplication cloud.

# 4. Règles métier

- **INV-86-01** : Toute classification est locale au device ; transmission réseau de contenu en clair interdite.
- **INV-86-02** : La détection n'est exécutée qu'au viewer ; jamais à l'import/upload.
- **INV-86-03** : Le fichier original n'est jamais modifié ; hash et scellement probatoire restent inchangés.
- **INV-86-04** : Seules les catégories whitelistées (`VIOLENCE`,`NUDITE`,`SEXUEL`,`CHOQUANT`) peuvent produire un flag sensible.
- **INV-86-05** : Si `isSensitive=true`, l'écran intermédiaire de consentement est obligatoire avant affichage.
- **INV-86-06** : Pour mineur, l'action "Afficher quand même" nécessite confirmation explicite additionnelle.
- **INV-86-07** : Le cache ne stocke jamais de média dérivé (images, frames, thumbnails), embeddings, OCR, texte extrait.
- **INV-86-08** : Le cache local des verdicts est chiffré.
- **INV-86-09** : En cas de timeout, indisponibilité modèle ou erreur critique d'analyse, politique prudente : avertir avant affichage.
- **INV-86-10** : Zeroization best-effort des buffers visuels en fin de traitement, y compris en cas d'échec/annulation.
- **INV-86-11** : Aucune écriture de fichier temporaire en clair durant le pipeline d'analyse.
- **INV-86-12** : Les logs ne contiennent ni pixel data, ni miniatures, ni métadonnées reconstituables du contenu.
- **INV-86-13** : Le résultat de classification est invalidé si `modelVersion` change.
- **INV-86-14** : Le paramètre utilisateur `enabled=false` désactive toute nouvelle classification, sans supprimer les preuves ni modifier les fichiers.
- **INV-86-15** : La préférence "ne plus me prévenir pour ce document" est réversible à tout moment par l'utilisateur.

## 4.1 Transitions d'état et transitions retour

| Domaine | Transition | Statut | Comportement retour |
|---|---|---|---|
| Détection globale | `ENABLED -> DISABLED` | Autorisée | Arrêt des nouvelles analyses ; aucun impact sur fichiers/probatoire |
| Détection globale | `DISABLED -> ENABLED` | Autorisée | Reprise immédiate des analyses au viewer |
| Préférence document | `WARN -> SUPPRESSED` | Autorisée | Plus d'écran intermédiaire pour ce document |
| Préférence document | `SUPPRESSED -> WARN` | Autorisée | Avertissement réactivé immédiatement |
| Verdict analyse | `UNANALYZED -> ANALYZED` | Autorisée | Verdict cacheable |
| Verdict analyse | `ANALYZED -> UNANALYZED` (changement modèle) | Autorisée | Invalidation cache, reclassification requise |
| Affichage | `HIDDEN -> SHOWN` | Autorisée après consentement | Affichage explicite contrôlé |
| Affichage | `SHOWN -> HIDDEN` | Autorisée | Retour immédiat au masquage |

## 4.2 SLA temporels

Aucune transition temporelle identifiée.

# 5. Interfaces

## 5.1 Contrat du module de détection locale

### Entrées

- `documentId: String`
- `documentType: IMAGE | VIDEO | PDF`
- `decryptedContentHandle: InMemoryHandle`
- `settingsSnapshot: SensitiveDetectionSettings`
- `context: { isMinor: Bool }`

### Sorties

- `SensitiveAnalysisResult` :
  - `status: analysisStatus`
  - `isSensitive: Bool`
  - `detectedCategories: Category[]`
  - `scores: Map<Category, Float>`
  - `modelVersion: String`
  - `durationMs: Int`

### Erreurs contractuelles

- `MODEL_UNAVAILABLE`
- `UNSUPPORTED_FORMAT`
- `CORRUPTED_INPUT`
- `TIMEOUT`
- `INTERNAL_ERROR`

## 5.2 Contrat du gate viewer

- Si `status=SUCCESS && isSensitive=true` -> modal obligatoire.
- Si `status in {TIMEOUT, MODEL_UNAVAILABLE, CORRUPTED_INPUT, INTERNAL_ERROR}` -> modal prudent obligatoire.
- Si préférence document `suppressWarning=true` -> modal omis uniquement pour ce document.
- Si utilisateur choisit `KEEP_HIDDEN` -> aucun rendu du contenu.

# 6. Critères d'acceptation

- **CA-86-01** : L'ouverture d'une image sensible détectée affiche un modal avant tout rendu du média.
- **CA-86-02** : L'ouverture d'une vidéo sensible détectée (agrégation `max`) affiche un modal avant tout rendu.
- **CA-86-03** : L'ouverture d'un PDF sensible détecté affiche un modal avant toute page.
- **CA-86-04** : Le système ne transmet aucun contenu visuel ni score à un endpoint distant.
- **CA-86-05** : Le hash probatoire du document reste identique avant/après classification.
- **CA-86-06** : Le cache local contient uniquement les champs autorisés (flag, catégories, scores arrondis, version modèle, timestamp, statut).
- **CA-86-07** : Aucun fichier temporaire média n'est présent sur disque après classification réussie ou échouée.
- **CA-86-08** : En cas de `TIMEOUT`, le modal "contenu potentiellement sensible/non analysé" est affiché.
- **CA-86-09** : En cas de modèle absent du bundle/runtime, le flux reste fonctionnel avec modal prudent.
- **CA-86-10** : La désactivation globale utilisateur empêche toute nouvelle classification tant qu'elle reste active.
- **CA-86-11** : La préférence "ne plus me prévenir" est appliquée par document et réversible.
- **CA-86-12** : À changement de `modelVersion`, un document précédemment classifié est reclassifié à la prochaine consultation.

# 7. Sécurité

- Architecture zero-knowledge stricte : traitement local uniquement, aucun appel d'analyse externe.
- Zeroization best-effort obligatoire des buffers mémoire de rendu/analyse en fin de flux (succès, erreur, timeout, annulation).
- Interdiction de persister des dérivés visuels (frames, thumbnails, previews non chiffrées).
- Interdiction de logs contenant données visuelles ou métadonnées reconstituables.
- Contrôle des mécanismes système pouvant générer des aperçus persistants non maîtrisés.
- Le cache de verdict doit être chiffré localement et limité au minimum nécessaire.

# 8. Performance

- Budget d'analyse cible par ouverture : `<= 700 ms` (hors décodage viewer incompressible).
- Exécution en arrière-plan ; blocage UI prolongé interdit.
- Warm-up du moteur au contexte "ouverture du coffre" (fallback lazy au premier document).
- Downscaling agressif autorisé et attendu pour l'analyse.
- Vidéo : échantillonnage 12 frames max (uniforme + début), sans décodage intégral.
- PDF : échantillonnage de pages représentatives, rendu mémoire uniquement.

# 9. Gestion d'erreurs

| Cas d'erreur | Code | Comportement utilisateur | Persistance |
|---|---|---|---|
| Modèle absent/inaccessible | `MODEL_UNAVAILABLE` | Modal prudent, possibilité d'afficher quand même | Statut erreur horodaté |
| Image/PDF/vidéo corrompu(e) | `CORRUPTED_INPUT` | Message non culpabilisant + modal prudent | Statut erreur horodaté |
| Format non supporté | `UNSUPPORTED_FORMAT` | Message clair "format non pris en charge", pas de crash | Statut erreur horodaté |
| Timeout inférence | `TIMEOUT` | Modal prudent "non analysé" | Statut erreur horodaté |
| Erreur interne inattendue | `INTERNAL_ERROR` | Dégradation contrôlée, contenu masqué par défaut | Statut erreur horodaté |

Règles communes :
- Aucune erreur ne doit provoquer de crash du viewer.
- Aucune erreur ne doit déclencher d'envoi réseau de contenu.
- Le mode dégradé conserve le contrôle utilisateur ("Afficher quand même" / "Garder masqué").

# 10. Contraintes techniques

- Domaine : iOS (Swift/SwiftUI), traitement visuel local hors ligne.
- Modèle compatible exécution offline sur device, versionné, et identifiable (`modelVersion`).
- Taille du composant modèle compatible distribution mobile (contrainte bundle), avec validation perf sur devices cibles.
- Compatibilité minimale iOS : décision d'implémentation (doit être explicitée avant développement).
- Stratégie DDL : **Non applicable** (aucune base serveur modifiée).
- Atomicité DB+queue : **Non applicable** (aucun flux serveur asynchrone).

📌 Artefact produit (à générer dans le Canvas) ➡️ PD-86-tests.md 📂 Chemin de destination : ProbatioVault-app/docs/epics/b2c-mineurs/PD-86-detection-contenu-sensible/PD-86-tests.md Structure : matrice de couverture, scénarios (TC-86-XX), tests sécurité, performance, erreurs, UX, signalement.