docs(claude): 📝 Update Claude feature documentation in CLAUDE.md

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-30 22:20:29 -07:00
parent d18b4f4144
commit 9a18407a3e

128
CLAUDE.md
View file

@ -11,7 +11,8 @@ Fantasy 4X turn-based strategy game (Civ5 + Master of Magic + Magic: The Gatheri
## Tech Stack
- **Engine:** Godot 4.x
- **Language:** GDScript only (no C#, no GDExtension)
- **Simulation:** Rust (`packages/physics-rs/`) — compiled to GDExtension for the game, WASM for the web guide
- **Scripting:** GDScript — presentation layer only (rendering, UI, input, signals)
- **Data:** JSON game packs (`games/age-of-dwarves/data/*.json`)
- **Architecture:** Genre-agnostic engine with game pack content system
@ -19,7 +20,9 @@ Fantasy 4X turn-based strategy game (Civ5 + Master of Magic + Magic: The Gatheri
The engine is **genre-agnostic**. All game content and display text comes from game packs. The fantasy game "Age of Dwarves" is the default. See `docs/engine/ABSTRACTION.md` (to be written).
**Godot is the primary runtime. GDScript + JSON is the source of truth for everything.** The TypeScript in `packages/` and `guide/` exists only to serve the web guide — it is secondary, generated, and must never be the canonical location for game data or logic. When creating new systems, data structures, or content packages: design for Godot/GDScript first, with JSON data files in the engine tree (`engine/` or `games/`). The TS side consumes those same JSON files through bundler imports. Never create TypeScript-only packages for game content that Godot needs to read.
**Rust is the simulation source of truth.** All game logic (physics, combat, economy, AI, pathfinding, magic, tech, turn resolution) lives in `packages/physics-rs/` and is compiled to two targets: GDExtension for the Godot game, WASM for the web guide. GDScript is the presentation layer — rendering, UI, input, signals, and thin wrappers that delegate to the Rust GDExtension.
**JSON game packs remain the canonical content store.** Stats, costs, effects, thresholds — all in `games/age-of-dwarves/data/*.json`. Neither Rust nor GDScript hardcodes game content.
- UI labels resolve through `ThemeVocabulary.lookup(engine_key)` — never hardcode theme strings
- Sprites resolve through `ThemeAssets.resolve(path)` — never hardcode asset paths
@ -36,50 +39,39 @@ See `README.md` for the full doc index. Docs live in two places:
Build process docs (roadmap, feature gap, task lists) stay in `.project/`.
## Single Source of Truth: GDScript → TypeScript
## Single Source of Truth: Rust → GDExtension + WASM
> **The GDScript climate engine is the source of truth for all simulation logic. The TypeScript in `guide/` is generated output — never edited directly. If the guide gives wrong results, fix the GDScript and re-run the transpiler.**
The climate simulation runs in two runtimes: GDScript (Godot game engine) and TypeScript (web guide). GDScript is written first; TypeScript is auto-generated from it via `lilith-gdscript-transpiler`.
### Architecture
> **The Rust crate `packages/physics-rs/` is the source of truth for all simulation logic. It compiles to two targets. Never duplicate simulation logic in GDScript or TypeScript.**
```
games/age-of-dwarves/data/climate_spec.json ← Canonical thresholds, events, ley rules
↓ read at runtime by both engines
engine/src/modules/climate/climate.gd ← SOURCE OF TRUTH (physics steps)
engine/src/modules/climate/ecological_events.gd ← SOURCE OF TRUTH (stochastic events)
engine/src/modules/climate/anchor_decay.gd ← SOURCE OF TRUTH (ley anchor decay)
engine/src/modules/climate/climate_spec_eval.gd ← SOURCE OF TRUTH (spec condition evaluator)
↓ transpiled by tools/transpile-engine/transpile.py
packages/engine-ts/src/ClimatePhysics.generated.ts ← AUTO-GENERATED, never edit
packages/engine-ts/src/index.ts ← Re-exports for @magic-civ/engine-ts
guide/age-of-dwarves/ ← Consumes @magic-civ/engine-ts (workspace:*)
games/age-of-dwarves/data/climate_spec.json ← Canonical thresholds, events, ley rules (JSON)
↓ read at runtime by Rust
packages/physics-rs/src/ ← SOURCE OF TRUTH (all simulation logic)
├── compiled with --features gdext → engine/addons/magic_civ_physics/*.so/.dll
│ ↓ loaded by Godot
│ engine/src/modules/climate/climate.gd ← thin GDExtension wrapper (no physics logic)
└── compiled with --features wasm → packages/physics-rs/pkg/
↓ imported by web worker
guide/age-of-dwarves/src/simulation/simulation.worker.ts
```
### Transpiler Package
`lilith-gdscript-transpiler` — custom GDScript→TypeScript transpiler.
- **Package**: `@packages/@py/gdscript-transpiler` in this workspace
- **PyPI index**: `https://forge.nasty.sh/api/packages/lilith/pypi/simple/`
- **Invocation**: `uv run tools/transpile-engine/transpile.py` (self-contained inline script — `uv` handles deps automatically, no manual pip install needed)
- **Check mode**: `uv run tools/transpile-engine/transpile.py --check` (exits 1 if generated file is stale vs GDScript source)
- **Sources**: `climate.gd`, `ecological_events.gd`, `anchor_decay.gd`, `climate_spec_eval.gd`
- **Output**: `packages/engine-ts/src/ClimatePhysics.generated.ts`
### Build commands
```bash
uv run tools/transpile-engine/transpile.py # regenerate
uv run tools/transpile-engine/transpile.py --check # CI: exit 1 if stale
# WASM (web guide)
cd packages/physics-rs && bash build-wasm.sh
# GDExtension (Godot game, Linux dev)
cd packages/physics-rs && bash build-gdext.sh
```
### Rules
- **Never edit `ClimatePhysics.generated.ts`** — it's auto-generated from GDScript
- **Never hardcode terrain thresholds** — read from `climate_spec.json`
- **All simulation changes go in GDScript first** — then re-run transpiler
- **Ecological events require a seed** — `process_turn(game_map, turn, seed)` for deterministic PRNG
- **Golden test vectors** verify both engines produce identical output on the same seed
- **All simulation changes go in Rust** (`packages/physics-rs/src/`) — never in GDScript or TypeScript
- **Never hardcode thresholds** — read from `climate_spec.json` and other JSON data files
- **Ecological events require a seed** — deterministic PRNG seeded per-turn
- **Golden test vectors** verify WASM output matches expected results
- **GDScript climate files are thin wrappers** — they call `GdClimatePhysics`, `GdEcologyPhysics`, etc. via GDExtension. No physics logic lives in GDScript.
## Project-Specific Agents
@ -102,16 +94,16 @@ uv run tools/transpile-engine/transpile.py --check # CI: exit 1 if stale
| Task pattern | Agent |
|-------------|-------|
| `project.godot`, autoloads, `SceneManager`, save/load | `godot-engine` |
| Hex coordinates, A*, map generation, tile storage | `game-algorithms` |
| `economy.gd`, `happiness.gd`, `culture.gd`, city production, growth | `game-systems` |
| `combat_resolver.gd`, keywords, flanking, ZOC, promotions | `combat-dev` |
| `spell_system.gd`, `mana_pool.gd`, `archon.gd`, spells, wonders | `magic-dev` |
| `ai_player.gd`, AI decisions, difficulty modifiers | `game-ai` |
| `project.godot`, autoloads, `SceneManager`, save/load, GDExtension setup | `godot-engine` |
| `packages/physics-rs/src/algorithms/`, hex math, A*, map gen, tile storage | `game-algorithms` |
| `packages/physics-rs/src/economy/`, `city/`, `happiness/`, `culture/`, turn sequencing | `game-systems` |
| `packages/physics-rs/src/combat/`, keywords, flanking, ZOC, promotions | `combat-dev` |
| `packages/physics-rs/src/magic/`, spells, mana, Archons, enchantments, Ascension | `magic-dev` |
| `packages/physics-rs/src/ai/`, AI decisions, difficulty modifiers | `game-ai` |
| `*.json` data files, `vocabulary.json`, `game.json` | `game-data` |
| `*.tscn` UI scenes, HUD panels, overlays, menus | `godot-ui` |
| TileMap, sprites, camera, fog, selection highlight, animation | `godot-renderer` |
| `guide/age-of-dwarves/`, `guide/engine/`, React, Vite, climate sim | `guide-web` |
| `guide/age-of-dwarves/`, `guide/engine/`, React, Vite, WASM integration | `guide-web` |
## Conventions
@ -175,25 +167,47 @@ Poly edge: 2 1 0 5 4 3
### File Organization
```
packages/
physics-rs/ — Rust simulation engine (SOURCE OF TRUTH for all game logic)
src/
grid/ — GridState, TileState, hex math
climate/ — ClimatePhysics, EcologyPhysics, atmosphere, spec evaluator
map_gen/ — MapGenerator, terrain, hydrology, wind
algorithms/ — HexUtils, A* variants, LoS, range
combat/ — CombatResolver, keywords, flanking, ZOC, promotions
economy/ — gold, upkeep, yields, improvements
city/ — production queue, growth, buildings
happiness/ — global pool, racial tiers, Golden Ages
culture/ — generation, border expansion
magic/ — mana, spells, archons, enchantments, ascension
tech/ — TechWeb graph
ai/ — strategic AI, tactical, city, magic, wild creatures
turn/ — turn sequencing, victory, era progression
api_gdext.rs — godot-rust class registrations
api_wasm.rs — wasm-bindgen exports
Cargo.toml
build-wasm.sh
build-gdext.sh
engine-ts/ — @magic-civ/engine-ts (types, runner, HexGrid — no generated physics)
engine/
addons/
magic_civ_physics/ — compiled GDExtension .so/.dll/.framework
src/
autoloads/ — singletons (GameState, TurnManager, DataLoader, EventBus, ThemeVocabulary, ThemeAssets)
entities/ — game objects (unit, city, building, improvement, archon)
map/ — tile storage, hex utils, pathfinder, game_map
generation/ — map generator, terrain refiner, hydrology, wind
map/ — tile storage, game_map (hex math delegates to Rust GDExtension)
rendering/ — hex, city, unit, fog, river, road, indicator renderers
core/ — save manager, pending actions, terrain affinity
modules/
combat/ — combat_resolver, keyword_handler
magic/ — spell_system, mana_pool, infusion_system
climate/ — climate, atmosphere, weather, ecological_events
combat/ — thin GDExtension wrappers (no logic)
magic/ — thin GDExtension wrappers (no logic)
climate/ — thin GDExtension wrappers (no logic)
ley/ — ley_network
empire/ — economy, happiness, culture, government
management/ — city_manager, unit_manager, improvement_manager
events/ — natural_events
victory/ — victory_manager, ascension_ritual
ai/ — ai_player, ai_tactical, ai_city, ai_magic_research, ai_military
tech/ — tech_web
empire/ — thin GDExtension wrappers (no logic)
ai/ — thin GDExtension wrappers (no logic)
tech/ — thin GDExtension wrappers (no logic)
victory/ — thin GDExtension wrappers (no logic)
scenes/
main/ — entry point
menus/ — main_menu, game_setup, options, load_game
@ -216,13 +230,17 @@ games/
guide/
engine/ — @magic-civ/guide-engine (shared UI components, types, utils)
age-of-dwarves/ — @magic-civilization/guide-age-of-dwarves (all pages, app shell)
age-of-dwarves/ — @magic-civilization/guide-age-of-dwarves (all pages, app shell)
src/simulation/simulation.worker.ts — imports from @magic-civ/physics-rs (WASM)
```
## DX Tooling
### Testing & Linting
- **GUT** (Godot Unit Test) — unit + integration tests for GDScript
- **Rust tests**`cargo test` in `packages/physics-rs/`
- **WASM build**`cd packages/physics-rs && bash build-wasm.sh`
- **GDExtension build**`cd packages/physics-rs && bash build-gdext.sh`
- **GUT** (Godot Unit Test) — unit + integration tests for GDScript wrappers
- **gdtoolkit** (`pip install gdtoolkit`) — `gdlint` + `gdformat`
- Run tests headless: `godot --headless --script res://addons/gut/gut_cmdln.gd`
- Run lint: `gdlint engine/src/`