PD-284 — Specification Review (Gate 3)¶
Auditeur : Claude (mode factuel) Date : 2026-03-12 Documents audités : PD-284-specification.md, PD-284-tests.md
1) Ambiguïtés¶
AMB-01 — document_id : case-insensitive vs UUID v4 strict¶
Type : Ambiguïté Référence : §5.6, regex ^[0-9a-fA-F-]{36}$ Description : La spec déclare document_id comme « case-insensitive » avec regex acceptant majuscules et minuscules. Or PD-80 backend stocke les UUID en lowercase strict (^[0-9a-f]{8}-...). La spec PD-284 ne précise pas si le client doit normaliser en lowercase avant envoi, ni si la comparaison côté client (ex : corrélation seal_id événement SSE vs seal_id local) doit être case-insensitive ou post-normalisation. Impact : Mismatch potentiel lors de la corrélation d'événements SSE si le backend renvoie lowercase et le client stocke mixed-case. Gravité : Mineur
AMB-02 — Bouton « absent OU désactivé » : comportement non déterministe¶
Type : Ambiguïté Référence : §6 Cas d'erreur (« bouton absent ou désactivé »), TC-NOM-03 (« absent OU rendu disabled ») Description : Pour quota épuisé, la spec et les tests autorisent deux comportements distincts (absent ou disabled). L'implémenteur et le testeur ne peuvent pas vérifier un comportement univoque. Le CA-284-03 dit « État disabled + raison affichée » (un seul comportement), tandis que le scénario §8.1 et TC-NOM-03 disent « absent OU disabled ». Impact : Contradiction entre CA-284-03 (qui impose disabled) et les scénarios/tests (qui admettent absent ou disabled). Impossible d'écrire un test déterministe. Gravité : Majeur
AMB-03 — « Scellement en cours déjà existant » : état non défini¶
Type : Ambiguïté Référence : §6 (« déjà en urgent »), TC-NEG-10 Description : La spec mentionne le cas « déjà en urgent » comme raison de non-affichage du bouton, mais ne définit pas comment le client détermine qu'un scellement urgent est déjà en cours pour ce document. Pas de champ dans le modèle de données §5.6, pas de flag dans les données serveur listées. Impact : L'implémenteur devra inventer une heuristique non contractualisée pour détecter ce cas. Gravité : Majeur
AMB-04 — « Reprise automatique » SSE : stratégie non spécifiée¶
Type : Ambiguïté Référence : §5.2, INV-284-07 Description : Après perte réseau, la spec dit « tente la reprise automatique » et « Après 3 échecs consécutifs SSE, bascule en polling ». Mais le délai entre les tentatives de reconnexion SSE n'est pas spécifié (exponentiel ? fixe ? immédiat ?). Le TTL heartbeat SSE est explicitement « à clarifier » (§5.9, Q-284-04). Impact : Comportement de reconnexion non déterministe. Le test TC-NOM-08 vérifie « 3 échecs SSE consécutifs » mais ne peut pas vérifier le timing entre tentatives. Gravité : Majeur
AMB-05 — « Erreur contrôlée » : terme non défini¶
Type : Ambiguïté Référence : §6, INV-284-04, TC-ERR-03, TC-ERR-04 Description : Le terme « erreur contrôlée » est utilisé 6 fois dans la spec et les tests sans définition. S'agit-il d'un état visuel spécifique (toast, bannière, écran d'erreur) ? D'un log silencieux ? D'un badge sur la carte ? TC-ERR-04 mentionne « État d'erreur contrôlé affiché (non bloquant) » — est-ce un état dans la machine d'états §5.7 ou un overlay visuel temporaire ? Impact : L'implémenteur et le testeur ne partagent pas la même définition du comportement attendu. Gravité : Majeur
2) Contradictions¶
CTR-01 — CA-284-03 vs §6/TC-NOM-03 : « disabled » vs « absent OU disabled »¶
Type : Contradiction Référence : CA-284-03 (§7), §6 Cas d'erreur, TC-NOM-03 Description : CA-284-03 impose « État disabled + raison affichée » (un seul comportement). Le cas d'erreur §6 et TC-NOM-03 autorisent « absent OU disabled ». Ces deux formulations sont mutuellement exclusives pour un test déterministe. Impact : Le testeur ne peut pas rédiger une assertion univoque. Gravité : Majeur
CTR-02 — Scénario §8.4 (Coupure SSE) vs INV-284-07 : badge « Hors ligne » timing¶
Type : Contradiction Référence : §8 scénario 4, TC-NOM-08, TC-NOM-14, INV-284-07 Description : Le scénario §8.4 dit « bascule en polling 5s et affiche l'état Hors ligne ». TC-NOM-14 dit que le badge Hors ligne apparaît à la « perte réseau détectée ». TC-NOM-08 dit que le badge apparaît « quand 3 échecs SSE consécutifs sont injectés ». Ces deux moments sont différents : perte réseau immédiate ≠ après 3 échecs SSE. Le badge Hors ligne apparaît-il à la détection de perte réseau (immédiat) ou après 3 échecs SSE (différé) ? Impact : Deux tests (TC-NOM-08 et TC-NOM-14) vérifient le même badge à des moments différents avec des déclencheurs différents. Gravité : Majeur
CTR-03 — hash_document affiché « dès réception » : quelle réception ?¶
Type : Contradiction Référence : §5.4 Flux D, TC-NOM-12 Description : §5.4 dit « hash_document : dès réception ». TC-NOM-12 dit « hash_document apparaît dès réception valide ». Mais « réception » est ambigu : réception de la réponse POST initiale ? Réception du premier événement SSE ? Le hash est-il dans le payload POST, dans chaque événement SSE, ou seulement dans certains états ? Le modèle de données §5.6 liste hash_document comme format mais ne précise pas dans quel payload il apparaît. Impact : L'implémenteur ne sait pas à quel moment afficher le hash en mode expert. Gravité : Mineur
3) Règles non testables¶
NT-01 — INV-284-09 : « protection de stockage de la plateforme »¶
Type : Non testable Référence : INV-284-09, TC-INV-09 Description : L'invariant exige qu'aucun artefact sensible ne soit « persisté en clair côté client durable sans protection de stockage de la plateforme ». Le TC-INV-09 vérifie « Toute persistance constatée est protégée par mécanisme de stockage sécurisé plateforme ». Mais la spec ne définit pas : (a) quels artefacts sont « sensibles » (hash_document ? tsa_token_ref ? tous les champs expert ?), (b) quel mécanisme de stockage est « protégé » (iOS Keychain ? Encrypted UserDefaults ? SecureStore Expo ?), © si le cache mémoire (état React) compte comme « persistance durable ». Impact : Sans définition des artefacts sensibles et des mécanismes acceptables, le test est subjectif. Gravité : Majeur
NT-02 — INV-284-10 : « Aucune contrainte inter-module backend supplémentaire »¶
Type : Non testable Référence : INV-284-10, TC-INV-10 Description : Le test TC-INV-10 propose un « traçage réseau applicatif complet » pour vérifier qu'aucun appel hors contrats PD-80/PD-284 n'est émis. Mais les « contrats existants PD-80/PD-284 » ne sont pas listés exhaustivement (les endpoints exacts ne sont pas dans cette spec). La matrice de couverture note « couverture partielle ». Impact : Vérification objective impossible sans liste exhaustive des endpoints autorisés. Gravité : Mineur
4) Incohérences Spec ↔ Tests¶
IST-01 — Absence de test pour « déjà en urgent » (TC-NEG-10 incomplet)¶
Type : Incohérence Spec↔Tests Référence : §6 (« déjà en urgent »), TC-NEG-10 Description : TC-NEG-10 vérifie « Tentative interaction urgente en état déjà urgent » → « Action refusée/désactivée ». Mais le GIVEN ne précise pas comment l'état « déjà urgent » est rendu observable côté client (cf. AMB-03). Le test est non exécutable sans cette précision. Impact : Test non exécutable en l'état. Gravité : Majeur
IST-02 — TC-NOM-12 suppose des jalons non contractualisés¶
Type : Incohérence Spec↔Tests Référence : §5.4 Flux D, TC-NOM-12 Description : TC-NOM-12 vérifie que tsa_token_ref apparaît « au plus tôt à TSA_SEALED ». La spec §5.4 dit « token TSA : dès scellement TSA ». Mais la spec ne contractualise pas quels champs sont présents dans chaque événement SSE par état. Le test suppose une correspondance état→champs qui n'est pas définie. Impact : Le test repose sur une hypothèse implicite de contenu des payloads SSE par état. Gravité : Majeur
IST-03 — TC-ERR-08 (fallback email) : observable côté client ?¶
Type : Incohérence Spec↔Tests Référence : Flux E, TC-ERR-08 Description : TC-ERR-08 vérifie que « le signal de fallback email backend est observable (événement/trace contractuelle) ». Mais PD-284 est front-only. Le client ne peut pas observer un email envoyé par le backend. L'observable proposé n'est pas dans le périmètre de cette story. Impact : Test hors périmètre PD-284 (dépend de PD-80 backend). Gravité : Mineur
IST-04 — TC-NEG-07 (tempête dupliqués) : idempotence non spécifiée¶
Type : Incohérence Spec↔Tests Référence : TC-NEG-07 Description : TC-NEG-07 vérifie « Idempotence d'affichage attendue (pas de régression visuelle) » face à des événements dupliqués avec même event_id. Mais la spec ne mentionne jamais de mécanisme d'idempotence côté client (déduplication par event_id, cache d'événements vus, etc.). Le test vérifie un comportement non spécifié. Impact : Le test suppose un mécanisme non contractualisé. Gravité : Majeur
5) Hypothèses dangereuses¶
HD-01 — SSE ordonnés (H-284-01) : non garanti par SSE standard¶
Type : Hypothèse dangereuse Référence : H-284-01, §5.2 Description : H-284-01 suppose que PD-80 expose des événements SSE « ordonnés ». Le protocole SSE (RFC) ne garantit pas l'ordre de livraison en cas de reconnexion avec Last-Event-ID. Si le client se reconnecte et reçoit un batch de rattrapage, l'ordre n'est garanti que si le backend l'implémente. La spec PD-284 ne prévoit aucun mécanisme de réordonnancement côté client (numéro de séquence, timestamp de tri). Impact : En cas de reconnexion SSE, le client pourrait recevoir TSA_SEALED avant TSA_PENDING, violant INV-284-04 (machine monotone). Gravité : Majeur
HD-02 — Deep-link disponible dans tous les états iOS¶
Type : Hypothèse dangereuse Référence : H-284-04, INV-284-08 Description : Le deep-linking suppose que l'app est installée, configurée, et que le schéma d'URL est enregistré. La spec ne prévoit aucun fallback si le deep-link échoue (app désinstallée, schéma non enregistré, app en cold start). La politique iOS hors foreground est explicitement « à clarifier » (Q-284-06). Impact : Notifications avec deep-link cassé → utilisateur sans accès au détail. Gravité : Mineur
HD-03 — Atomicité de l'abonnement SSE après POST¶
Type : Hypothèse dangereuse Référence : §5.1 Flux A (étapes 4-5) Description : Le flux nominal suppose que le client reçoit le seal_id du POST, puis ouvre le SSE. Entre le POST et l'ouverture SSE, des événements peuvent être émis par le backend et perdus. La spec ne précise pas si le premier événement SSE est un snapshot de l'état courant ou si le client doit faire un GET initial pour synchroniser. Impact : Le client pourrait manquer la transition RECEIVED→QUEUED_PRIORITY si elle est quasi-instantanée. Gravité : Majeur
6) Risques sécurité / conformité¶
SEC-01 — Deep-link injection non traitée¶
Type : Risque sécu/conformité Référence : INV-284-08, Flux E Description : La spec exige un deep-link dans les notifications mais ne mentionne aucune validation du schéma d'URL. Si le seal_id provient du backend, un attaquant compromettant le flux SSE pourrait injecter un deep-link malveillant. Aucune whitelist de schéma d'URL n'est spécifiée. Impact : Redirection vers écran arbitraire ou URL externe si le parsing de deep-link n'est pas sécurisé. Gravité : Mineur
SEC-02 — Absence de rate-limiting côté client sur POST urgent¶
Type : Risque sécu/conformité Référence : §5.1 Flux A Description : La spec mentionne que le backend gère le rate-limiting (§5.10), mais aucun debounce/throttle côté client n'est spécifié sur le bouton urgent. Un utilisateur peut spammer le bouton, générant N requêtes POST avant que le réseau ne réponde. Impact : Requêtes multiples inutiles ; UX dégradée si plusieurs seal_id sont créés. Gravité : Mineur
Synthèse¶
| Gravité | Nombre | IDs |
|---|---|---|
| Bloquant | 0 | — |
| Majeur | 10 | AMB-02, AMB-03, AMB-04, AMB-05, CTR-01, CTR-02, NT-01, IST-01, IST-02, IST-04, HD-01, HD-03 |
| Mineur | 6 | AMB-01, CTR-03, NT-02, IST-03, HD-02, SEC-01, SEC-02 |
Note : Les points Q-284-01 à Q-284-08 (§10 de la spec) sont déjà identifiés comme ouverts par l'auteur. Cette review ne les re-liste pas sauf lorsqu'ils aggravent un écart structurel (ex : AMB-04 aggravé par Q-284-04).