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_jsoncomme 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.