Une consigne dans CLAUDE.md est suivie la plupart du temps. Ce module traite de ce qui s'applique à coup sûr, quoi que décide l'agent : l'historique Git qui permet de revenir en arrière, la chaîne d'intégration qui vérifie ailleurs que sur votre poste, et les hooks qui s'exécutent à un moment précis de la session sans demander l'avis du modèle.
Un agent écrit 300 lignes en deux minutes. Sans Git, une session qui tourne mal détruit une demi-journée de travail sans recours ; avec Git, « annule tout ça » est une commande. L'historique du dépôt de référence QuizOps est construit ainsi : un changement, un commit, une fusion par fonctionnalité.
$ git log --oneline --graph -8
* 783c14f Complète le README
* ff91c65 Ajoute un hook de formatage et un hook garde-fou
* 1a8faec Fusionne la branche statistiques
|\
| * 8682a9a Ajoute la chaîne d'intégration continue
| * 0562f56 Ajoute les tests des statistiques
| * 9a2394d Ajoute la route /api/statistiques
|/
* 7fd2e22 Ajoute une question sur le port du kubelet
git checkout -- ., sans rien relire.git diff plutôt qu'en faisant confiance au résumé.Un commit contient un changement, complet et cohérent. Livré à lui-même, un agent produit volontiers cinq changements dans le même mouvement :
Un commit fourre-tout
« Ajoute les catégories, corrige le score, met à jour le README
et refactorise le calcul »
impossible à annuler partiellement, impossible à relire
Quatre commits atomiques
« Filtre les questions par catégorie »
« Corrige le score des parties incomplètes »
« Complète le README »
« Ajoute un hook de formatage et un hook garde-fou »
Sur le dépôt de référence, chaque commit se relit en une ligne de --stat :
$ git log --stat --oneline -2
783c14f Complète le README
README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
ff91c65 Ajoute un hook de formatage et un hook garde-fou
.claude/hooks/garde-fou.sh | 24 ++++++++++++++++++++++++
.claude/settings.json | 24 ++++++++++++++++++++++++
2 files changed, 48 insertions(+)
La règle de travail avec un agent : une demande, une vérification, un commit. Trois fonctionnalités d'un coup se relisent mal et s'annulent encore plus mal.
L'agent rédige un bon message quand il lit le diff réel. Sur une modification du README, la demande tient en deux contraintes et une interdiction :
Rédige le message de commit pour les modifications en attente : un titre
à l'impératif en français de 60 caractères maximum, une ligne vide, puis
un paragraphe qui explique le pourquoi. Ne committe pas, ne modifie rien.
⏺ Bash(git diff README.md)
⎿ diff --git a/README.md b/README.md (+10 lignes)
⏺ Voici le message de commit proposé :
Précise que le quiz est en français
Le README décrivait le quiz sans mentionner sa langue, ce qui pouvait
prêter à confusion pour de nouveaux contributeurs découvrant le projet
sans avoir lancé l'application au préalable.
Le premier appel d'outil est git diff : le message décrit ce qui a changé, pas ce dont le modèle se souvient. Relisez-le avant de valider. Le défaut le plus fréquent est un titre qui liste les fichiers (« Modifie README.md ») au lieu de l'intention.
Un agent qui a le droit d'écrire crée des fichiers que vous n'avez pas demandés : caches, base locale, environnement virtuel, réglages personnels. Le .gitignore du dépôt de référence existe avant eux :
# .gitignore
.venv/
__pycache__/
*.pyc
.pytest_cache/
.ruff_cache/
quizops.db
.env
CLAUDE.local.md
.claude/settings.local.json
Un .gitignore n'affecte pas un fichier déjà suivi. Si quizops.db a été committé une fois, l'ajouter au .gitignore ne change rien : il faut git rm --cached quizops.db. Et retirer un fichier du suivi ne le retire pas de l'historique. Le script outils/fuite.sh du dépôt reproduit l'erreur classique, un .env committé avec une clé :
$ outils/fuite.sh
Le fichier .env est committé avec une fausse clé. À vous de jouer.
$ git rm --cached .env && git commit -m "Retire le fichier .env du dépôt"
$ git log -p --all | grep -c sk-ant-fausse
2
Le compte est passé de 1 à 2 : le commit de retrait contient lui aussi la clé, sous forme de ligne supprimée. Seule la réécriture de l'historique l'efface, ici git reset --hard HEAD~2 puisque rien n'a été poussé :
$ git reset --hard HEAD~2
$ git log -p --all | grep -c sk-ant-fausse
0
L'ordre des opérations ne se discute pas : on révoque la clé d'abord, on réécrit l'historique ensuite. Une clé qui a été visible une minute sur un dépôt partagé est une clé compromise.
Une branche par tâche. Avec un agent, c'est l'espace où l'essai ne coûte rien : s'il échoue, on supprime la branche. Les worktrees vont plus loin et ouvrent plusieurs branches dans des dossiers différents, en même temps :
$ git worktree add ../qz-d-essai -b essai-classement
Preparing worktree (new branch 'essai-classement')
HEAD is now at 783c14f Complète le README
$ git worktree list
~/projets/quizops 783c14f (detached HEAD)
~/projets/qz-d-essai 783c14f [essai-classement]
Deux dossiers, deux branches, deux sessions de Claude Code qui ne se gênent pas. C'est le montage du TP 4 : la même tâche confiée à deux modèles, chacun dans son worktree, puis comparée sur des mesures. Le nettoyage tient en deux commandes :
$ git worktree remove ../qz-d-essai
$ git branch -d essai-classement
Deux agents ne travaillent jamais sur le même fichier au même moment : le worktree n'aide que si le découpage du travail est disjoint.
La pull request est le point où le code écrit par un agent rencontre un humain qui n'était pas dans la session. gh, le client en ligne de commande de GitHub, permet à l'agent de la créer, de lire les commentaires et de suivre les vérifications sans quitter le terminal.
git switch -c statistiques
# travailler, committer, puis :
git push -u origin statistiques
gh pr create --fill
Le skill livré /code-review relit les modifications en cours, une branche ou une pull request désignée, et rend des constats classés par confiance. Avec --comment, il publie les constats en commentaires en ligne sur la pull request ; avec --fix, il applique les corrections retenues dans votre copie de travail. Le module 05 montre sa sortie sur un diff saboté.
L'agent trouve les oublis, les incohérences, les entrées non validées, les modifications hors sujet. Il ne dit pas si la fonctionnalité était une bonne idée ni si elle correspond au besoin : il prépare la revue, la personne la décide.
| Stratégie | Commande | Quand |
|---|---|---|
| Fusion avec commit de fusion | git merge --no-ff statistiques |
Une branche de plusieurs commits atomiques qu'on veut garder lisibles, comme statistiques dans le graphe ci-dessus |
| Écrasement en un commit | gh pr merge --squash |
Une branche d'essais successifs (« corrige », « corrige encore ») dont seul le résultat compte |
| Avance rapide | git merge --ff-only |
Un seul commit, aucune divergence |
Avec un agent, le squash est tentant parce que les branches d'essai sont nombreuses. Il fait perdre la trace des étapes, donc la possibilité d'annuler l'une d'elles. La règle du dépôt de référence : des commits atomiques sur la branche, une fusion --no-ff.
main. Un agent qui a le droit de pousser ne peut plus contourner la revue..gitignore et le garde-fou de ce module protègent le poste ; le secret de dépôt protège la chaîne.permissions.deny avec Bash(git push --force*) côté Claude Code, et l'interdiction du push forcé dans la protection de branche côté GitHub. Les deux ensemble.Ce qui gouverne l'agent sur votre poste doit gouverner l'agent sur celui de vos collègues : le dépôt versionne la configuration de Claude Code comme il versionne le code. Le CLAUDE.md du projet et ses portées sont décrits au module 02 ; ce qui suit ne traite que du partage.
| Fichier | Versionné | Rôle |
|---|---|---|
CLAUDE.md |
Oui | Les conventions et commandes du projet, lues à chaque session |
.claude/settings.json |
Oui | Permissions, hooks, plugins activés : les mêmes règles pour toute l'équipe |
.claude/rules/, .claude/skills/, .claude/commands/, .claude/agents/ |
Oui | Les procédures et les sous-agents du projet |
.mcp.json |
Oui | Les serveurs MCP du projet, approuvés par chacun à la première session |
.claude/settings.local.json |
Non | Vos permissions et réglages personnels, ignorés par Git |
CLAUDE.local.md |
Non | Vos notes personnelles sur le projet |
.env, quizops.db |
Non | Secrets et données locales |
Une modification de CLAUDE.md ou de .claude/settings.json change le comportement de l'agent sur tous les postes. Elle se relit en revue au même titre que du code, et un hook mal calibré committé dans settings.json bloque la session de toute l'équipe : on le teste d'abord dans settings.local.json, on le versionne quand il a fait ses preuves.
Dans un worktree, ces fichiers sont ceux de la branche : deux worktrees peuvent avoir deux settings.json différents si les branches divergent. C'est rarement voulu ; on fusionne main dans la branche d'essai avant d'y modifier la configuration.
La première chose à automatiser n'est pas l'IA, c'est la vérification. La séquence du module 04 devient un fichier dans le dépôt de référence :
# .github/workflows/ci.yml
name: CI
on:
push:
pull_request:
jobs:
verifier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: astral-sh/[email protected]
with:
python-version: "3.13"
- run: uv sync
- run: uvx ruff format --check .
- run: uvx ruff check .
- run: uv run pytest -q
Les étapes vont du moins cher au plus cher, et la chaîne s'arrête au premier échec. ruff format --check plutôt que ruff format : en intégration, on ne corrige pas, on constate. Les versions des actions se revérifient à chaque session de formation.
Claude Code s'exécute aussi sans interface : l'option -p envoie un prompt, imprime la réponse et rend la main. Un job de revue automatique tient en quelques lignes, données ici à titre d'exemple et non exécutées sur le dépôt de référence :
revue:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- run: npm install -g @anthropic-ai/claude-code
- run: claude -p "Relis le diff entre origin/main et HEAD et signale les régressions possibles." --allowedTools "Bash(git diff*)"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
L'authentification passe par une clé d'API lue dans les secrets du dépôt. Trois précautions : le coût n'est plus plafonné par votre patience mais par le nombre de poussées ; toute personne qui peut modifier la chaîne peut lire la clé ; un agent qui a le droit d'écrire dans la chaîne peut pousser du code que personne n'a relu. On commence par des étapes en lecture seule, et --allowedTools limite ce que l'agent peut lancer.
Un hook est une commande déclenchée par Claude Code à un moment précis de la session, avant ou après une action de l'agent. Le modèle ne décide pas de l'exécuter : il est exécuté. C'est la différence entre « formate tes fichiers » dans CLAUDE.md, qui est suivi la plupart du temps, et un hook PostToolUse qui lance ruff format après chaque écriture.
Le dépôt de référence en versionne deux dans .claude/settings.json à partir du tag tp8-depart : un formatage automatique et un garde-fou sur les commandes shell.
Une trentaine d'événements existent. Dix suffisent au quotidien :
| Événement | Quand | À quoi il sert |
|---|---|---|
SessionStart |
À l'ouverture d'une session, ou après /clear |
Injecter du contexte : état de Git, ticket en cours |
UserPromptSubmit |
Quand vous envoyez un message, avant que le modèle le lise | Enrichir ou refuser un prompt |
PreToolUse |
Avant l'exécution d'un outil | Bloquer ou autoriser un appel : le seul événement qui peut refuser une commande |
PostToolUse |
Après un outil qui a réussi | Formater, relancer les tests, journaliser |
PostToolUseFailure |
Après un outil qui a échoué | Journaliser l'échec, prévenir |
Stop |
Quand l'agent a fini de répondre | Vérifier qu'il a bien terminé, le relancer sinon |
SubagentStop |
Quand un sous-agent a fini | Même chose pour les sous-agents |
PreCompact |
Avant un compactage du contexte | Sauvegarder ce qui va être résumé |
Notification |
Quand Claude Code attend votre attention | Afficher une notification système |
SessionEnd |
À la fermeture de la session | Archiver la transcription, nettoyer |
| Type | Ce qu'il fait | Reçoit | Répond |
|---|---|---|---|
command |
Lance une commande shell | Le JSON de l'événement sur l'entrée standard | Code de sortie, texte, ou JSON de décision |
http |
Envoie le JSON en POST à une adresse |
Le même JSON dans le corps de la requête | Le même format, dans le corps de la réponse |
prompt |
Pose une question à un modèle, en un tour | Le prompt configuré et $ARGUMENTS |
Une décision en JSON |
mcp_tool |
Appelle un outil d'un serveur MCP connecté | Les champs choisis dans input |
La sortie de l'outil, traitée comme celle d'une commande |
agent |
Lance un sous-agent qui vérifie une condition | Le prompt configuré | Une décision, après plusieurs tours (expérimental) |
Ce module ne montre que command en action. Les quatre autres suivent la même structure de configuration.
Trois niveaux : l'événement, un groupe avec son matcher qui filtre les outils concernés, et la liste des hooks à exécuter.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | grep '\\.py$' | xargs -r uvx ruff format -q"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/garde-fou.sh"
}
]
}
]
}
}
C'est le fichier .claude/settings.json du dépôt de référence, sans sa section permissions. Le matcher est une expression régulière sur le nom de l'outil ; $CLAUDE_PROJECT_DIR désigne la racine du projet, quel que soit le dossier courant au moment de l'appel.
Les hooks peuvent aussi vivre dans ~/.claude/settings.json (tous vos projets), .claude/settings.local.json (ce projet, vous seul), dans le fichier hooks/hooks.json d'un plugin, ou dans le frontmatter d'un skill ou d'un sous-agent.
Un hook command lit sur son entrée standard un JSON qui décrit l'événement. Capturé sur le dépôt de référence, en abrégé, ce que reçoit le garde-fou avant une commande ls -1 app :
{
"cwd": "~/projets/quizops",
"permission_mode": "acceptEdits",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "ls -1 app",
"description": "Liste le contenu du dossier app"
},
"tool_use_id": "toolu_01L3xmJF5sfvbvcu1RAqYoWX"
}
Le JSON complet contient aussi session_id, transcript_path, prompt_id et le niveau d'effort. En PostToolUse, un champ tool_response s'ajoute avec le résultat de l'outil : pour un Edit, le chemin, l'ancienne et la nouvelle chaîne, et le patch appliqué. Tout ce dont un hook a besoin est là, et il s'extrait avec jq :
jq -r '.tool_input.file_path' # le fichier écrit ou modifié
jq -r '.tool_input.command' # la commande shell qui va être lancée
| Code de sortie | Effet |
|---|---|
0 |
Tout va bien. Ce que le hook écrit sur la sortie standard est ajouté au contexte pour SessionStart et UserPromptSubmit, montré en mode verbeux ailleurs |
2 |
Blocage. Sur PreToolUse, l'appel d'outil n'a pas lieu et le message écrit sur l'erreur standard est transmis à l'agent ; sur Stop, l'agent est relancé avec ce message |
| Autre | Erreur non bloquante : le message est montré, la session continue |
Un hook peut aussi répondre en JSON sur sa sortie standard, avec le code 0, pour une décision plus fine :
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Commande interdite par la politique du projet"
}
}
permissionDecision vaut allow, deny ou prompt. allow court-circuite la demande de permission : c'est ainsi qu'un hook peut approuver automatiquement les commandes qu'il reconnaît.
Le hook du dépôt de référence : après chaque Write ou Edit, extraire le chemin, ne garder que les fichiers Python, formater.
jq -r '.tool_input.file_path' | grep '\.py$' | xargs -r uvx ruff format -q
Pour le voir agir, on demande à l'agent de produire un fichier mal formaté, en le lui interdisant de le formater :
Crée le fichier app/exemple.py avec une fonction addition(a, b) qui renvoie
la somme, écrite volontairement avec des espaces en trop autour des
parenthèses et des opérateurs, et deux imports inutiles sur une même ligne.
Ne la formate pas, je veux la version brute.
⏺ Write(~/projets/quizops/app/exemple.py)
⎿ File created successfully at: ~/projets/quizops/app/exemple.py
⏺ Read(~/projets/quizops/app/exemple.py)
⎿ 1 import os, sys (+5 lignes)
⏺ A PostToolUse hook in this project auto-formats files after every write
(likely running `ruff format`), so it reformatted `app/exemple.py` back
to clean style right after I wrote it. I can't bypass that hook myself.
L'agent a écrit le fichier brut, le hook l'a reformaté avant même que l'agent le relise, et l'agent le constate. Le fichier sur le disque en témoigne :
$ cat app/exemple.py
import os, sys
def addition(a, b):
return a + b
$ uvx ruff format --check app/exemple.py
1 file already formatted
Les deux imports inutiles sont restés sur leur ligne : ruff format formate, il ne corrige pas. Les retirer est le travail de ruff check --fix, qu'on peut chaîner dans le même hook. Sur un projet JavaScript, la même structure appelle npx prettier --write.
Le second hook du dépôt de référence lit la commande shell que l'agent s'apprête à lancer, et la refuse si elle est destructrice :
#!/usr/bin/env bash
# .claude/hooks/garde-fou.sh
# Code de retour 2 : la commande est bloquée et le message sur stderr est montré à l'agent.
# Code de retour 0 : la commande passe.
commande="$(jq -r '.tool_input.command // empty')"
bloquer() {
echo "Commande interdite par la politique du projet : $commande" >&2
exit 2
}
# rm récursif, sauf sous /tmp
if [[ "$commande" =~ rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*[[:space:]]+([^[:space:]]+) ]]; then
[[ "${BASH_REMATCH[1]}" == /tmp/* ]] || bloquer
fi
# push forcé
if [[ "$commande" =~ git[[:space:]]+push.*(--force|[[:space:]]-f([[:space:]]|$)) ]]; then
bloquer
fi
exit 0
Un hook se teste sans agent : on lui envoie le JSON à la main et on lit le code de sortie.
$ echo '{"tool_input":{"command":"rm -rf data"}}' | .claude/hooks/garde-fou.sh; echo $?
Commande interdite par la politique du projet : rm -rf data
2
$ echo '{"tool_input":{"command":"rm -rf /tmp/cache"}}' | .claude/hooks/garde-fou.sh; echo $?
0
$ echo '{"tool_input":{"command":"git push --force origin main"}}' | .claude/hooks/garde-fou.sh; echo $?
Commande interdite par la politique du projet : git push --force origin main
2
Un motif trop large rend la session inutilisable jusqu'à ce que vous éditiez le script : c'est pourquoi /tmp/* passe, et pourquoi on teste sur des leurres avant de viser une vraie commande.
Le test avec l'agent réserve une surprise. À la demande « lance la commande rm -rf data », le modèle a regardé le dossier, puis a refusé de lui-même :
⏺ Bash(ls -la ~/projets/quizops/data)
⎿ total 8 (+3 lignes)
⏺ Je n'ai pas exécuté la commande : `data/` contient `questions.json`, le
fichier source des questions du quiz chargé par `app/donnees.py`.
Confirmes-tu que tu veux vraiment le supprimer ?
Le hook n'a pas eu à intervenir. C'est précisément la raison d'être du hook : le jugement du modèle est bon la plupart du temps, et « la plupart du temps » ne suffit pas pour une commande irréversible. Quand le modèle passe outre, le hook bloque et l'agent reçoit « Commande interdite par la politique du projet ».
StopUn hook Stop s'exécute quand l'agent estime avoir terminé. S'il rend le code 2, l'agent est relancé avec le message d'erreur : la suite de tests devient une condition de fin.
#!/usr/bin/env bash
# .claude/hooks/tests-avant-de-conclure.sh
if uv run pytest -q > /tmp/pytest.log 2>&1; then
exit 0
fi
echo "Les tests échouent, corrige avant de conclure :" >&2
tail -15 /tmp/pytest.log >&2
exit 2
{
"hooks": {
"Stop": [
{ "hooks": [ { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tests-avant-de-conclure.sh", "timeout": 120 } ] }
]
}
}
Ce hook n'est pas versionné dans le dépôt de référence : c'est l'exercice optionnel du TP 7. Ce qu'on observe : l'agent conclut, le hook lance pytest, et si un test est rouge, l'agent repart avec la sortie de pytest sous les yeux. Un Stop qui rend toujours 2 crée une boucle sans fin ; la condition de sortie doit pouvoir être remplie.
SessionStartCe qu'un hook SessionStart écrit sur sa sortie standard entre dans le contexte de la session. L'état de Git au démarrage évite à l'agent une commande, et vous évite une surprise :
{
"hooks": {
"SessionStart": [
{ "hooks": [ { "type": "command", "command": "echo 'État du dépôt :'; git status --short; git log --oneline -3" } ] }
]
}
}
À l'ouverture, l'agent sait quels fichiers sont modifiés et quels sont les trois derniers commits. Le même mécanisme sert à injecter le ticket en cours ou les décisions de la veille lues dans un fichier.
NotificationQuand Claude Code attend une permission ou a terminé une tâche longue, l'événement Notification permet une alerte système :
{
"hooks": {
"Notification": [
{ "hooks": [ { "type": "command", "command": "osascript -e 'display notification \"Claude Code attend votre attention\" with title \"QuizOps\"'" } ] }
]
}
}
Sur Linux, notify-send "QuizOps" "Claude Code attend votre attention" remplace osascript. Ce hook vit dans ~/.claude/settings.json plutôt que dans le projet : il dépend du poste, pas du dépôt.
httpLe même événement peut prévenir un service plutôt qu'un poste : un canal de discussion, un tableau de bord, un journal centralisé. Le hook http envoie le JSON de l'événement en POST :
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "http",
"url": "https://hooks.example.com/claude-code",
"headers": { "Authorization": "Bearer $WEBHOOK_TOKEN" },
"allowedEnvVars": ["WEBHOOK_TOKEN"],
"timeout": 10
}
]
}
]
}
}
Le service reçoit le même JSON qu'un hook command et répond avec le même format de décision. Le jeton n'est jamais écrit dans le fichier : il est lu dans l'environnement, et allowedEnvVars liste explicitement les variables qui peuvent être interpolées.
En session, /hooks ouvre un menu qui liste les hooks actifs par événement et permet d'en ajouter ; la commande n'existe qu'en mode interactif. Pour tracer ce qu'un hook a réellement fait, claude --debug journalise chaque appel de hook, le matcher qui l'a déclenché, son code de sortie et sa sortie. Un hook qui ne se déclenche pas a presque toujours un matcher qui ne correspond pas au nom exact de l'outil, ou un chemin de script relatif au mauvais dossier.
L'agent documente bien ce qu'il vient de lire, mal ce dont il se souvient. À la génération, une contrainte dans le prompt suffit : « chaque fonction publique a une docstring d'une ligne en français ». Ajoutée après coup, la documentation est une tâche de plus que personne ne fait.
On part des fichiers, pas de la mémoire de l'agent :
Lis @pyproject.toml, @app/main.py et @CLAUDE.md.
Écris le README.md du projet : ce que fait l'application, comment
l'installer et la lancer, comment lancer les tests, la structure des
dossiers. Chaque commande citée doit exister dans le projet.
Contraintes : en français, une page maximum, pas de section « contribuer »
ni de licence.
Le README du dépôt de référence à partir du tag tp8-depart a été produit ainsi. Le test qui compte : cloner le dépôt dans un dossier vide et suivre les instructions à la lettre, sans rien deviner.
$ git clone ~/projets/quizops /tmp/quizops-clone && cd /tmp/quizops-clone
$ uv sync
$ uvx ruff format . && uvx ruff check . && uv run pytest -q
Une commande qui manque, un chemin faux ou un prérequis oublié apparaît à la première ligne qui échoue. Ce qu'on ne documente pas : les commentaires qui paraphrasent le code, et la documentation d'API que FastAPI génère déjà sur /docs.
git diff, pas depuis la mémoire du modèle.git rm --cached : on révoque d'abord, on réécrit l'historique ensuite.CLAUDE.md, .claude/settings.json, les skills et .mcp.json se versionnent ; settings.local.json et .env jamais.exit 2, et se teste sans agent.TP associé : TP 7 : Git, CI/CD et Hooks
➡️ Module suivant : MCP, Plugins et Outils Externes