# Solution : [T2] Vérifier la publication sur l'agenda mairie (+ lien direct) ## Problème résolu Un bénévole qui avait poussé un événement vers le formulaire mairie n'avait **aucun retour** : le formulaire est modéré, la publication arrive plus tard, et rien dans l'Écho du Huit ne disait « c'est en ligne ». On ne savait pas si un événement était *à faire* ou *déjà publié*, et pour le retrouver sur le site il fallait le chercher à la main — au risque de le re-soumettre. Objectif tenu : l'extension **détecte** qu'un événement Nextcloud est réellement publié sur l'agenda public de la mairie, affiche un badge **« Publié »** + le **lien direct** vers la fiche, et **retire le bouton « Créer »** (anti-doublon). Ce statut *publié* est **dérivé** de l'agenda à la volée (décision 2026-06-29), jamais un tag écrit dans Nextcloud : c'est la mairie qui fait foi, pas notre intention. Aucun stockage, aucun `credentials` (agenda public). ## Approche choisie **Scraping HTML de l'agenda + match strict `date locale Cugnaux + titre normalisé`, biais précision v1** (approche 1 du brainstorm, variante conservatrice). Il n'y a **pas d'API mairie** : la seule source est le HTML public. Le vrai risque n'était donc pas « aller chercher la page » mais **la fiabilité du match** — et il est gouverné par une **asymétrie de risque** structurante : - *Faux positif* (marquer « publié » à tort) → le bénévole croit que c'est fait, l'événement **ne sera jamais publié**. **Grave.** - *Faux négatif* (rater un match réel) → au pire il revérifie, et le formulaire **modéré** rattrape un doublon. **Bénin.** → On privilégie la **précision** : match strict (date identique + titre normalisé identique), et dans le doute on **n'affirme pas** « publié ». Alternatives écartées : - **Match par slug dérivé** : la slugification WordPress de la mairie n'est pas connue et ajoute des suffixes (`atelier-dessin-2`) → égalité stricte cassée, faux négatifs. On matche sur `date + titre` ; le lien est **lu** dans la carte, jamais reconstruit. - **Score de confiance + confirmation humaine** des cas ambigus (approche 3) : meilleur rappel mais 3ᵉ état d'affichage et un état à mémoriser — or *publié* n'a pas de tag et l'état local est proscrit. Reporté hors v1 ; la porte reste ouverte si les faux négatifs se voient à l'usage. ## Décisions clés - **Seam extraction impure / matching pur.** `DOMParser` est **absent de Bun** : impossible de tester l'extraction DOM en `bun test`. On isole donc `extraireCartes(html)` (impur, DOMParser, non testé unitairement — validé par le test humain) du **matching pur** `publication.js`, testé à fond car c'est le vrai risque produit. Même logique que `formulaire-valeurs.js` (pur, testé) vs `formulaire-mairie.js` (impur, DOM). La boucle de pagination reste testable en **injectant** `fetchImpl`/`extraireCartesImpl`. - **L'agenda est paginé** (`.../agenda/page/N/?f=1&…`). Un seul fetch « période » ne suffit pas : on boucle jusqu'à une page vide, avec un **plafond anti-boucle** (12) au cas où un changement HTML masquerait la page vide. - **Fuseau : jamais de conversion.** Le `