# 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 est `poetry run ruff check .`, le formateur `poetry run ruff format .`.
- Les tests d'intégration (`tests/integration/`) exigent une base PostgreSQL locale lancée par `docker 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 par `poetry 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.py` lit des variables d'environnement ; en local, elles viennent de `.env` (jamais commité).
- Branches : `main` protégée, on travaille sur `feature/xxx`, PR obligatoire, revue obligatoire.

### Votre tâche

Rédigez le fichier `CLAUDE.md` complet du projet.

**Contraintes :**
1. Les **4 sections canoniques** : vue d'ensemble, conventions, commandes courantes, pièges connus (gotchas).
2. Toutes les commandes doivent être **exactes et copiables** (telles que fournies ci-dessus).
3. Au moins **3 gotchas** spécifiques au projet.
4. **Moins de 100 lignes.** La densité est un critère de notation.
5. 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 :
1. Se déclencher sur `pull_request` (ouverture et synchronisation).
2. Faire le checkout du code **avec assez d'historique pour calculer le diff** de la PR.
3. Installer Claude Code sur le runner. ⚠ *Vérifiez la commande d'installation actuelle dans la documentation officielle — elle évolue.*
4. 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 du `CLAUDE.md`, sortie en Markdown.
5. Poster la sortie en commentaire de la PR (via `gh pr comment` ou l'API GitHub).
6. Être **non bloquant** : un échec du job de revue ne doit pas faire échouer la PR (`continue-on-error` ou équivalent).
7. Avoir un **timeout** raisonnable (protection coûts).

Squelette de départ (à compléter — les `# TODO` sont votre travail) :

```yaml
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 `Bash` sont nécessaires pour lire le diff de la PR ?
- `WebFetch` : risque ou nécessité ici ?

#### Artefact C — Note d'architecture (½ page)

Répondez :
1. Pourquoi la revue doit-elle être **non bloquante** au lancement ? Dans quelles conditions pourriez-vous la rendre bloquante plus tard ?
2. 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 ?
3. 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 `psql` avec l'hôte `prod-db` doit ê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.sh` dé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 :
1. 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*).
2. Ne s'intéresser qu'aux appels `Bash`.
3. Bloquer si la commande contient à la fois `psql` et `prod-db` (soyez robuste : casse, espaces).
4. 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)

1. **E1 pourrait aussi être traité par une permission `deny`** (ex. interdire `Bash(psql:*)`). Comparez les deux approches : que perd-on, que gagne-t-on avec le hook ? Quand choisir l'un ou l'autre ?
2. **E3 en `PreToolCall` ou `PostToolCall` ?** L'énoncé demande de journaliser les *résultats* — qu'est-ce que cela impose ? Peut-on avoir besoin des deux ?
3. Un hook `PostToolCall` de reformatage (E2) qui **échoue** (ruff plante) : que doit-il se passer ? La session doit-elle s'arrêter ? Justifiez votre politique d'échec.
4. 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 autoriser `psql` vers les bases de dev/staging (logique conditionnelle fine, impossible avec un simple motif deny).
- **E2** → `PostToolCall` (filtré sur `Edit`/`MultiEdit`/`Write`), non bloquant. Instruction CLAUDE.md insuffisante : le formatage doit être *garanti*, pas probable.
- **E3** → `PostToolCall` pour capter les résultats (le `PreToolCall` ne 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, appelle `notify-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. »
