Aller au contenu

PD-101 — Rapport de confrontation (Étape 5 — Gate AMBIGUITY)

Ce rapport est produit par l'orchestrateur Claude avant la gate 5 PMO. Il confronte les documents produits pour identifier convergences, divergences et zones d'ombre.

1. Sources confrontées

Document Étape d'origine Rôle
PD-101-specification.md Étape 1 Spécification contractuelle
PD-101-tests.md Étape 2 Scénarios de tests contractuels
PD-101-plan.md Étape 4 Plan d'implémentation
PD-101-code-contracts.yaml Étape 4 Code contracts par composant

2. Convergences

2.1 Architecture crypto — Alignement total

Les 4 documents convergent sur : - Chiffrement FILE-LEVEL (un seul nonce AES-256-GCM 12 bytes par doc_id) — Spec §3.3, Tests TC-INV-06/TC-NR-06/TC-NEG-11, Plan §3 mapping INV-06, Contracts CC-101-T3. - Interdiction chunk-level : explicitement listé comme forbidden dans CC-101-T3, testé par TC-NEG-11, justifié dans Spec §3.3, rappelé dans Plan §9 « Pièges connus ». - Séquence stricte hash → chiffrement → vérification intégrité : Spec §5.1 étapes 6-7, Tests TC-INV-05, Plan §2.1 flux technique, Contracts CC-101-T3 invariants.

2.2 Machine à états — Cohérence stricte

Les transitions d'état UI (UPLOADING, FAILED, COMPLETED, CANCELLED) et backend (PENDING, READY, PENDING_SEAL, CANCELLED) sont identiques dans : - Spec §5.7 (table de transitions avec transitions inverses). - Tests TC-INV-09 (toutes transitions autorisées/interdites testées). - Plan §3 mapping INV-09 (C8 machine à états stricte). - Contracts CC-101-T8 invariants (transitions conformes §5.7).

2.3 Seuil multipart — Cohérence exacte

Seuil fixé à 10 000 000 bytes (SI base 10) dans les 4 documents : - Spec §5.5 : « 10 MB (10 000 000 bytes, SI base 10) ». - Tests TC-NOM-02 : « 9 999 999 bytes (< 10 000 000 bytes) / 10 000 000 bytes ». - Plan §2.1 : « SI < 10MB (10 000 000 bytes) ». - Contracts CC-101-T1 : MULTIPART_THRESHOLD = 10_000_000.

2.4 Invariants — Couverture complète

Les 10 invariants (INV-01 à INV-10) sont : - Définis dans la Spec §4. - Tous couverts par au moins un TC-INV-* dans les Tests (matrice §2). - Tous mappés à des mécanismes dans le Plan §3. - Tous référencés dans au moins un code contract (validation inv_coverage du YAML).

2.5 Purge sensible — Convergence totale

purgeStale() au démarrage du flux : - Spec INV-07 : « purge fichier stricte + purgeStale() au démarrage du flux ». - Tests TC-NOM-06 / TC-INV-07 : GIVEN artefacts résiduels, THEN purge avant chiffrement. - Plan §2.1 : purgeStale() affiché avant génération doc_id dans le flux technique. - Contracts CC-101-T2 : « purgeStale() DOIT être appelé AVANT toute opération de chiffrement ». - Contracts CC-101-T9 : « INV-07: purgeStale() en PREMIÈRE étape, AVANT toute opération crypto ».

2.6 Retry et backoff — Alignement complet

Séquence backoff [1, 2, 4]s + jitter obligatoire, max 3 tentatives : - Spec §5.5 : backoff séquence ½/4, jitter activé, retry max 3. - Tests TC-ERR-03 : « backoff 1s/2s/4s + jitter », TC-NEG-08 : jitter désactivé = non conforme. - Plan §4 mapping CA-07 : « Backoff [1,2,4]s + jitter ». - Contracts CC-101-T5 : BACKOFF_SEQUENCE = [1000, 2000, 4000], jitter OBLIGATOIRE.

2.7 Annulation orchestrée et race condition — Convergence

Les 4 documents décrivent la même séquence : - Signal abort → AbortMultipartUpload S3 (best-effort) → backend CANCELLED (garde atomique UPDATE WHERE status='READY'). - Race condition READY→CANCELLED vs READY→PENDING_SEAL documentée et testée (TC-ERR-08, ST-11).

2.8 Retry après FAILED — Flux entièrement nouveau

Convergence sur : nouveau doc_id, nouveau nonce, nouveau chiffrement. Ancien enregistrement backend inchangé. Spec §5.4, Tests TC-NOM-08, Plan §2.3, Contracts CC-101-T9.

2.9 Anti-enumeration — Convergence

Messages d'erreur uniformes (404) pour doc inexistant ou non autorisé : - Spec : non explicitement mentionné (point d'attention, mais pas de divergence). - Plan §7 : « Messages d'erreur uniformes (404) pour doc inexistant ou non autorisé ». - Contracts CC-101-T4 : forbidden « Distinguer 'not found' de 'not accessible' ».

2.10 Format consentement optimisé — Cohérence §3.4

Le format JSON de preuve de consentement (action, timestamp, doc_id, user_confirmed) est identique dans Spec §3.4, Tests TC-NOM-03, Contracts CC-101-T1 (OptimizedConsent).

3. Divergences

⚠️ Les conflits ne doivent JAMAIS être lissés. Chaque divergence est rendue visible.

DIV-01 : Composant C8 — Transitions backend dans le store UI

  • Source A (Spec §5.7) : Les transitions backend (PENDING → READY → PENDING_SEAL → CANCELLED) sont distinctes des transitions UI. Le backend a sa propre machine à états.
  • Source B (Contracts CC-101-T8) : Le store Zustand useUploadStore ne déclare que les états UI (UploadUIState). Aucun état backend n'est déclaré dans le store.
  • Source C (Contracts CC-101-T8 tests_coverage) : TC-NOM-01 est mappé au store avec la mention « transitions PENDING->READY->PENDING_SEAL observées via store ».
  • Impact : Contradiction entre l'absence de BackendDocState dans le store Zustand (CC-101-T8) et la couverture de test TC-NOM-01 qui affirme observer ces transitions « via store ». Le store ne contient pas les états backend, donc cette observation n'est pas possible via le store seul. Le test devra observer les transitions backend via le réseau (réponses HTTP) et non via le store.

DIV-02 : Validation metadata côté client vs côté backend

  • Source A (Spec §3.2) : Table de formats avec colonne « Si invalide → Rejet (400) » pour doc_id, sha3_256, nonce, auth_tag, etc. Cible implicitement le backend (code HTTP 400).
  • Source B (Plan §6) : ERR-09 mentionne « Validation format metadata côté client avant envoi + côté backend ».
  • Source C (Contracts CC-101-T4) : La validation est assignée à uploadNetwork.ts (côté client).
  • Source D (Contracts CC-101-T3) : uploadCrypto.ts valide le nonce (longueur 12 bytes) côté client.
  • Impact : La spec ne distingue pas explicitement la validation client de la validation backend. Le plan et les contracts ajoutent une validation client pré-envoi qui n'est pas contractualisée dans la spec. Ce n'est pas un conflit bloquant (la validation client est une bonne pratique), mais la spec devrait expliciter cette double validation si elle est contractuelle.

DIV-03 : cancelUpload() — Garde sur quels états ?

  • Source A (Spec §5.7) : La garde atomique pour l'annulation est UPDATE WHERE status='READY'. La transition PENDING → CANCELLED est aussi autorisée (sans garde explicitement documentée).
  • Source B (Plan §2.2) : cancelUpload utilise la garde UPDATE WHERE status='PENDING' OR status='READY'.
  • Source C (Contracts CC-101-T4) : « cancelUpload() utilise garde atomique côté backend (UPDATE WHERE status='READY' ou 'PENDING') ».
  • Impact : La spec ne mentionne la garde atomique que pour READY → CANCELLED (contexte race condition avec worker ancrage). Le plan et les contracts étendent la garde à PENDING → CANCELLED. Ce n'est pas contradictoire (PENDING n'a pas de race condition avec le worker), mais la spec n'impose pas de garde sur PENDING. Divergence formelle, non bloquante fonctionnellement.

DIV-04 : crypto.randomUUID() pour le jitter

  • Source A (Contracts CC-101-T5 forbidden) : « Utiliser Math.random() pour le jitter (utiliser crypto.getRandomValues) ».
  • Source B (Spec §5.5) : Ne précise pas le mécanisme de génération du jitter.
  • Source C (Learning « crypto.randomUUID obligatoire ») : Le learning porte sur les identifiants, pas sur le jitter.
  • Impact : Le contract interdit Math.random() pour le jitter et impose crypto.getRandomValues. L'utilisation d'un CSPRNG pour du jitter de backoff est un over-engineering sécuritaire (le jitter n'est pas un secret cryptographique). Non bloquant, mais divergence entre le contract et la justification du learning d'origine.

DIV-05 : Nombre de composants (13) vs nombre de tâches dans les contracts

  • Source A (Plan §1) : 13 composants listés (C1-C13).
  • Source B (Contracts header) : total_tasks: 13.
  • Source C (Contracts détail) : 13 contracts (CC-101-T1 à CC-101-T13).
  • Observation : Pas de divergence numérique, mais la correspondance C↔T est implicite. C1=T1, C2=T2, etc. C12 (UI) = T12, C13 (tests) = T13. Convergence confirmée.

DIV-06 : Disclaimer EXIF — Journalisation dans l'audit

  • Source A (Spec §5.1 étape 5) : « L'utilisateur doit confirmer explicitement avant que l'upload ne puisse continuer ». Ne mentionne pas explicitement la journalisation audit de cette confirmation.
  • Source B (Tests TC-NOM-07) : « La confirmation EXIF est journalisée dans le journal d'audit ».
  • Source C (Contracts CC-101-T11) : auditExifConfirmation(docId) est un export explicite.
  • Impact : La spec ne contractualise pas la journalisation audit de la confirmation EXIF. Les tests et contracts l'imposent. Le besoin est cohérent avec INV-09 (traçabilité), mais la spec devrait l'expliciter. Divergence mineure — les tests sont plus exigeants que la spec.

DIV-07 : Transcodage — Journalisation choix utilisateur

  • Source A (Spec §5.1 étape 4) : « Si l'utilisateur continue, ios_transcoded: true est enregistré dans les metadata ». Ne mentionne pas la journalisation du choix dans l'audit.
  • Source B (Tests TC-NOM-04) : « Le choix utilisateur (continuer/changer de source) est journalisé dans le journal d'audit ».
  • Source C (Contracts CC-101-T11) : auditTranscodingChoice(docId, continued) est un export.
  • Impact : Même pattern que DIV-06. Les tests imposent un audit non contractualisé dans la spec. Divergence mineure — l'ajout d'audit est une bonne pratique, mais la spec devrait le mentionner pour cohérence.

4. Zones d'ombre

ZO-01 : TTL des URL pré-signées (Q-02 non résolu)

Identifié comme point à clarifier dans la Spec (Q-02), rappelé dans les Tests §9 « Règles non testables », et dans le Plan §10 « Hors périmètre ». Aucun document ne propose de valeur par défaut. Risque : échec upload si le TTL expire pendant un chiffrement de gros fichier (500 MB) ou pendant les retries.

ZO-02 : Liste blanche MIME (Q-03 non résolu)

Non résolu dans aucun document. Le Plan mentionne C9 validation MIME pattern mais le pattern exact n'est pas défini. Les tests TC-NEG-06 testent un MIME « invalide RFC » mais ne définissent pas la liste blanche acceptée. Risque : rejet ou acceptation de types MIME inattendus en production.

ZO-03 : Persistance état UI après redémarrage app (Q-04 non résolu)

Non traité. Le Plan mentionne un UploadSession dans le store Zustand, mais ne spécifie pas si le store est persisté (zustand/middleware/persist). Risque : après un kill + relaunch, l'utilisateur pourrait voir un état incohérent ou perdre l'historique d'upload.

ZO-04 : Contrat backend exact pour champs probatoires (Q-05 non résolu)

Le Plan §8 mentionne HT-04: URLs presigned S3 supportent PUT multipart et H-05: Transport nonce/auth_tag en base64. Le contrat API exact (endpoint paths, request/response schemas) n'est défini dans aucun document de cette story. Risque : divergence mobile/backend à l'intégration.

ZO-05 : Lecture streaming fichiers volumineux (HT-01)

Le Plan §8 mentionne l'hypothèse HT-01: expo-file-system supporte la lecture streaming pour fichiers > 100MB. Le Contracts CC-101-T3 n'impose pas de mécanisme de streaming. La Spec §5.5 permet des fichiers jusqu'à 500 MB. Risque : OOM sur device lors du chiffrement d'un fichier de 500 MB (plaintext ~500 MB + ciphertext ~500 MB = ~1 GB RAM). Le Plan §9 l'identifie mais la mitigation (« warning UX au-delà de 200 MB ») n'est pas contractualisée dans la spec ni dans les contracts.

ZO-06 : @noble/ciphers AES-256-GCM sur gros fichiers (HT-03)

Le Plan §8 mentionne l'hypothèse HT-03: @noble/ciphers AES-256-GCM supporte des fichiers > 50MB en mémoire sur iPhone 12. La bibliothèque crypto utilisée n'est pas spécifiée dans la Spec. Le contract CC-101-T3 ne précise pas la bibliothèque AES-GCM. Risque : si @noble/ciphers ne supporte pas > 50 MB, l'architecture crypto est à revoir.

ZO-07 : Background upload — Mécanisme natif exact

Le Plan §9 mentionne URLSessionConfiguration.background et expo-task-manager. Le contract CC-101-T10 reste générique (« via URLSession background ou expo-task-manager »). La spec §5.3 ne prescrit pas de mécanisme technique. Risque : les contraintes iOS background (30s pour beginBackgroundTask) peuvent rendre l'upload de gros fichiers impossible en background sans URLSession natif, ce qui peut nécessiter un module natif Swift non prévu dans le plan (tous les fichiers sont .ts/.tsx).

ZO-08 : Barrel export index.ts — Contenu non spécifié

Le contract CC-101-T9 crée src/services/upload/index.ts (barrel export) mais ne spécifie pas quels exports sont re-exportés. Aucune contrainte dans les autres documents. Impact : mineur, mais peut entraîner des fuites d'API interne.

ZO-09 : Audit — Fail-closed vs fire-and-forget

  • Le contract CC-101-T11 spécifie « Émission non bloquante (fire-and-forget via auditQueue existant) ».
  • Le learning « Anti-catch-absorb pour audit fail-closed » (CLAUDE.md) impose que l'audit soit fail-closed.
  • Contradiction potentielle : le fire-and-forget de l'audit upload contredit le pattern fail-closed appris des REX PD-85/PD-63/PD-250/PD-262/PD-265. Si l'audit échoue silencieusement, on perd la traçabilité (INV-10, Art. III CONSTITUTIONAL).

ZO-10 : UploadDocumentScreen.tsx — Existence et structure actuelle

Le Plan §1 indique action: update pour UploadDocumentScreen.tsx. Le contract CC-101-T12 fait de même. Mais aucun document ne décrit l'état actuel de cet écran ni les modifications requises. Risque : si l'écran actuel a une structure incompatible, le plan de mise à jour est aveugle.

5. Recommandation

  • Procéder — convergence confirmée, aucun conflit bloquant
  • Rework nécessaire — divergences à résoudre avant de continuer
  • Escalade — décision humaine requise sur un point structurant

Justification :

Les convergences sont solides et couvrent la totalité des invariants et des mécanismes critiques (crypto, machine à états, retry, purge, annulation). Les 4 documents sont cohérents sur l'essentiel.

Cependant : 1. DIV-01 (transitions backend observées « via store » alors que le store ne contient pas les états backend) est une incohérence dans la couverture de test qui doit être clarifiée. 2. ZO-09 (fire-and-forget audit vs fail-closed) est en tension avec les learnings constitutionnels et doit être tranchée. 3. ZO-05/ZO-06 (OOM sur gros fichiers) représentent un risque technique non mitigé contractuellement — le plan identifie le risque mais ne le contractualise pas.

Les autres divergences (DIV-02 à DIV-07) et zones d'ombre (ZO-01 à ZO-04, ZO-07, ZO-08, ZO-10) sont mineures ou héritées des points Q-01 à Q-06 déjà identifiés en Gate 3.