# JWE — ADR-001 & ADR-002 (format audit OKI) Deux décisions d'architecture rédigées selon le format du prompt d'audit post-exécution : critères bloquants, preuves exigées, rapport tabulaire, interdits. Elles **ne modifient pas** le build v1 en cours ; elles cadrent la phase 2 et une optimisation perf. --- # ADR-001 — Mode Défi : seed-link en v1, PlaySocketJS en phase 2 conditionnée - **Statut** : Accepté (v1) / À étudier (v2, gate explicite) - **Date** : 2026-07-17 - **Décideurs** : Architecte OKI - **Références** : PlaySocketJS (MIT, rooms, CRDT, server-authoritative, battle-tested >1,3 M duels sur 1 instance Node) ; Geoguess/Geoguess (modèle de room sans compte, max 5 joueurs) ; prompt v2 §A5 (pas de comptes, pas de leaderboard serveur). ## Contexte Le prompt v2 interdit comptes et leaderboard serveur. L'usage réel visé (OKI, AKILPA, salles de classe, événements) crée pourtant un besoin : **jouer les mêmes 5 lieux que d'autres et comparer les scores**. Deux horizons : maintenant (zéro serveur) et plus tard (temps réel, si le projet prouve sa traction). ## Décision **v1 — Défi asynchrone par lien à seed partagée.** - L'URL de défi contient une seed opaque (ex. `/defi/`) ; le serveur (ou la fonction SvelteKit) dérive de la seed les mêmes 5 lieux pour tous les joueurs (PRNG seedé côté serveur, jamais côté client). - Aucune coordonnée dans l'URL ni dans le payload initial (anti-triche §6 du prompt v2 maintenu). - Fin de partie : l'image de partage canvas (déjà prévue prompt v2 §4.4) affiche le score ; la comparaison se fait humainement (screenshot dans le groupe WhatsApp/Signal). Zéro serveur d'état, zéro compte, cohérent doctrine. **v2 — Duel temps réel via PlaySocketJS, UNIQUEMENT si le gate §"Conditions" est franchi.** - Modèle : rooms sans compte à la Geoguess (nom de room partagé, 2–5 joueurs), état synchronisé par PlaySocketJS en mode **server-authoritative** (le serveur valide les guesses et calcule les scores — jamais le client). - Hébergement : 1 instance Node sur le VPS OKI existant, dans l'enveloppe 40–50 €/mois. Aucune dépendance Google/Firebase (contraire à Geoguess — voir annexe). ## Options considérées | Option | Souveraineté | Coût serveur | Effort | Anti-triche | Verdict | |---|---|---|---|---|---| | Seed-link asynchrone (v1) | Totale | Nul | S | Serveur dérive les lieux, scores non comparés en ligne | **Retenu v1** | | PlaySocketJS rooms (v2) | Bonne (MIT, self-host) | 1 instance Node | M | Server-authoritative natif | **Retenu v2 si gate** | | Socket.io maison | Bonne | 1 instance Node | M–L | À réimplémenter | Rejeté (réinvention) | | Supabase/Firebase Realtime | Mauvaise (cloud propriétaire) | Abonnement | M | Faible côté client | Rejeté (doctrine) | ## Conditions de déclenchement de la v2 (gate — bloquant) La v2 n'est étudiée que si TOUTES les conditions suivantes sont remplies et documentées dans le rapport de gate : 1. Traction mesurée : ≥ 30 parties/semaine pendant 4 semaines consécutives (mesure locale anonymisée, pas d'analytics tiers). 2. Demande explicite : ≥ 5 retours d'utilisateurs ou partenaires (OKI, AKILPA, enseignants) demandant le défi en direct. 3. Capacité d'hébergement confirmée sur le VPS OKI sans dépasser l'enveloppe 40–50 €/mois. ## Vérifications (obligatoires, preuves à l'appui) - **V1 (bloquant, v1)** — Déterminisme : la même URL de défi produit les mêmes 5 lieux, dans le même ordre, sur deux machines différentes. Preuve : double exécution filmée ou logs serveur. - **V2 (bloquant, v1)** — Anti-fuite : l'URL et les payloads initiaux ne contiennent ni coordonnées, ni commune, ni `wikidata_id` décodable. Preuve : inspection réseau + revue de l'URL. - **V3 (bloquant, v1)** — L'image de partage n'affiche que score/pseudo libre, jamais les réponses des autres joueurs. - **V4 (bloquant, v2)** — POC PlaySocketJS : room à 3 clients, déconnexion/reconnexion d'un joueur sans perte d'état (CRDT), score validé côté serveur (tentative de triche client rejetée — preuve par requête forgée). - **V5 (v2)** — Licence : `npm view playsocketjs license` = MIT, consigné dans le rapport. - **V6 (v2)** — Charge : 10 rooms simultanées × 5 joueurs sur le VPS sans saturation (preuve : log ressources). ## Rapport (format obligatoire) ``` | ID | Vérification | Méthode / Preuve | PASS/FAIL | Correctif appliqué | ``` ## Interdits - Aucun compte utilisateur, aucun leaderboard global, aucun stockage de pseudos au-delà de la room. - Aucune dépendance Google/Firebase/Supabase pour le temps réel. - Aucune copie de code Geoguess/Geoguess (Vue.js) : référence de design uniquement. - La v2 ne doit pas régresser le hors-scope du prompt v2 pour le solo. --- # ADR-002 — Carte SVG/topojson légère pour les écrans hors-jeu (perf 4G) - **Statut** : Accepté - **Date** : 2026-07-17 - **Références** : composant carte SVG/topojson d'OpenGuessr Education (inspiration uniquement — licence MIT + Commons Clause = non-commercial, **aucune copie de code**) ; geo.api.gouv.fr (contours de communes GeoJSON, gratuit, sans clé — testé OK) ; prompt v2 §4.1 (lazy-load MapLibre) et §4.5 (perf). ## Contexte MapLibre GL JS pèse ~800 kB de JS avant la première tile. L'accueil, le récapitulatif final et les fiches « Aprann » n'ont pas besoin d'une carte interactive complète. Sur 4G caribéen, charger MapLibre sur ces écrans détruit le budget < 3 s du prompt v2. ## Décision - Les écrans **hors-jeu** (accueil 4 régions, mini-carte du récapitulatif, vignette de fiche lieu) utilisent une **carte SVG statique** générée au build depuis un topojson des communes des 4 régions, projection précalculée, aucune tile, aucun JS carte. - MapLibre reste réservé aux écrans **de jeu** (devinette, résultat, explorer interactif) et reste lazy-loadé. - Pipeline données : contours communes via `geo.api.gouv.fr/communes?codeRegion=01|02|03|04&fields=nom,code,contour&format=json&geometry=contour` → simplification (mapshaper, seuil à calibrer) → topojson versionné dans le repo → composant Svelte SVG from scratch (MIT). ## Vérifications - **V1 (bloquant)** — Budget poids : topojson simplifié des 4 régions ≤ 150 kB gzippé ; preuve : taille du fichier + `gzip -9` consignés. Si dépassement : simplifier davantage ou découper par région avec import dynamique. - **V2 (bloquant)** — LCP accueil < 2,5 s en throttling 4G (Lighthouse mobile, 3 runs, médiane consignée). - **V3** — Rendu sans JS : la carte SVG s'affiche en SSG avec JS désactivé ; chaque région est un `` ou `