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.
This commit is contained in:
parent
5146798f5c
commit
3077b2d669
1 changed files with 87 additions and 1 deletions
88
CLAUDE.md
88
CLAUDE.md
|
|
@ -40,7 +40,10 @@ scene, lit des commandes JSON dans `<dir>/inbox/`, ecrit les resultats dans
|
||||||
comportement normal — overhead zero.
|
comportement normal — overhead zero.
|
||||||
|
|
||||||
Cote agent, un wrapper Python stdlib (`tools/automation/harness.py`) expose une API
|
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
|
### Build + utilisation
|
||||||
|
|
||||||
|
|
@ -101,3 +104,86 @@ Cette boucle permet de valider que :
|
||||||
- Les fichiers IPC sont ecrits `.tmp` puis renommes (atomique sur Windows).
|
- 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
|
- 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).
|
> 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/<run_name>/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.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue