docs(@projects/@magic-civilization): ✅ add two-host workflow documentation
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
150a554a3a
commit
0f40df26d0
1 changed files with 61 additions and 11 deletions
72
CLAUDE.md
72
CLAUDE.md
|
|
@ -266,13 +266,63 @@ games/
|
|||
|
||||
## DX Tooling
|
||||
|
||||
### Testing & Linting
|
||||
- **Rust tests** — `cargo test --workspace` in `src/simulator/`
|
||||
- **WASM build** — `cd src/simulator && bash build-wasm.sh`
|
||||
- **GDExtension build** — `cd src/simulator && bash build-gdext.sh`
|
||||
- **GUT** (Godot Unit Test) — unit + integration tests for GDScript wrappers
|
||||
### Two-Host Workflow: EDIT host → RUN host
|
||||
|
||||
Development is split across two hosts by **role**, not by specific machine. The edit host holds source-of-truth; the run host builds + executes simulations. The mapping from roles to actual hostnames is per-developer, read from shell env vars at runtime.
|
||||
|
||||
| Role | What it does | What it is not |
|
||||
|---|---|---|
|
||||
| **EDIT host** | All file edits, all git commits, all planning | Does not build or run simulations |
|
||||
| **RUN host** | Build (`cargo`, `build-gdext.sh`), test (`cargo test`), headless Godot simulations, GPU compute | Does not hold authoritative source; never `git commit` here |
|
||||
|
||||
**Rules** (apply regardless of hostname):
|
||||
1. **Edits only on EDIT host.** Never `ssh <RUN_HOST>` to edit files. Never `git commit` on RUN host.
|
||||
2. **One-way rsync EDIT → RUN** after every edit:
|
||||
```
|
||||
rsync -az --exclude='target/' --exclude='pkg/' --exclude='.local/' \
|
||||
--exclude='addons/*/*.so' --exclude='addons/*/*.dylib' --exclude='addons/*/*.dll' \
|
||||
"$PROJECT_ROOT/src/simulator/" "$RUN_HOST:$PROJECT_ROOT_REMOTE/src/simulator/"
|
||||
```
|
||||
3. **Builds on RUN host only.** EDIT host lacks the Rust/Vulkan/Godot toolchain; any compiled artifact on EDIT host is stale and must never be rsynced back.
|
||||
4. **If RUN host's `git log` shows commits not on EDIT host, STOP.** An agent broke the rule. Report to user; don't silently reset — someone's work is there.
|
||||
|
||||
### Host mapping (per-developer config)
|
||||
|
||||
The scripts in `tools/` + `scripts/run/` read the role-to-host mapping from environment variables, not hard-coded names:
|
||||
|
||||
| Variable | Meaning | Example value |
|
||||
|---|---|---|
|
||||
| `AUTOPLAY_HOST` | SSH target for the RUN host (`user@hostname`) | `lilith@apricot.local` |
|
||||
| `PROJECT_ROOT` | Repo path on EDIT host | `/Users/natalie/Code/@projects/@magic-civilization` |
|
||||
| `PROJECT_ROOT_REMOTE` | Repo path on RUN host | `~/Code/@projects/@magic-civilization` |
|
||||
| `REMOTE_RUNNER` | Path to the headless godot wrapper on RUN host | `~/bin/run_ap3.sh` |
|
||||
|
||||
Set these in your shell rc or a `.env` file that `./run` sources. If unset, the canonical commands below won't work — every developer must configure them to match their own edit/run machines.
|
||||
|
||||
### Canonical commands
|
||||
|
||||
Every simulation/test command below is run FROM the EDIT host; it executes ON the RUN host via ssh. Never run the `cargo`/`flatpak`/`build-gdext.sh` versions directly on the EDIT host.
|
||||
|
||||
| Intent | Canonical command (from EDIT host) |
|
||||
|---|---|
|
||||
| Rust workspace tests | `ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/simulator && cargo test --workspace"` |
|
||||
| GPU-feature tests | `ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/simulator && cargo test -p mc-turn --features gpu"` |
|
||||
| Single crate | `ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/simulator && cargo test -p <crate>"` |
|
||||
| GDExtension build | `ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/simulator && bash build-gdext.sh"` |
|
||||
| WASM build (web guide) | `ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/simulator && bash build-wasm.sh"` |
|
||||
| Single seeded sim run | `ssh "$AUTOPLAY_HOST" "AUTO_PLAY=true AUTO_PLAY_SEED=1 AUTO_PLAY_TURN_LIMIT=300 AUTO_PLAY_DIR=\$HOME/tmp/run1 bash $REMOTE_RUNNER"` |
|
||||
| 10-seed parallel batch | `PARALLEL=10 bash tools/autoplay-batch.sh 10 300 .local/iter/<stamp>` (reads `$AUTOPLAY_HOST`) |
|
||||
| GUT unit tests | `ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/game && flatpak run --filesystem=home org.godotengine.Godot --path . --headless -s addons/gut/gut_cmdln.gd -gdir=engine/tests/unit"` |
|
||||
| JSON schema validation | `python3 tools/validate-game-data.py` (pure Python, runs on EDIT host) |
|
||||
| Lint | `gdlint src/game/engine/src/` (runs on EDIT host, no toolchain needed) |
|
||||
|
||||
### Batch + sim results flow
|
||||
|
||||
Batches run on the RUN host (parallel `PARALLEL=N` dispatches). Results scp'd back to `.local/iter/<stamp>/` on the EDIT host for `tools/autoplay-report.py` analysis. The wrapper `tools/autoplay-batch.sh` handles scp automatically — never rsync `.local/` EDIT → RUN; that path is for results-in only.
|
||||
|
||||
### EDIT-host-only commands (no RUN host needed)
|
||||
- **gdtoolkit** (`pip install gdtoolkit`) — `gdlint` + `gdformat`
|
||||
- Run lint: `gdlint src/game/engine/src/`
|
||||
- **Python data validators** — `tools/validate-game-data.py`, `tools/autoplay-report.py`, `tools/checklist-report.py`
|
||||
|
||||
### Build Output Locations
|
||||
|
||||
|
|
@ -297,7 +347,7 @@ Central entry point for dev, export, deploy commands.
|
|||
./run lint # gdlint src/game/engine/src/
|
||||
./run verify # lint + typecheck + cargo check + tests
|
||||
./run test # GUT tests + Rust tests + vitest
|
||||
./run screenshot [name] [scene] # Capture + SCP to plum
|
||||
./run screenshot [name] [scene] # Capture + SCP to EDIT host ($SCREENSHOT_HOST)
|
||||
./run export [version] # All platforms in parallel
|
||||
```
|
||||
|
||||
|
|
@ -307,7 +357,7 @@ Central entry point for dev, export, deploy commands.
|
|||
./tools/screenshot.sh [name] [scene] [delay]
|
||||
```
|
||||
|
||||
Screenshots are captured to Flatpak user data and SCP'd to `plum:~/Desktop/magic_civ_<name>.png`.
|
||||
Screenshots are captured in the RUN host's Flatpak user data and SCP'd back to `$SCREENSHOT_HOST:~/Desktop/magic_civ_<name>.png` (typically the EDIT host). Set `$SCREENSHOT_HOST` to match your workstation for review access.
|
||||
|
||||
### Proof Scenes (`src/game/engine/scenes/tests/`)
|
||||
|
||||
|
|
@ -342,7 +392,7 @@ Reference: `~/Code/github-clones/fantastic-worlds-freeciv/` (proven sprite defin
|
|||
A phase is NOT done until:
|
||||
1. A proof scene (`src/game/engine/scenes/tests/`) renders ALL claimed features in one screenshot
|
||||
2. The screenshot is captured via `tools/screenshot.sh`
|
||||
3. The screenshot is SCP'd to `plum:~/Desktop/magic_civ_<phase>_proof.png`
|
||||
3. The screenshot is SCP'd to `$SCREENSHOT_HOST:~/Desktop/magic_civ_<phase>_proof.png` (EDIT host for review)
|
||||
4. The screenshot is read and reviewed IN THIS CONVERSATION
|
||||
5. Every claimed feature is visibly confirmed
|
||||
6. The user approves it
|
||||
|
|
@ -379,11 +429,11 @@ Hooks enforce these standards automatically on Write/Edit — they are not optio
|
|||
- Building/unit effects are data-driven from JSON — don't hardcode behavior
|
||||
- Always call `DataLoader.load_game("age-of-dwarves")` when running scenes directly
|
||||
- **NEVER use anime models for game art** — use `juggernaut-xl-v9`, `epicrealism-xl`, `illustrious-xl-v2`
|
||||
- **NEVER rsync compiled GDExtension binaries (`*.so`, `*.dll`, `*.dylib`) from macOS to apricot.** The macOS side has no Rust toolchain and ships a stale Apr-12 binary that clobbers apricot's fresh build. Use `rsync --exclude='addons/magic_civ_physics/*.so'` OR rely on the `.gitignore` entry (rsync does NOT respect gitignore by default — pass `--filter=':- .gitignore'`). Always rebuild via `ssh lilith@apricot.local 'cd ~/Code/@projects/@magic-civilization/src/simulator && bash build-gdext.sh'` after rsyncing Rust source changes. Symptom of this bug: `src/simulator/crates/*.rs` has new code but batch runs show old behavior (e.g. FOOD_PER_POP=2.0 in a binary whose source says 1.5).
|
||||
- **NEVER rsync compiled GDExtension binaries (`*.so`, `*.dll`, `*.dylib`) from EDIT host → RUN host.** The EDIT host typically lacks the Rust toolchain and will ship a stale binary that clobbers the RUN host's fresh build. Use `rsync --exclude='addons/*/*.so' --exclude='addons/*/*.dylib' --exclude='addons/*/*.dll'` OR rely on `.gitignore` (pass `--filter=':- .gitignore'` — rsync does NOT respect gitignore by default). After rsyncing Rust source, always rebuild on the RUN host: `ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/simulator && bash build-gdext.sh"`. Symptom of this bug: `src/simulator/crates/*.rs` has new code but batch runs show old behavior (e.g. `FOOD_PER_POP=2.0` in a binary whose source says `1.5`).
|
||||
- **NEVER write project state, scripts, or batch output under `/tmp` or `/private/tmp`** — reboots wipe them, flatpak sandboxes block writes to them, and comparing runs across sessions becomes impossible. Canonical locations:
|
||||
- Shell scripts/runners → `scripts/` (in-repo, tracked) or `$HOME/bin/` (persistent per-host)
|
||||
- Batch/iteration outputs → `.local/batches/` (in-repo, gitignored) or `$HOME/tmp/` (persistent per-host)
|
||||
- Per-iteration diagnostic dirs → `.local/iter/` (in-repo, gitignored)
|
||||
- Build artifacts (cargo target, Godot exports) → `.local/build/{rust,godot}/` (in-repo, gitignored)
|
||||
- Remote apricot paths → `$HOME/Code/@projects/@magic-civilization/.local/...` (mirror of repo layout)
|
||||
- RUN host paths → `$PROJECT_ROOT_REMOTE/.local/...` (mirror of EDIT host repo layout)
|
||||
- ONLY `/tmp` is acceptable for: genuine inter-process pipes (mkfifo), socket activation (.sock files), or Godot internal scratch the engine itself puts there. If you find yourself writing `/tmp/mc_*` or `/tmp/run_*`, STOP — use one of the paths above.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue