docs: record the measured CalDAV write path and the T2 decisions

This commit is contained in:
Pierre Martin
2026-08-13 16:50:16 +02:00
parent 69c10abbd7
commit c6b0e7799a
2 changed files with 86 additions and 0 deletions
+59
View File
@@ -218,3 +218,62 @@ gracieuse, jamais d'exception, aucun faux positif). Deux cartes de même clé
**additive** : la liste est rendue immédiatement, l'agenda ne la bloque jamais.
Fetch de l'agenda **sans** `credentials` (public). Aucune modification de
`manifest.json` (`host_permissions` couvre déjà `ville-cugnaux.fr`).
## 2026-08-13 — [T2] Écriture du statut : REPORT + If-Match, garde-fou, annulable
**Résolution paresseuse de la ressource, jamais d'URL devinée.** Au moment
d'écrire, un `REPORT` (`calendar-query` filtrée sur l'`UID`) rend `href` + `ETag`
+ `calendar-data`, puis on `PUT` avec `If-Match`. Mesuré : le nom du fichier
`.ics` **ne correspond pas** à l'UID (cf. RECHERCHE.md §1), l'approche
« `<uid>.ics` » aurait produit une panne partielle. `If-Match` transforme
l'écriture concurrente en **412** (« recharge la liste ») au lieu d'un écrasement
silencieux. 0 réponse → `introuvable`, plusieurs `href` → `ambigu` : on n'écrit
jamais dans une ressource choisie au hasard.
**Garde-fou de non-régression.** L'aller-retour `ical.js` pourrait abîmer une
propriété exotique. Avant tout `PUT`, on re-parse l'ICS **avant** et **après**,
on retire `CATEGORIES` des deux arbres jCal et on compare : différence ailleurs →
**aucun PUT**, message honnête. La comparaison est **structurelle**, donc le
repliement de ligne et l'espacement peuvent changer librement — une **valeur**,
non. On écrit dans l'agenda réel d'une association : un tag non posé se repose,
un événement abîmé se répare à la main.
**Ni `SEQUENCE` ni `DTSTAMP`/`LAST-MODIFIED` ne sont bumpés** : le garde-fou
l'interdit, et poser une catégorie n'est pas une modification de planification.
La synchro des autres clients passe par l'ETag/ctag.
**Le CSRF est un confort, pas une condition** : `/csrftoken` rend 200, mais un
jeton **invalide** passe quand même sur DAV (mesuré). On envoie l'en-tête
`requesttoken` quand on l'a, et un échec de récupération n'est **pas fatal** :
le serveur tranche.
**Le tag `soumis` est posé au clic « Créer » et reste annulable.** Rien ne prouve
qu'un formulaire modéré a été envoyé ; le clic est le meilleur signal disponible,
donc le statut porte toujours « Annuler l'envoi » et le bouton « Créer »
disparaît (c'est le doublon d'annonce qu'on combat). **Un échec d'écriture
n'empêche pas d'ouvrir le formulaire** : le tag est un confort d'équipe, remplir
le formulaire est la mission.
**Jamais d'affichage optimiste** : le statut affiché ne change qu'après
confirmation du serveur, et ce sont les catégories **renvoyées par
l'aller-retour** qui sont conservées. Mentir sur un état que d'autres bénévoles
lisent est pire que ne rien afficher. Seule la **ligne concernée** est re-rendue :
recharger la liste effacerait les badges « publié » déjà posés.
**Les ignorés sont grisés et repoussés en fin de liste, jamais masqués** : un
ignoré invisible est un ignoré qu'on ne peut plus dé-ignorer.
**Orthographe du tag : `mairie:ignoré`, avec l'accent** — ces tags sont lus par
des humains dans Nextcloud et l'ICS est de l'UTF-8. À re-constater au premier
aller-retour réel : si Nextcloud renormalise, basculer en ASCII et le noter.
**Limite assumée v1** : une ressource = un tag, donc un récurrent porte le même
statut pour toutes ses occurrences (cohérent avec le report des récurrences en T3).
**Limite honnête du garde-fou** : il compare `parse(ics)` à
`parse(toString(parse(ics)))`. Il attrape donc ce que la **sérialisation** perd,
pas ce que le **parsing** perdrait des deux côtés à la fois. Vérifié à
l'écriture : `ical.js` round-trippe sans dommage les formes exotiques d'un vrai
calendrier (paramètre quoté à virgule, propriété inconnue, `ATTACH` binaire,
`EXDATE` multiple, accent sur la frontière de repliement) — ces cas sont figés en
test. Le vrai arbitre reste le test humain « rien d'autre n'a bougé ».