Exercices — Session 6 : Claude Code & CI/CD
Programme : Applied AI — Niveau Avancé — Instructeur : Yann Isola
Format : 3 exercices. Exercice 1 en séance (20 min), Exercice 3 démarré en séance (15 min), Exercice 2 cadré en séance et terminé à la maison.
Contexte fil rouge : le projet facturation-api — une API REST de facturation en Python (FastAPI), avec tests pytest, déployée sur un cloud interne.
Exercice 1 — Rédiger un CLAUDE.md (20 min, en séance)
Contexte
Vous rejoignez l’équipe facturation-api. Le dépôt n’a pas de CLAUDE.md : chaque développeur qui utilise Claude Code répète les mêmes explications à chaque session, et l’agent commet toujours les mêmes erreurs (mauvaise commande de test, modifications interdites dans migrations/, commentaires en anglais alors que l’équipe travaille en français).
Informations sur le projet (extraites d’un entretien avec la lead dev)
- API REST de facturation en Python 3.12 / FastAPI, base PostgreSQL, ORM SQLAlchemy (ORM : Object-Relational Mapping, correspondance objet-relationnel).
- Gestion des dépendances avec poetry — pas pip. La commande de test est
poetry run pytest, le linter estpoetry run ruff check ., le formateurpoetry run ruff format .. - Les tests d’intégration (
tests/integration/) exigent une base PostgreSQL locale lancée pardocker compose up -d db. Sans elle, ils échouent avec des erreurs de connexion trompeuses. - Le dossier
app/migrations/est généré par Alembic : ne jamais l’éditer à la main — toute modification passe parpoetry run alembic revision --autogenerate. - Convention d’équipe : commentaires et docstrings en français, noms de variables/fonctions en anglais.
- Les montants sont toujours manipulés en centimes (entiers), jamais en flottants. C’est la source du bug le plus coûteux de l’histoire du projet.
- Le fichier
app/config.pylit des variables d’environnement ; en local, elles viennent de.env(jamais commité). - Branches :
mainprotégée, on travaille surfeature/xxx, PR obligatoire, revue obligatoire.
Votre tâche
Rédigez le fichier CLAUDE.md complet du projet.
Contraintes :
- Les 4 sections canoniques : vue d’ensemble, conventions, commandes courantes, pièges connus (gotchas).
- Toutes les commandes doivent être exactes et copiables (telles que fournies ci-dessus).
- Au moins 3 gotchas spécifiques au projet.
- Moins de 100 lignes. La densité est un critère de notation.
- Bonus : une section « Ce que Claude Code ne doit jamais faire » (interdictions explicites).
Vous pouvez utiliser le constructeur de CLAUDE.md de la page web comme échafaudage — mais la version finale doit être retravaillée à la main.
Critères d’évaluation (/10)
| Critère | Points |
|---|---|
| 4 sections canoniques présentes et pertinentes | 4 |
| Commandes exactes, copiables | 2 |
| ≥ 3 gotchas spécifiques (dont centimes et migrations) | 2 |
| Concision < 100 lignes | 2 |
Question de réflexion (à rédiger en 3 lignes)
L’interdiction « ne jamais éditer app/migrations/ à la main » figure dans votre CLAUDE.md. Est-ce suffisant pour la garantir ? Si non, quel mécanisme complémentaire proposez-vous, et pourquoi ? (Indice : pensez à la division persuasion / capacité / contrôle vue en Partie D.)
Exercice 2 — Mettre en place un pipeline CI/CD avec Claude Code (cadré en séance, terminé à la maison)
Contexte
La lead dev de facturation-api veut automatiser la revue de PR : à chaque ouverture ou mise à jour d’une Pull Request (demande de fusion), Claude Code doit produire une revue de code postée en commentaire. La revue est non bloquante (elle informe, elle n’empêche pas le merge). Le dépôt est hébergé sur GitHub, la CI est GitHub Actions.
Votre tâche
Livrez trois artefacts :
Artefact A — Le workflow GitHub Actions (.github/workflows/claude-review.yml)
Écrivez le workflow complet. Il doit :
- Se déclencher sur
pull_request(ouverture et synchronisation). - Faire le checkout du code avec assez d’historique pour calculer le diff de la PR.
- Installer Claude Code sur le runner. ⚠ Vérifiez la commande d’installation actuelle dans la documentation officielle — elle évolue.
- Exécuter Claude Code en mode headless (
claude -p "...") avec une instruction de revue qui exige : bugs potentiels, problèmes de sécurité, respect des conventions duCLAUDE.md, sortie en Markdown. - Poster la sortie en commentaire de la PR (via
gh pr commentou l’API GitHub). - Être non bloquant : un échec du job de revue ne doit pas faire échouer la PR (
continue-on-errorou équivalent). - Avoir un timeout raisonnable (protection coûts).
Squelette de départ (à compléter — les # TODO sont votre travail) :
name: Revue Claude Code
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
continue-on-error: true
timeout-minutes: 10 # protection coûts / blocage
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # historique complet pour le diff
# TODO : installer Claude Code
# TODO : exécuter claude -p avec l'instruction de revue
# (clé API dans les secrets du dépôt : ANTHROPIC_API_KEY)
# TODO : poster la sortie en commentaire de PR
Artefact B — Le fichier .claude/settings.json du job de revue
Configurez les permissions au strict nécessaire pour une revue en lecture seule. Rappel : en headless, personne ne clique sur « autoriser » — tout doit être décidé à l’avance, et tout superflu est une faille.
Questions guides :
- La revue a-t-elle besoin de
Write? d’Edit? (non — justifiez dans un commentaire) - Quelles variantes de
Bashsont nécessaires pour lire le diff de la PR ? WebFetch: risque ou nécessité ici ?
Artefact C — Note d’architecture (½ page)
Répondez :
- Pourquoi la revue doit-elle être non bloquante au lancement ? Dans quelles conditions pourriez-vous la rendre bloquante plus tard ?
- Quel est le risque d’injection de prompt dans ce pipeline (pensez : le contenu de la PR est écrit par des tiers) et comment vos permissions le mitigent-elles ?
- Estimez les leviers de maîtrise des coûts : timeout, taille du diff, prompt caching, fréquence de déclenchement. Proposez une politique.
Critères d’évaluation (/15)
| Critère | Points |
|---|---|
| Pattern correct, workflow plausible et complet | 4 |
| Déclencheurs, non-bloquant, timeout | 3 |
settings.json au moindre privilège, justifié |
4 |
| Note d’architecture : injection + coûts traités sérieusement | 4 |
Faute rédhibitoire : un mode « autorise tout » (--dangerously-skip-permissions ou permissions totales) dans un runner ayant accès à des secrets → note plafonnée à 7/15. Un architecte ne fait pas ça.
Extension bonus (+3)
Ajoutez un second job : génération de tests quand la couverture baisse. Contrainte : les tests générés partent sur une branche dédiée avec PR, jamais un push direct sur main. Décrivez les permissions (indice : Write est nécessaire — comment le limiter au dossier tests/ ? La réponse propre implique l’Exercice 3…).
Exercice 3 — Concevoir un système de hooks (15 min de démarrage en séance, finition à la maison)
Contexte
facturation-api traite des données de facturation clients. L’équipe conformité impose quatre exigences avant d’autoriser Claude Code dans l’équipe :
- E1. Aucune commande shell ne doit jamais toucher la base de données de production (toute commande contenant
psqlavec l’hôteprod-dbdoit être bloquée, même si un humain l’approuve). - E2. Tout fichier modifié par l’agent doit être immédiatement reformaté avec
ruff format(garantie de style, sans dépendre de la bonne volonté du modèle). - E3. Chaque action de l’agent (chaque appel d’outil : quel outil, quels paramètres, quel résultat) doit être consignée dans un journal d’audit horodaté — exigence d’auditabilité réglementaire.
- E4. Quand une session de l’agent se termine, un résumé doit être envoyé sur le canal Slack de l’équipe (via un webhook interne, script
notify-slack.shdéjà fourni).
Votre tâche
Partie 1 — Table de conception
Pour chaque exigence E1–E4, remplissez la table :
| Exigence | Événement de hook choisi (PreToolCall / PostToolCall / Notification / Stop) |
Bloquant ? | Logique du script (pseudo-code, 3–6 lignes) | Pourquoi PAS une simple instruction dans CLAUDE.md ? |
|---|---|---|---|---|
| E1 | ||||
| E2 | ||||
| E3 | ||||
| E4 |
Partie 2 — Écrire un hook complet
Écrivez le script du hook E1 (bash ou python, au choix). Il doit :
- Recevoir les informations de l’appel d’outil (l’outil appelé et ses paramètres — en pratique fournis en JSON sur l’entrée standard ⚠ vérifiez le format exact dans la doc de votre version).
- Ne s’intéresser qu’aux appels
Bash. - Bloquer si la commande contient à la fois
psqletprod-db(soyez robuste : casse, espaces). - En cas de blocage : sortir avec un code d’échec et émettre un message explicatif — le modèle le recevra et pourra ajuster sa stratégie au lieu de réessayer bêtement.
Partie 3 — Questions d’architecte (3–5 lignes chacune)
- E1 pourrait aussi être traité par une permission
deny(ex. interdireBash(psql:*)). Comparez les deux approches : que perd-on, que gagne-t-on avec le hook ? Quand choisir l’un ou l’autre ? - E3 en
PreToolCallouPostToolCall? L’énoncé demande de journaliser les résultats — qu’est-ce que cela impose ? Peut-on avoir besoin des deux ? - Un hook
PostToolCallde reformatage (E2) qui échoue (ruff plante) : que doit-il se passer ? La session doit-elle s’arrêter ? Justifiez votre politique d’échec. - La direction demande : « peut-on faire confiance au modèle pour respecter E1 si on l’écrit en MAJUSCULES dans CLAUDE.md ? » Rédigez la réponse d’architecte en 3 phrases, avec les mots probabiliste et déterministe.
Critères d’évaluation (/10)
| Critère | Points |
|---|---|
| Bon événement pour chaque exigence, blocage correct | 4 |
| Script E1 : robuste, message d’explication, code de sortie | 3 |
| Questions d’architecte : distinction persuasion/capacité/contrôle maîtrisée | 3 |
Corrigé attendu (aide-mémoire enseignant — ne pas distribuer)
- E1 →
PreToolCall, bloquant. Hook plutôt que permission si l’on veut autoriserpsqlvers les bases de dev/staging (logique conditionnelle fine, impossible avec un simple motif deny). - E2 →
PostToolCall(filtré surEdit/MultiEdit/Write), non bloquant. Instruction CLAUDE.md insuffisante : le formatage doit être garanti, pas probable. - E3 →
PostToolCallpour capter les résultats (lePreToolCallne connaît pas encore le résultat) ; les deux si l’on veut aussi tracer les tentatives bloquées. Non bloquant, mais politique d’échec stricte à discuter (un audit qui ne journalise plus est un audit mort). - E4 →
Stop, non bloquant, appellenotify-slack.sh. - Question 4, réponse type : « Non. Une instruction de prompt est probabiliste : le modèle la suit presque toujours, mais “presque” est inacceptable pour une exigence de conformité. Un hook est déterministe : il s’exécute à chaque fois, quel que soit l’état du contexte. Les exigences réglementaires vont dans du code, pas dans du texte. »