Aller au contenu

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 : 2.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 échantillonnées contractuellement)
  • 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", avec garde-fous mineur et politique prudente.
  • Cache local minimal des verdicts de classification, non ré-identifiant du contenu visuel.
  • Réévaluation locale de isSensitive lors d'un changement de seuils utilisateur à partir des scores bruts en cache (sans ré-inférence si scores disponibles).

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 externe au produit (parental gate côté parent).
  • Aucune classification de texte (OCR/NLP).
  • Aucune persistance serveur des verdicts.

2.3 Clarifications contractuelles (Q1–Q12)

  • 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
  • Bornes contractuelles : [0.50, 0.95].
  • Garde-fou mineur : si isMinor=true, les seuils effectifs sont plafonnés aux valeurs par défaut (aucune relaxation possible).
  • Q6 (cache chiffré) : Tranché. Cache local chiffré obligatoire.
  • Q7 (timeout inférence) : Tranché. Timeout contractuel d'inférence fixé à 5000 ms ; au-delà : TIMEOUT et comportement prudent obligatoire.
  • Q8 (thumbnails PDF) : Tranché. Rendu local en mémoire uniquement, sans persistance disque, avec stratégie d'échantillonnage déterministe :
  • maxPagesAnalyzed = 5
  • Si totalPages <= 5 : analyser toutes les pages.
  • Si totalPages > 5 : analyser page 1, page N, et 3 pages intérieures uniformément réparties (quartiles), dédupliquées puis complétées par la page intérieure non sélectionnée la plus proche si nécessaire.
  • 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). Les verdicts déjà en cache sont conservés mais non utilisés pour afficher un modal tant que enabled=false.
  • Q11 (source isMinor) : Tranché. isMinor provient du profil utilisateur authentifié (flag serveur B2C-MINEURS non falsifiable localement).
  • Q12 (suppressWarning vs sécurité) : Tranché. Politique prudente et règles mineur priment toujours sur suppressWarning.

3. Modèle de données

3.1 Entités locales

Entité Champs Contraintes
SensitiveDetectionSettings enabled: Bool, thresholds: Map<Category, Float>, updatedAt: LocalTimestamp, thresholdsVersion: Int threshold ∈ [0.50, 0.95]
DocumentSensitiveVerdict documentId: String, modelVersion: String, isSensitive: Bool, detectedCategories: Category[], rawScores: Map<Category, Float>, roundedScores: Map<Category, Float(2d)>, analyzedAt: LocalTimestamp, analysisStatus: Enum, thresholdsVersionAtDecision: Int 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, requiresExplicitConfirmation = isMinor && isSensitive

3.2 Types

  • Category : VIOLENCE | NUDITE | SEXUEL | CHOQUANT
  • analysisStatus : SUCCESS | TIMEOUT | MODEL_UNAVAILABLE | UNSUPPORTED_FORMAT | CORRUPTED_INPUT | OUT_OF_MEMORY | INTERNAL_ERROR
  • InMemoryHandle : handle opaque vers buffer mémoire déchiffré, non sérialisable, non loggable, taille maximale contractuelle 64 MB par artefact d'analyse ; si dépassement -> OUT_OF_MEMORY.
  • Décision isSensitive :
  • calculée avant arrondi sur rawScores,
  • isSensitive = true si au moins un rawScore[categorie] >= seuil_effectif[categorie],
  • roundedScores servent uniquement à la persistance et à l'observabilité locale.

3.3 Relations

  • SensitiveDetectionSettings (1) influence l'évaluation de DocumentSensitiveVerdict (N).
  • DocumentSensitivePreference (0..1) par documentId surcharge l'affichage d'alerte pour ce document, sous réserve des règles de priorité sécurité/mineur.
  • 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 analysisStatus=SUCCESS et 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, MODEL_UNAVAILABLE, UNSUPPORTED_FORMAT, CORRUPTED_INPUT, OUT_OF_MEMORY ou INTERNAL_ERROR, politique prudente : avertir avant affichage ; cette politique prime toujours sur suppressWarning.
  • INV-86-10 : Zeroization best-effort des buffers visuels en fin de traitement, y compris en cas d'échec/annulation, via parcours try/finally sur toutes les allocations d'analyse.
  • 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, selon politique de whitelist stricte.
  • 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.
  • INV-86-16 : Si isMinor=true, suppressWarning est ignoré ; les obligations de gate mineur restent appliquées.
  • INV-86-17 : Si isMinor=true, les seuils effectifs ne peuvent pas être plus permissifs que les seuils par défaut (pas de relaxation).
  • INV-86-18 : Si enabled=false, le viewer n'affiche aucun modal de détection (même si verdict sensible en cache) et rend directement le contenu.
  • INV-86-19 : Le module de détection est idempotent par (documentId, modelVersion) et serialize les demandes concurrentes pour une même clé (mutex/PQueue mono-clé).
  • INV-86-20 : En cas de changement de seuils utilisateur, isSensitive est recalculé localement à partir de rawScores en cache sans ré-inférence ; si rawScores indisponibles, reclassification requise.

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 ; modals désactivés ; 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, sauf règles mineur/politique prudente
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
Décision sensible DECIDED@thresholdsVx -> DECIDED@thresholdsVy (changement seuils) Autorisée Réévaluation isSensitive depuis rawScores sans ré-inférence
Affichage HIDDEN -> SHOWN Autorisée après consentement si gate actif Affichage explicite contrôlé
Affichage SHOWN -> HIDDEN Autorisée Retour immédiat au masquage

4.2 SLA temporels

  • Timeout d'inférence contractuel : 5000 ms (au-delà -> TIMEOUT).
  • Budget performance cible (non bloquant fonctionnel) défini en §8.

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, isMinorSource: AUTH_PROFILE_SERVER_FLAG, profileVersion: String }
  • executionKey: { documentId: String, modelVersion: String } (pour idempotence/serialization concurrente)

Sorties

  • SensitiveAnalysisResult :
  • status: analysisStatus
  • isSensitive: Bool
  • detectedCategories: Category[]
  • rawScores: Map<Category, Float>
  • roundedScores: Map<Category, Float(2d)>
  • modelVersion: String
  • durationMs: Int
  • samplingMetadata :
    • videoFramesAnalyzed: Int
    • pdfPagesAnalyzed: Int
    • pdfPageIndexes: Int[]

Erreurs contractuelles

  • MODEL_UNAVAILABLE
  • UNSUPPORTED_FORMAT
  • CORRUPTED_INPUT
  • TIMEOUT
  • OUT_OF_MEMORY
  • INTERNAL_ERROR

5.2 Contrat du gate viewer

Règles évaluées dans cet ordre (ordre contractuel de précédence) :

  1. Si enabled=false -> pas de modal de détection, rendu direct.
  2. Si status in {TIMEOUT, MODEL_UNAVAILABLE, UNSUPPORTED_FORMAT, CORRUPTED_INPUT, OUT_OF_MEMORY, INTERNAL_ERROR} -> modal prudent obligatoire.
  3. Si status=SUCCESS && isSensitive=true -> modal obligatoire.
  4. Si isMinor=true -> suppressWarning ignoré.
  5. Si isMinor=false et status=SUCCESS && isSensitive=true et suppressWarning=true -> modal omis pour ce document.
  6. Si utilisateur choisit KEEP_HIDDEN ou CANCEL -> aucun rendu du contenu.

Règle explicite : - requiresExplicitConfirmation = isMinor && isSensitive

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 (documentId, isSensitive, catégories, rawScores, scores arrondis, modelVersion, analyzedAt, analysisStatus, thresholdsVersionAtDecision).
  • 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 (>5000 ms), 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, sous contraintes de précédence sécurité/mineur.
  • CA-86-12 : À changement de modelVersion, un document précédemment classifié est reclassifié à la prochaine consultation.
  • CA-86-13 : Les buffers visuels alloués par le pipeline d'analyse sont libérés/zeroized en best-effort via try/finally sur succès, erreur, timeout et annulation.
  • CA-86-14 : Si isMinor=true, suppressWarning=true ne supprime pas le modal et la confirmation additionnelle reste obligatoire.
  • CA-86-15 : Si isMinor=true, toute tentative de seuil au-dessus des valeurs par défaut est refusée ou clampée aux valeurs par défaut.
  • CA-86-16 : Un changement de seuils utilisateur réévalue isSensitive à partir de rawScores cache sans ré-inférence.
  • CA-86-17 : Le module serialize les classifications concurrentes pour une même clé (documentId, modelVersion) sans doublon persistant.
  • CA-86-18 : La stratégie d'échantillonnage PDF est déterministe (max 5 pages, première + dernière + intérieures uniformes).
  • CA-86-19 : Si enabled=false, un document déjà classé sensible est affiché sans modal de détection.

7. Sécurité

  • Architecture zero-knowledge stricte : traitement local uniquement, aucun appel d'analyse externe.
  • isMinor est un attribut de profil authentifié côté serveur (B2C-MINEURS), non modifiable localement.
  • Zeroization best-effort obligatoire des buffers mémoire de rendu/analyse en fin de flux (succès, erreur, timeout, annulation) avec obligation de try/finally.
  • 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.
  • Politique de logging (INV-86-12) :
  • Whitelist autorisée : eventName, documentId, analysisStatus, durationMs, modelVersion, detectedCategories, thresholdsVersion, errorCode.
  • Blacklist interdite : bytes média, chemins de fichiers temporaires, miniatures, dimensions exactes corrélables, EXIF, noms de fichiers source, InMemoryHandle dump.
  • Contrôle des mécanismes iOS pouvant générer des aperçus persistants non maîtrisés : App Switcher snapshots, Spotlight indexing, QuickLook previews.
  • Le cache de verdict doit être chiffré localement et limité au minimum nécessaire.

8. Performance

  • Budget d'analyse cible par ouverture : <= 700 ms au P95 sur iPhone 12 (A14, iOS 16.0+), hors décodage viewer incompressible.
  • Timeout fonctionnel distinct : 5000 ms (cf. §9), non confondu avec l'objectif performance.
  • 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 déterministe max 5 pages (première + dernière + 3 intérieures uniformes), rendu mémoire uniquement, zéro persistance disque.

8bis. Diagrammes Mermaid

8bis.1 Diagramme d'états — Cycle de vie du verdict de détection

Ce diagramme couvre les transitions d'état du verdict d'analyse et de la décision sensible (§4.1), incluant l'invalidation par changement de modèle (INV-86-13) et la réévaluation par changement de seuils (INV-86-20).

stateDiagram-v2
    [*] --> UNANALYZED : Document ouvert au viewer

    UNANALYZED --> ANALYZING : enabled=true (INV-86-02)
    ANALYZING --> SUCCESS : Inférence < 5000ms
    ANALYZING --> TIMEOUT : durationMs > 5000ms (INV-86-09)
    ANALYZING --> MODEL_UNAVAILABLE : Modèle absent (INV-86-09)
    ANALYZING --> CORRUPTED_INPUT : Fichier corrompu (INV-86-09)
    ANALYZING --> UNSUPPORTED_FORMAT : Format non supporté (INV-86-09)
    ANALYZING --> OUT_OF_MEMORY : Handle > 64MB (INV-86-09)
    ANALYZING --> INTERNAL_ERROR : Erreur inattendue (INV-86-09)

    SUCCESS --> SENSITIVE : isSensitive=true (rawScore >= seuil)
    SUCCESS --> NOT_SENSITIVE : isSensitive=false

    SENSITIVE --> RE_EVALUATED : Changement seuils (INV-86-20)
    NOT_SENSITIVE --> RE_EVALUATED : Changement seuils (INV-86-20)
    RE_EVALUATED --> SENSITIVE : rawScores recalculés → sensible
    RE_EVALUATED --> NOT_SENSITIVE : rawScores recalculés → non sensible

    SENSITIVE --> UNANALYZED : modelVersion change (INV-86-13)
    NOT_SENSITIVE --> UNANALYZED : modelVersion change (INV-86-13)

    state SENSITIVE {
        [*] --> GATE_ACTIVE
        GATE_ACTIVE --> SHOWN : Consentement utilisateur (INV-86-05)
        GATE_ACTIVE --> HIDDEN : KEEP_HIDDEN ou CANCEL
        SHOWN --> HIDDEN : Retour masquage
    }

    note right of TIMEOUT : Politique prudente :\nmodal obligatoire\n(prime sur suppressWarning)
    note right of RE_EVALUATED : Sans ré-inférence :\nrecalcul depuis rawScores cache

8bis.2 Diagramme d'états — Préférences et gardes mineur

Ce diagramme couvre l'interaction entre la préférence document (suppressWarning), le flag mineur et la détection globale (INV-86-14, INV-86-16, INV-86-17, INV-86-18).

stateDiagram-v2
    state "Detection Settings" as DS {
        ENABLED --> DISABLED : Utilisateur désactive (INV-86-14)
        DISABLED --> ENABLED : Utilisateur réactive
    }

    state "Document Preference" as DP {
        WARN --> SUPPRESSED : suppressWarning=true (INV-86-15)
        SUPPRESSED --> WARN : Utilisateur réactive alerte
    }

    state "Gate Viewer Decision" as GV {
        state eval <<choice>>
        [*] --> eval
        eval --> NO_MODAL : enabled=false (INV-86-18)
        eval --> MODAL_PRUDENT : status ∈ erreurs (INV-86-09)
        eval --> MODAL_SENSIBLE : isSensitive=true (INV-86-05)
        MODAL_SENSIBLE --> MODAL_SKIP : !isMinor && suppressWarning (§5.2 règle 5)
        MODAL_SENSIBLE --> MODAL_CONFIRM : isMinor (INV-86-06, INV-86-16)

        state "Décision utilisateur" as DU {
            SHOW_ANYWAY
            KEEP_HIDDEN
            CANCEL
        }
    }

    note right of MODAL_CONFIRM : isMinor=true :\nsuppressWarning ignoré\nseuils plafonnés (INV-86-17)

8bis.3 Diagramme de séquence — Flux nominal d'analyse au viewer

Ce diagramme détaille le flux complet depuis l'ouverture d'un document jusqu'à l'affichage, incluant le warm-up (Q9), le cache (INV-86-08), la sérialisation concurrente (INV-86-19), la zeroization (INV-86-10) et la gate viewer (§5.2).

sequenceDiagram
    participant U as Utilisateur
    participant V as Viewer
    participant G as Gate Viewer (§5.2)
    participant C as Cache Chiffré (INV-86-08)
    participant D as Detection Module (§5.1)
    participant M as Modèle IA Local (INV-86-01)

    Note over V,M: Warm-up au démarrage du coffre (Q9)
    V->>M: Warm-up modèle (lazy fallback si non prêt)

    U->>V: Ouvre document (documentId, type)
    V->>G: Évalue précédence (§5.2)

    alt enabled=false (INV-86-18)
        G-->>V: Pas de modal → rendu direct
        V-->>U: Affiche contenu
    else enabled=true
        V->>C: Lookup (documentId, modelVersion)
        alt Cache hit & modelVersion identique
            C-->>V: Verdict existant
        else Cache miss ou modelVersion différent (INV-86-13)
            V->>D: Demande analyse (executionKey)
            Note over D: Mutex mono-clé (INV-86-19)
            D->>D: Déchiffre handle mémoire (InMemoryHandle ≤ 64MB)

            alt Image
                D->>M: Inférence image (downscaled)
            else Vidéo
                D->>D: Échantillonne 12 frames (uniforme + 2s début, Q4)
                D->>M: Inférence par frame
                D->>D: Agrégation max(score) par catégorie (Q3)
            else PDF
                D->>D: Échantillonne ≤5 pages déterministe (Q8)
                D->>M: Inférence par page (rendu mémoire, INV-86-11)
                D->>D: Agrégation max(score) par catégorie
            end

            M-->>D: rawScores par catégorie (INV-86-04)

            alt durationMs > 5000ms
                D-->>V: status=TIMEOUT (INV-86-09)
            else Succès
                D->>D: isSensitive = rawScore ≥ seuil_effectif (§3.2)
                D->>D: roundedScores = arrondi 2d (persistance)
                D->>C: Persiste verdict chiffré (INV-86-07, INV-86-08)
                D-->>V: SensitiveAnalysisResult
            end

            D->>D: Zeroization buffers (try/finally, INV-86-10)
        end

        V->>G: Applique règles de précédence (§5.2)

        alt status ∈ erreurs
            G-->>V: Modal prudent obligatoire (INV-86-09)
        else isSensitive=true
            alt isMinor=true (INV-86-16)
                G-->>V: Modal + confirmation explicite (INV-86-06)
            else !isMinor && suppressWarning=true
                G-->>V: Modal omis (§5.2 règle 5)
            else !isMinor && suppressWarning=false
                G-->>V: Modal standard (INV-86-05)
            end
        else isSensitive=false
            G-->>V: Rendu direct
        end

        U->>V: Décision (SHOW_ANYWAY / KEEP_HIDDEN / CANCEL)
        alt SHOW_ANYWAY
            V-->>U: Affiche contenu
        else KEEP_HIDDEN / CANCEL
            V-->>U: Contenu non rendu
        end
    end

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" + modal prudent ; pas de crash Statut erreur horodaté
Timeout inférence (durationMs > 5000) TIMEOUT Modal prudent "non analysé" Statut erreur horodaté
Dépassement mémoire handle OUT_OF_MEMORY Dégradation contrôlée + modal prudent 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é"). - La politique prudente prime sur suppressWarning.

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 : iOS 16.0.
  • Mécanismes iOS à contrôler explicitement : App Switcher snapshot redaction, exclusion Spotlight, désactivation des previews QuickLook non maîtrisées.
  • Concurrence : sérialisation mono-clé (documentId, modelVersion) via mutex/PQueue ; écriture idempotente.
  • Stratégie DDL : Non applicable (aucune base serveur modifiée).
  • Atomicité DB+queue : Non applicable (aucun flux serveur asynchrone).