Pourquoi une boucle de rendu
Après une édition, un agent peut lire un diff de source et croire que
tout va bien. Mais le diff ne montre pas l'effet réel : un
include partagé modifié, une fonction commune, une requête
qui vide une table, une édition concurrente sur la même page. Sur une
page legacy sans test, personne ne voit ces effets avant la production.
RenderLoop compare l'état rendu avant et après. Il normalise le HTML, masque le bruit déclaré, puis diffe. L'agent reçoit un résumé compact et un diff unifié. Tout ce qui n'est pas le changement voulu est un signal de régression.
Un agent travaille souvent dans un périmètre étroit : un fichier, un composant, une fonction. RenderLoop lui montre l'effet rendu de son édition, y compris les conséquences indirectes qui sortent de ce périmètre. Grâce à ce retour, il peut identifier une régression sur un sujet qu'il n'avait pas prévu de toucher — un include partagé, un bloc voisin, une valeur affichée ailleurs — au lieu de la découvrir en production.
La boucle à suivre
-
Découvrir avant de deviner.
render-loop -a capabilities -f jsonrenvoie chaque action, ses options requises, la forme de son JSON et la sémantique des codes retour.render-loop -a advise -f jsondonne le mode opératoire et les pièges. -
Capturer avant de toucher au code.
render-loop -a capture -u <url> -t before. Sans snapshot « before », il n'y a rien à differ. - Éditer, puis servir le nouveau fichier. Attention à opcache : un recheck immédiat peut lire l'ancienne version et afficher un diff vide.
-
Re-vérifier.
render-loop -a recheck -u <url> -f json. Le diff doit contenir uniquement l'édition voulue. -
Déclarer le rayon d'impact.
--expect-selector='.price'donne un verdict binaire et sort en erreur si un changement fuit hors du sélecteur. -
Cas particuliers.
Page JS/SPA : pousser le HTML avec
-a store. Fragment alimenté par un AJAX interne : capturer l'endpoint avec--ajax, ou en POST avec--method POST --data.
# 1. contrat machine
$ render-loop -a capabilities -f json
# 2. état avant
$ render-loop -a capture -u "http://localhost:8000/compte" -t before
Snapshot saved: localhost_compte_1792492262.7813
# 3. édition du code
# 4. verdict, avec portée déclarée à l'avance
$ render-loop -a recheck -u "http://localhost:8000/compte" \
--expect-selector='.balance' -f json
{
"summary": { "lines_added": 1, "lines_removed": 1, "hunks": 1, "changes": [ … ] },
"assertion": { "satisfied": true, "inside_scope_changed": true, … },
"diff": "@@ -12,7 +12,7 @@"
}
exit 0 — rien n'a fui hors de .balance
Un contrat machine, pas du texte d'aide
capabilities décrit la CLI elle-même. Un agent n'a pas à
parser l'aide de Symfony ni à deviner les options : il lit un manifeste
stable.
{
"tool": "render-loop",
"formats": ["table", "json"],
"exit_codes": { … },
"actions": [
{
"name": "recheck",
"summary": "…",
"required": ["url"],
"optional": ["exit-code", "expect-selector", …],
"output": "…",
"exit_code": "…"
}
]
}
advise va plus loin : il renvoie le comment, pas
seulement le quoi. Le même contenu est versionné dans
AGENTS.md
pour les agents qui lisent le dépôt avant d'exécuter quoi que ce soit.
En cas de désaccord entre les deux, la CLI fait foi.
{
"audience": "AI coding agents",
"one_liner": "…",
"workflow": ["…"],
"reading_the_output": ["…"],
"pitfalls": ["…"]
}
Lire la sortie sans brûler de tokens
-
Lire
summaryd'abord.lines_added,lines_removed,hunks,tags_touchedetchanges. Ne déplierdiffque si le résumé est non vide. -
Utiliser
changespour comprendre. Une entrée par changement localisé :kind,selector,context,before,after. Pas besoin de parser le diff unifié. -
Décider sur le JSON, pas sur le code retour.
rechecketcomparesortent en 0 même avec un diff. Pour brancher sur le statut, passer--exit-code. -
Se souvenir que
changesest au mieux. Sur du markup sale, une insertion structurelle peut vider le tableau. Le diff unifié est le plancher fiable.
Déclarer le rayon d'impact
Le piège classique d'un agent, c'est de voir un changement inattendu et
de le rationaliser après coup. --expect-selector inverse
l'ordre : l'intention est déclarée avant de voir le diff.
render-loop -a recheck -u "http://localhost/page.php" \
--expect-selector='.price' -f json
L'option est répétable. Le bloc assertion isole la fuite
dans out_of_scope, et le code retour est non nul si
satisfied est faux. C'est un garde-fou par édition, pas un
filet anti-régression figé : pour cela, utiliser le golden master.
satisfied: false avec la raison « matched no element ».
satisfied: true ne prouve pas que l'édition est correcte,
seulement qu'aucun changement visible n'a fui.
Les pièges à éviter
- Masquage par sélecteur uniquement. Jamais de regex sur le contenu : un prix à
0.00doit rester visible. - Config du projet. Pointer
-cvers un fichier versionné dans le projet, pas vers celui devendor/, en lecture seule. - opcache. Vérifier que le serveur sert bien le nouveau fichier avant de conclure qu'il n'y a pas de diff.
- Cookie de session. Passer la valeur par
RENDERLOOP_COOKIE, jamais en argument :psest public. - Hôtes autorisés. La capture est bornée par une liste blanche et ne suit pas les redirections. Garder cette liste étroite.
- Périmètre. Le rendu seulement : ni base de données, ni export, ni PDF, ni API. Un calcul critique mérite un test unitaire.