TL;DR
Le devblog n'avait qu'une lecture : une pile chronologique de 523 articles avec des facettes. Il en a désormais deux — la pile, et un arbre de savoir (/arbre/) qui range le même corpus en sept sections ordonnées. Chaque article porte en pied son fil de projet, ses voisins par tags et ses rétroliens, tous dérivés du corpus : aucun lien n'a été saisi à la main.
En vérifiant le travail, trois gardes se sont révélées hors service. Elles sont réparées.
Le problème : une hiérarchie qui existait sans être visible
Les articles vivent dans devblog/posts/<catégorie>/. Quarante-quatre catégories, dont une vingtaine correspondent à de vrais projets VFG et le reste à des thèmes propres au blog. Cette structure était utilisée pour filtrer, jamais pour naviguer.
Les quatre groupes projet existants — métaprojet, quatuor, compagnons, outils — ne couvraient que la moitié du corpus. Les œuvres, la méthode agentique et la chronique tombaient hors hiérarchie : project: null, donc aucun groupe, donc nulle part.
Trois sections ont été ajoutées pour que chaque catégorie ait une place :
| Section | Ce qu'elle tient | Articles |
|---|---|---|
| Le métaprojet | VFVFGPT, le kit, le workspace | 102 |
| Le quatuor | Hero, Matrice, VFGate, Châtaigne | 18 |
| Les compagnons | IdeaPit, Pulse, Grimoire, VFJah, RainbowJourney | 49 |
| Les outils | Gandalf Idle, Unified POC, SkinEditor, Skyblog | 7 |
| Les créations | Fabrique à Psytrance, Côté jardin, Ondes, ZEvent… | 18 |
| L'atelier | workflows agentiques, configuration IA, chaînes | 150 |
| Le journal | statuts, audits, passations, infrastructure | 179 |
L'ordre va du cœur du métaprojet vers sa périphérie. Il est stable, pas alphabétique, pas trié par volume : c'est une lecture, pas un classement.
PROJECT_GROUPS n'a pas bougé — toujours seize projets canoniques. Les sections sont un second axe, pas un remplacement.
Les articles reliés, sans écrire un seul lien
Sur 523 articles, un seul portait un frontmatter related. Demander à quelqu'un de relier le reste à la main n'était pas un plan, c'était un vœu. Le graphe se dérive donc de ce qui existe déjà :
- le fil — l'article précédent et le suivant dans la même catégorie, par chronologie ;
- les voisins — proximité par tags partagés (Jaccard), avec un bonus quand la section ou le projet coïncident ;
- les rétroliens — « cité par », extraits des liens markdown entre articles.
Soixante-seize articles sont cités par au moins un autre, par 87 liens au total. Le graphe complet — rétroliens compris, soit une lecture disque du corpus — se calcule en environ 330 ms, puis se met en cache sur le mtime du dossier posts/, côté statique comme côté serveur.
La leçon est plus générale que le blog : quand une donnée n'a pas été saisie pendant deux ans, elle ne sera pas saisie rétroactivement. Il faut la dériver de ce que les gens ont réellement écrit.
La lecture
Fil d'Ariane, sommaire automatique des h2/h3 avec surlignage de la section courante, barre de progression.
Le sommaire a d'abord été fait en encart flottant collant à droite. Mauvaise idée : un float combiné à position: sticky garde l'exclusion de texte calculée à sa position d'origine, donc l'encart chevauche les paragraphes dès qu'on défile. Il est revenu en flux normal, repliable, sur deux colonnes en large. Moins spectaculaire, correct partout, y compris sous les huit skins.
Ce que l'audit a réveillé
Trois gardes ne gardaient plus rien.
La CI était éteinte. Le workflow ci.yml est disabled_manually depuis le 8 juillet — comme blog-deploy et auto-collect. Seul code-deploy tourne encore. Pendant deux mois, aucune vérification n'a été exécutée sur une PR. C'est pour ça que les deux casses suivantes ont pu s'installer sans bruit.
Une fuite échappait au garde-fou de publication. stripPublicHtml désamorce les liens vers audits/ avant l'export public. La règle ne couvrait que ../audits/… et laissait passer ../../un-dossier/audits/…. Un article y pointait ; check:publish le signalait ; personne ne lançait check:publish.
Les compteurs avaient divergé. check:counts comparait 524 fichiers markdown à 523 articles indexés. L'écart venait d'un README.md de dossier : un catalogue éditorial, que build-index.mjs écartait déjà faute de date, mais que le compteur additionnait. Le README du dépôt, lui, annonçait 520 articles.
Les trois sont corrigés. La pile complète — tests, liens, compteurs, build, publication — repasse au vert.
Les flux RSS étaient des fantômes
Dernier fil tiré, et le plus instructif. public/feed/**/*.xml figure dans .gitignore et AGENTS.md le range parmi les artefacts générés. Mais les fichiers étaient suivis par git depuis avant l'existence de la règle, et git continue de suivre ce qu'il suit déjà. Résultat : cent quatre fichiers XML que chaque build local salissait, et qui se retrouvaient dans des PR de code sans rapport.
Ils ne servaient à rien en production. Le VPS fabrique ses propres flux : publish-article.mjs appelle generate-rss.mjs après chaque publication. Les fichiers versionnés étaient écrasés au premier article publié.
Ils sont détrackés. Seul public/feed/index.html — la page d'accueil des flux, écrite à la main — reste versionné, ce qui garde le dossier présent dans un checkout frais. Et code-deploy lance désormais generate-rss.mjs sur le VPS après le sync, pour qu'un déploiement neuf ne démarre pas sans flux.
Vérifié avant de toucher quoi que ce soit : le rsync du déploiement n'a pas de --delete, donc le détrackage ne supprime rien sur le VPS. Le serveur démarre sans les fichiers, les tests les régénèrent, le build les reproduit depuis zéro.
Ce qui reste ouvert
Le workflow ci.yml est toujours désactivé. Le réactiver est un réglage du dépôt, pas un commit — et il réveillerait aussi code-deploy vers le VPS. La décision appartient à FG.
En attendant, la pile se lance à la main, dans l'ordre du workflow :
npm test
npm run check:links
npm run check:counts
npm run build:public
npm run check:publish
Une garde éteinte ne prévient jamais. Elle laisse simplement les choses se casser en silence, et on découvre l'ardoise deux mois plus tard en voulant faire autre chose.