--- name: ticket13-f2-web-readonly-client-delivered description: Le client web read-only (pairing cookie, liste projets, ouverture read-only + snapshot work-state) du chantier client/serveur #13 est livré et vert. metadata: type: reference --- Ticket #13 lot F2 livré sur feature/ticket13-server-client-mode. Clôture frontend du premier incrément livrable (B0→B4/F2). **Nouveau** : `adapters/http/webSession.ts` (WebSession : flag localStorage « paired » — PAS le cookie HttpOnly ; `pair(code)`→POST /api/pair ; `notifyUnauthorized()` clear+notify ; singleton `getWebSession()`), `features/web/` (PairingScreen, WebWorkspace read-only, WebApp gate). Tests : webSession.test.ts, WebApp.test.tsx. **Modifié** : httpInvoker (credentials same-origin + callback onUnauthorized sur 401), http/index.ts (câble onUnauthorized→webSession), app/main.tsx (monte si resolveTransport()==="http", desktop inchangé). **Flux** : non-paired→PairingScreen ; pair OK (cookie HttpOnly posé serveur)→WebWorkspace ; 401 d'un /api/invoke→retour pairing ; « se déconnecter »=clear flag local (révocation serveur=B8). Cookie jamais lisible en JS (HttpOnly) : on se fie au 200 + flag de routage. **Read-only** : WebWorkspace n'appelle QUE list_projects, open_project, get_project_work_state (via gateways DI). N'appelle PAS onDomainEvent/health/firstRunState → aucune commande hors-allowlist, pas de WS (live update = F3/B5, snapshot ponctuel pour l'instant). **À confirmer B4** : get_project_work_state dans l'allowlist ; open_project sans effet de bord dangereux en read-only ; 401 (pas 403) sur cookie manquant ; code HTTP mauvais code /api/pair (401/403 → « Code d'appairage invalide »). Build vert, garde no-direct-invoke verte, 79 fichiers/736 tests verts, desktop inchangé. Suite de [[ticket13-f1-http-ws-adapter-delivered]] ; inventaire [[ticket13-f0-frontend-transport-inventory]].