diff --git a/CLAUDE.md b/CLAUDE.md
index be847be..cdefcf9 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -40,7 +40,10 @@ scene, lit des commandes JSON dans `
/inbox/`, ecrit les resultats dans
comportement normal — overhead zero.
Cote agent, un wrapper Python stdlib (`tools/automation/harness.py`) expose une API
-simple. Godot attendu a `C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe`.
+simple. Le binaire Godot est detecte via `GODOT_BIN` (fallback Windows
+`C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe`, Linux
+`/opt/godot/godot`). Sous Linux sans `DISPLAY`, la commande Godot est auto-wrappee
+dans `xvfb-run` (framebuffer virtuel 1280x720).
### Build + utilisation
@@ -101,3 +104,86 @@ Cette boucle permet de valider que :
- Les fichiers IPC sont ecrits `.tmp` puis renommes (atomique sur Windows).
- La campagne se charge via `load_mission("campaign_01", 0)`. Passer a une mission
> 0 n'est pas supporte directement (il faut passer par `MissionComplete` reel).
+
+## Mode autonome dans le devcontainer
+
+Claude tourne dans un devcontainer Linux (`.devcontainer/`) qui embarque deja
+`.NET 9 SDK`, `Godot 4.6.2 mono Linux`, `Xvfb`, `Python 3`. Tout le workflow
+ci-dessous est executable sans sortir du container, sans display physique.
+
+### Sanity check toolchain (une fois par session)
+
+```bash
+dotnet --version # 9.0.x
+godot --version # 4.6.2.stable.mono.official.*
+which godot-xvfb # /usr/local/bin/godot-xvfb (auto xvfb-run wrapper)
+```
+
+### Piege permissions `.godot/`
+
+Si une etape precedente (build Docker, editeur…) a laisse `.godot/` detenu par
+`root`, le build dotnet echoue avec `MSB3374: Access to the path '...Up2Date'
+is denied`. Les perms sont a 777 -> **supprimer le cache suffit**, pas besoin de
+`sudo` :
+
+```bash
+rm -rf /workspace/.godot
+dotnet build Chessistics.csproj
+```
+
+### Recette pour verifier que Claude peut jouer tout seul
+
+```bash
+dotnet build Chessistics.csproj # doit etre vert
+python3 tools/automation/smoke.py # charge mission 1, screenshots, determinisme
+ls .automation_runs/smoke/screens/ # PNG non-noirs
+```
+
+Si `smoke.py` passe, tout le pipeline marche : Godot boot -> IPC inbox/outbox
+-> screenshots lisibles via l'outil `Read`.
+
+### Driver le jeu en Python depuis Claude
+
+```python
+import sys, time
+sys.path.insert(0, '/workspace')
+from tools.automation.harness import Harness
+
+with Harness.launch(run_name="claude_drive") as h:
+ h.load_mission("campaign_01", 0)
+ s = h.state() # dict: phase, turn, width, height,
+ # grid, productions, demands,
+ # transformers, pieces, remainingStock
+ h.screenshot("01_loaded")
+ h.place("Pawn", (0, 0), (0, 1))
+ h.screenshot("02_placed")
+ h.set_speed(0.05); h.play()
+ time.sleep(2); h.pause()
+ h.screenshot("03_after_play")
+```
+
+Les PNG atterrissent dans `/workspace/.automation_runs//screens/`.
+Claude les lit directement via `Read` (multimodal).
+
+### Boucle typique de validation visuelle
+
+1. `h.load_mission("campaign_01", N)` -> `h.screenshot(f"m{N}_start")` -> `Read`
+2. Verifier sur le PNG : titre, bandeau flavor, board, panneau OBJECTIFS,
+ compteurs PIECES.
+3. Poser des pieces via `h.place(...)`, relire l'etat (`remainingStock`
+ diminue, `pieces` grandit).
+4. `h.play()` + `sleep` + `h.pause()` (ou `h.step()` en boucle), screenshot
+ a chaque palier.
+5. Boucler jusqu'a `state()["phase"] == "MissionComplete"` ou un objectif
+ `demands[i].satisfied == True`.
+
+### Details qui surprennent
+
+- Le snapshot `state()` expose `width`/`height` au niveau racine (pas
+ `board.width`).
+- `remainingStock` est un dict `{"Pawn": 4, ...}` ; verifier qu'il decremente
+ apres un `place()` confirme que la commande a bien ete appliquee.
+- Les logs Godot affichent des warnings ALSA (pas de carte son) et V-Sync —
+ inoffensifs en headless, les filtrer avant d'afficher a l'utilisateur.
+- Le firewall du container bloque tout sauf l'allowlist ; le runtime Godot
+ n'a besoin d'aucun reseau, donc aucun probleme.