From 3077b2d6699ef983a71a71afc2a8445ea9ada0a0 Mon Sep 17 00:00:00 2001 From: Samuel Bouchet Date: Fri, 17 Apr 2026 21:13:05 +0200 Subject: [PATCH] Teach Claude the autonomous devcontainer test recipe in CLAUDE.md Covers toolchain sanity check, the root-owned .godot permissions pitfall, build -> smoke -> Harness loop, Python snippet, visual validation loop, and non-obvious state shape details. Also fixes the stale Windows-only Godot path mention. --- CLAUDE.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) 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.