### **0. Périmètre, Hypothèses et Registre des Décisions**

#### **0.1. Périmètre V1 et exclusions explicites**

La V1 livre une seule boucle, complète et mesurée : un administrateur capture un parcours sur son propre site sans écrire de code, le publie, ce parcours s'exécute chez ses utilisateurs finaux en résistant aux évolutions de l'interface, et son taux de complétion est mesuré.

**Dans le périmètre :**

| Brique | Contenu V1 |
| --- | --- |
| SDK client | Rendu des étapes, résolution de cible résiliente, branchements, persistance multi-pages, télémétrie |
| Extension d'édition | Inspecteur visuel, capture de sélecteurs, édition des étapes et des branchements, prévisualisation, publication |
| Backend | API de lecture publique et d'administration, publication, ciblage, ingestion et agrégation des analytics |
| Dashboard | Liste et statuts des parcours, tunnel de complétion, gestion des projets et des clés |

**Hors périmètre, et pourquoi :**

- **Module IA « Page-Skill »** (chapitre 5 fonctionnel). Il ajoute un fournisseur de modèle de langage, un budget de jetons, une anonymisation PII, un magasin de vecteurs et un cycle de modération. Aucun de ces éléments n'a de sens tant que l'inspecteur visuel n'a pas prouvé qu'il capture des sélecteurs qui tiennent. Traité au chapitre 10.
- **Synchronisation event-driven par webhooks** (différenciateur 1.2 fonctionnel). Faire dépendre une étape d'un événement serveur (validation KYC, paiement) suppose un client qui le demande et un cas réel à modéliser. Aucun n'est écrit à ce jour. Traité au chapitre 10.
- **Magasin de vecteurs**, mentionné dans les documents de travail du module IA. Le rattachement d'un parcours à une page se fait par motif d'URL, ce qu'un index relationnel résout. Supprimé sans remplacement.
- **Passkeys / WebAuthn** (7.3 fonctionnel). Reportées : voir ADR-05.
- **Connexion « Zero-Click » entre le dashboard et l'extension** (7.3 fonctionnel). Reportée : voir ADR-05.

#### **0.2. Hypothèses de charge**

Aucun composant de ce cahier des charges n'est dimensionné sans chiffre. Les hypothèses suivantes servent de référence unique ; elles sont volontairement basses car elles décrivent la première année, et toute décision qui les dépasse doit citer une mesure réelle.

| Grandeur | Hypothèse première année |
| --- | --- |
| Organisations clientes | 50 |
| Parcours publiés, tous clients confondus | 500 |
| Requêtes de lecture de parcours | 10 000 par jour, pic à 50 par seconde |
| Sessions guidées démarrées | 2 000 par jour |
| Événements de télémétrie | 20 000 par jour, soit environ 600 000 par mois |
| Volume de données cumulé | quelques gigaoctets par an |

Ces volumes tiennent sur une instance applicative unique et une base PostgreSQL managée de premier niveau, avec plusieurs ordres de grandeur de marge. C'est le socle de ADR-06, ADR-07 et ADR-10 : ni cache distribué, ni moteur analytique dédié, ni orchestrateur de conteneurs.

**Date de péremption de ces hypothèses :** elles sont réexaminées lorsque l'une d'elles est dépassée d'un facteur dix, ou au bout de dix-huit mois d'exploitation, selon ce qui arrive en premier.

#### **0.3. Registre des Décisions (ADR)**

---

**ADR-01 — Monorepo unique en pnpm workspaces**

*Problème.* Quatre briques partagent le même contrat JSON et le même langage. Des dépôts séparés imposent de publier et de synchroniser ce contrat à chaque évolution.

*Décision.* Un dépôt Git unique, découpé en paquets pnpm, avec le contrat de données dans un paquet partagé.

*Alternative rejetée.* Trois dépôts et un paquet npm publié pour les types partagés : la mise à jour d'un champ du contrat exigerait une publication puis trois mises à jour de dépendances, pour une équipe qui déploie les quatre briques ensemble.

*Coût accepté.* Le dépôt contient des cibles de compilation hétérogènes ; la discipline des dépendances entre paquets doit être vérifiée mécaniquement (section 1.2).

---

**ADR-02 — Moteur d'affichage bâti sur `@floating-ui/dom`, sans Shepherd.js**

*Problème.* Le cahier fonctionnel impose Shepherd.js comme moteur d'affichage. Trois obstacles apparaissent à l'examen.

*Décision.* Le SDK utilise directement `@floating-ui/dom` (licence MIT) pour le positionnement et embarque sa propre couche de rendu dans `packages/sdk`.

*Justification.* Shepherd.js est en double licence AGPL-3.0 et commerciale ; sa licence vise nommément l'usage « White-Label, Resale, or OEM », c'est-à-dire l'intégration dans un produit redistribué, ce qui est exactement le modèle de Circuy. Son éditeur commercialise par ailleurs une plateforme d'adoption numérique, donc un concurrent direct : dépendre de lui pour le cœur du produit est un risque stratégique, pas seulement un coût de licence. Enfin, Shepherd rend ses éléments dans le document global et son isolation dans un Shadow Root, bien que possible par les options `stepsContainer` et `modalContainer`, exige de réinjecter sa feuille de style dans la racine fantôme.

*Alternative rejetée.* Acquérir la licence commerciale (300 $ à vie) et conserver Shepherd. Rejetée car le code réellement économisé se limite au découpage de l'overlay, à la navigation clavier et au gabarit de la bulle, soit de l'ordre de 350 lignes, une fois écrits le thème Circuy et l'encapsulation. Le positionnement, seule partie véritablement difficile, provient de `@floating-ui/dom` dans les deux cas : Shepherd n'en est qu'un enrobage.

*Bénéfice complémentaire.* L'extension doit prévisualiser exactement ce que le SDK affichera. Un moteur possédé permet à l'extension d'importer le même paquet, rendant la prévisualisation fidèle par construction (section 4.5).

*Coût accepté.* Environ 350 lignes de rendu à écrire, tester et maintenir, dont la navigation clavier et la gestion du focus, qui sont des exigences d'accessibilité non négociables.

---

**ADR-03 — Distribution par manifeste et versions immuables, sans purge de cache propriétaire**

*Problème.* Le cahier fonctionnel décrit un protocole « Zero Redeploy » reposant sur l'appel d'une API de purge du CDN à chaque publication.

*Décision.* Les parcours publiés sont servis sous des URL versionnées et immuables, mises en cache très longtemps. Un manifeste léger, mis en cache brièvement, indique au SDK quelles versions charger. Aucune purge n'est jamais déclenchée.

*Alternative rejetée.* La purge à la publication. Rejetée parce qu'elle lie le produit à l'API d'un fournisseur précis, et surtout parce qu'un appel de purge qui échoue laisse le système dans un état incohérent silencieux, sans qu'aucun mécanisme ne le rattrape.

*Coût accepté.* Une publication devient visible au terme de la durée de cache du manifeste, et non instantanément : 60 secondes sur un site à trafic régulier, jusqu'à 10 minutes pour un visiteur isolé arrivant sur un manifeste périmé. Ce délai est borné et connu, là où l'échec d'un appel de purge produirait une incohérence non bornée et silencieuse.

*Condition de validation.* Cette décision n'est acceptable que si le délai est **explicitement précisé** : documentation d'intégration, conditions de service, et affichage dans le dashboard au moment de la publication (sections 6.4 et 7.2). Le taire transformerait un compromis assumé en défaut perçu.

---

**ADR-04 — Shadow DOM en mode ouvert**

*Problème.* Le cahier fonctionnel impose `mode: 'closed'` en présentant ce mode comme une garantie de sécurité.

*Décision.* Les racines fantômes du SDK et de l'extension sont créées en `mode: 'open'`.

*Justification.* Le mode fermé n'apporte aucune sécurité réelle : le SDK s'exécute dans le contexte JavaScript du site hôte, qui peut redéfinir `Element.prototype.attachShadow` avant son chargement et récupérer toute racine créée ensuite. La protection recherchée, qui est l'isolation des styles, est assurée par la racine fantôme elle-même, indépendamment du mode.

*Alternative rejetée.* Le mode fermé, qui coûte le débogage en production, l'inspection par les tests de bout en bout, et complique les relations d'accessibilité entre racines.

*Coût accepté.* Le site hôte peut inspecter et modifier l'interface du SDK. C'était déjà vrai en mode fermé ; la décision ne fait que cesser de prétendre le contraire.

---

**ADR-05 — Une seule méthode d'authentification en V1**

*Problème.* Le cahier fonctionnel empile trois mécanismes : SSO OAuth 2.0, Passkeys WebAuthn, et détection automatique de session par permission de cookies dans l'extension.

*Décision.* La V1 ne livre que le SSO OAuth 2.0 vers le dashboard, l'extension obtenant son jeton par délégation au dashboard. Les Passkeys et la connexion sans clic sont reportées.

*Alternative rejetée.* Livrer les trois. Chaque mécanisme ajoute son propre chemin d'échec, sa révocation et ses tests, pour une population d'administrateurs qui se connaît et se compte en dizaines.

*Coût accepté.* Une friction de connexion supplémentaire par rapport à la cible. Les Passkeys s'ajoutent sans rupture de modèle une fois le socle de session en place ; le report ne crée pas de dette structurelle.

---

**ADR-06 — Analytics dans PostgreSQL**

*Problème.* Stocker et agréger les événements de progression pour calculer les taux de complétion et les points d'abandon.

*Décision.* Une table d'événements dans la base PostgreSQL existante, agrégée par requête sur index.

*Alternative rejetée.* Un moteur analytique dédié. À 600 000 événements par mois (section 0.2), PostgreSQL est en dessous de son seuil d'échauffement de plusieurs ordres de grandeur. Introduire un second moteur ajouterait une sauvegarde, une supervision et une astreinte pour aucun gain mesurable.

*Coût accepté.* La table d'événements croît linéairement et devra être partitionnée ou purgée le jour où la rétention le justifie. Le seuil de déclenchement est fixé au chapitre 5.

---

**ADR-07 — Dashboard en application monopage servie par le backend**

*Problème.* Le cahier fonctionnel ne tranche pas la technologie du dashboard.

*Décision.* Une application Vite + React + Tailwind, compilée en fichiers statiques et servie par le backend Hono, qui expose déjà l'API.

*Alternative rejetée.* Un framework à rendu serveur. Le dashboard est un outil d'administration derrière authentification : il n'a besoin ni de référencement, ni de rendu serveur. Le framework ajouterait un second processus applicatif à déployer et à superviser pour un bénéfice nul sur cet usage.

*Coût accepté.* Un éventuel site vitrine public devra être traité séparément ; il n'appartient pas à ce périmètre.

---

**ADR-08 — Extension multi-navigateurs par construction, publication Chrome en V1**

*Problème.* Le besoin nomme une extension Chrome, mais WXT compile pour plusieurs navigateurs depuis une base unique.

*Décision.* Le code n'utilise aucune API propre à Chrome sans repli, mais seule la version Chrome est publiée et testée en V1.

*Alternative rejetée.* Publier sur tous les magasins d'extensions dès la V1, ce qui multiplierait les cycles de validation et les matrices de tests sans demande client.

*Coût accepté.* Les navigateurs autres que Chrome ne sont pas testés ; leur compatibilité est une intention, pas une garantie tant qu'aucun test ne l'établit.

---

**ADR-09 — Retrait de XPath de la stratégie de ciblage**

*Problème.* Le cahier fonctionnel liste quatre critères de ciblage : CSS, XPath, ARIA et texte.

*Décision.* La stratégie de repli retient les sélecteurs CSS, les attributs de test et d'accessibilité, et le contenu textuel. XPath est retiré.

*Justification.* Tout ce qu'un XPath exprime utilement pour cet usage s'écrit en CSS, à l'exception de la sélection par texte, que le contenu textuel couvre déjà et plus lisiblement. Un XPath positionnel est par ailleurs le sélecteur le plus fragile face à un changement d'interface, soit l'inverse de l'objectif recherché.

*Coût accepté.* Aucun cas d'usage identifié n'est perdu.

---

**ADR-10 — Hébergement sur instance unique avec base managée**

*Problème.* Les documents de travail évoquent Kubernetes managé et une distribution par stockage objet.

*Décision.* Une instance de calcul unique exécutant le backend en conteneur, et une base PostgreSQL avec sauvegardes quotidiennes, chez **Greenshift**, prestataire déjà en relation avec l'équipe, dont le centre de données se situe aux Pays-Bas (section 7.4).

*Alternative rejetée.* Un orchestrateur de conteneurs. Aux volumes de la section 0.2, il apporte une haute disponibilité dont aucun engagement contractuel n'a encore besoin, contre un coût d'exploitation permanent qu'une petite équipe paierait chaque semaine.

*Coût accepté.* Un redémarrage de l'instance interrompt le service quelques secondes. Comme le SDK dégrade silencieusement en cas d'indisponibilité (section 3.6), l'impact sur les utilisateurs finaux est l'absence temporaire de guidage, jamais une page cassée.

---

**ADR-11 — Pas de réseau de diffusion en périphérie en V1**

*Problème.* Le cahier fonctionnel fait du cache en périphérie une pièce centrale, avec une cible de latence inférieure à 20 millisecondes. L'hébergeur retenu (ADR-10) fournit des serveurs managés, pas de réseau de diffusion.

*Décision.* Les documents publics sont servis directement par l'instance applicative, sur l'hôte dédié `cdn.circuy.net`, avec les en-têtes de cache de la section 7.3. Aucun réseau de diffusion n'est souscrit en V1.

*Justification.* Le travail est déjà fait par le cache du navigateur : les versions publiées sont immuables et mises en cache un an (ADR-03), le manifeste pèse quelques kilooctets et se revalide par ETag en réponse 304 sans corps. Aux 50 requêtes par seconde en pic de la section 0.2, l'instance n'est pas sollicitée de manière significative. La cible des 20 millisecondes ne repose sur aucune mesure et ne correspond à aucun besoin exprimé : le SDK se charge de façon différée et asynchrone, personne n'attend sa réponse.

*Alternative rejetée.* Souscrire un réseau de diffusion tiers dès la V1, ce qui ajouterait un fournisseur, une facture, une configuration de cache à maintenir en cohérence avec l'origine et un point de défaillance supplémentaire, pour une latence dont l'amélioration ne serait perceptible par personne.

*Coût accepté.* Les visiteurs géographiquement éloignés subissent la latence réseau jusqu'aux Pays-Bas au premier chargement, et l'instance devient le point de défaillance unique de la lecture. Le nom d'hôte dédié préserve intégralement l'option : intercaler un cache en périphérie devant `cdn.circuy.net` se fera par une modification DNS, sans toucher à l'intégration déployée chez les clients.

*Seuil de déclenchement.* Ce dispositif sera reconsidéré si le trafic de lecture dépasse 500 requêtes par seconde en pic, ou si un client sous engagement contractuel exploite une audience hors d'Europe.

---

#### **0.4. Écarts assumés avec le cahier fonctionnel**

| Point fonctionnel | Écart | Référence |
| --- | --- | --- |
| 3.2 et 7.1 — Shepherd.js comme moteur | Remplacé par `@floating-ui/dom` et une couche de rendu propre | ADR-02 |
| 3.1 — Bundle strictement inférieur à 15 Ko | Remplacé par un budget mesuré et surveillé en intégration continue | section 3.1 |
| 3.2 et 4.1 — Shadow DOM en mode fermé | Mode ouvert | ADR-04 |
| 1.2 et 3.3 — XPath dans le ciblage | Retiré | ADR-09 |
| 4.1 — Trois modes de positionnement de l'overlay | Seul le panneau latéral est livré en V1 | section 4.2 |
| 6.1 et 7.2 — Purge du cache à la publication | Manifeste et versions immuables, délai de propagation annoncé | ADR-03 |
| 2.2 et 7.2 — Distribution par CDN, latence inférieure à 20 ms | Service direct par l'instance, cache navigateur | ADR-11 |
| 7.4 — Hébergement en France | Hébergement aux Pays-Bas chez Greenshift | ADR-10, section 7.4 |
| 7.3 — Passkeys et connexion sans clic | Reportées après la V1 | ADR-05 |
| 5 — Module IA « Page-Skill » | Hors périmètre V1 | section 0.1, chapitre 10 |
| 1.2 — Architecture event-driven à webhooks | Hors périmètre V1 | section 0.1, chapitre 10 |

Le différenciateur commercial « souveraineté et conformité RGPD » et le différenciateur « résilience du ciblage » sont intégralement conservés : ce sont les seuls que la V1 défend, et ils sont traités respectivement aux chapitres 8 et 3.
