diff --git a/docs/handoffs/2026-07-25-1545-locale-race-et-csp-sonner.md b/docs/handoffs/2026-07-25-1545-locale-race-et-csp-sonner.md new file mode 100644 index 0000000..4129a9d --- /dev/null +++ b/docs/handoffs/2026-07-25-1545-locale-race-et-csp-sonner.md @@ -0,0 +1,133 @@ +# Handoff CC Ankora — 25 juillet 2026, 15h45 + +**Agent** : @cc-ankora (Opus 5) +**Mandat** : « tu avances tout seule avec précaution et à mon retour tout doit être fini » — @thierry +**Compte** : `thierryvm` vérifié sur `gh` **et** `git config` avant chaque commit + +--- + +## 1. Ce qui a été livré + +| PR | Sujet | État | +| ---- | ----------------------------------------------------------------------------- | ---------- | +| #254 | agents `prod-bug-investigator` + `test-quality-auditor` | ✅ mergée | +| #253 | handoff session précédente | ✅ mergée | +| #255 | diagnostic de la course sur le cookie de langue | ✅ mergée | +| #256 | correction de la cause des échecs de login locaux (`e2e-auth`) | ✅ mergée | +| #257 | CSP : style des toasts restauré (violations console conservées, délibérément) | ⏳ ouverte | + +--- + +## 2. Bug 1 — sélecteur de langue qui repasse en anglais + +**Cause racine établie**, **correctif non livré**, **décision d'architecture en attente de @thierry**. + +`next-intl` réécrit `NEXT_LOCALE` dès que le locale résolu par l'URL diffère du cookie, et +le préfixe d'URL gagne toujours. Toute requête `/en…` alors que le cookie vaut `fr-BE` +rebascule le cookie en `en` pour un an. C'est une **course** : la requête `/en…` encore en +vol se termine après le `Set-Cookie` de la Server Action. Asymétrique par construction — +le sens FR→EN est immunisé (URLs non préfixées), d'où « ça revient toujours à l'anglais ». + +### Le résultat négatif, qui est le vrai livrable + +Trois correctifs middleware construits et mesurés sur build de production, **trois échecs +structurels** : + +| Signal | Mesure | Verdict | +| ---------------------------------------------- | ------------------------------------------------------ | ------------------------------------- | +| `rsc` / `next-router-prefetch` / `sec-purpose` | envoyés par le navigateur, absents côté middleware | Next les retire avant | +| `accept: text/x-component` | un vrai prefetch envoie `accept=(none)` | ne matche que les `fetch()` fabriqués | +| `?_rsc=` | `curl "/en?_rsc=probe"` → `nextUrl.search = "(empty)"` | Next retire aussi le paramètre | + +**Next canonise les requêtes RSC avant le middleware.** Un prefetch et une navigation +document sont indiscernables depuis `proxy.ts`. Aucun correctif à cette couche ne peut +fonctionner — ni le nôtre, ni celui de `next-intl`. Ne pas retenter cette voie. + +**Piège d'ordonnancement à conserver** : toute manipulation brute de l'en-tête `set-cookie` +dans `proxy.ts` doit être la **dernière** opération, après `updateSession` — `ResponseCookies` +re-sérialise le jar entier à chaque `.set()` et restaure ce qu'on vient de retirer. + +### Décision attendue de @thierry + +- **Option A (recommandée)** — `localeCookie: false` : `syncCookie` devient inerte, la + course disparaît par construction. Coût : une arrivée sur `/` nu ne mémorise plus une + langue qui contredit `Accept-Language`. PR dédiée + `plan-reviewer`. +- **Option B** — navigation dure sur changement de langue : atténue sans supprimer la + cause, non prouvable par un test. Jamais seule. + +Diagnostic complet : `docs/audits/2026-07-25-locale-cookie-race-diagnostic.md`. +Le test de non-régression est dans la suite en `test.fixme` — il décrit le défaut tant +qu'il n'est pas clos. + +--- + +## 3. Bug 2 — violations CSP `inline-style` (PR #257) + +Cause : `sonner` insère un `