Aller au contenu

PD-286 — Review de spécification (Gate 3)

Auditeur : technique indépendant — orienté contractualisation, testabilité, conformité. Documents audités : - PD-286-specification.md (faisant foi contractuelle) - PD-286-tests.md (limité aux ambiguïtés / contradictions / non-testabilité / hypothèses dangereuses / risques)

Méthode : revue stricte sans correction, sans reformulation, sans proposition d'implémentation. Couverture des tests volontairement non auditée (cf. cadrage prompt).


Synthèse des écarts

# Type Référence (loc.) Gravité
01 Contradiction spec §4 INV-286-02/04 + §5.1 (totalVolumes max=14) Bloquant
02 Contradiction spec §3 + §9 H-286-01 + §10.2 Q-286-01 + §5bis séquence Bloquant
03 Ambiguïté spec §5.4 step 4 + §5bis séquence (manifest_partial) Bloquant
04 Contradiction spec §6 ERR-286-01..08 + §5.1 + §10.2 Q-286-02 Majeur
05 Hypothèse dangereuse spec §4 INV-286-10 vs §5.1 modèle + §2 Exclusions Majeur
06 Ambiguïté spec §5.2 SLA (SIGNED_URL_TTL vs EXPORT_SESSION_TTL) Majeur
07 Incohérence Spec↔Tests tests §5 (TC-INV-01/02/04/06/08/09/10/11/12) Majeur
08 Contradiction spec §4 INV-286-08 vs tests §6 TC-NR-02 Majeur
09 Règle non testable spec §4 INV-286-09 (audit fail-closed sans définition) Majeur
10 Ambiguïté spec §4 INV-286-08 vs §5.1 modèle (single-vol) Majeur
11 Hypothèse dangereuse spec §4 §5bis (Mermaid état) — cas totalSize == 0 Majeur
12 Ambiguïté spec §5bis (diagramme état — flèches "INTERDITE") Mineur
13 Incohérence Spec↔Tests tests §7 TC-NEG-04/05 vs spec §5.1 (5xx self-émis) Majeur
14 Contradiction spec §4 INV-286-11 ⊃ INV-286-12 (redondance) Mineur
15 Règle non testable spec §5.2 SLA (perf "4G / iPhone 12" sans tolérance) Mineur
16 Règle non testable spec §6 ERR-286-04 (« message explicite utilisateur ») Mineur
17 Ambiguïté spec §7 CA-286-04 (observable « Logs/tests ») Mineur
18 Hypothèse dangereuse spec §5.5 (idempotence/retry app multi-volumes) Majeur
19 Hypothèse dangereuse spec §9 H-286-03 + §4 INV-286-05 vs tests TC-NR-01 Majeur
20 Hypothèse dangereuse spec §5.4 step 4 (assemblage stream sur .pvproof) Majeur
21 Ambiguïté spec §5.2 (TTL pendant DOWNLOADING actif) Majeur
22 Risque sécu/conformité spec §5bis séquence (audits hash_ok ≠ statut final) Majeur
23 Risque sécu/conformité spec §5bis séquence (App mobile → WORM direct) Majeur
24 Risque sécu/conformité spec §5.2 (SIGNED_URL_TTL défaut 24h sur mobile) Mineur
25 Ambiguïté spec §4 INV-286-02 (« Taille totale exportée ») Majeur
26 Incohérence Spec↔Tests spec INV-286-10 + tests TC-INV-10 (« conditionnel ») Majeur
27 Ambiguïté spec §3 (« Etat terminal » défini ≠ §5.2 EXPIRED) Mineur
28 Contradiction spec §5.1 volumeIndex max=13 vs §4 INV-286-06 Mineur

Détail des écarts

Écart 01 — Cardinalité des volumes incompatible avec atomicité des preuves

Type        : Contradiction
Référence   : spec §4 INV-286-02 (≤ 10 GB) + INV-286-04 (atomicité) + §5.1 modèle de
              données (`totalVolumes` max=14, `volumeIndex` max=13)
Description : Avec `INV-286-04` (preuve atomique non scindable), la borne supérieure
              `totalVolumes ≤ 14` n'est pas garantie pour tout dossier conforme à
              `INV-286-02`. Contre-exemple déterministe : 21 preuves atomiques de
              480 MB (~9,84 GB total ≤ 10 GB), chacune ne pouvant cohabiter avec une
              autre dans un volume ≤ 768 MB (480+480 = 960 MB > 768 MB) ⇒ 21 volumes
              requis, donc `totalVolumes = 21 > 14` et `volumeIndex` jusqu'à 20 > 13.
              Le contrat de réponse rejette alors une situation pourtant autorisée
              par INV-286-02 et INV-286-04.
Impact      : Configuration nominale métier conduisant à un rejet `5xx` non prévu
              dans la matrice d'erreurs §6 (aucun `ERR-286-*` ne couvre « > 14
              volumes requis »). Dossiers réels possiblement bloqués sans recours.
Gravité     : Bloquant

Écart 02 — Canonicalisation utilisée comme source d'observable mais non spécifiée

Type        : Contradiction
Référence   : spec §3 (def. « Canonicalisation JSON » : « règle … définie par le
              module export existant (référence contractuelle interne) »)
              + §9 H-286-01 (« Le module supporte déjà la canonicalisation
              déterministe »)
              + §10.2 Q-286-01 (« Spécification formelle … manquante »)
              + §5bis séquence (`canonical_json(manifest_partial)`)
              + INV-286-07, CA-286-04
Description : Le hash `integrityHash = SHA3-256(manifest partiel canonicalisé)` est
              le pivot de l'invariant d'intégrité (INV-286-07) et du critère
              d'acceptation CA-286-04. La règle de canonicalisation est définie
              dans §3 par renvoi à un « module existant » réputé déterministe
              (H-286-01), tout en étant déclarée non spécifiée (Q-286-01). Deux
              implémenteurs indépendants (backend et app) ne peuvent garantir un
              recalcul identique sans cette règle.
Impact      : INV-286-07 et CA-286-04 ne sont pas vérifiables a priori : tout test
              de comparaison hash dépend d'une norme inexistante. Faux négatifs /
              faux positifs d'intégrité non discriminables d'un bug.
Gravité     : Bloquant

Écart 03 — Provenance du « manifest partiel » côté app non spécifiée

Type        : Ambiguïté
Référence   : spec §5.4 step 4 (« téléchargement → vérification hash volume → ajout
              assembleur ») + §5bis séquence (`App: recompute sha3_256(manifest_
              partial)`)
              + INV-286-07
Description : L'app est tenue de recalculer `SHA3-256(manifest_partial)` mais ni
              §5.1 (modèle de données), ni §5.4 (flux), ni §5bis (séquence) ne
              spécifient comment l'app obtient le `manifest_partial` à hasher :
              - Inclus dans le payload `volumes[]` de la réponse API ?
              - Distribué via un `signedUrl` séparé ?
              - Embarqué dans le bundle téléchargé du volume ?
              - Reconstruit côté app à partir des fichiers ?
              Aucune réponse n'est contractualisée.
Impact      : Test TC-NOM-04 (« recalcule SHA3-256 du manifest partiel ») non
              implémentable de façon déterministe ; deux implémentations
              divergentes peuvent prétendre conformer.
Gravité     : Bloquant

Écart 04 — Codes d'erreur normatifs et utilisés en clauses, mais non figés

Type        : Contradiction
Référence   : spec §6 (ERR-286-01..08, codes nommés `EXPORT_TOTAL_LIMIT_EXCEEDED`,
              `PROOF_TOO_LARGE_FOR_VOLUME`, etc.)
              + §5.1 (`INVALID_EXPORT_ID`, `VOLUME_INTEGRITY_HASH_INVALID`,
              `SIGNED_URL_INVALID`, `INVALID_TOTAL_VOLUMES`,
              `INVALID_VOLUME_INDEX_RANGE`, `MANIFEST_ROOT_HASH_INVALID`)
              + §10.2 Q-286-02 (« Validation de la nomenclature » attendue)
Description : La spécification utilise ces codes comme contractuels (référencés par
              les CA, GWT et tests) tout en signalant en §10.2 Q-286-02 que la
              taxonomie n'est pas validée. Les tests TC-ERR-01, TC-ERR-02,
              TC-NEG-01..05 reposent sur ces littéraux comme observables.
Impact      : Verdicts QA divergents inter-environnements selon l'interprétation
              des littéraux ; les tests négatifs ne discriminent pas entre « code
              non final » et « non-conformité ».
Gravité     : Majeur

Écart 05 — INV-286-10 envelope-encryption sans entité dans le périmètre

Type        : Hypothèse dangereuse
Référence   : spec §4 INV-286-10 (« tout artefact crypto temporaire — DEK,
              fragment, clé, rekey — chiffré au repos »)
              + §5.1 (modèle de données : aucun champ DEK/fragment/clé/rekey)
              + §5.4 (flux nominal : aucune génération crypto temporaire)
              + §2 Exclus (« pipeline de validation des preuves » non modifiée)
Description : INV-286-10 introduit un invariant crypto-proof citant des entités
              (DEK, fragment, clé, rekey) absentes du modèle de données et du
              flux nominal de cette story. Aucun lien explicite n'est établi
              entre l'export multi-volumes et un nouvel artefact crypto temporaire.
              L'invariant est par ailleurs présenté avec un identifiant nominatif
              (« INV-286-envelope-encryption ») dérogeant à la convention
              `INV-286-NN` du reste de la table — signe d'injection externe non
              contextualisée.
Impact      : (1) Champ d'application de l'invariant indéterminé : porte-t-il sur
              les volumes téléchargés temporairement par l'app, sur le manifest
              partiel, ou sur des artefacts non décrits ? (2) Test TC-INV-10
              déclaré « conditionnel », non spécifié en §3 du document tests.
              (3) Risque de scope-creep ou d'invariant inopérant.
Gravité     : Majeur

Écart 06 — Interaction des deux TTL non spécifiée

Type        : Ambiguïté
Référence   : spec §5.2 (`SIGNED_URL_TTL` défaut 24h, bornes 1h–72h ; `EXPORT_
              SESSION_TTL` défaut 24h, bornes 1h–72h, indépendants en config)
Description : Les deux TTL sont configurables indépendamment et peuvent diverger
              (ex. `SIGNED_URL_TTL=1h` mais `EXPORT_SESSION_TTL=72h`, ou inverse).
              Aucune règle ne stipule :
              (a) si l'un doit être ≤ l'autre ;
              (b) ce qui se produit lorsque `SIGNED_URL_TTL` expire avant la fin
                  de la session (ré-émission de signedUrl ? export → EXPIRED ?) ;
              (c) si la transition `→ EXPIRED` est déclenchée par le premier
                  expiré ou par les deux.
Impact      : Test TC-ERR-05 (« SIGNED_URL_TTL ou EXPORT_SESSION_TTL expiré ») ne
              spécifie pas lequel des deux et n'observe pas leur composition.
              Comportement métier non déterministe.
Gravité     : Majeur

Écart 07 — Tests TC-INV-* déclarés mais non rédigés

Type        : Incohérence Spec↔Tests
Référence   : tests §5 (TC-INV-01, TC-INV-02, TC-INV-04, TC-INV-06, TC-INV-08,
              TC-INV-09, TC-INV-10, TC-INV-11, TC-INV-12)
              + tests §3 et §4 (sections « Flux nominaux » et « Cas d'erreur »
              ne contenant aucun TC-INV-*)
Description : La matrice §5 référence 9 identifiants `TC-INV-*` comme tests
              dédiés aux invariants, mais aucun n'est défini en Given/When/Then
              dans les sections §3/§4 du document tests. Seul leur existence est
              affirmée par le tableau §5. Les invariants INV-286-01, -02, -04,
              -06, -08, -09, -10, -11, -12 reposent ainsi sur des tests fantômes.
Impact      : Couverture déclarée non démontrable. Verdict QA §10 (« Testable
              partiellement ») insuffisamment fondé : neuf invariants n'ont qu'un
              test nominatif (TC-NOM-* ou TC-ERR-*) en couverture vérifiable.
Gravité     : Majeur

Écart 08 — Format .pvproof modifié vs « format final inchangé »

Type        : Contradiction
Référence   : spec §4 INV-286-08 (« .pvproof final … contient `volumes_count` +
              `assembled_from[]` si totalVolumes ≥ 2 »)
              + §2 Exclus (« Changement du format final `.pvproof` » exclu)
              + tests §6 TC-NR-02 (« Format final `.pvproof` inchangé »,
              « Ajout champs multi-volumes sans rupture »)
Description : §2 exclut la modification du format final `.pvproof`. INV-286-08
              ajoute deux champs (`volumes_count`, `assembled_from[]`)
              conditionnellement. TC-NR-02 affirme conjointement « inchangé »
              et « ajout champs multi-volumes sans rupture » — formulation
              auto-contradictoire selon que le consommateur applique une
              validation de schéma stricte (additional properties refused) ou
              tolérante.
Impact      : Tension contractuelle entre §2 et §4 INV-286-08. Comportement des
              consommateurs `.pvproof` legacy non garanti, pourtant imposé par
              INV-286-05.
Gravité     : Majeur

Écart 09 — « Audit WORM fail-closed » sans définition de la défaillance

Type        : Règle non testable
Référence   : spec §4 INV-286-09
Description : L'invariant impose un audit WORM « fail-closed » incluant
              `exportId`, `volumes_count`, `integrityHash[]`, statut final, mais
              aucune section ne définit le comportement attendu lorsque
              l'écriture WORM échoue pendant le flux :
              - L'export est-il bloqué (fail-closed strict) ?
              - Re-tenté ? Mis en quarantaine ?
              - Faut-il arrêter le flux après chaque écriture WORM échouée ou
                seulement à la fin ?
              Le terme « fail-closed » est invoqué sans observable.
Impact      : Aucune assertion testable n'est dérivable. Test TC-NOM-05 vérifie
              la présence d'entrées en succès, mais aucun test ne couvre l'échec
              d'audit. Risque de divergence d'implémentation.
Gravité     : Majeur

Écart 10 — Format .pvproof single-volume non spécifié

Type        : Ambiguïté
Référence   : spec §4 INV-286-08 (conditionne les champs « si totalVolumes ≥ 2 »)
              + §5.1 (`assembled_from[]` listé dans le modèle global, sans
              distinction single/multi)
              + §7 CA-286-03 (« Un seul fichier final, statut COMPLETED »)
              + tests TC-NOM-03 (n'aborde que multi-volumes)
Description : Pour `totalVolumes = 1`, la spécification ne stipule pas si
              `volumes_count` et `assembled_from[]` sont :
              (a) absents du `.pvproof` (silence par défaut) ;
              (b) présents avec valeur `1` et tableau de cardinalité 1 ;
              (c) présents avec valeur 0 et tableau vide.
              INV-286-05 (rétrocompatibilité legacy) suggère (a), mais sans
              l'imposer. Les tests TC-NOM-01 / TC-NR-02 n'observent pas la
              présence/absence de ces champs en single-volume.
Impact      : Lecture divergente possible côté consommateurs ; rétrocompatibilité
              legacy non garantie en pratique.
Gravité     : Majeur

Écart 11 — Cas totalSize == 0 non spécifié

Type        : Hypothèse dangereuse
Référence   : spec §5bis (Mermaid état) `REQUESTED → PLANNED_SINGLE: totalSize ≤
              768MB` ; `REQUESTED → PLANNED_MULTI: 768MB < totalSize ≤ 10GB`
              + §4 INV-286-01 (`0 < estimatedBytes ≤ 805_306_368`)
Description : Pour un dossier sans preuve validée (`totalSize == 0` ou
              `estimatedBytes == 0`), le diagramme route vers `PLANNED_SINGLE`,
              mais INV-286-01 exige `estimatedBytes > 0` strict. La transition
              produit un volume invalide selon le propre invariant de la spec.
              Aucun ERR-286-* ne couvre le cas dossier vide.
Impact      : Transition légale du diagramme conduisant à un état contradictoire
              avec un invariant non négociable. État `FAILED` implicite mais
              non couvert par la matrice d'erreurs.
Gravité     : Majeur

Écart 12 — Diagramme d'état : transitions « INTERDITE » représentées comme arêtes

Type        : Ambiguïté
Référence   : spec §5bis (Mermaid stateDiagram) lignes
              `COMPLETED --> REQUESTED: INTERDITE`,
              `COMPLETED --> DOWNLOADING: INTERDITE`,
              `FAILED --> REQUESTED: INTERDITE`,
              `EXPIRED --> REQUESTED: INTERDITE`
Description : Une convention Mermaid associe chaque arête à une transition
              autorisée. Marquer ces arêtes « INTERDITE » comme arêtes du
              graphe contredit la sémantique du diagramme et autorise une
              lecture erronée (ex. générateur d'automate qui les considérerait
              comme transitions valides étiquetées « INTERDITE »).
Impact      : Risque d'interprétation automatisée erronée (génération de FSM,
              vérification formelle). Bruit visuel non discriminable des
              transitions valides.
Gravité     : Mineur

Écart 13 — TC-NEG-04/05 : 5xx « auto-émis » par le backend non descriptible

Type        : Incohérence Spec↔Tests
Référence   : tests §7 TC-NEG-04 (`totalVolumes=0` ou `>14` → `500 INVALID_TOTAL_
              VOLUMES`)
              TC-NEG-05 (`volumeIndex<0` ou `>13` → `500 INVALID_VOLUME_INDEX_
              RANGE`)
              + spec §5.1 (mêmes contraintes décrites comme contraintes
              côté backend)
Description : Les valeurs invalides de `totalVolumes` et `volumeIndex` ne
              proviennent pas d'une entrée client : elles sont produites par
              le backend dans la réponse `volumes[]`. Les tests négatifs
              décrivent donc un backend qui valide sa propre sortie et
              renvoie 500 — sans expliciter le déclencheur (mock interne ?
              injection ? mutant ?). La condition `WHEN` n'est pas observable
              côté API client.
Impact      : Tests non implémentables tels qu'écrits ; ambiguïté sur le
              fournisseur de l'invalidité (auto-tests backend, fault-injection,
              ou cas réel).
Gravité     : Majeur

Écart 14 — INV-286-12 redondant avec INV-286-11

Type        : Contradiction
Référence   : spec §4 INV-286-11 (« toute transition non listée = interdite »)
              + INV-286-12 (« États terminaux … sans transition sortante »)
Description : Les transitions sortantes d'un état terminal n'étant pas listées
              en §4 « Transitions autorisées », elles sont déjà interdites par
              INV-286-11. INV-286-12 ré-énonce ce cas sans nouveau contenu
              normatif.
Impact      : Double comptage en couverture (CA-286-07 mappé à INV-286-11 et
              INV-286-12 simultanément) ; pas d'erreur logique mais risque de
              divergence en cas de modification ultérieure d'un seul des deux.
Gravité     : Mineur

Écart 15 — Référence performance « 4G / iPhone 12 » sans observable

Type        : Règle non testable
Référence   : spec §5.2 (« Contexte perf de référence : réseau mobile 4G
              standard, appareil classe iPhone 12 équivalent »)
Description : Aucun seuil de performance (latence, débit, durée totale d'export)
              n'est dérivé de cette référence. Aucun test (TC-NOM/ERR/INV/NEG)
              ne s'y appuie. La mention introduit un cadrage non opérationnel.
Impact      : Référence inerte ; ne contraint aucun test ni aucun verdict ;
              lecteur peut croire que des SLA performance existent.
Gravité     : Mineur

Écart 16 — « Message explicite utilisateur » non testable

Type        : Règle non testable
Référence   : spec §6 ERR-286-04 (« arrêt immédiat, export global FAILED, message
              explicite utilisateur »)
Description : « Message explicite utilisateur » est un critère subjectif sans
              format ni canal défini. Aucun observable n'est associé.
Impact      : Critère non vérifiable ; tests TC-ERR-04 et TC-NOM-04 ne
              l'observent pas.
Gravité     : Mineur

Écart 17 — CA-286-04 observable « Logs/tests »

Type        : Ambiguïté
Référence   : spec §7 CA-286-04 (Observable : « Logs/tests montrent recalcul +
              comparaison effective »)
Description : « Logs ou tests » mélange deux canaux de vérification de natures
              distinctes : la trace runtime (production) et l'exécution de
              tests (CI). Le critère d'acceptation ne précise pas lequel doit
              être observé pour valider l'invariant en exploitation.
Impact      : Critère acceptable trivialement par la seule présence de tests,
              sans garantie d'observabilité production.
Gravité     : Mineur

Écart 18 — Idempotence / retry du flux multi-volumes côté app non spécifiée

Type        : Hypothèse dangereuse
Référence   : spec §5.5 (« Aucun mécanisme de protection distribuée additionnel
              applicable »)
              + §5.4 step 4 (téléchargement séquentiel)
              + ERR-286-07 (« Echec téléchargement d'un volume » → FAILED)
Description : Un flux mobile multi-volumes (jusqu'à 10 GB sur 4G) implique
              probablement des reprises réseau côté HTTP. La spec écarte
              explicitement tout mécanisme de protection distribuée
              (idempotence, retry contrôlé, lock). Elle ne précise pas non
              plus si une reprise (re-GET du même `signedUrl` après timeout
              partiel) est autorisée ou si tout incident TCP/HTTP transitoire
              déclenche immédiatement un FAILED global (ERR-286-07).
Impact      : Comportement de robustesse mobile sous-spécifié ; deux
              implémentations conformes peuvent diverger drastiquement (« retry
              transparent N fois » vs « FAILED au premier socket reset »).
Gravité     : Majeur

Écart 19 — Rétrocompatibilité legacy posée par hypothèse non vérifiée

Type        : Hypothèse dangereuse
Référence   : spec §9 H-286-03 (« Les clients legacy tolèrent la présence
              optionnelle de `volumes[]` sans régression »)
              + §4 INV-286-05
              + tests §6 TC-NR-01
Description : INV-286-05 (rétrocompatibilité) repose sur H-286-03, posée comme
              hypothèse explicitement non validée (« Risque de casse client,
              besoin de versionnement API » en colonne « Impact si faux »).
              TC-NR-01 vérifie le contrat backend mais pas la consommation
              effective par les clients legacy en exploitation.
Impact      : Invariant non négociable adossé à une hypothèse explicitement
              fragile ; aucun test ne couvre la régression côté client legacy.
Gravité     : Majeur

Écart 20 — Faisabilité « ajout assembleur » sur format .pvproof

Type        : Hypothèse dangereuse
Référence   : spec §5.4 step 4 (« téléchargement → vérification hash volume →
              ajout assembleur »)
              + §2 Exclus (« Changement du format final `.pvproof` »)
Description : L'« ajout assembleur » suppose que le format `.pvproof` est
              concaténatif (stream-friendly) ou que l'ordre d'ingestion des
              volumes est compatible avec sa structure interne. Aucune section
              n'établit cette propriété. Si `.pvproof` est un conteneur clos
              (zip, tar, format propriétaire avec table d'index globale en
              tête), l'ajout incrémental n'est pas trivial.
Impact      : Faisabilité technique du flux nominal multi-volumes non démontrée
              par la spec ; risque de bloquer l'implémentation après
              décomposition.
Gravité     : Majeur

Écart 21 — TTL pendant un téléchargement actif

Type        : Ambiguïté
Référence   : spec §5.2 (« état export → EXPIRED, téléchargement refusé »)
              + ERR-286-05 (« Expiration signed URL/session pendant
              traitement » → EXPIRED)
              + §5bis état `DOWNLOADING → EXPIRED: ttl_expired`
Description : « Téléchargement refusé » se réfère à une nouvelle requête. Le
              comportement attendu d'un téléchargement HTTP en cours
              (connexion TCP ouverte, octets en vol) au moment exact de
              l'expiration n'est pas spécifié : annulation immédiate, drain
              jusqu'à fin du volume courant, accept then refuse-next ?
Impact      : Ambiguïté sur la limite de l'état `EXPIRED` ; comportement
              d'arrêt non testable de manière déterministe.
Gravité     : Majeur

Écart 22 — Écritures audit avant statut final FAILED

Type        : Risque sécu/conformité
Référence   : spec §5bis séquence (« App→Audit: append {volumeIndex, hash_ok}
              » émis dans la boucle, pour chaque volume validé)
              + INV-286-09 (« audit … inclut … statut final »)
Description : La séquence émet un événement WORM `hash_ok` pour chaque volume
              validé pendant la boucle. Si un volume ultérieur échoue (hash
              mismatch ou réseau), l'export passe FAILED, mais les `hash_ok`
              déjà écrits restent immuables (WORM). La corrélation avec un
              statut final FAILED n'est pas définie : faut-il un événement
              `cancel` ou est-ce le statut final qui contextualise les
              `hash_ok` antérieurs ? La piste d'audit peut suggérer une
              progression de succès partielle contrairement à INV-286-09 (qui
              exige « statut final »).
Impact      : Lisibilité forensique compromise ; un auditeur lisant uniquement
              les premiers événements peut conclure à un succès partiel
              jamais réalisé. Tension avec « pas de succès partiel »
              (CA-286-05).
Gravité     : Majeur

Écart 23 — App mobile écrivant directement dans WORM

Type        : Risque sécu/conformité
Référence   : spec §5bis séquence (« App → Audit: append … » direct depuis
              participant `App as ProbatioVault App »)
              + INV-286-09 (« Audit WORM fail-closed »)
Description : La séquence montre l'application mobile (React Native + Expo)
              comme source directe d'événements WORM. La spec ne définit pas
              le canal d'écriture (API backend dédiée signée ? push direct sur
              un journal append-only ?), ni la chaîne de confiance entre une
              app mobile potentiellement compromise et un journal probatoire.
              L'auditabilité WORM repose alors sur une source non
              authentifiable selon les sections présentes.
Impact      : Risque d'injection ou de falsification d'événements
              probatoires depuis le terminal client. Modèle de menace
              implicite, non documenté.
Gravité     : Majeur

Écart 24 — SIGNED_URL_TTL défaut 24h sur appareil mobile

Type        : Risque sécu/conformité
Référence   : spec §5.2 (`SIGNED_URL_TTL` défaut 24h)
              + §3 def. signedUrl (`max 4096 chars`)
Description : Le défaut 24h pour des URLs pré-signées sur volumes contenant des
              preuves probatoires est élevé sur appareil mobile, et offre une
              fenêtre étendue à un attaquant disposant d'un accès lecture
              ponctuel à la mémoire/log/keystore. La spec ne discute pas de la
              révocation possible, ni du couplage entre `SIGNED_URL_TTL` et
              `EXPORT_SESSION_TTL`.
Impact      : Surface d'exposition élevée pendant la fenêtre de validité ;
              non-aligné avec une posture de moindre privilège temporel.
Gravité     : Mineur

Écart 25 — « Taille totale exportée » polysémique

Type        : Ambiguïté
Référence   : spec §4 INV-286-02 (« Taille totale exportée ≤ 10 GB »)
Description : « Taille totale exportée » peut désigner :
              (a) somme des tailles brutes des preuves côté backend avant
                  partition ;
              (b) somme des `estimatedBytes` des volumes (qui peut différer
                  des preuves brutes par overhead manifest) ;
              (c) volumétrie réellement transférée à l'app (post-compression
                  / chiffrement) ;
              (d) taille du `.pvproof` final.
              Le test TC-ERR-01 retient (a) implicitement (« dossier validé de
              taille strictement > 10 737 418 240 bytes »), sans que la spec
              le tranche.
Impact      : Détection du dépassement potentiellement divergente entre
              implémentations ; les invariants d'overflow et le code 413
              dépendent du référent choisi.
Gravité     : Majeur

Écart 26 — TC-INV-10 « testable conditionnel » sans condition contractuelle

Type        : Incohérence Spec↔Tests
Référence   : tests §2 (matrice INV-286-10 → TC-INV-10 « Oui (conditionnel) »)
              + tests §5 (« INV-286-10 / TC-INV-10 / Nécessite preuves
              techniques d'infra »)
              + §9 (« règle non testable » non listée pour INV-286-10)
Description : Le test est mappé à un invariant non négociable (INV-286-10)
              avec qualification « conditionnel » et observable « Nécessite
              preuves techniques d'infra » — sans préciser la nature de ces
              preuves, ni le critère d'acceptation. Pourtant INV-286-10 n'est
              pas listé en §9 « Règles non testables ».
Impact      : Invariant crypto-proof non négociable réputé testable, mais
              dépendant d'observables flous, et non répertorié comme
              non-testable malgré la formulation.
Gravité     : Majeur

Écart 27 — Définition « Etat terminal » vs traitement de EXPIRED

Type        : Ambiguïté
Référence   : spec §3 (« Etat terminal : état sans transition sortante
              autorisée »)
              + §4 INV-286-12 (terminaux : `COMPLETED`, `FAILED`, `EXPIRED`)
              + §4 transitions (« REQUESTED|PLANNED_*|DOWNLOADING|ASSEMBLING
              → EXPIRED »)
Description : `EXPIRED` est listé comme terminal en INV-286-12, mais la
              définition §3 et la table de transitions ne distinguent pas
              entre « EXPIRED entrant depuis flux » et « EXPIRED » résultat
              d'expiration locale (TTL). La transition `→ EXPIRED` est listée
              comme entrante seulement ; aucun verbe d'observation n'est
              donné côté backend (qui décide de la transition ?).
Impact      : Lecture cohérente possible mais surface d'interprétation pour
              le déclencheur de la transition (cron côté backend, polling
              côté app, lazy-evaluation côté API).
Gravité     : Mineur

Écart 28 — volumeIndex max=13 vs INV-286-06 « [0..totalVolumes-1] »

Type        : Contradiction
Référence   : spec §5.1 (`volumeIndex` min 0, max 13)
              + §4 INV-286-06 (« volumeIndex continu [0..totalVolumes-1] »)
Description : Si `totalVolumes` peut atteindre 14 (max §5.1), alors
              `volumeIndex` peut valoir 13. Dans ce cas, INV-286-06 et §5.1
              sont cohérents en limite haute. En revanche, l'écart 01
              ci-dessus établit que `totalVolumes > 14` est métier-réalisable.
              Indépendamment de l'écart 01, la valeur exacte du couple
              (max 13 / max 14) n'est jamais reliée arithmétiquement dans la
              spec (« 10 GB / 768 MB arrondi sup » est un commentaire en
              colonne « Taille / bornes », pas une formule normative).
Impact      : Sans expression normative du couplage, une montée du seuil
              `VOLUME_MAX_BYTES` (par patch ultérieur) désynchronise les
              deux bornes silencieusement.
Gravité     : Mineur

Notes complémentaires

  • Diagrammes Mermaid (axe 5bis) : la couverture des transitions textuelles vs Mermaid est globalement alignée à l'exception de l'écart 12 (transitions « INTERDITE » représentées comme arêtes). Le diagramme de séquence cite canonical_json comme primitive, ce qui aggrave l'écart 02.
  • Hypothèses de §9 jugées « non bloquantes » par la spec mais critiques : H-286-01 (canonicalisation) et H-286-03 (rétrocompatibilité) sous-tendent INV-286-07/05 — invariants non négociables. Les hypothèses sont documentées comme telles, mais leur statut de pré-condition non vérifiée n'est pas reflété dans la criticité affichée.
  • Verdict QA §10 du document tests (« Testable partiellement, sous réserve de Q-286-01 et Q-286-02 ») — cohérent avec les écarts 02 et 04 ; en revanche il omet les écarts 03, 05, 09, 11, 18, 22, 23, 25, 26 qui rendent plusieurs invariants non testables.

⚠️ Aucune correction proposée. Aucune reformulation. Aucune implémentation suggérée.