PD-284 — Agent Developer — seal-detail-screen (C14)
Module
seal-detail-screen — Écran détail scellement intégrant C8 (UrgentSealButton), C9 (SealProgressCard), C10 (SealExpertPanel).
Fichier produit
src/screens/vault/SealDetailScreen.tsx
Résumé
Écran React Native composant l'ensemble des modules seal en une vue cohérente. Il orchestre le cycle de vie complet d'un scellement urgent : affichage du bouton, gestion de l'orchestrateur SSE/polling, affichage de la progression, panneau expert, toasts d'erreur contrôlée, et cleanup à la destruction.
Architecture
SealDetailScreen
├── Document header (nom + ID)
├── UrgentSealButton (C8) — visible si pas de seal actif
├── Loading indicator — pendant POST/GET sequence
├── Error message — si orchestration échouée
├── SealProgressCard (C9) — visible si seal actif
├── SealExpertPanel (C10) — visible si seal actif + mode_expert
└── Toast overlay — erreur contrôlée (5s auto-dismiss)
Invariants couverts
| Invariant | Mécanisme | Observable |
| INV-284-01 / INV-284-02 | Délégué à C8 (UrgentSealButton) via props | Bouton visible/absent selon account_type |
| INV-284-05 | Aucun calcul local de seuil — tout délégué aux composants enfants | Pas de Date.now() ni comparaison de durée |
| INV-284-09 | NetInfo listener → isOffline state → prop SealProgressCard.isOffline | Badge "Hors ligne" uniquement si isConnected === false |
| INV-284-10 | Purge purgeSealArtifacts(activeSealId) au unmount via useEffect cleanup | Aucun artefact sensible résiduel |
| INV-284-12 | sendTerminalNotification callback → C11 | Deep-link inclus dans notification terminale |
| INV-284-13 | Aucun endpoint hors contrat (POST /seals/urgent, GET /seals/{id}/status, SSE) | Front-only, pas de nouveau contrat backend |
Critères d'acceptation couverts
| CA | Mécanisme |
| CA-284-05 | Séquence POST→GET→SSE stricte via createSealOrchestrator (C7) |
| CA-284-14 | Sélecteurs Zustand granulaires (un par champ) — pas de re-render cascadant |
| CA-284-15 | modeExpert prop passée au SealExpertPanel — panneau absent si false |
| CA-284-17 | Artefacts sensibles purgés via C12 au démontage écran |
Cycle de vie (useEffect)
- Mount : Crée l'orchestrateur (C7) + souscrit NetInfo + charge le détail document
- Active : Orchestrateur gère POST→GET→SSE automatiquement après clic bouton
- Unmount :
orchestratorRef.current.dispose() (arrête SSE/polling) + purgeSealArtifacts (C12) + useSealStore.reset()
Gestion des erreurs
| Situation | Comportement |
| POST /seals/urgent échoue | Toast 5s via orchestrateur, pas de carte |
| GET status échoue post-POST (R-02) | Toast 5s, init RECEIVED, SSE démarre normalement |
| Événement SSE invalide | Délégué à C4/C7, toast via callback |
| Perte réseau | Badge "Hors ligne" via NetInfo + conservation dernier état |
| Auth token absent | Exception propagée, toast d'erreur |
Stubs
- STUB: PD-80 — Le chargement du détail document (
loadDocumentDetail) utilise des données stub. En production, ce sera un appel GET /documents/{documentId} retournant account_type, urgent_quota_remaining, has_active_urgent_seal.
Tests associés
| Test ID | Objet | Niveau |
| TC-NR-05 | Cleanup SSE/polling au démontage | Integration |
| TC-NR-06 | Purge artefacts sensibles au démontage | Integration |
| TC-NOM-01 | minor ne voit pas le bouton (via C8) | Unit |
| TC-NOM-04 | POST→GET→SSE sequence (via C7) | Integration |
| TC-NOM-14 | Badge "Hors ligne" via NetInfo | Unit |
| TC-INV-09 | Aucun artefact sensible résiduel post-unmount | Sec |
Décisions architecturales
architectural_decisions:
- decision: "Toast implémenté en state local plutôt qu'en lib tierce"
rationale: "Évite une dépendance externe pour un composant simple (auto-dismiss 5s). Le contrat §3 impose un comportement précis (non bloquant, auto-dismiss) plus facile à garantir en local."
alternatives_considered: ["react-native-toast-message", "react-native-snackbar"]
trade_offs: "Pas de queue de toasts (le dernier remplace le précédent). Acceptable car les erreurs contrôlées sont rares et espacées."
- decision: "NetInfo listener dans l'écran plutôt que dans un hook partagé"
rationale: "Le badge offline est spécifique au flux seal (INV-284-09). Un hook partagé imposerait un re-render à toute l'app sur changement réseau."
alternatives_considered: ["useNetworkStatus global hook", "NetInfo dans SealProgressCard directement"]
trade_offs: "Duplication si d'autres écrans ont besoin du même signal. Acceptable pour PD-284 (single screen)."
Matrice de couverture
TC-NR-05 → src/screens/vault/__tests__/SealDetailScreen.test.tsx
TC-NR-06 → src/screens/vault/__tests__/SealDetailScreen.test.tsx
TC-NOM-14 → src/screens/vault/__tests__/SealDetailScreen.test.tsx
TC-INV-09 → src/screens/vault/__tests__/SealDetailScreen.test.tsx