Belweave/belay

Atteignez n'importe quel objectif mesurable par une commande (corriger tous les tests en échec, terminer un audit, rendre quelque chose plus rapide ou plus petit, porter un outil, peaufiner une UI ou un texte) en grimpant par petites étapes vérifiées. Chaque modification est évaluée par une vérification verrouillée, validée si elle gagne et annulée si elle perd, sur un ou plusieurs dépôts. À utiliser lorsque l'utilisateur dit 'belay', 'hill climb', 'climb this' ou 'keep going until it passes', souhaite une longue exécution sans surveillance vers un objectif mesurable, ou demande de reprendre ou de faire un rapport sur une ascension.

Qu'est-ce que belay ?

Belay est un outil d'automatisation d'ascension (hill climbing) pour les agents IA qui vous permet d'atteindre des objectifs mesurables en effectuant de nombreuses petites modifications vérifiées. Il est particulièrement utile pour les tâches de développement répétitives et mesurables telles que les audits de sécurité, l'optimisation des performances, les corrections de bugs ou les portages d'outils. Le cœur du système est `scripts/belay.py`, qui exécute une vérification sur chaque modification, valide le changement si le score s'améliore, ou le défait dans le cas contraire. Il prend en charge plusieurs dépôts, des vérifications parallèles, des garde-fous (pour protéger les fonctionnalités non directement évaluées), des données de validation (holdout) pour éviter le sur-apprentissage, et un mécanisme de verrouillage pour empêcher la triche sur la vérification. L'agent communique avec l'utilisateur via une 'carte d'ascension', et l'état est conservé sur le disque sous `.belay/<nom>/`, avec des commandes pour initialiser, grimper, suspendre/reprendre, et générer des rapports détaillés. Les plateformes prises en charge incluent tout environnement où un script Python peut être exécuté, avec accès à git. Le processus se déroule en six étapes : reprise, construction de la vérification, création de la carte et initialisation, ascension, gestion des blocages, et arrêt avec rapport. L'outil interdit strictement de tricher sur la vérification et oblige à réévaluer périodiquement la pertinence du score par rapport à l'objectif réel.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Belweave/belay/tree/HEAD/skills/belay

Installed? Explore more Productivité et collaboration skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait belay ?

Vous atteignez un objectif en plusieurs petites étapes. Une vérification évalue chaque étape sans intervention humaine. scripts/belay.py tient le score : vous éditez, il exécute la vérification, valide une victoire ou annule une perte, et écrit le journal. Vous n'écrivez jamais un score vous-même, et vous ne décidez jamais qu'une étape a "globalement" réussi.

Tout objectif devient la même chose : une commande qui affiche un nombre. Plus rapide est un temps ou un nombre. Un audit est le nombre de tests en échec, un par constat. Un portage est le nombre de sorties qui correspondent. Une UI est le score d'un juge.

Comment travailler :

  • L'utilisateur démarre une ascension et s'en va. Ne vous arrêtez pas pour donner des mises à jour. Il lit trois choses : la carte, une question à laquelle lui seul peut répondre, et le rapport. Demandez avec l'outil de question du harnais s'il en a un.
  • L'état réside sur le disque, pas dans le chat. La carte, le journal, vos notes et la dernière sortie de vérification se trouvent sous .belay/<nom>/. Après une compaction ou un crash, status redonne tout en un seul appel.
  • N'attendez jamais avec une boucle de sommeil. Exécutez les longues commandes en arrière-plan et planifiez l'étape suivante pendant qu'elles s'exécutent.
  • BELAY ci-dessous est le chemin absolu de scripts/belay.py à côté de ce fichier. Écrivez le chemin complet dans chaque commande. Exécutez-la depuis l'intérieur du dépôt cible. Les chemins et commandes que vous passez sont relatifs à la racine du dépôt.
  • L'ascension a sa propre branche, climb/<nom>. Une étape annulée réinitialise l'arbre et supprime les fichiers non suivis que git n'ignore pas. Si quelqu'un d'autre travaille dans ce checkout, grimpez dans une worktree séparée. Passez --here lorsque vous êtes déjà sur une branche créée pour ce travail.

1. Reprendre avant de commencer

Exécutez python3 "$BELAY" list. Si une ascension pour cet objectif existe, basculez sur sa branche, exécutez status <nom> et continuez à l'étape 4. "Continuer" après un crash ou une nouvelle session signifie cela. Ne recommencez pas.

2. Construire la vérification

L'ascension n'est aussi bonne que sa vérification. Une vérification qui mesure la mauvaise chose passe toute la durée à améliorer la mauvaise chose. Faites d'abord fonctionner une vérification approximative et affinez-la plus tard avec relock.

Demandez : qu'est-ce que j'exécuterais pour voir que l'objectif est atteint ? Faites en sorte que cette commande affiche un nombre. Utilisez la forme la plus forte qui convient : une correspondance exacte ou un nombre de tests, puis un comptage du travail (requêtes, allers-retours, octets), puis une horloge, et un juge modèle uniquement lorsque seul le goût peut décider.

ObjectifScore
Bug ou fonctionnalitéTests en échec écrits à partir du cahier des charges, pas de votre correctif
Audit, stabiliser, "faire fonctionner X"Tests en échec, un par constat
Vitesse ou tailleUn comptage du travail derrière le temps, ou le temps
Portage ou cloneSorties qui correspondent à l'outil réel
Plusieurs objectifs à la foisProgrès vers la pire barrière
Aspect, sensation, écriturejudge.py avec une grille d'évaluation et de bons exemples

Des recettes pour chacun se trouvent dans references/checks.md. Lisez la partie qui correspond à votre objectif avant d'écrire la vérification.

Utilisez ce que le dépôt a. --score '<regex>' lit le score depuis la sortie de n'importe quel exécuteur de test, comme --score '(\d+) failed'. Il prend la dernière ligne correspondante et additionne les nombres qu'elle capture. Une exécution réussie sans correspondance donne un score de 0. Sans --score, la dernière ligne de la sortie standard de la vérification doit être le nombre nu. Répétez --check pour exécuter plusieurs commandes à la fois ; leurs scores s'additionnent. Ajoutez --repo <chemin> pour chaque autre checkout que l'ascension modifie. Une étape est alors conservée ou annulée dans tous ensemble.

Prouvez la vérification avant de lui faire confiance :

  • Elle peut échouer. Cassez le code intentionnellement et voyez le score se dégrader. Restaurez avec git checkout -- <fichier>.
  • Elle peut voir une victoire. Une petite amélioration réelle doit faire évoluer le score.
  • Elle est stable. Exécutez-la 3 fois. Si le score dérive sans changement de code, remplacez l'horloge par un comptage, ou utilisez --runs 3, un --min-gain au-dessus de la dérive (un pourcentage comme 3% lorsque le score va beaucoup diminuer), et --paired.
  • Elle ne peut pas être contournée. Variez l'entrée à chaque exécution et comparez avec une référence. Essayez une triche à la main, comme coder en dur la réponse, et confirmez que la vérification ou une garde l'attrape.
  • Elle est rapide. Vous payez le temps de vérification à chaque étape. Obtenez une étape en moins de 2 minutes : évaluez un échantillon ou un comptage par étape, et conservez la suite complète ou l'exécution chronométrée comme garde ou comme holdout.
  • La barrière est atteignable. Déterminez le meilleur résultat que la conception actuelle peut offrir. Si la barrière est au-delà, dites-le sur la carte et proposez une barrière que vous pouvez atteindre.

Protégez tout ce que le score ne mesure pas :

  • Gardes (--guard, passez en sortant 0). Listez ce que le code que vous allez modifier touche : appelants, autres plateformes, données sauvegardées, API publiques. Chacun a besoin d'une garde : les tests qui passent aujourd'hui, la vérification de type, le lint, la construction. Lorsque le score est un nombre d'échecs, conservez ces tests réussis dans leurs propres fichiers en tant que garde, afin qu'un crash ne puisse pas passer pour "moins d'échecs".
  • Verrouillage (--lock) de la vérification, des tests, des fixtures et du holdout. try refuse de noter tant qu'un fichier verrouillé diffère. Les nouveaux fichiers sous un chemin --lock deviennent le premier commit sur la branche d'ascension. Toute autre modification non validée arrête init.
  • Holdout (--holdout), lorsque la vérification utilise des échantillons, un benchmark ou un juge : des cas sur lesquels l'ascension ne s'entraîne jamais, conservés sous .belay/<nom>/holdout/. Ne les ouvrez pas à nouveau. Si le score augmente mais pas le holdout, l'ascension est en sur-apprentissage. Arrêtez et dites-le.

Audits et objectifs ouverts. Listez ce que l'objectif couvre comme une liste de contrôle dans .belay/<nom>/notes.md. Chassez les bugs secteur par secteur. Écrivez un test en échec par constat réel, à partir du cahier des charges ou du rapport de bug. Lorsqu'une étape ultérieure trouve un nouveau bug, ajoutez son test et exécutez relock --why "trouvé <bug>". Le score augmente pour une raison honnête et le journal montre pourquoi. Un constat qui nécessite la décision de l'utilisateur va dans les notes et le rapport, pas dans le score.

3. La carte, puis init

Envoyez un message avec la carte et rien d'autre pour décider :

Carte d'ascension : build-speed

Objectif: construction à froid 2x plus rapide, même sortie

ScoreSecondes de construction médianes, plus bas c'est mieux : 47,0 maintenant, barrière 23,5
Vérification./scripts/bench-build.sh, 3 exécutions, apparié, gain min 3%
Gardesnpm test, npm run typecheck
Holdout3 des 12 applis échantillons
Verrouilléscripts/bench-build.sh, test/, samples/
Limiteaucune (environ 2 min par étape)

Nécessite votre réponse : aucune

Répondez go, ou modifiez une ligne.

Remplissez chaque ligne avec votre meilleur choix. Si l'utilisateur a donné un objectif sans barrière, proposez-en une qui soit ambitieuse et vérifiable, comme "2x plus rapide" ou "chaque échantillon correspond". Attendez "go", qui approuve la carte, la branche et ses commits. Ignorez l'attente et placez la carte en haut du rapport lorsque l'utilisateur a déjà dit de s'exécuter sans demander, ou lorsque l'objectif est petit et la barrière hors de doute. Après cela, ne demandez rien que vous puissiez trouver ou décider vous-même.

python3 "$BELAY" init build-speed --goal "construction à froid 2x plus rapide" \
  --check ./scripts/bench-build.sh --direction min --target 2x \
  --runs 3 --paired --min-gain 3% --guard "npm test" --guard "npm run typecheck" \
  --holdout ./scripts/bench-holdout.sh --lock scripts --lock test \
  --lock .belay/build-speed/holdout

Un audit sur un backend et un frontend :

python3 "$BELAY" init billing-audit --goal "tous les constats de facturation corrigés" \
  --check "pytest tests/facturation -q" --check "cd ../web && npx vitest run facturation" \
  --score '(\d+) failed' --direction min --target 0 --repo ../web \
  --guard "pytest tests/legacy -q" --guard "cd ../web && npx tsc --noEmit" \
  --lock tests/facturation --lock ../web/src/facturation/__tests__

init crée la branche, mesure la ligne de base et refuse de démarrer si une garde échoue déjà. Corrigez ce dont elle vous avertit avant la première étape. Définissez --max-hours ou --max-steps uniquement lorsque l'utilisateur nomme une limite. Sans cela, l'ascension s'exécute jusqu'à ce que la barrière soit atteinte ou qu'elle soit bloquée.

4. Grimper

Chaque étape est une idée, un changement, un try :

  1. Choisissez l'idée. Ne réessayez pas une que le journal a déjà annulée, sauf si vous pouvez dire ce qui est différent maintenant. Si des juges sont utilisés, lisez .belay/judge-notes.md.
  2. Faites un changement pour une raison et devinez son effet. Mettez la supposition dans l'idée : "mettre en cache l'analyseur (attendu -10%)". Lorsqu'une supposition est loin du compte, votre modèle du code est erroné. Profitez avant l'étape suivante.
  3. python3 "$BELAY" try "<idée>". Elle valide une victoire ou réinitialise l'arbre. Dans les deux cas, passez à autre chose. La sortie complète de la vérification de l'étape est dans .belay/<nom>/last.txt, et la meilleure dans best.txt. Lisez-les plutôt que de réexécuter la vérification.
  4. Pendant qu'une vérification s'exécute, ne modifiez pas l'arbre. Une seule commande belay.py qui modifie l'ascension peut s'exécuter à la fois. Si elle dit qu'une autre est toujours en cours, attendez-la.
  5. Toutes les 5 étapes, mettez à jour .belay/<nom>/notes.md en quelques lignes : ce qui fonctionne, ce qui ne fonctionne pas, quoi essayer ensuite. La session suivante reprend à partir de là.

Si vous croyez en une idée que la vérification a annulée deux fois, la vérification ne peut pas la voir. Rendez la vérification plus fine et relock. Ne modifiez jamais un fichier verrouillé pour faire passer une étape.

Ne trichez jamais sur la vérification

Chacune de ces actions augmente le score et aggrave le travail. Chacune est une ascension échouée même si try la conserve :

  • Cas spéciaux pour les entrées que la vérification utilise, ou codage en dur ou mise en cache des sorties attendues.
  • Sauter, affaiblir ou supprimer des tests, ou attraper et cacher des erreurs.
  • Faire faire moins de travail au benchmark au lieu de rendre le code plus rapide.
  • Utiliser relock pour supprimer ou assouplir un cas difficile afin que la barrière soit atteinte. Relock ne fait que rendre la vérification plus correcte. Si la barrière ne peut pas être atteinte, signalez le plancher que vous avez mesuré.
  • Lire le holdout.

Une vraie victoire rend le même code meilleur sur des entrées que la vérification n'a jamais vues. Si la seule voie à suivre est l'une de ces actions, la vérification est erronée. Dites-le.

Réévaluez en permanence

Le score ne représente que ce que l'utilisateur veut. Vérifiez qu'il le fait toujours :

  • Toutes les 10 étapes conservées, et à la barrière : regardez la chose réelle vous-même. Ouvrez l'application, lisez la sortie, visualisez la capture d'écran. Exécutez la suite de tests complète et holdout. Si le score et la réalité ont divergé, corrigez la vérification et relock --why "...".
  • Quelque chose s'est cassé qu'aucune garde n'a attrapé : trouvez l'étape avec git bisect run <commande en échec>, puis réparez-la avec try --fix "<ce qui s'est cassé>", qui conserve une modification qui maintient le score. Si la seule réparation abandonne le gain de cette étape, git revert l'étape et relock --why "étape N annulée : a cassé X". Ajoutez la garde qui manquait.
  • Avant le rapport : examinez git diff <début>..HEAD comme un seul changement, avec un œil neuf. Recherchez les tests supprimés, les erreurs avalées, une fonctionnalité désactivée, une valeur par défaut modifiée et les modifications de données ou d'API. Réparez avec try --fix, puis exécutez la vérification et holdout --why final une fois de plus pour que le rapport cite des nombres mesurés.

5. Quand ça stagne

try et status affichent PLATEAU après 5 étapes sans gain. Arrêtez d'essayer des idées et trouvez le bloqueur. Mesurez avant de deviner :

  • D'où vient réellement le temps ou le nombre d'erreurs ? Profilez-le.
  • La vérification est-elle instable, trop grossière pour voir de petits gains, ou aveugle au problème ?
  • Y a-t-il une idée plus grande que les petites étapes ne peuvent pas atteindre ?
  • Quel outil, accès, données ou calcul changerait les dix prochaines étapes ?

Enregistrez-le avec python3 "$BELAY" review "<ce que vous avez trouvé>", qui fait taire PLATEAU pendant 5 étapes. Corrigez ce que vous pouvez : une vérification plus rapide, une métrique plus fine, une approche différente. Ne demandez à l'utilisateur que ce que lui seul peut donner, comme un accès ou de l'argent, et uniquement après avoir épuisé toutes les autres idées.

6. Arrêter et faire un rapport

Arrêtez lorsque l'une de ces conditions est vraie :

  • try affiche BARRIÈRE ATTEINTE. Il exécute le holdout pour vous. Une victoire visible avec un holdout plat n'est pas une victoire.
  • status affiche BLOQUÉ : deux revues de bloqueur sans gain entre elles.
  • try refuse avec "Budget utilisé".
  • Le holdout n'est pas d'accord avec le score.
  • La prochaine étape a besoin de quelque chose que seul l'utilisateur peut donner.

Ensuite, effectuez la révision finale ci-dessus, supprimez .belay/<nom>/data/ et envoyez le rapport. Rendez-le lisible en dix secondes :

Ascension build-speed : barrière atteinte

47,1s → 18,7s, 2,5x plus rapide (barrière 23,5s, atteinte à l'étape 41)

Holdout51,0s → 20,2s, a évolué avec le score
Étapes19 conservées sur 52
SécuritéGardes réussies à chaque étape conservée. Suite complète et révision finale propres
Brancheclimb/build-speed, 19 commits

Ce qui a fonctionné

  • Cache de type incrémental : −12s

Essayé et annulé

  • Remplacement par swc : a cassé 3 gardes

Votre prochaine action

  • Révision : git log --oneline 1a2b3c4..climb/build-speed

Le titre est l'un des suivants : barrière atteinte, bloqué, budget épuisé, holdout en désaccord, nécessite votre intervention. Citez les nombres mesurés par le script, jamais des estimations. Si vous avez modifié la vérification ou la barrière en cours de route, dites-le dans le titre. Les plus grandes victoires en premier. Au maximum 3 puces par section. Sautez les sections vides.

Si l'utilisateur souhaite ensuite plus, définissez une barrière plus dure pour la même revendication avec raise 2x --why "..." et continuez à grimper. Ne passez jamais à un nouvel objectif.

Sous-agents

Si le harnais peut démarrer des sous-agents, utilisez-les pour une lecture parallèle pendant la configuration et pour un regard neuf sur la revue de bloqueur et la revue finale. Demandez des conclusions, pas des vidages de fichiers. Vous seul écrivez dans l'arbre d'ascension, car try le réinitialise. Un assistant lit, ou rédige un correctif dans sa propre worktree pour que vous l'appliquiez et le try. Sans sous-agents, faites-le en ligne.

Commandes

CommandeFait
init <nom> --goal "..." --check "..." --direction min|maxLigne de base, branche, verrouillage. Options : --check (répéter), --score '<regex>', --target (nombre ou 2x), --guard (répéter), --lock (répéter), --holdout, --repo (répéter), --runs, --paired, --min-gain, --timeout, --max-hours, --max-steps, --here
try "<idée>" [--fix]Note la modification, puis la conserve ou l'annule. --fix conserve une réparation qui maintient le score
status [nom]Score, dernières étapes, notes, PLATEAU ou BLOQUÉ
relock --why "..."Accepte une vérification modifiée et mesure à nouveau
holdout --why "..."Exécute le holdout maintenant
review "<trouvé>"Enregistre une revue de bloqueur
raise <barrière> --why "..."Définit une barrière plus dure après avoir atteint celle-ci
budget [--hours N] [--steps N]Fixe une limite budgétaire pour la session en cours
listListe les ascensions enregistrées dans ce dépôt

Référence

  • references/checks.md : Recettes de vérification par type d'objectif
  • references/judge.md : Comment configurer un juge modèle
  • Toutes les données et l'état sont stockés sous .belay/<nom>/ dans le dépôt cible.

Skills associés