PD-284 — Confrontation Gate 8 (CLOSURE) — v1¶
Reviewer : claude -p (MODE FACTUEL) Date : 2026-03-13
Convergences (10)¶
- Machine d'états monotone —
ALLOWED_TRANSITIONStable exhaustive, pas de retour arrière - Séquence POST→GET→SSE — Orchestrateur respecte la séquence contractuelle CA-284-05
- Branded types — SealId, DocumentId empêchent inversions UUID à la compilation
- Backoff/failover — SSE client avec backoff exponentiel 1s/2s/4s → failover polling
- Déduplication FIFO — Ring buffer avec éviction correcte, cache 100 entries
- Sécurité stockage — SecureStore + kSecAttrAccessibleWhenUnlockedThisDeviceOnly
- Validation Zod — Toute donnée entrante (API + SSE) validée avant consommation
- Badge hors ligne — TransportMode DISCONNECTED → badge visible dans ProgressCard
- Notifications terminales — SEALED/FAILED_TIMEOUT → push + deep-link
- Dégradation server-only — INV-284-05 respecté, pas de seuil local
Divergences (7)¶
| ID | Criticité | Description | Impact |
|---|---|---|---|
| DIV-01 | MAJEUR | Contradiction coverage UrgentSealButton : 100% dans jest vs "aucun test C8" dans review QA | Artefact d'injection — le test urgent-button.test.tsx existe (199 lignes). La review QA n'avait pas les fichiers de test dans son contexte. Résolu par vérification. |
| DIV-02 | MAJEUR | merkle_proof[] non stocké dans le store Zustand, pas de mécanisme alternatif documenté | L'ExpertPanel n'affiche pas merkle_proof (ligne merkle_proof: () => null dans FIELD_BUILDERS). Le proof est vérifiable via proof_package_url. Design intentionnel, pas un écart. |
| DIV-03 | MINEUR | Étape visuelle "Arbre Merkle" (index 2) jamais assignée dans STATE_TO_VISUAL_STEP | L'état ANCHOR_PENDING est le seul à mapper sur cette étape. Si aucun event ANCHOR_PENDING n'est reçu (transition directe TSA_SEALED→SEALED), l'étape 2 est sautée visuellement. Acceptable — l'UI montre la progression réelle. |
| DIV-04 | MINEUR | resyncFromServer() contourne ALLOWED_TRANSITIONS sans exception dans la spec | Le resync est un recalibrage forcé après détection de gap irréparable. La spec §5.14 prévoit le resync comme mécanisme de reprise. Le contournement est par design. |
| DIV-05 | MINEUR | Regex UUID /^[0-9a-fA-F-]{36}$/ accepte des formats invalides (36 tirets) | Atténué par validation serveur PD-80. Le client sert de validation de confort. |
| DIV-06 | MINEUR | Store applyEvent ne re-valide pas les transitions (confiance en C4 EventProcessor) | Architecture en couches : C4 valide les transitions, C5 (store) applique les events validés. Pas de double validation par design (séparation des responsabilités). |
| DIV-07 | INFO | 7 composants sur 15 sans livrable agent auditable | Les composants non livrés par agent (orchestrator, SSE client, screen, deep-link, barrel exports) ont été implémentés directement par l'orchestrateur en Phase 6b/6c. |
Zones d'ombre (7)¶
- Politique iOS hors foreground — Comportement SSE quand l'app passe en background non spécifié
- Quotas par plan —
SEAL_QUOTAS_BY_PLANhardcodé côté client, pas de sync serveur - Heartbeat SSE — Pas de heartbeat côté serveur pour détecter connexion morte
- Purge au logout — Intégration de
purgeSealArtifactsdans le flux de logout non documentée - Timeout POST /seals/urgent — Pas de timeout explicite dans le fetch (config par défaut)
- Retry POST après 5xx — Pas de retry automatique côté client
- Locale TSA timestamp — Format d'affichage non spécifié (UTC vs local)
Recommandation¶
ACCEPTÉ AVEC RÉSERVES — Les 2 divergences MAJEUR (DIV-01, DIV-02) sont résolues/justifiées. Les 5 MINEUR sont atténués. Les zones d'ombre sont des points d'amélioration pour des stories futures, pas des écarts bloquants.