Documents the single storage key (revising the sibling task's "key = uid" seam, which had no way to transport the uid), the explicit ISO serialisation, and why the café fallback is display-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.3 KiB
Décisions d'architecture
Choix actés et leur pourquoi. But : éviter aux futurs agents de relitiger ce qui est déjà tranché. Format léger (date — décision — pourquoi).
2026-06-29 — Extension Firefox plutôt que CLI
Première piste : un CLI (Bun) qui fetch l'ICS et auto-soumet le formulaire. Abandonnée au profit d'une extension Firefox.
Pourquoi :
- La revue manuelle avant envoi est le vrai besoin (descriptions à enrichir, formulaire modéré) → pré-remplir + envoyer soi-même bat l'auto-submit.
- L'extension réutilise la session du navigateur pour Nextcloud → aucun mot de passe à stocker.
- Pas de gestion des tokens CSRF : le formulaire les porte lui-même.
2026-06-29 — v1 = page autonome de l'extension
Plutôt que d'injecter des boutons dans le calendrier Nextcloud (SPA Vue, DOM fragile, casse à chaque mise à jour), la v1 est une page propre à l'extension. L'injection dans le calendrier reste un bonus ultérieur.
2026-06-29 — Statut dans le tag CATEGORIES (CalDAV)
L'intention (ignoré / soumis) est écrite dans l'événement Nextcloud, pas dans un stockage local.
Pourquoi : partagé entre bénévoles, durable, visible dans le calendrier.
Un state.json local serait privé à une machine et invisible des autres.
Le statut publié n'est pas un tag : il est dérivé de l'agenda public de la mairie (match titre + date), ce qui donne aussi le lien direct.
2026-06-29 — Image ajoutée manuellement
Un content script ne peut pas remplir un <input type=file> (sécurité
navigateur). L'extension facilite le geste (affiche l'image à glisser) mais ne
l'automatise pas. L'affiche vient souvent de la newsletter Brevo, hors
calendrier.
2026-06-30 — Parseur iCalendar (T1) : contrat Event figé + ical.js vendoré
Le flux brut est transformé en Event[] par une fonction pure
parserEvenements(flux, aujourdhui) (extension/evenements.js), via ical.js
v2.2.1 vendoré (approche 3 du brainstorm : pas de build, dépendance en .js).
Contrat Event figé (ne pas rouvrir sans accord) :
{ uid, titre, description, lieu, debut: Date, fin: Date, categories: string[], journeeEntiere: boolean }. Champs texte coercés en "" si absents (l'aval
suppose des chaînes). Le défaut « café » du lieu est appliqué en aval, pas
ici.
Décisions tranchées :
uidseul, pas dehref/ETag. Le transport actuel (?export) renvoie un ICS concaténé sanshref/ETagpar événement. L'UIDest la clé stable ; T2 résoudra l'adressage CalDAV par UID au moment du PUT. Le contrat n'est pas alourdi.journeeEntieredès T1 (drapeau porté, comportement « heure exigée » différé à T3).- Cas limites traités « au plus simple », différés à T3 : pas d'expansion
RRULE (récurrents pris au DTSTART maître + filtre futur → un récurrent au
maître passé disparaît, trou assumé et documenté), exceptions
d'occurrence (
RECURRENCE-ID) ignorées, journées entières au minuit local. - Tri & message « 0 événement futur » hors parseur : le parseur renvoie les événements dans l'ordre du flux, non triés (responsabilité de la liste).
2026-06-29 — Pré-remplissage par content script, pas par URL
Gravity Forms n'accepte ?input_X= que si chaque champ est configuré
« population dynamique » côté mairie (improbable). On remplit donc le DOM par
id (ids confirmés dans RECHERCHE.md).
2026-07-14 — [T1] Liste : couture page → content script
La liste dépose l'événement choisi dans storage.local puis ouvre le
formulaire mairie (extension/transfert.js). Ce que la tâche sœur
(« formater les données pour le formulaire mairie ») doit lire :
Clé unique evenement-en-attente, PAS evenement:<uid> (révise la couture
« clé = uid » annoncée par la tâche sœur, qui avait un trou). Le content script
s'exécute sur le site de la mairie : il n'a aucun canal pour apprendre un
uid, le fragment d'URL ayant été écarté (pas de pollution d'une URL tierce).
Une clé connue d'avance le rend lisible sans transporter quoi que ce soit.
- Pas de résidus : un seul slot, écrasé au clic suivant (le dernier clic gagne — limite assumée, le geste réel est séquentiel et le formulaire est modéré).
- On n'efface pas à la lecture : le formulaire survit à un rechargement.
- Le dépôt est
awaitavanttabs.create: sinon le content script peut lire avant l'écriture.
debut/fin sérialisés en ISO 8601 (le payload n'est pas un Event). On ne
parie pas sur le structured clone : le contrat reste vrai quel que soit le
moteur, le content script réhydrate avec new Date(iso).
⚠️ Si journeeEntiere est vrai, l'instant ISO n'est PAS significatif. Une
DTSTART;VALUE=DATE est une date flottante (RFC 5545) : elle n'a pas de
fuseau. ical.js la matérialise à minuit local, donc l'ISO produit dépend de
la machine (2026-07-10 devient ...T15:00:00Z depuis Tokyo). Le consommateur
doit tester journeeEntiere d'abord et n'en lire que les composantes
locales (getFullYear/getMonth/getDate) — jamais l'heure, jamais une
conversion de fuseau. C'est pour la même raison que la liste formate les
journées entières sans timeZone (presentation.js) : forcer Europe/Paris
faisait glisser la date au jour précédent depuis Tokyo ou Auckland.
TZ=Europe/Paris sur bun test est PORTEUR — ne pas le retirer. Le runner
de Bun force TZ=UTC quand TZ est absente (vérifié : bun -e lit le
fuseau système, le runner non). Sans le pin, les tests ne tournent donc pas dans
le fuseau des bénévoles. Corollaire pour l'écriture des tests : un fixture de
date flottante (journée entière, DTSTART sans Z ni TZID) se construit en
composantes locales (new Date(2026, 6, 10)), jamais en instant absolu
(new Date("2026-07-10T00:00:00+02:00")) — ce dernier n'est juste que sur une
machine à +02:00 et ment partout ailleurs.
Défaut café = affichage seulement ; lieu stocké brut ("" si absent). Les
deux avals du parseur ont chacun le leur : la liste affiche l'adresse pour ne pas
montrer une ligne vide (cas majoritaire), le content script décide s'il pose
aussi les coordonnées (input_18). Écrire l'adresse dans le payload
détruirait le signal « pas de lieu » dont il a besoin.