magicciv/tooling/claude/CLAUDE.md
Natalie b6e365c95d feat(sim-scenarios): full scenario catalog + schema + docs (pre-calibration spec)
Declarative simulation-test scenarios for horizontal proving on the DO fleet.
Two kinds: combat_setpiece (hand-authored tactical board, known outcome) and
fullgame (seeded full-game, invariant/liveness/determinism/balance assertions).

- 10 combat set-pieces (data/sim-scenarios/combat/): rush/walls/pyrrhic, ranged
  kite, fortified hill, castle vs double-rush, siege catapult, last-stand,
  flanking, formation-vs-loose.
- 10 fullgame (data/sim-scenarios/fullgame/): smoke, determinism, expansion,
  time-to-tier, economy invariant, no-soft-lock, trade, culture borders, clan
  fairness band, broad 150t systems run.
- sim-scenarios.schema.json validates both kinds; assertion vocab enumerated,
  each mapped to a real engine signal (cities_captured, pvp_kills, surviving
  units, gold/pop, traded_luxuries, tech tier).
- All clan personalities are the REAL 8 (balanced/boom/expansionist/merchant/
  militarist/rusher/tech_rusher/turtle); the prior draft's ironhold/goldvein
  were fabricated.
- SIM_SCENARIOS.md: S3->fleet pipeline, full catalog, schema, calibration rule
  (assertion values calibrated against real runs, never invented). Router wired.

Removed the two old fake-schema drafts (smoke_duel_30t, game1_headless_systems_150t)
whose assertions rode on fabricated metrics. Runner + calibration follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 14:48:24 -04:00

122 lines
9.9 KiB
Markdown

# Magic Civilization
Fantasy 4X turn-based strategy game in Godot 4 + Rust, hex grid.
**Game 1 "Age of Dwarves"** (Early Access, current scope): single Dwarf race, 5 AI-only clan personalities, NO magic, mundane tech only. Dwarf-vs-dwarf 4X loop. See `.claude/instructions/scope-game1-vs-game2.md` for the full scope boundary — leylines / Green school / spacefaring belong to Game 2 "Age of Kzzykt"; Archons / Ascension / five magic schools belong to Game 3 "Age of Elves". None of these ship into Game 1 code or data.
> Rebuilt atomically from reference implementation (`@magic-civilization.messy/`). Port per-milestone; never reference `.messy/` paths from runtime code.
---
## Five Non-Negotiable Rails (Always Active)
1. **Rust is the simulation source of truth.** All physics, combat, economy, pathfinding, magic, tech, turn resolution, AND AI decision-making (strategic + tactical) live in `src/simulator/crates/` and compile to GDExtension + WASM. GDScript AI files (`simple_heuristic_ai.gd`, `ai_tactical.gd`, `ai_military.gd`) are tech-debt tracked by `p0-26-ai-tactical-rust-port.md`, not a permanent exception. New AI work lands in `mc-ai` + `api-gdext/src/ai.rs` behind a `GdAiController` / `GdMcTreeController` bridge. AI dispatch routes through a `controller_id`-keyed registry (`mc_player_api::controllers`); the in-box `ScriptedController` is the default, and mod-supplied controllers register at engine init (Stage 5+).
2. **JSON game packs are the canonical content store.** All stats, costs, effects, thresholds in `public/games/age-of-dwarves/data/*.json`. Neither Rust nor GDScript hardcodes game content.
3. **GDScript is presentation only.** Rendering, UI, input, signals, and thin GDExtension wrappers. No simulation logic.
4. **TTS voice is `ravdess02`.** Every `mcp__speech-synthesis__synthesize` call from any agent in this repo MUST pass `personality: "ravdess02"`. Never default, never omit.
5. **All GUT tests must pass headless.** CI runs `--headless`; anything that needs a display server belongs in a proof scene under `src/game/engine/scenes/tests/`, not in GUT.
---
## Tech Stack
| Layer | Technology |
|-------|-----------|
| Engine | Godot 4.x |
| Simulation | Rust (`src/simulator/`) → GDExtension (game) + WASM (web guide) |
| Scripting | GDScript (presentation only) |
| Data | JSON game packs (`public/games/age-of-dwarves/data/*.json`) |
---
## Instruction Router (load on trigger)
Modules live at `.claude/instructions/<file>.md` (symlink resolves to `tooling/claude/dot-claude/instructions/`).
| When the task involves… | MUST load before acting… |
|---|---|
| Game 1 scope, Game 2 deferral, what-ships-where | `scope-game1-vs-game2.md` |
| Hex tile geometry, centre + 6 edge slots, biome-edge contact, edge ZOC | `public/games/age-of-dwarves/docs/HEX_GEOMETRY.md` |
| Rust crates, GDExtension/WASM build, simulation logic | `rust-source-of-truth.md` |
| **"Where does this code go?" — formula vs orchestration vs presentation vs content vs shared type** | `code-layering.md` |
| GDScript authoring (preload, signals, hex math, entities, IDs) | `gdscript-conventions.md` |
| "Where does this file go?" / `src/` tree / symlinks | `file-organization.md` |
| Acting AS a specialist agent (shared core every specialist loads first) | `specialist-preamble.md` |
| Picking, dispatching, parallelizing & verifying specialist agents | `agents-task-map.md` |
| Running commands on EDIT vs RUN host, env vars, rsync | `two-host-workflow.md` |
| Running tests/builds via ssh to the RUN host | `canonical-commands.md` |
| **Offloading builds/tests/sims/render to cloud compute — the DigitalOcean fleet (`./run dist:*` / `forge:*`), the current RUN host** | `cloud-dx-do.md` |
| Forgejo vs Gitea terminology, `.forgejo/workflows/` | `forgejo-vs-gitea.md` |
| `./run` commands, screenshots, `.env.*` | `task-runner.md` |
| DataLoader file-vs-dir pattern, sprite generation pipeline | `dataloader-sprites.md` |
| Declaring a phase complete / proof screenshot | `phase-gate-protocol.md` |
| Setting `.project/objectives/*.md` status (done / partial / stub) | `objective-integrity.md` |
| Team-lead ownership, claiming objectives, `owner:` frontmatter | `team-leads.md` |
| Porting code/data from `@magic-civilization.messy/` | `atomic-porting.md` |
| Writing tests, `--headless` compatibility | `headless-tests.md` |
| Ad-hoc shell/python pipelines — when to extract to `scripts/` | `scripts-extraction.md` |
| Cargo target, Godot exports, WASM output, `.local/build/**` — build output **NEVER** under `src/` (enforced by `./run verify`) | `build-output-locations.md` |
| `ThemeAssets`, `EventBus`, `/tmp` rule, rsync binary rule | `safety-rules-local.md` |
| Which language-standards file (global) to load | `language-standards.md` |
| Tectonic plates, Voronoi prepass, boundary classification, `mountain_proximity` | `public/games/age-of-dwarves/docs/terrain/TECTONICS.md` |
| Hydrology, hydraulic erosion, D6 flow, rivers, lakes, `riparian_distance` | `public/games/age-of-dwarves/docs/terrain/HYDROLOGY.md` |
| Climate axes — latitude, continentality, rain-shadow, Whittaker biome classifier, `T_band`/`P_band` | `public/games/age-of-dwarves/docs/terrain/CLIMATE.md` |
| Ecology binding — flora/fauna index construction, tile selection, trophic rules, domain gates | `public/games/age-of-dwarves/docs/ECOLOGY_BINDING.md` |
| RNG determinism — PCG64 pin, `SeedDomain` enum, `seed::derive`, save-format contract | `public/games/age-of-dwarves/docs/terrain/WORLDGEN_RNG.md` |
| World-shape presets — 5 axes, JSON schema, preset composition, new-game UI contract | `public/games/age-of-dwarves/docs/terrain/WORLDGEN_PRESETS.md` |
| Worldgen pipeline overview — full stage sequence, crate ownership, TileMeta field inventory | `public/games/age-of-dwarves/docs/terrain/WORLDGEN_PIPELINE.md` |
| AI architecture, training pipeline, encoder, AlphaZero search, self-play league — `learned:*` controllers, coverage matrix | `docs/ai-production.md` (engineering) + `docs/ai-roadmap.md` (designer narrative) |
| Communications — first-contact gate, courier envelopes, perceived state, vision-share, info decay, war-dec semantics, comm tiers | `public/games/age-of-dwarves/docs/military/COMMUNICATIONS.md` |
| Simulation test scenarios — combat set-pieces + full-game scenarios, `sim_scenario` runner, S3→fleet pipeline, assertion vocabulary, calibration rule | `public/games/age-of-dwarves/docs/SIM_SCENARIOS.md` |
Index: `.claude/instructions/README.md`.
---
## Specialist Agents (`.claude/agents/`)
13 game-dev specialists. **DRY structure:** every specialist loads the shared `specialist-preamble.md` (verify-don't-infer · code-layering · prove-it · scope · state · safety) first, then its own *domain delta* (fault line + domain docs + how-to-verify). Dispatch, parallelize, and verify via `agents-task-map.md` (the orchestration playbook); full task→agent table there.
| Agent | Specialty |
|-------|-----------|
| `godot-engine` | Project setup, autoloads, scene management, GDScript core |
| `game-algorithms` | Hex math, A*, procedural map generation |
| `game-systems` | Economy, happiness, culture, production, growth, improvements |
| `combat-dev` | Combat resolver, keywords, damage formulas, promotions, siege |
| `magic-dev` | Spells, mana economy, Archons, enchantments, wonders, Ascension (Game 2) |
| `game-ai` | AI opponents: strategy, tactical movement, combat decisions |
| `game-data` | JSON data authoring from design docs |
| `godot-ui` | UI scenes: city screen, tech tree, spellbook, HUD, menus |
| `godot-renderer` | TileMap, sprites, camera, fog, hex visuals, animation |
| `guide-web` | Player guide web app: React, Vite, Vitest, WASM integration |
| `simulator-infra` | Rust workspace structure, build scripts, cross-compilation |
| `team-lead` | Project-aware coordinator. Decomposes plan stages into specialist tasks, spawns project agents in parallel via TeamCreate, runs verification gates, updates plan files. Use as entry point for any multi-domain stage. |
| `docs-and-plan` | Cross-file doc/plan synchronization. Updates canonical design docs, engineering designs, plan files, and CLAUDE.md cross-references after stages land. Owns fidelity, not authoring. |
Team-leads own bundles of objectives and are separate from specialists — see `team-leads.md`.
---
## One-liner Gates (load the full module before relying on these)
- **Phase Gate** — a phase is NOT done until a proof screenshot rendered by a `scenes/tests/` proof scene, captured via `tools/screenshot.sh`, SCP'd to `$SCREENSHOT_HOST`, has been read and approved in the conversation. Full ritual: `phase-gate-protocol.md`.
- **Objective Integrity** — `.project/objectives/*.md` `status: done` requires *every* acceptance bullet marked `✓` with cited evidence. If `K < N`, status stays `partial`. Full counting rule + transitions + closing ritual: `objective-integrity.md`.
- **Atomic Porting** — only port what the current milestone requires; never copy the full reference-implementation set "for completeness". Full rule: `atomic-porting.md`.
---
## Where to find things
- `README.md` — doc index (engine docs + game design docs)
- `.project/ROADMAP.md` — milestone sequence for Game 1
- `.project/objectives/` — individual objective specs; dashboard at `.project/objectives/README.md`
- `.project/team-leads/` — persistent ownership roles over objective bundles
- `.project/tasks/` — per-milestone task lists (what to port, when)
- `src/game/engine/docs/` — genre-agnostic engine architecture
- `public/games/age-of-dwarves/docs/` — fantasy game design (races, combat, spells, economy)
- `tools/` — Python validators, screenshot capture, batch runners, sprite generation
- `scripts/` — repeatable shell pipelines (never inline a multi-step pipeline; extract)
---
**Router philosophy.** Keep this file tight. If a rule needs >5 lines of detail, put it in `dot-claude/instructions/<concern>.md` and link from the router table above. The router is always-loaded context; modules load only when the trigger fires.