Projet

Journal

Ce qui a été fait, quand, et surtout ce que ça a appris. Les décisions sont dans decisions/, l'état courant dans plan-action.md ; cette page garde la trace du chemin, parce qu'un correctif sans sa raison se défait tout seul six mois plus tard.

Les chiffres cités sont mesurés. Ceux qui ne le sont pas le disent.

24 – 25 octobre 2026 — Le socle

Marque blanche, deux instances, moisson de 11 sources, fusion, 23 444 pages construites, version anglaise, recherche Pagefind, observabilité. Première mesure des requêtes de référence : 14/20 à Saclay, 16/20 à Paris 1, attribués à la racinisation française de Pagefind.

POC d'analytics auto-hébergée (P0) : paquets, pile Docker à quatre services, 15 vérifications au vert — mais rien n'était branché sur l'application.

26 octobre — Le branchement, et trois défauts qui se présentaient en succès

Le site construit n'envoyait rien. Trois causes, toutes silencieuses :

  1. Illegal invocation : fetch conservé en champ de classe puis appelé par this.fetchImpl(...) reçoit l'adaptateur comme receveur ; le navigateur refuse. Toutes les vues de page étaient perdues. Un premier test, qui appelait la fonction nue, concluait que tout allait bien.
  2. skipFirst sautait la première vue de chaque session. Sur un site à chargements complets, « la première » est toutes les fois.
  3. Le test de fumée mesurait le filtre antirobot : Umami répond 200 avec le corps littéral {"beep":"boop"} à un User-Agent HeadlessChrome, et la lecture Rybbit traitait un tableau de tranches horaires comme un nombre.

Leçon, écrite dans analytics/revue.md : chaque échec se présentait comme un succès. Un code HTTP 2xx ne prouve rien ; seule l'interrogation des données après coup compte.

26 octobre — Style, catalogue, audit d'interface

src/theme/tokens.css devient la source unique des valeurs visuelles. Trois fonds blancs étaient écrits en dur : une instance dont la charte n'est pas blanche obtenait un site à moitié blanc.

Storybook monté sur la même enveloppe que l'application. Audit UX établi sur le code des 16 pages et sur les pages réellement servies (audit-UXUX.md). Constat central : la vitrine se parcourait, elle ne se fouillait pas — 60 projets atteignables sur 3 647, aucun filtre nulle part, et /recherche?q=… jamais relu au chargement.

26 – 27 octobre — L'interface reprise

Correctifs d'accès (lien d'évitement, aria-current, carte cliquable, retour en haut, grille des chiffres sur mobile, catégories vides masquées), recherche portée dans l'en-tête et dans l'accueil, résultats paginés, état « zéro résultat » utile, filtres de liste, projets paginés par financeur.

La recherche passe de 14/20 à 18/20, sur les deux instances. Ce n'était pas la racinisation :

  • l'index répond avant d'être chargé — biologie renvoie 6 fiches à froid, 88 après un preload sur biolog, et une requête à plusieurs mots exigeant tous les termes, un seul terme sous-chargé la vidait ;
  • les accents ne sont pas repliés — écologie et ecologie sont deux mots de l'index, 12 fiches contre 31.

Détail et mesures : decisions/009-recherche-pagefind.md.

Bannière de consentement : sans elle, le mode required laissait le site muet.

27 octobre — Licence, mesure, reproductibilité, données

  • Licence MIT (decisions/014-licence.md). Le dépôt se disait open source sans l'être : sans licence, aucun établissement tiers n'avait le droit de le reprendre.
  • Quatre événements câblés, un seul point d'appel par geste, deux destinations : les compteurs Prometheus restent la mesure de référence, le paquet ajoute les dimensions sous consentement. record_viewed n'est pas émis pour les personnes — enregistrer quels chercheurs sont consultés dépasse l'ADR 006.
  • Reproductibilité : pnpm data:freeze fige un manifeste et 15 fiches par entité dans tests/reference/. Jusque-là, « 248 structures » était invérifiable après coup.
  • Moissonneur ANR écarté après mesure : l'open data ne couvre que 14 % des 3 057 projets ANR. Mais l'URL de la fiche se déduit du code du projet — 99 % des projets ont désormais un lien, sans source à maintenir.
  • pnpm data:export produit les quatre tableurs que a-faire-valider.md appelait « la liste jointe » depuis le début sans qu'ils existent.

27 octobre — Tableau de bord de couverture

packages/analytics-ui et la route /admin, construite seulement si NEXT_PUBLIC_ADMIN=true.

Le validateur de palette a chiffré un réflexe : le vert et le rouge des statuts sont à ΔE 4,1 en deutéranopie. Chaque statut porte donc une icône et un mot. Et il a fallu deux jeux de couleurs de statut — aplats à 3:1, texte à 4,5:1 — la première version échouant à l'audit axe sur huit badges.

Piège de rendu : les sous-composants Mantine (Table.Thead…) ne traversent pas la frontière serveur et valent un « Element type is invalid » qui ne dit rien.

27 – 28 octobre — Matomo, et un faux positif de mesure

Matomo exercé depuis l'application : 5 vues côté Matomo pour 5 côté Umami, donc aucun double comptage, événements retrouvés par Live.getLastVisitsDetails.

Trouvé en route, et plus grave : classifyOutboundUrl comparait url.hostname, qui ne porte pas le port, à window.location.host, qui le porte. Hors du port 443, tout lien interne était compté comme lien sortant. Invisible en production, mais toute mesure de développement, de préproduction et de test était fausse.

28 octobre — Déploiement Vercel

Les JSON publiés sont versionnés — 18 Mo, pas 79 : le markdown n'est lu par personne. Le build n'est pas poussé : 6,7 Go, 287 905 fichiers, que Vercel refait en moins de quatre minutes.

Deux pièges, trouvés en servant pour de vrai un build de déploiement :

  1. next build fige public/, et Pagefind s'exécute après — ses 23 787 fichiers n'auraient jamais été déployés ;
  2. servi depuis /_next/static/, Pagefind préfixe toutes ses URL de résultat par cet emplacement : les titres restaient justes, tous les liens menaient à un 404. Mon premier essai l'avait manqué parce qu'il n'affichait que les titres.

29 octobre — Premier déploiement réel, et un troisième piège

Le build Vercel échouait sur « Pagefind was not able to build an index » : sur la plateforme, Next tourne avec un adaptateur et range les pages pré-rendues sous .next/server/route-cache/…, une empreinte par route source, au lieu de .next/server/app/. Le build local ne le montrait pas, faute d'adaptateur. Correctif dans scripts/vercel-build.mjs ; reproduit puis vérifié en local avec un adaptateur vide : 0 page trouvée avant, 4 835 après (Paris 1).

Le build passait ensuite, mais /recherche affichait « index non construit » : l'index était construit, puis jamais servi (404). Avec un adaptateur, Next remet la liste des fichiers à déployer à la fin de next build ; tout ce qui est écrit après est perdu, et public/ n'entre même pas dans cette liste. Une seconde passe de build, essayée, ne changeait rien. scripts/pagefind-adapter.mjs s'intercale désormais devant l'adaptateur de Vercel : il indexe le HTML dans onBuildComplete et déclare l'index. Vérifié avec un adaptateur de test : 0 fichier d'index reçu avant, 4 829 après.

Leçon : un build local n'est pas un build de déploiement tant qu'il ne tourne pas dans les mêmes conditions — ici, une variable d'environnement posée par la plateforme. Et « la compilation passe » ne dit rien de ce qui est servi : seule une requête sur le site déployé le dit.

La documentation passe sur Zudoku (docs/, projet pnpm autonome) : sections numérotées, navigation générée, schémas Mermaid de l'architecture et des workflows, contrôle MDX et liens avant push.

Lefthook : formatage au commit ; avant le push, données déployables, conformité à la référence, make check. Rien ne reconstruit le site.

29 octobre — Le branchement à CRISalid Paris-Saclay

La DiBISO ouvre un accès en lecture, temporaire, à son instance CRISalid de développement. Toute la pile tourne, le graphe porte 18,5 millions de nœuds, et le chemin « unité → chercheurs → publications » répond en moins d'une seconde. Mais 51 unités sur 235 seulement ont des chercheurs rattachés, et l'enrichissement est arrêté depuis le 7 septembre.

Leçon : le chemin critique n'est plus technique, il est documentaire. CRISalid entrera champ par champ, les sources ouvertes en repli. Suite et protocole du prochain test : Projection.

3 novembre — Lucarne, et une adresse par établissement

Le projet devient Lucarne, « une fenêtre ouverte sur les laboratoires ». Prisme a été écarté après vérification : plusieurs laboratoires portent ce nom, dont PRISM Sorbonne, présent dans l'instance Paris 1. Nouveau logo, palette propre au produit, références à LPI retirées de la documentation et du code, et documentation réorganisée en sept onglets.

Le plan des instances fixe une adresse par établissement, <id>.lucarne.vicode.fr, et recense onze candidats parmi les membres du consortium CRISalid. Une instance servie hors du domaine de son établissement passe en mode démonstration : vérifié sur un build complet de Paris 1, robots.txt fermé, noindex et bandeau sur chaque page, dans les deux langues.

Mise en ligne le même jour : la documentation sur lucarne.vicode.fr, les instances sur saclay.lucarne.vicode.fr et paris1.lucarne.vicode.fr. Relevé à chaud : les deux instances tournent encore sans mode démonstration ni adresse publique (sitemap sur localhost ou sur l'ancienne adresse vercel.app) ; c'est à poser dans Vercel. Logo définitif : la loupe allumée, nom en Merriweather Black.

Leçon : un build Next réécrit tsconfig.json pour y ajouter son dossier de sortie. Un build de test dans un dossier à part laisse donc une trace à effacer.

État mesuré au 28 octobre 2026

ContrôleRésultat
make check75 tests, au vert
Requêtes de référence18/20 à Saclay, 18/20 à Paris 1
Accessibilité (axe, WCAG 2.1 AA)0 violation, français et anglais, /admin compris
Playwright7/7
Fumée analytics (paquets)15/15
Fumée analytics (application)12/12, Matomo compris
Secrets dans le bundleaucun, 11 valeurs réelles cherchées
Build Vercel< 4 min, 23 696 pages, liens de recherche valides