Le dog-fooding comme méthode
Le dog-fooding — utiliser son propre produit — n'est pas nouveau dans ce corpus. Le système tspec se traque lui-même : si tspec casse, le score de compliance de tspec baisse, ce qui est détecté par tspec. Le HomeLab se construit lui-même : la documentation est hébergée par l'infrastructure qu'elle documente, le pipeline CI est généré par le DSL qu'il compile.
Mais ici la boucle est plus intime. Ce n'est pas un produit qui utilise ses propres outils. C'est un site qui décrit une transformation M1→M0 et qui est une transformation M1→M0. Le doigt qui montre la lune est la lune.
Ce que le lecteur lit en ce moment
L'article que vous lisez en ce moment est un fichier markdown :
content/blog/non-identite-md-html/03-la-boucle.mdcontent/blog/non-identite-md-html/03-la-boucle.mdSon frontmatter commence par :
---
title: "Partie 3 : La boucle — le site comme preuve de sa propre thèse"
section: blog
order: 3
parent: non-identite-md-html
date: "2026-04-10"
------
title: "Partie 3 : La boucle — le site comme preuve de sa propre thèse"
section: blog
order: 3
parent: non-identite-md-html
date: "2026-04-10"
---Ce frontmatter a été parsé par parseFrontmatter() de build-toc.ts. Le titre a été extrait, la date lue, le parent résolu. Ces données ont alimenté buildMetaTags() et buildJsonLd() de page-renderer.ts — les deux fonctions décrites dans la partie 2.
Le résultat est dans votre navigateur en ce moment. Ouvrez F12, onglet Elements, et cherchez dans le <head> :
- Un
<title>Partie 3 : La boucle — ... — Stéphane Erard</title>— généré parbuildMetaTags() - Un
<meta property="article:published_time" content="2026-04-10">— la date du frontmatter, transformée en meta tag - Un
<script type="application/ld+json">— le JSON-LD généré parbuildJsonLd()
Chaque affirmation de la partie 2 est vérifiable par F12 sur la page même qui l'affirme. Ce n'est pas un exemple pédagogique — c'est la page elle-même.
La réflexivité en acte : cinq boucles concrètes
Voici cinq instances concrètes où cette série est auto-référentielle — où le mécanisme décrit est le mécanisme qui opère :
Boucle 1 — Le TOC
La table des matières dans la sidebar gauche, sous la section « This Site / Meta », contient une entrée pour cette série. Cette entrée a été générée par extractHeadings() de build-toc.ts — la fonction dont le code source est cité dans la partie 2, étape 1.
La série décrit la génération du TOC, et son propre TOC est généré par la fonction qu'elle décrit.
Boucle 2 — Les liens
Les liens internes de cette série — [partie 1](01-la-these.md), [partie 2](02-le-compilateur.md) — ont été validés par validate-md-links.ts, le script décrit dans la partie 2, étape 2. Et ils ont été réécrits par rewriteLinks() de page-renderer.ts, la fonction dont les trois regex sont citées dans la partie 2, étape 3.
La série décrit la réécriture des liens, et ses propres liens sont réécrits par la fonction qu'elle cite.
Boucle 3 — Les diagrammes Mermaid
Le diagramme MOF de la partie 1 et le flowchart du pipeline dans la partie 2 ont été rendus par mermaid-renderer.ts — le module dont la machine à états (BlockState, TRANSITIONS) est citée dans la partie 2, étape 4.
La série décrit le rendu Mermaid, et ses propres diagrammes sont rendus par le module qu'elle décrit.
Boucle 4 — Les slugs
Le slug de la section que vous lisez en ce moment — la-reflexivite-en-acte--cinq-boucles-concretes — a été calculé par la même fonction slugify() + extractHeadings() qui gère tous les headings du site. Le slug hiérarchique avec le séparateur -- est le résultat du mécanisme parentSlugs cité dans la partie 2.
Boucle 5 — Les orphelins
Si le fichier content/blog/non-identite-md-html/03-la-boucle.md était supprimé du dépôt, pruneOrphanHtml() de build-static.ts supprimerait automatiquement son .html correspondant dans public/. C'est l'étape 6 de la partie 2 — et elle s'appliquerait à la page qui la décrit.
La série décrit le nettoyage des orphelins, et elle-même serait nettoyée par la fonction qu'elle décrit.
Le point fixe : M3 et la récursion
Dans la pile MOF, le niveau M3 a une propriété remarquable : il est son propre métamodèle. Le méta-méta-modèle se décrit lui-même — c'est un point fixe (voir le post M3 foundations et la discussion dans Modeling).
Ce blog a une propriété analogue, bien que plus faible. Le pipeline de build est à la fois :
- L'outil : il transforme les fichiers markdown en fichiers HTML
- L'objet : il est décrit dans les fichiers markdown qu'il transforme
Ce n'est pas M3 au sens strict — il n'y a pas d'auto-description formelle. Le pipeline ne parse pas sa propre description pour se configurer. Mais c'est la même structure de réflexivité : un système qui opère sur des artefacts qui parlent de lui. Un compilateur qui compile des textes qui l'expliquent.
La différence entre M3 et ce blog est la différence entre un miroir parfait (M3 est formellement auto-descriptif) et un miroir partiel (le blog décrit le pipeline en prose, pas en code exécutable). Mais le miroir partiel a un avantage : il est lisible par un humain. Le M3 est auto-descriptif pour la machine. Le blog est auto-descriptif pour le lecteur.
Connexion au corpus métacratique
Le pattern se généralise. La série Métacratie — Compilateur croisé décrit un compilateur qui transforme des spécifications juridiques (M1) en cadres de travail typés (M0). La même structure de non-identité est à l'œuvre :
- Le code source du DSL juridique n'est pas le cadre généré — non-identité ontologique
- La transformation ajoute des informations (contraintes de cohérence inter-DSL, diagnostics Roslyn) et en perd (la forme déclarative du YAML d'entrée) — non-identité informationnelle
- Le législateur utilise le DSL pour écrire, le citoyen utilise le cadre pour agir — non-identité opérationnelle
L'essai MDE and Métacratie a construit les ponts entre la pile MOF et la pile métacratique. Cette série en est la micro-démonstration sur le cas le plus simple possible : un site web. Si la non-identité M1→M0 tient pour un pipeline SSG de 1400 lignes de TypeScript, elle tient a fortiori pour un compilateur juridique de 50 000 lignes de C#.
Le site web est le terrain d'essai. Le compilateur juridique est le terrain de déploiement. La structure est la même.
La conception en public : pourquoi publier le mécanisme
La plupart des sites publient le résultat (le HTML) et gardent le mécanisme pour eux (le pipeline de build). Ce site fait l'inverse : le pipeline est documenté, cité, expliqué. Le code source est public. Le lecteur peut vérifier chaque affirmation.
Ce n'est pas de la transparence gratuite. C'est un choix épistémologique qui rejoint la thèse de Contention over Convention : un système est contestable quand ses règles sont explicites et vérifiables. Publier le pipeline rend le site contestable — le lecteur peut dire « cette fonction ne fait pas ce que tu dis qu'elle fait » et le vérifier dans le code.
Le post About Me and This Site racontait la genèse du site — 165 commits en 10 jours. Cette série-ci raconte le fonctionnement du site — comment il se transforme, et pourquoi cette transformation est non-triviale.
Le design in public n'est pas de l'exhibitionnisme technique. C'est la condition pour que le dog-fooding soit vérifiable. Si je dis « ce site est sa propre preuve », le lecteur doit pouvoir examiner la preuve.
Ce qui reste ouvert
Les limites honnêtes de cette analogie :
Le pipeline n'est pas un compilateur formel. Il n'a pas de système de types, pas de vérification formelle, pas de preuve de correction. Les regex de rewriteLinks() peuvent casser sur des cas limites. Le parser frontmatter n'est pas un parser YAML complet. La transformation est « correcte » au sens empirique (elle produit le bon HTML pour les cas testés), pas au sens formel (elle ne peut pas prouver qu'elle produit le bon HTML pour tous les cas).
Le M2 est implicite. Le métamodèle du site — le schéma du frontmatter, les conventions de contenu, les catégories valides — n'est pas formalisé comme un vrai métamodèle déclaratif. Il est imbriqué dans le code de parseFrontmatter(), de build-toc.ts, des validations de validate-md-links.ts. Un vrai M2 serait un fichier de schéma dont le pipeline serait un interpréteur. Ici, le schéma et l'interpréteur sont confondus.
Direction possible : extraire le schéma frontmatter comme un M2 déclaratif (un fichier YAML ou JSON Schema) et faire de parseFrontmatter() un validateur contre ce schéma plutôt qu'un parser ad hoc. Ce serait le passage de la non-identité implicite (le code contient les règles) à la non-identité explicite (les règles sont séparées du code qui les applique) — un pas de plus vers la pile MOF complète.
Mais ce n'est pas le sujet de cette série. Le sujet est la non-identité telle qu'elle existe aujourd'hui, dans le code déployé, sur le site que vous lisez. Le dog-food est servi. Il est mangeable. Et il a le goût de l'Aufhebung.