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>
119 lines
6.3 KiB
Markdown
119 lines
6.3 KiB
Markdown
# 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 :**
|
|
- **`uid` seul, pas de `href`/`ETag`.** Le transport actuel (`?export`) renvoie
|
|
un ICS concaténé sans `href`/`ETag` par événement. L'`UID` est la clé stable ;
|
|
T2 résoudra l'adressage CalDAV par UID au moment du PUT. Le contrat n'est pas
|
|
alourdi.
|
|
- **`journeeEntiere` dè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 `await` **avant** `tabs.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.
|