Aller au contenu

PD-287 — Revue de spécification (Gate 3)

Auditeur : indépendant, orienté contractualisation / testabilité / conformité. Portée : PD-287-specification.md (audit complet) + PD-287-tests.md (audit restreint : ambiguïtés / contradictions / non testabilité / hypothèses dangereuses / risques sécu). Couverture fonctionnelle des tests non évaluée.

Synthèse des écarts

# Type Gravité
1 Contradiction — anti-enumeration vs 429 sur blocage OTP Bloquant
2 Incohérence Spec↔Tests — payload consultation (document chiffré vs encrypted_doc_ref) Bloquant
3 Incohérence Spec↔Diagramme — endpoints /reauth/request-otp et /reauth/confirm non contractualisés Bloquant
4 Risque sécu/conformité — DoS ciblé via seuil OTP cumulé (D-287-31>=50 → REVOKED) Bloquant
5 Non testable — SLA P95 testé comme requête unique (CA-287-08, TC-NOM-09) Bloquant
6 Contradiction interne — transitions no-op X → X qualifiées INTERDITE … no-op Majeur
7 Ambiguïté — INV-287-08 borne SLA = valeur par défaut configurée vs §5.3 Configurabilité : Non (MVP) Majeur
8 Incohérence Spec↔Tests — réinitialisation compteur OTP session sur OTP_BLOCKED → ACTIVE (TC-NOM-04) non spécifiée Majeur
9 Ambiguïté — format owner_key_ref et encrypted_doc_ref absents du §5.1 Majeur
10 Règle manquante — durée de vie du code OTP (validité du code avant péremption) Majeur
11 Non testable — INV-287-01 grep mémoire process non déterministe en CI/CD Majeur
12 Ambiguïté — max_consultations_per_link définie en §5.2 sans invariant/CA/ST associé Majeur
13 Contradiction — F-287-02 « 429 uniquement pour rate-limit » vs ERR-287-07/ERR-287-20 (429 sur états OTP_BLOCKED / REVOKED) Majeur
14 Hypothèse dangereuse — ordre d’ancrage Merkle en cas de crash multi-batchs non garanti (§5.7) Majeur
15 Ambiguïté — chiffrement au repos des données destinataire (email, IP, UA) non contractualisé Majeur
16 Ambiguïté — scope de mémorisation du warning pas de DRM (user/device/session) Majeur
17 Hypothèse dangereuse — payload canonique/hashing pour clé idempotence non spécifié Majeur
18 Incohérence — borne otp_failed_attempts_total_per_link_max=50 vs déclencheur D-287-31>=50 (seuil atteint ≠ stockable) Mineur
19 Coquille — numérotation hypothèses : H-287-07 manquant (saut H-287-06 → H-287-08) Mineur
20 Incohérence Spec↔Tests — TC-NR-04 / TC-NOM-08 / TC-NEG-07 / CA-287-13 référencent l’export sans règle de skip si export_zip_enabled=false Mineur
21 Ambiguïté — D-287-09 expires_at_utc = created_at_utc + ttl_seconds classé en format de donnée (ligne tabulaire) au lieu de règle Mineur
22 Hypothèse dangereuse — changement de propriétaire de la preuve pendant la vie du lien non traité (§5.8) Mineur
23 Diagramme d’état — transitions no-op interdites non représentées graphiquement (note textuelle uniquement) Mineur
24 Ambiguïté — INV-287-01 « périmètre de la story » non énuméré exhaustivement Mineur
25 Ambiguïté — définition de trust-store incomplet (ERR-287-16) Mineur

Détail des écarts

Écart 1 — Anti-enumeration vs 429 sur blocage OTP (contradiction interne)

Type       : Contradiction
Référence  : PD-287-specification.md §4 INV-287-12 ; §5.4 F-287-02 ; §6 ERR-287-07 ; §6 ERR-287-20 ; §7 CA-287-18
Description:
  INV-287-12 impose « 404 générique uniquement (hors 429 rate-limit) » pour toutes les
  réponses destinataire (lien invalide, inconnu, expiré, révoqué, non autorisé).
  F-287-02 réaffirme « 429 uniquement pour rate-limit/anti-abus ».
  ERR-287-07 (5 échecs OTP session) retourne 429 + expose explicitement
  `share_state=OTP_BLOCKED`.
  ERR-287-20 (plafond cumulé) retourne 429 + transition `REVOKED`.
  Un attaquant observant le passage 404 → 429 sur la 5e tentative déduit :
  (a) que le token correspond à un lien existant, (b) que l’état interne a changé.
  La contradiction persiste si l’on considère « blocage OTP » comme rate-limit, car
  le code renvoyé reste différenciant entre « 4 mauvais OTP sur token valide »
  et « 5 mauvais OTP sur token valide ».
Impact     : L’anti-enumeration contractuelle (INV-287-12, CA-287-18) n’est pas
             respectée ; des tests (TC-ERR-07, TC-NEG-01) valident un comportement
             qui viole l’invariant qu’ils sont censés protéger.
Gravité    : Bloquant

Écart 2 — Consultation : document chiffré vs encrypted_doc_ref (contradiction Spec↔Diagramme)

Type       : Incohérence Spec↔Tests / contradiction interne
Référence  : §5.4 F-287-03 ; §5bis diagramme de séquence ; §7 CA-287-12 ; §8 ST-287-08 ; PD-287-tests.md TC-NOM-07
Description:
  §5.4 F-287-03 et CA-287-12 stipulent que la consultation restitue
  « document chiffré + capsule PRE + hash + Merkle + TSA + ancrage ».
  Le diagramme de séquence (§5bis) montre la réponse `GET /sharing/{token}/proof`
  retournant `encrypted_doc_ref` (référence), pas le contenu chiffré.
  `encrypted_doc_ref` n’est défini nulle part dans §5.1 (aucun D-287-*).
  Si c’est une URL interne, le flux de déchiffrement réel côté destinataire
  (récupération du blob, chaîne de capsules PRE, vérification intégrité)
  n’est pas contractualisé.
Impact     : Ambiguïté contractuelle sur la nature du payload probatoire livré.
             Risque d’implémentation divergente. Tests TC-NOM-07 / ST-287-08
             non observables sans convention partagée.
Gravité    : Bloquant

Écart 3 — Endpoints de réauthentification non documentés (incohérence Spec↔Diagramme)

Type       : Incohérence Spec↔Tests
Référence  : §5.4 F-287-02/F-287-03 ; §5bis diagramme de séquence ; §6 ERR-287-09
Description:
  Le diagramme de séquence mentionne deux endpoints destinataire :
  `POST /sharing/{token}/reauth/request-otp` et `POST /sharing/{token}/reauth/confirm`,
  déclenchés sur `session_inactive`. Aucun flux (F-287-*), aucun invariant,
  aucun CA et aucun test (TC-ERR-09, ST-287-*) ne contractualise ces endpoints.
  Les réponses attendues (codes, enveloppe, rate-limit partagé avec OTP initial ?)
  ne sont pas définies.
Impact     : Surface d’attaque non auditée (endpoints vivants dans la spec
             implicite mais non bornés). Testabilité impossible.
Gravité    : Bloquant

Écart 4 — DoS ciblé via seuil OTP cumulé

Type       : Risque sécurité/conformité
Référence  : §4 INV-287-20 ; §5.2 otp_failed_attempts_total_per_link_max ; §6 ERR-287-20 ; §8 ST-287-18 ; §7 CA-287-31
Description:
  Toute entité disposant du `share_url_token` (destinataire légitime, ou toute
  fuite via forward email, extensions navigateur, proxy corporate, historique
  navigateur sur poste partagé) peut provoquer 50 tentatives OTP invalides et
  déclencher une transition définitive vers `REVOKED` (terminal).
  Le propriétaire est notifié mais ne peut pas restaurer le lien : la seule voie
  est de créer un nouveau lien (nouveau token, nouveau OTP).
  Aucun mécanisme de blocage *temporaire* (équivalent à `OTP_BLOCKED` mais sur le
  compteur cumulé) n’est proposé en amont de la transition terminale.
Impact     : Vecteur de déni de service ciblant un partage légitime,
             exploitable sans compromission cryptographique.
Gravité    : Bloquant

Écart 5 — P95 mesuré sur requête unique

Type       : Non testable
Référence  : §4 INV-287-08 ; §5.2 revocation_effective_latency_p95_ms ; §5.3 sla_revocation_effective ;
             §7 CA-287-08 ; §8 ST-287-09 ; PD-287-tests.md TC-NOM-09
Description:
  CA-287-08 et TC-NOM-09 exigent « révocation effective <= 2s P95 (charge nominale :
  1 lien actif, 10 RPS, staging mono-instance) ». Un test de scénario unitaire
  ne peut pas produire un P95 : un P95 est une statistique sur une distribution
  (typiquement N>=100 échantillons). Le critère est défini en termes de percentile
  mais observé comme seuil ponctuel par l’implémentation de test indiquée.
  Idem pour `link_generation_latency_p95_ms` (§5.2).
Impact     : Critère mesurable uniquement en performance/charge ;
             non couvert par une suite E2E standard. Risque de passage trompeur
             si on teste sur une seule mesure.
Gravité    : Bloquant

Écart 6 — Transitions no-op qualifiées « INTERDITE … no-op »

Type       : Contradiction
Référence  : §5.5 machine d’états, sous-sections `PENDING_ACTIVATION`, `ACTIVE`, `OTP_BLOCKED`
Description:
  Les trois tableaux listent la ligne `-> X | INTERDITE (transition d’état) | no-op | Aucun changement d’état`.
  Une transition « interdite » implique un rejet (erreur). Un « no-op … aucun
  changement d’état » implique acceptation silencieuse. Les deux formulations
  sont sémantiquement incompatibles. Aucun test (TC-INV-02, TC-NEG-12) ne
  précise quel code d’erreur / absence d’erreur doit être observé pour ces cas.
Impact     : Ambiguïté contractuelle sur le comportement runtime et la
             réponse HTTP attendue en cas de re-soumission idempotente.
Gravité    : Majeur

Écart 7 — INV-287-08 : « valeur par défaut configurée » vs §5.3 non configurable

Type       : Ambiguïté / contradiction
Référence  : §4 INV-287-08 ; §5.3 sla_revocation_effective (Configurabilité: Non (MVP))
Description:
  INV-287-08 formule la borne comme « valeur par défaut configurée (2000ms P95) ».
  §5.3 déclare explicitement la colonne Configurabilité à « Non (MVP) ».
  Les deux formulations sont inconsistantes : « par défaut configurée » implique
  qu’une valeur alternative est possible, « Non (MVP) » exclut toute surcharge.
Impact     : Interprétation divergente possible sur la présence d’un knob de
             configuration. Impacte le contrat de robustesse SLA.
Gravité    : Majeur

Écart 8 — Réinitialisation compteur OTP session sur sortie de OTP_BLOCKED non spécifiée

Type       : Incohérence Spec↔Tests
Référence  : §5.5 état `OTP_BLOCKED → ACTIVE` ; PD-287-tests.md TC-NOM-04
Description:
  TC-NOM-04 exige « compteur d’essais OTP session réinitialisé » lors de la
  transition `OTP_BLOCKED → ACTIVE`. Aucun invariant (INV-287-*), aucun CA
  (CA-287-04) et aucune ligne de §5.5 n’explicite ce reset. La règle est
  inférée côté tests mais absente du corpus normatif.
Impact     : Comportement observable reposant sur une règle non contractualisée.
             Risque d’implémentation où le compteur n’est pas remis à 0.
Gravité    : Majeur

Écart 9 — Formats owner_key_ref et encrypted_doc_ref absents du §5.1

Type       : Ambiguïté
Référence  : §5.1 modèle de données contractuel ; §5bis diagramme de séquence
Description:
  Le diagramme de séquence utilise `owner_key_ref` (argument de `PRE_ReKeyGen`)
  et `encrypted_doc_ref` (réponse de `get_encrypted_proof`). Aucun des deux
  n’apparaît comme D-287-* dans §5.1, qui est pourtant déclaré « source unique
  des formats » (note finale §5.1).
Impact     : Brise la règle « Référencement unique » de §5.1.
             Format/encodage/taille/charset non contractualisés donc
             non vérifiables par schéma.
Gravité    : Majeur

Écart 10 — Durée de vie du code OTP absente

Type       : Règle non testable / donnée manquante
Référence  : §5.1 D-287-11 ; §5.2 (paramètres OTP) ; §4 INV-287-04
Description:
  Le corpus définit `otp_code_digits=6`, `otp_max_attempts_per_session=5`,
  `otp_block_duration_seconds=900`, `otp_resend_limit_per_hour=3`,
  mais aucune borne sur la durée de validité du code OTP lui-même
  (durée entre émission et péremption du code). Un OTP émis il y a 2 heures
  peut-il être validé ? La spec n’en parle pas.
Impact     : Règle de sécurité critique manquante. INV-287-04 (OTP requis
             à chaque nouvelle session) est testable sur la présence, pas
             sur la fraîcheur. Risque d’OTP à TTL infini en implémentation.
Gravité    : Majeur

Écart 11 — INV-287-01 : vérification bornée par grep mémoire process non déterministe

Type       : Non testable
Référence  : §4 INV-287-01 ; §7 CA-287-14 ; PD-287-tests.md TC-INV-11, TC-NEG-06
Description:
  L’invariant impose trois canaux de preuve : (a) audit flux réseau,
  (b) grep mémoire process, (c) absence dans logs/dumps.
  Un `grep mémoire process` dépend du snapshot : timing GC, pages swappées,
  pool d’allocation, zombie strings JIT. Un test CI/CD reproductible ne peut
  pas garantir que le document n’a jamais été en mémoire entre deux snapshots.
  Aucune fenêtre de tolérance (après GC forcé ? après redémarrage ?) n’est
  spécifiée.
Impact     : Critère tel que formulé non couvrable par un pipeline CI
             déterministe. Risque de test flaky ou de bypass silencieux.
Gravité    : Majeur
Type       : Incohérence Spec↔Tests / règle non testable
Référence  : §5.2 ligne `max_consultations_per_link (0=illimité)` ; §4 (aucun invariant) ;
             §7 (aucun critère) ; §8 (aucun scénario)
Description:
  Le paramètre est déclaré en §5.2 avec bornes 0..1000 (valeur par défaut 0 = illimité).
  Aucun invariant INV-287-*, aucun CA-287-*, aucun test ST-287-* ou TC-*-* ne
  spécifie le comportement (code de rejet, état atteint, événement d’audit) lorsque
  le plafond est atteint. La matrice de couverture (tests §2) ne le référence pas.
Impact     : Fonctionnalité partiellement spécifiée : présence d’un knob sans
             contrat d’usage. Implémentation et testabilité livrées à l’agent.
Gravité    : Majeur

Écart 13 — Divergence « 429 uniquement pour rate-limit » vs 429 sur états FSM

Type       : Contradiction
Référence  : §5.4 F-287-02 ; §6 ERR-287-07 ; §6 ERR-287-20
Description:
  §5.4 F-287-02 déclare « 429 uniquement pour rate-limit/anti-abus ».
  ERR-287-07 utilise 429 pour signaler l’atteinte d’un seuil FSM (OTP_BLOCKED).
  ERR-287-20 utilise 429 en conjonction avec une transition d’état terminale (REVOKED).
  Ces deux cas sont des transitions FSM déclenchées, pas des rejets préventifs
  de rate-limit au sens HTTP (pas de `Retry-After` pertinent sur un REVOKED terminal).
Impact     : Sémantique HTTP ambiguë. Rend la réponse d’erreur non homogène
             pour le destinataire et pour les clients (proxy / monitoring).
Gravité    : Majeur

Écart 14 — Ordre d’ancrage Merkle en cas de crash multi-batchs non garanti

Type       : Hypothèse dangereuse
Référence  : §5.7 (Crash post-commit avant ancrage) ; §4 INV-287-13 ; §5.3 sla_audit_anchor_delay
Description:
  §5.7 stipule « Crash post-commit avant ancrage → DB + audit cohérents;
  ancrage rattrapé sans perte d’ordre ». Avec un seul batch en flight, c’est
  trivial. Avec plusieurs batchs (haute activité, pool workers, réconciliation),
  le protocole de reprise et l’invariant d’ordre (Merkle root ancré dans l’ordre
  d’émission des événements) ne sont pas décrits. Le `audit_batch_merkle_root_sha256_hex`
  (D-287-33) est un champ, pas un contrat d’ordonnancement.
Impact     : Risque de tree poisoning ou réordonnancement post-crash.
             Compromet l’opposabilité probatoire (INV-287-13).
Gravité    : Majeur

Écart 15 — Chiffrement au repos des données destinataire non contractualisé

Type       : Risque sécurité/conformité
Référence  : §4 INV-287-15, INV-287-16 ; §5.1 D-287-03 (recipient_email), D-287-28 (client_ip), D-287-29 (user_agent) ; §5.11 politique de rétention
Description:
  INV-287-16 cible explicitement les « artefacts crypto temporaires (DEK,
  rekey, fragment, capsule) » : chiffrés au repos. Les **données destinataire**
  (email, IP, UA) sont soumises à rétention (§5.11, 90j post-terminal)
  mais aucun invariant n’impose leur chiffrement au repos. L’email destinataire
  est un pivot d’identification RGPD.
Impact     : Ambiguïté sur la protection au repos des données personnelles.
             Risque de non-conformité RGPD si base accessible (sauvegardes,
             réplicas, dumps DBA).
Gravité    : Majeur

Écart 16 — Scope de mémorisation du warning « pas de DRM »

Type       : Ambiguïté
Référence  : §4 INV-287-24 ; §7 CA-287-26 ; §8 ST-287-11 ; PD-287-tests.md TC-NOM-11
Description:
  CA-287-26 exige le warning « affiché une seule fois au propriétaire puis mémorisé ».
  Le scope de mémorisation (compte utilisateur serveur / device / navigateur /
  session) n’est pas spécifié. Conséquences opposées :
  - scope compte : un PO qui change de device voit le warning une seule fois total.
  - scope device : chaque nouveau device réaffiche.
  TC-NOM-11 ne tranche pas.
Impact     : Observable dépendant de l’interprétation. Testabilité dégradée.
Gravité    : Majeur

Écart 17 — Payload canonique/hashing pour clé idempotence non spécifié

Type       : Hypothèse dangereuse
Référence  : §5.1 D-287-25 ; §5.6 mécanisme idempotence ; §6 ERR-287-13, ERR-287-19
Description:
  L’idempotence compare « même clé + même payload » vs « même clé + payload
  différent ». Le critère d’égalité des payloads n’est pas défini : comparaison
  byte-à-byte ? normalisation JSON Canonical (JCS) ? hash SHA-256 ? ordre des clés ?
  Un client qui sérialise le même objet JSON dans un ordre différent recevrait
  un 409 `IDEMPOTENCY_CONFLICT` en violation du contrat retry-safe.
Impact     : Faux positifs de conflit idempotent. CA-287-23 devient
             non déterministe selon le sérialiseur du client.
Gravité    : Majeur

Écart 18 — Borne D-287-31 vs seuil REVOKED

Type       : Incohérence Spec↔Tests
Référence  : §5.1 D-287-31 (`otp_failed_attempts_total` regex 0..50) ; §5.2 otp_failed_attempts_total_per_link_max=50 ; §5.5 (seuil OTP cumulé) ; §7 CA-287-31
Description:
  Le champ stocke des valeurs 0..50. La transition vers REVOKED est déclenchée
  lorsque `D-287-31>=50` (§5.5 note de comportement). Deux ambiguïtés :
  (a) la valeur 50 est-elle atteinte, stockée, puis la transition se produit,
      ou la transition se produit lors de l’incrément qui porterait à 50 ?
  (b) la borne max du champ est 50 : si 51 devait être atteint (race condition),
      stockage impossible, compteur plafonné silencieusement ?
  TC-ERR-20 teste « 49 → OTP invalide → REVOKED », suggérant que 50 est le
  déclencheur, jamais 51. Mais pas de règle de garde-fou contre la double
  transition concurrente.
Impact     : Comportement frontière non déterministe, risque de race
             condition sur incrément concurrent.
Gravité    : Mineur

Écart 19 — Numérotation H-287-07 manquante

Type       : Ambiguïté
Référence  : §9 Hypothèses (saut H-287-06 → H-287-08)
Description:
  La liste d’hypothèses passe de H-287-06 à H-287-08 sans H-287-07.
  Soit un retrait non tracé, soit une coquille.
Impact     : Traçabilité contractuelle entachée. Aucune preuve que le
             périmètre hypothétique est stable.
Gravité    : Mineur

Écart 20 — Export ZIP optionnel : règle de skip absente

Type       : Incohérence Spec↔Tests
Référence  : §2 périmètre (export ZIP optionnel MVP) ; §9 Q-287-06 ; §7 CA-287-13 ;
             PD-287-tests.md TC-NOM-08, TC-NR-04, TC-NEG-07
Description:
  L’export est explicitement « optionnel » et subordonné à `export_zip_enabled=true`.
  Les tests TC-NOM-08, TC-NR-04, TC-NEG-07 n’expriment aucune règle de skip
  conditionnel. La matrice de couverture (§2) note « maintenue via CA-287-13,
  TC-NOM-08, TC-NEG-07 *si* la feature est activée » — mais cette phrase est
  dans la section commentaire, pas dans la spec elle-même.
Impact     : Si export désactivé, état de la suite de tests indéterminé.
             Non reproductible entre environnements.
Gravité    : Mineur

Écart 21 — D-287-09 classé en format de donnée au lieu de règle

Type       : Ambiguïté
Référence  : §5.1 D-287-09 `expires_at_utc` (colonne regex : `expires_at_utc = created_at_utc + ttl_seconds`)
Description:
  La colonne `Regex / contrainte` contient une **formule de calcul** (égalité
  métier), pas un pattern syntaxique. Cela dévie de la convention du tableau
  (toutes les autres lignes contiennent un regex ASCII/Base64/UUID/RFC3339).
  La formule devrait figurer comme règle de cohérence (§5.3 ou §4).
Impact     : Non testable comme regex par un validateur de schéma standard.
             Cohérence calculée = règle de business logic, pas contrainte de format.
Gravité    : Mineur

Écart 22 — Changement de propriétaire de la preuve pendant la vie du lien non traité

Type       : Hypothèse dangereuse
Référence  : §5.8 contraintes inter-modules ; §4 INV-287-22
Description:
  Le guard inter-module s’appuie sur « ownership indirect via lien ».
  Si `proofs.owner_user_id` change (transfert de compte, succession,
  suppression/anonymisation), l’état du lien n’est pas décrit : reste-t-il
  valide ? transitionne-t-il vers REVOKED ? devient-il zombie ?
Impact     : Hypothèse implicite (ownership immuable durant la vie du lien)
             non documentée. Risque d’état incohérent en cas d’évolution
             du compte propriétaire.
Gravité    : Mineur

Écart 23 — Diagramme d’état : transitions no-op absentes graphiquement

Type       : Incohérence Spec↔Diagramme (axe 5bis)
Référence  : §5.5 (no-op listées textuellement) ; §5bis diagramme d’état (seule note globale)
Description:
  §5.5 liste les `X → X` comme interdites. Le diagramme représente uniquement
  les transitions autorisées + note textuelle globale. Un lecteur du diagramme
  seul n’a pas la visibilité de l’interdiction explicite des no-op. La règle
  ne peut pas être validée sur le diagramme.
Impact     : Diagramme incomplet vs corpus normatif.
Gravité    : Mineur

Écart 24 — INV-287-01 : « périmètre de la story » non énuméré

Type       : Ambiguïté
Référence  : §4 INV-287-01
Description:
  L’invariant zero-knowledge est borné à « aucun endpoint, service, log ou
  dump mémoire listé dans le périmètre de la story ». §5.8 liste trois routes
  mais §5 ne fournit pas d’énumération exhaustive des services concernés
  (orchestrateur API, worker réconciliation, worker export, worker purge,
  worker ancrage). Le périmètre d’audit réseau/mémoire reste flou.
Impact     : Audit contractuel sous-spécifié ; portée du test TC-INV-11
             définie par convention tacite.
Gravité    : Mineur

Écart 25 — Définition de trust-store incomplet

Type       : Ambiguïté
Référence  : §4 INV-287-17 ; §6 ERR-287-16 ; §7 CA-287-29 ; PD-287-tests.md TC-ERR-16, TC-NEG-08
Description:
  ERR-287-16 distingue « absent / incomplet / invalide » mais aucune définition
  de ce qui constitue un trust-store « incomplet » n’est donnée (racine manquante ?
  CRL non téléchargée ? AIA non résolue ? OCSP indisponible ?).
Impact     : Cas d’erreur testable uniquement après clarification du
             critère d’incomplétude.
Gravité    : Mineur

Verdict

Statut Commentaire
Écarts Bloquants : 5 anti-enumeration 429, payload consultation, endpoints reauth, DoS seuil OTP cumulé, P95 ponctuel
Écarts Majeurs : 12 FSM no-op, INV-287-08 configurabilité, reset compteur OTP non spécifié, formats manquants, durée OTP, grep mémoire, max_consultations sans contrat, 429 sur FSM, ordre ancrage multi-batchs, chiffrement données destinataire, scope mémorisation warning, hashing idempotence
Écarts Mineurs : 8 borne D-287-31, H-287-07 manquante, export skip, D-287-09 en format, ownership mutable, diagramme no-op, INV-287-01 périmètre, trust-store incomplet

Aucune correction, reformulation ou implémentation n’est proposée dans le présent livrable (conformité au cadrage d’audit Gate 3).