PD-284 — Specification Review¶
Résultat de l'audit indépendant¶
Auditeur : Claude Opus 4.6 (mode factuel, temperature 0.1) Documents analysés : PD-284-specification.md (v2), PD-284-tests.md (v2) Date : 2026-03-12
Synthèse¶
| Gravité | Nombre |
|---|---|
| Bloquant | 2 |
| Majeur | 7 |
| Mineur | 5 |
| Total | 14 |
Points identifiés¶
1.¶
Type : Contradiction Référence : Spec §5.6 (tableau formats) vs Spec §5.12 (payload SSE par état) Description : Le tableau §5.6 définit event_id comme "entier positif séquentiel croissant (par seal_id)" avec regex ^[1-9][0-9]{0,18}$. Or le document de tests TC-NOM-16 parle d'un "cache de déduplication client configuré à N=100 event_id". Si event_id est séquentiel croissant, la déduplication par cache LRU/FIFO de 100 entrées est correcte. Cependant, la spec §5.2 point 3 dit "cache local des N=100 derniers event_id" sans préciser la politique d'éviction (FIFO ? LRU ?). Le test TC-NEG-07 mentionne "eviction FIFO/LRU conforme cache N=100" comme si les deux politiques étaient acceptables. Or FIFO et LRU produisent des comportements différents sous tempête de doublons : avec LRU un doublon refresh sa position, avec FIFO non. La spec ne tranche pas. Impact : Un implémenteur tierce pourrait choisir FIFO, un autre LRU — comportement divergent sous charge. Gravité : Mineur
2.¶
Type : Ambiguïté Référence : Spec §5.1 étape 3 — "l'action envoie la demande de scellement urgent pour document_id" Description : Le verbe "envoie la demande" ne précise pas l'endpoint, la méthode HTTP ni le payload. Le point Q-284-02 reconnaît que le "contrat exact payload POST /seals/urgent" est à clarifier, mais la spec utilise néanmoins POST /seals/{id}/status dans le flux sans jamais formaliser l'endpoint de déclenchement. Les tests (TC-NOM-04) mentionnent "Mock API POST urgent répond succès avec seal_id valide" sans URL. Pour un document à valeur contractuelle, l'absence d'endpoint de déclenchement rend le flux A non implémentable de manière déterministe. Impact : Un implémenteur ne sait pas quelle URL appeler ni quel body envoyer pour déclencher un scellement urgent. Le seal_id est retourné mais le contrat requête/réponse est absent. Gravité : Bloquant
3.¶
Type : Incohérence Spec↔Tests Référence : Spec INV-284-05 vs Tests §2 matrice de couverture — ligne INV-284-05 Description : La matrice de couverture associe INV-284-05 ("L'UI n'invente aucun seuil de dégradation") au test TC-NOM-10 et au critère CA-284-07. Or CA-284-07 dans la spec concerne la "déduplication SSE active sur event_id", pas les badges de dégradation. Le critère pertinent serait CA-284-11 ("Badge dégradation reflète les flags serveur uniquement"). La matrice contient donc une erreur de mapping. Impact : Traçabilité INV→CA→TC erronée ; un auditeur suivant la matrice ne validerait pas le bon critère. Gravité : Majeur
4.¶
Type : Ambiguïté Référence : Spec §5.2 point 4 — "en cas de réception désordonnée, le client réordonne par sequence_number avant rendu" Description : Le mécanisme de réordonnancement n'est pas borné temporellement. Combien de temps le client doit-il attendre un sequence_number manquant avant de considérer un gap ? Si le client reçoit 10, 12 : doit-il attendre 11 indéfiniment, ou détecter immédiatement un gap ? La spec dit "gap (current_sequence - previous_sequence > 1) → GET resync" mais ne distingue pas le cas "désordre temporaire" (11 arrive 50ms après 12) du cas "gap réel" (11 ne viendra jamais). Aucun délai de grâce n'est spécifié. Impact : Sans fenêtre de réordonnancement, l'implémentation peut osciller entre resync excessive (coûteux) et attente infinie (bloquant). Gravité : Majeur
5.¶
Type : Hypothèse dangereuse Référence : Spec §5.2 point 5-6 — backoff exponentiel puis polling Description : Le backoff est spécifié comme "tentative 1: 1s, tentative 2: 2s, tentative 3: 4s" avec les paramètres sse_reconnect_base_delay=1, sse_reconnect_backoff_factor=2, sse_reconnect_max_attempts_before_polling=3. Or le tableau §5.8 définit des bornes : base_delay min=0.5 max=5, factor min=1 max=3, attempts min=1 max=10. Avec la configuration max (base=5s, factor=3, attempts=10), le backoff total avant failover serait 5+15+45+135+... = ~147 635 secondes (~41 heures). La spec ne plafonne pas le délai cumulé avant failover polling, uniquement le nombre de tentatives et les paramètres individuels. Impact : Une configuration extrême (dans les bornes autorisées) rend le failover polling inaccessible pendant des heures. L'utilisateur reste sans feedback pendant ce temps. Gravité : Majeur
6.¶
Type : Contradiction Référence : Spec §5.6 (format tsa_token_ref) vs Spec §5.11 (classification artefacts) vs Tests §9 (règles non testables) Description : La spec §5.6 définit tsa_token_ref avec regex ^[A-Za-z0-9:_-]{1,256}$ (ASCII alphanumérique + ponctuation limitée). Or le même tableau indique "string UTF-8" comme format/encodage. La regex exclut tout caractère UTF-8 hors ASCII (accents, CJK, etc.). Le document de tests §9 classe la "validation formelle exhaustive tsa_token_ref" comme règle non testable car "spécification de format encore incomplète". Pourtant, la spec fournit bien une regex. Il y a contradiction entre la regex fournie et la mention UTF-8, et entre la spec (regex fournie) et les tests (format "incomplet"). Impact : Ambiguïté sur le format réel accepté ; la regex est testable contrairement à ce qu'affirment les tests. Gravité : Mineur
7.¶
Type : Incohérence Spec↔Tests Référence : Spec CA-284-04 vs Tests §2 matrice — ligne "N/A | CA-284-04 | TC-NOM-04" Description : Le critère CA-284-04 dans la spec est : "Quand has_active_urgent_seal=true, bouton urgent disabled + tooltip état actif". Or la matrice de couverture associe CA-284-04 au test TC-NOM-04 qui concerne "Après POST, GET /seals/{id}/status AVANT ouverture SSE". Le bon critère pour TC-NOM-04 serait CA-284-05 ("Après déclenchement réussi, le client fait GET avant ouverture SSE"). La matrice confond CA-284-04 et CA-284-05. Impact : Erreur de traçabilité dans la matrice. Le test TC-NOM-04 couvre CA-284-05, pas CA-284-04. CA-284-04 est couvert par TC-NOM-15. Gravité : Majeur
8.¶
Type : Non testable Référence : Spec INV-284-13 — "Aucune contrainte inter-module backend supplémentaire n'est créée par cette story" Description : L'invariant exige de prouver l'absence de toute contrainte inter-module backend additionnelle. Le test TC-INV-10 propose un "traçage réseau applicatif complet" pour vérifier qu'aucun "nouvel appel inter-module backend hors contrats existants" n'est observé. Cet observable est fragile : il suppose que le traçage réseau est exhaustif et que les contrats existants PD-80/PD-284 sont formellement délimités. Un appel indirect (via queue BullMQ, via event bus) ne serait pas visible en traçage réseau HTTP. La matrice de couverture le reconnaît ("Partielle"). Impact : L'invariant est formulé comme absolu ("aucune") mais la preuve ne peut être que partielle. Un auditeur ne pourra pas conclure de manière formelle. Gravité : Mineur
9.¶
Type : Ambiguïté Référence : Spec §5.3 — Flux C — "Badge En retard" et "Badge Critique" Description : La spec mentionne trois niveaux de dégradation (Normal, Retard, Critique) mais ne définit pas le nom exact du champ/flag backend qui les porte. Le §5.12 mentionne degradation_flag? (optionnel) dans les champs communs SSE, mais ne formalise pas les valeurs possibles de ce champ. Le test TC-NOM-10 utilise les valeurs none, delayed, critical — ces valeurs ne figurent nulle part dans la spec. Pour un document contractuel, le enum de degradation_flag doit être explicite. Impact : L'implémenteur front et l'implémenteur back peuvent diverger sur les valeurs exactes du flag. Gravité : Majeur
10.¶
Type : Risque sécu/conformité Référence : Spec §5.11 — Classification artefacts — tsa_token_ref sensible, persistance "Oui, chiffrée uniquement" Description : La spec autorise la persistance de tsa_token_ref en iOS Keychain / Expo SecureStore. Or la spec ne précise pas quand ce secret doit être purgé. Un tsa_token_ref persisté indéfiniment en Keychain survit à la déconnexion utilisateur, à la suppression du compte, voire au changement de device (restauration iCloud Keychain). Aucune politique de rétention ou de purge n'est définie. Pour un artefact probatoire classé sensible, l'absence de TTL ou de règle de nettoyage est un risque de conformité. Impact : Accumulation de secrets probatoires sans purge, potentiellement exposés via restauration iCloud cross-device. Gravité : Majeur
11.¶
Type : Hypothèse dangereuse Référence : Spec §5.2 point 8 — "Si SSE redevient disponible, le client revient à SSE et arrête le polling" Description : Le mécanisme de détection de la disponibilité SSE pendant le mode polling n'est pas spécifié. Le client doit-il tenter une connexion SSE à chaque cycle de polling ? À intervalle fixe ? Après un certain nombre de polls ? Le test TC-NOM-09 dit "le canal SSE redevient disponible et connexion réussie" sans préciser comment le client détecte cette disponibilité. Si le client ne re-tente jamais SSE, il reste en polling indéfiniment. Impact : Sans mécanisme de re-tentative SSE documenté, le retour à SSE est non déterministe. Gravité : Majeur
12.¶
Type : Incohérence Spec↔Tests Référence : Spec §3 — "Erreur contrôlée" vs Tests TC-NOM-16 Description : La définition §3 d'une erreur contrôlée inclut "journalisation telemetry obligatoire avec event_type et seal_id". Le test TC-NOM-16 (déduplication) indique "une trace telemetry de déduplication est émise" pour chaque doublon ignoré. Or la déduplication n'est pas une erreur — c'est un comportement nominal attendu. Classer chaque déduplication comme erreur contrôlée avec toast 5s serait incorrect (les doublons SSE sont courants). La spec §5.2 point 3 dit "événement ignoré silencieusement", ce qui contredit l'émission d'un toast. Le test ajoute une trace telemetry non prévue par le flux nominal. Impact : Contradiction entre "ignoré silencieusement" (spec §5.2) et "trace telemetry de déduplication" (test TC-NOM-16). L'implémenteur ne sait pas si un doublon génère un toast + telemetry ou est silencieux. Gravité : Mineur
13.¶
Type : Bloquant — Ambiguïté Référence : Spec §5.9 — SLA temporels — "TTL connexion SSE côté client (heartbeat) : à clarifier PD-80" Description : Le TTL heartbeat SSE est le seul mécanisme permettant au client de distinguer "connexion SSE morte silencieusement" de "pas d'événement car rien ne se passe". Sans heartbeat défini, le client ne peut pas savoir quand déclencher la séquence de reconnexion. Ce paramètre conditionne directement le comportement de reconnexion (§5.2 points 5-6), le failover polling, et le badge hors-ligne. L'ensemble du flux B repose sur ce TTL. Impact : Sans heartbeat contractualisé, le client ne peut pas détecter une connexion SSE morte silencieusement → le flux B de reconnexion est non implémentable de manière déterministe. Gravité : Bloquant
14.¶
Type : Mineur — Incohérence Spec↔Tests Référence : Spec §7 CA-284-03 vs Tests §2 matrice — INV-284-03 mapping Description : La matrice de couverture associe INV-284-03 à CA-284-03 pour les deux conditions (quota=0 et urgent actif). Or le texte de CA-284-03 dans la spec §7 ne mentionne que le quota : "Quand quota épuisé, bouton urgent disabled + tooltip quota". La condition has_active_urgent_seal=true est couverte par CA-284-04. La matrice attribue correctement TC-NOM-15 à INV-284-03/CA-284-03, mais CA-284-03 ne couvre textuellement que le quota. C'est CA-284-04 qui couvre le cas urgent actif. Impact : Mapping CA imprécis dans la matrice ; un auditeur strict constaterait que CA-284-03 ne couvre pas has_active_urgent_seal. Gravité : Mineur
Récapitulatif par gravité¶
Bloquants (2)¶
| # | Sujet |
|---|---|
| 2 | Endpoint de déclenchement urgent non formalisé (flux A non implémentable) |
| 13 | TTL heartbeat SSE non défini (flux B de reconnexion non implémentable) |
Majeurs (7)¶
| # | Sujet |
|---|---|
| 3 | Erreur mapping INV-284-05 → CA-284-07 (devrait être CA-284-11) |
| 4 | Absence de fenêtre temporelle de réordonnancement avant détection de gap |
| 5 | Backoff cumulé non plafonné — configuration extrême = 41h sans failover |
| 7 | Erreur mapping CA-284-04 → TC-NOM-04 (devrait être CA-284-05) |
| 9 | Enum degradation_flag non formalisé dans la spec |
| 10 | Absence de politique de purge/TTL pour artefacts sensibles persistés en Keychain |
| 11 | Mécanisme de re-tentative SSE depuis mode polling non spécifié |
Mineurs (5)¶
| # | Sujet |
|---|---|
| 1 | Politique d'éviction cache event_id non tranchée (FIFO vs LRU) |
| 6 | Contradiction format UTF-8 vs regex ASCII-only pour tsa_token_ref |
| 8 | INV-284-13 ("aucune contrainte") non prouvable formellement |
| 12 | Contradiction "ignoré silencieusement" vs "trace telemetry" pour déduplication |
| 14 | CA-284-03 ne couvre textuellement que le quota, pas has_active_urgent_seal |