kwa/ workflows
Encadrer l'agent

Parcours 3 : Harness

De l'histoire du processus de développement jusqu'à un harness complet, puis dix étapes sur un dépôt jetable, avec les vrais gardes : les sorties affichées sont celles de l'exécution, recalculées à chaque génération du site. La pratique prépare un dépôt jetable pour le plugin, puis lance les gardes à la main : ce sont de simples scripts, les mêmes que ceux que Claude Code appelle. Prérequis : les parcours Culture et Contexte, Python 3 et le plugin Kwa installé (voir Avant de commencer). Les exercices tournent sans agent, sans compte ni clé ; Claude Code ou Codex sert pour les essais en situation, et jq pour l'exemple de hook en bash. Environ quatre heures, à calibrer en séance.

Parcours pas encore éprouvé avec un groupe : les durées et les exercices sont des estimations de conception, à ajuster après une première séance.

  1. 0 · L'histoire
  2. 1 · Instructions
  3. 2 · Un garde
  4. 3 · Branchement
  5. 4 · Événements
  6. 5 · Politique
  7. 6 · Une skill
  8. 7 · Mémoire
  9. 8 · Workflow
  10. 9 · Le flux
  11. 10 · Limites

Introduction

D'où vient le harness

Le processus protégeait la qualité. L'agent l'a contourné par vitesse.

Pendant cinquante ans, la qualité d'un logiciel ne dépendait pas que du talent de chacun : elle venait d'un processus (spécifier, tester, relire, intégrer, déployer). Chaque époque a ajouté une couche de vérification. Un agent de code écrit et exécute plus vite que ce processus ne peut suivre : sans cadre, il décide et agit seul. Le harness est ce cadre : tout ce qui entoure le modèle pour qu'il travaille dans le processus, et non à côté.

ÉpoquePratiqueQui écritCe qui protège la qualité
1970Cycle séquentielLes personnes, en phases successivesSpécification et recette finales, revues de documents
2001Méthodes agilesLes personnes, par petits lotsItérations courtes, tests, revue entre pairs
2008 à 2010Pull requests, CI/CD, DevOpsLes personnes, avec des machines qui vérifientRevue de code sur chaque changement, intégration continue, déploiement automatisé, branches protégées
2021Complétion dans l'éditeurLa personne, avec des suggestionsInchangé : la personne relit chaque ligne qu'elle accepte
2022Assistant par conversationLa personne, qui copie et colleInchangé, mais le volume de texte relu augmente
2024 à 2025Agents avec outils (MCP, terminal)Un agent, qui lit, modifie, lance et commitLes demandes d'autorisation de l'outil existent, mais elles ignorent votre processus et les règles de votre équipe
Aujourd'huiHarnessUn agent, dans un cadreRègles, gardes, skills, mémoire et preuve rebranchés autour de l'agent

Dates : repères généraux de l'histoire du génie logiciel (article de Royce en 1970, Manifeste agile en 2001, GitHub en 2008, GitHub Copilot en 2021, ChatGPT fin 2022, MCP fin 2024). La lecture « le harness rebranche le processus » est celle de Kwa, pas un consensus.

Chaque étape du processus d'équipe a donc son équivalent autour de l'agent :

Étape du processusDans le harness (Kwa)
Cadrer le besoin/kwa:brainstorm, /kwa:plan
Ticket et branche dédiée/kwa:start-dev et kwa start
Tests d'abord/kwa:tdd
Revue de code/kwa:review, retours traités par /kwa:review-feedback
Branches protégées, pas de force-pushGardes guard-git, guard-write
Intégration continue/kwa:verify, vérifications de la politique
Pull request avec preuve/kwa:ship, guard-github
Déploiement vérifié/kwa:deploy
Rétrospective/kwa:learn, mémoire de session

Étape 1

Instructions et contrainte

Ce que l'agent lit, et ce qui l'oblige.

Préparez un dépôt jetable pour le plugin. kwa init n'écrit que de la configuration : les scripts restent dans le plugin. Regardez ce qui est écrit pour l'agent, puis demandez-vous : quelle ligne l'empêche de committer sur main ?

$ kwa init ./mon-projet --modules core,memory,craft,issue-flow
kwa v0.2.2 (plugin) → mon-projet
  modules : core, memory, craft, issue-flow
  gabarits copiés :
    - .github/pull_request_template.md
    - .github/ISSUE_TEMPLATE/bug.md
    - .github/ISSUE_TEMPLATE/feature.md
    - .github/ISSUE_TEMPLATE/config.yml
  AGENTS.md : créé
  CLAUDE.md : créé
  politique .claude/kwa.policy.json : créée à partir de la détection — à relire et compléter

Dans une session Claude Code, le plugin met kwa dans le PATH. Dans un terminal ordinaire, appelez-le depuis le dossier du plugin, noté $KWA dans ce parcours : ~/.claude/plugins/cache/kwa/kwa/<version>, ou un clone du dépôt Kwa ("$KWA"/bin/kwa init …).

CLAUDE.md contient une ligne, un pointeur vers le fichier commun :

@AGENTS.md

Début du bloc géré de AGENTS.md :

# AGENTS.md
<!-- kwa:begin v0.2.2 profile=pr-flow (géré ; toute retouche ici est écrasée à la mise à jour) -->
## Kwa — socle commun
Ce bloc est géré par Kwa (`kwa init`) : ne pas l'éditer ici, le modifier dans le pack.
Tout ce qui est propre au projet va **au-dessus ou en dessous** des marqueurs, ou dans `.claude/kwa.policy.json`.
- `AGENTS.md` est la source unique. `CLAUDE.md` n'est qu'un pointeur `@AGENTS.md`.
- Modules actifs : core, memory, craft, issue-flow.
- Règles communes : les règles communes de Kwa (rappelées au démarrage de session). Garde-fous automatiques : les hooks du plugin Kwa ; ce sont des
  filets contre la bévue, pas une frontière de sécurité : ne jamais les contourner ni les éditer sans accord.
Réponse

Aucune ligne ne l'empêche. Un fichier d'instructions est du contexte : l'agent le lit et peut s'en écarter. Ce qui contraint, c'est un hook qui s'exécute avant l'outil. Voici le même commit, tenté sur main, quand le garde est appelé :

{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Kwa — git commit sur main refusé : travailler sur une branche dédiée et ouvrir une PR (KWA_ALLOW_MAIN=1 pour une exception assumée)"}}

Étape 2

Un garde, de la commande au verdict

Un hook reçoit du JSON et répond refuse, demande ou laisse passer.

Un garde est un petit programme. C'est Claude Code qui le lance, pas le modèle : il lui envoie ce que l'agent s'apprête à faire, et le garde répond sur la sortie standard. Appelez-le à la main :

$ echo '{"tool_name": "Bash", "tool_input": {"command": "git commit -m wip"}, "cwd": "."}' | python3 "$KWA"/core/hooks/guard-git.py

Pas de sortie veut dire « laisse passer ». Avant de regarder les réponses, faites votre pronostic pour chaque commande, puis ouvrez :

CommandeVerdict réel
cat .env
branche de travail
Mon pronostic
refusé commande refusée : touche un fichier de secrets (.env)
cat .env.example
branche de travail
Mon pronostic
passe
git push --force origin feat/12-filtre
branche de travail
Mon pronostic
refusé push forcé ou destructif refusé (--force, -f, +ref, --delete, --all, --mirror). Après un rebase : --force-with-lease, avec confirmation.
rm -rf build
branche de travail
Mon pronostic
passe
git commit -m 'wip'
branche main
Mon pronostic
refusé git commit sur main refusé : travailler sur une branche dédiée et ouvrir une PR (KWA_ALLOW_MAIN=1 pour une exception assumée)

Verdicts calculés en exécutant les gardes à chaque génération de la page.

Étape 3

Le branchement

Un hook relie un événement à un script. Le plugin le déclare, le projet active le plugin.

Le plugin déclare ses hooks dans un fichier hooks.json. Voici l'entrée qui relie le garde git à l'événement PreToolUse pour l'outil Bash ; run.py vérifie d'abord que le projet a une politique Kwa et le module concerné :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/core/hooks/run.py\" guard-git",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Le projet, lui, active le plugin dans .claude/settings.json :

{
  "enabledPlugins": {
    "kwa@kwa": true
  }
}

Vérifiez l'état de l'installation (ici sur une machine où le plugin n'est pas encore installé) :

$ kwa doctor ./mon-projet
• plugin Claude Code sur cette machine : non installé (/plugin install kwa@kwa)
✓ cible conforme (plugin)

Cassez-le : retirez enabledPlugins de settings.json sans toucher au script, puis relancez le diagnostic.

• plugin Claude Code sur cette machine : non installé (/plugin install kwa@kwa)
✗ .claude/settings.json : plugin kwa@kwa non activé pour le projet
Que fait le script maintenant ?

Il refuse toujours quand on l'appelle à la main, mais plus personne ne l'appelle dans ce projet. Un garde qui existe sans être branché ne protège de rien.

{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Kwa — git commit sur main refusé : travailler sur une branche dédiée et ouvrir une PR (KWA_ALLOW_MAIN=1 pour une exception assumée)"}}

Étape 4

Les autres événements de hooks

Le garde ne sert qu'un événement sur plus de trente. Le choix de l'événement fait la différence.

Jusqu'ici, un seul événement : PreToolUse, avant un outil. Il en existe plus de trente. Cinq suffisent pour commencer :

ÉvénementQuandUsage typiqueDans Kwa
SessionStartDébut, reprise, après effacement ou compactionRéinjecter le contexte utilememory-context
UserPromptSubmitAvant que l'agent traite votre messageAjouter du contexte, repérer un signalsignal-prompt
PreToolUseAvant un outilBloquer ou demanderles gardes
PostToolUseAprès un outil réussiFormater, lancer un contrôle rapideformat-after-edit
StopQuand l'agent a fini de répondreRefuser de s'arrêter tant que ce n'est pas vérifiéverify-stop

Un hook est une commande, mais aussi, selon le besoin, un appel HTTP, un outil MCP, une évaluation par un modèle, ou un sous-agent de vérification (ces deux derniers sont réservés aux décisions qui demandent du jugement). La règle des codes de sortie est la même partout : 0 laisse faire, 2 bloque et renvoie le message à l'agent.

Pour sentir le principe sans Kwa, voici un garde complet, hors de tout outillage : un script et sa déclaration.

.claude/hooks/protege.sh :

#!/bin/bash
# bloque l'édition des fichiers protégés
FILE=$(cat | jq -r '.tool_input.file_path // empty')
for motif in ".env" "package-lock.json" ".git/"; do
  if [[ "$FILE" == *"$motif"* ]]; then
    echo "Bloqué : $FILE correspond à $motif" >&2
    exit 2
  fi
done
exit 0

.claude/settings.json :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protege.sh" }]
      }
    ]
  }
}

Pronostic : pour chaque besoin, quel événement ?

BesoinÉvénement
Formater chaque fichier modifié
Mon pronostic

PostToolUse sur Edit|Write : le fichier existe déjà, on le nettoie.

Rappeler au démarrage où en était la session précédente
Mon pronostic

SessionStart : sa sortie est ajoutée au contexte.

Empêcher de terminer avec des tests rouges
Mon pronostic

Stop, avec un test de stop_hook_active pour ne pas boucler.

Étape 5

La politique du projet

Ce qui change d'un projet à l'autre tient dans un fichier, pas dans un script.

Les gardes sont communs, la politique est locale (.claude/kwa.policy.json). Dans le dépôt jetable, la commande npm run deploy:prod n'est pas gardée : passe

Ajoutez cette règle dans bash.deny :

{
  "id": "deploy-prod",
  "reason": "Le déploiement de production passe par la PR, pas par ce script.",
  "all": [
    "npm run deploy:prod"
  ]
}

Même commande, même garde, nouveau verdict : refusé

[deploy-prod] Le déploiement de production passe par la PR, pas par ce script.

Étape 6

Une skill, et comment la casser

Une procédure que l'agent déclenche ou que vous tapez.

Une skill est un fichier SKILL.md : un en-tête qui dit quand l'appeler, un corps qui dit comment. Celui de /kwa:rephrase :

---
name: rephrase
description: Reformuler le dernier message quand l'utilisateur n'a pas compris. À utiliser dès qu'il dit « stop », « je ne comprends pas », « explique autrement », « reformule », « c'est quoi ça ? », « /kwa:rephrase ».
disable-model-invocation: true
---

Dans un clone du dépôt Kwa, créez une skill dont la description contient « : » (un piège YAML qui tronque l'en-tête sans bruit), puis lancez la suite de contrôle des skills. Elle échoue deux fois : le premier échec est le piège YAML, le second rappelle qu'une skill doit être déclarée dans manifest.json.

$ python3 -m unittest tests.test_skills.Lint
FAIL: test_description_survives_yaml_parsing (tests.test_skills.Lint) (skill='demo')
AssertionError: ': ' unexpectedly found in 'Faire une démonstration. À lancer quand : on le demande.' : « : » dans une description non quotée casse le YAML
FAIL: test_manifest_covers_every_skill (tests.test_skills.Lint)
FAILED (failures=2)
Pourquoi ce test existe

Une description mal formée ne plante rien : la skill devient simplement introuvable. Un test qui échoue vaut mieux qu'une skill invisible.

Étape 7

La mémoire qui s'enrichit

Noter pendant, trier à la fin, valider soi-même.

Capturez un piège rencontré pendant le travail, sans rien écrire dans la documentation :

$ kwa memory capture gotcha "Les tests d'intégration ont besoin du fichier d'environnement local"
$ kwa memory pending
1 note(s) en attente
- [2026-10-15] [gotcha] Les tests d'intégration ont besoin du fichier d'environnement local

En fin de session, /kwa:learn propose où chaque note devrait vivre (documentation, politique, mémoire de l'agent). Rien n'est appliqué sans votre accord.

Étape 8

Le workflow complet

Une demande, une branche, une preuve, une PR.

Démarrez un changement : une branche dans son propre worktree, à côté du dépôt, jamais sur main.

$ kwa start feat status-filter "Ajouter un filtre de statut" --no-issue
aucune issue créée (flow.auto_issue: false ou --no-issue)
branche : feat/status-filter
worktree : mon-projet-wt-status-filter

Travailler dans : mon-projet-wt-status-filter
mon-projet                   abc1234 [main]
mon-projet-wt-status-filter  abc1234 [feat/status-filter]

La suite se joue avec les skills : /kwa:brainstorm, /kwa:plan, /kwa:tdd, /kwa:verify, /kwa:review, /kwa:ship. La page Les workflows déroule ce workflow sur une demande réelle, et le Terminal montre les commandes.

Étape 9

Le flux d'un développeur aujourd'hui

Tout mis ensemble : une journée où presque tout est automatisé, sauf les décisions.

Les étapes précédentes sont des pièces. Voici l'assemblage tel qu'une équipe qui l'a bien outillé le vit : l'agent code, mais chaque affirmation est adossée à une preuve, et les humains gardent les deux décisions qui engagent (valider ce qu'on construit, accepter ce qui est livré).

  1. 1
    La demande devient une issue et une branche/kwa:start-dev ouvre l'issue, crée la branche dans son worktree. Rien ne s'écrit sur main.
    Kwa
  2. 2
    Explorer, puis planifierEn mode plan, l'agent lit le code sans rien modifier ; des sous-agents explorent chacun un domaine et renvoient un résumé.
    Sous-agents
  3. 3
    Vous validez le plan et les critères de réussiteLe point où votre jugement compte le plus : ce que « fini » veut dire, y compris les captures attendues.
    Humain
  4. 4
    Documentation à jour, puis test avant codeContext7 donne la doc de la bonne version ; /kwa:tdd fait écrire le test qui échoue, puis le code qui le fait passer.
    Context7
  5. 5
    Les contrôles automatiques tournent seulsUn hook PostToolUse formate, un hook Stop, s'il est activé (dans Kwa : KWA_STOP_VERIFY=1), refuse de rendre la main tant que tests, lint et build ne sont pas verts.
    Hooks
  6. 6
    Playwright ouvre l'application et fait ses capturesParcours du formulaire, capture à 1280 px et à 375 px, lecture de la console et des requêtes réseau.
    Playwright
  7. 7
    Un agent vérificateur compare aux critèresUn sous-agent en contexte neuf, qui n'a pas écrit le code, confronte captures et diff aux critères et ne signale que les écarts qui comptent.
    Sous-agent
  8. 8
    Pull request avec ses preuves ; vous relisez et fusionnez/kwa:ship ouvre la PR avec captures et sorties de tests. La fusion reste une décision humaine.
    Humain

Lancez la scène : chaque étape s'allume à son tour.

Parcours idéal : tout projet n'a pas ces huit étapes. Les points humains (3 et 8) sont voulus.

Le travail d'un développeur se déplace : moins de frappe, plus de cadrage (critères de réussite précis), de mise en place (hooks, MCP, règles) et de relecture de preuves. Context7 évite l'API périmée, Playwright évite le « ça marche chez moi » sans l'avoir vu, et un agent vérificateur en contexte neuf évite le biais de celui qui relit ce qu'il vient d'écrire.

Deux promesses à ne pas faire : que la vérification attrape tout (un vérificateur invité à trouver des écarts en trouve toujours, d'où la consigne de ne retenir que ceux qui touchent aux exigences), et que le garde-fou remplace la relecture humaine.

À vous

Prenez une tâche récente de votre équipe. Pour chacune des huit étapes, écrivez : ce qui était fait à la main, ce qui peut être automatisé, et le contrôle qui prouverait que c'est bon. Marquez les deux décisions que vous refusez de déléguer.

Étape 10

Les limites, sans maquillage

Un garde est un filet contre la bévue, pas une frontière de sécurité.

Essayez de contourner le garde des secrets. Ces trois commandes lisent le même fichier d'environnement ; seule la forme change :

CommandeVerdict réel
cat $(echo .e)nvpasse
cat .e*passe
python3 -c "print(open('.env').read())"passe

Le garde lit le texte de la commande, il ne l'exécute pas : un nom calculé, un glob ou un interpréteur lui échappent.

Alors, à quoi ça sert, et que mettre en plus ?

À arrêter l'erreur honnête. Les règles permissions.deny de Claude Code, seules, ne font pas mieux : Read(./.env) bloque l'outil de lecture, pas python3 -c. Contre une attaque, il faut une barrière que le système d'exploitation ou GitHub fait respecter : le bac à sable de Claude Code (/sandbox, désactivé par défaut), qui applique aussi ces règles aux commandes shell ; un conteneur ou une machine isolée ; des identifiants séparés ; et la protection de branche avec la CI obligatoire. Voir Ce qui est imposé, ce qui est suggéré.

Vous avez compris si vous savez

  • dire pourquoi un fichier d'instructions ne contraint pas, et nommer ce qui contraint ;
  • prédire le verdict d'une commande, puis relier un garde à son entrée dans settings.json ;
  • ajouter une règle de projet et montrer qu'elle change un verdict ;
  • écrire l'en-tête d'une skill et lire l'échec d'un test ;
  • dire ce qu'aucun de ces garde-fous ne garantit, et quelle barrière complète.

Pour les consignes de chaque outil, les documentations officielles font foi : mémoire et instructions, hooks, réglages. Les commandes et les noms de réglages évoluent avec les versions : vérifiez avant une séance.