Mercredi 21 mai, début de session, café à côté du clavier. Je demande à l'agent où on en est sur la série DEV.to. Réponse propre, articulée, « Quatre articles en stand-by, prêts à publier. » Je relis. Une demi-seconde de gêne, parce qu'il me semble que j'en ai vu passer deux ou trois sur DEV.to la semaine dernière, mais j'ai dormi entre-temps et je n'en suis plus très sûr. Je tape la question qui change tout, « Tu es sûr que des articles restent à publier ? » L'agent ré-interroge l'API DEV.to en parallèle, ouvre scripts/devto/state.json
, croise les deux. Les quatre articles sont publiés depuis deux ou trois jours.
Ce que je viens de lire, ce n'était pas une hallucination. L'agent avait fait exactement ce qu'on attendait de lui, c'est-à-dire ouvrir articles/backlog.md
, lire la table, restituer ce qu'elle disait. C'est moi qui avais cessé de mettre ce fichier à jour. sync-backlog.ts
n'avait pas tourné après les push de la semaine. Le markdown disait « en stand-by » pendant que la prod disait « published ». La dactylo n'a pas menti. Elle a lu fidèlement un fichier que j'avais écrit moi-même et que je traitais en autorité alors que rien ne le maintenait.
C'est le mode de défaillance le plus commun d'un projet solo qui dure. Chaque jour produit deux flux. D'un côté la matière qui bouge, faite de commits, de déploiements, de lignes en base, de statuts qui transitent. De l'autre les écritures qu'on rédige pour s'y retrouver, à savoir backlog.md
, le MEMORY.md
à la racine, la note de session du dimanche soir, le README du dossier qu'on a refactoré la semaine d'avant. Ces écritures sont produites vite, dans le geste qui termine un sprint, et elles sont maintenues lentement, ou pas du tout, parce que rien dans le pipeline ne se déclenche pour les refermer.
R6 du Counterpart Toolkit le dit pour les colonnes SQL, Live / Snapshot / Cache mandatory. Toute colonne dérivable d'autres données doit déclarer sa catégorie dans le commit qui la crée. Si c'est un Cache, le mécanisme de rafraîchissement (GENERATED ALWAYS AS
, trigger SQL, vue matérialisée avec REFRESH planifié) est livré dans le même commit. Pas de catégorie déclarée, pas de commit.
backlog.md
est exactement le même objet logique. Sa valeur est dérivable de state.json
plus quelques constantes éditoriales. sync-backlog.ts
est son trigger applicatif. Sans appel, le Cache se décale.
R2, Filesystem over summary, codifie le geste depuis le 15 mai. Avant tout rapport de statut, quatre commandes shell, dans cet ordre. Le markdown vient en dernier, et il vient comme un brouillon de pensée, jamais comme une source.
git log --since='7d' --oneline
git status --porcelain
ls docs/adr/ | wc -l
cat scripts/devto/state.json | jq 'to_entries|map(select(.value.published))|length'
Le résultat se lit en trois secondes. Si quelque chose me surprend, qu'il s'agisse d'un commit que j'avais oublié ou d'un ADR de plus que ma mémoire prévoyait, je creuse là, pas dans backlog.md
. Quand je lis le markdown, je le lis avec la question implicite « qui l'a mis à jour, quand », et si la réponse ne sort pas du git log
en quinze secondes, je le traite comme du Cache pourri.
Un sommaire qui ne dit pas quand il a été produit, et par quel mécanisme, ne dit rien. Soit on déclare son rafraîchisseur dans le commit qui le crée, qu'il s'agisse d'un script, d'un trigger ou d'un cron, et alors il vit comme un Cache géré, soit on cesse de le traiter comme une source et il redevient un brouillon. R6 dit la règle pour la base de données. R2 dit la même règle pour les écritures qu'on s'adresse à soi-même. Un agent qui ouvre backlog.md
avant git log
n'est pas un mauvais agent, c'est un agent qui exécute fidèlement un geste humain qu'il aurait fallu interdire en amont. Le matin du 21 mai, ce n'est pas l'agent qui m'a menti, c'est ma propre dactylo qui, la veille au soir, n'avait pas refermé la note.
Counterpart Toolkit v0.7, règle R2 — Filesystem over summary. Extraite de R1 dans la v0.4.1, promue à part entière. La version courante du toolkit vit en CC-BY-4.0 sur github.com/michelfaure/doctrine-counterpart.