docs(@projects/@magic-civilization): 📡 p3-29 steps 1-2 done — Rust turn emits + surfaces UI events; swap is render-gated

Step 1 (CityGrew/CityBordersExpanded/FloraSuccession) + step 2 (events in step result dict)
complete + headless-safe. Latent siege-suppress bug fixed (7f4b69eac). Remaining: the live
turn_manager swap (steps 3-5) — render-gated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-27 03:58:32 -04:00
parent a9b92df51b
commit e307db755d
3 changed files with 57 additions and 10 deletions

View file

@ -17,8 +17,8 @@
| **P0** | 44 | 0 | 0 | 0 | 0 | 0 | 44 |
| **P1** | 88 | 0 | 0 | 0 | 0 | 1 | 89 |
| **P2** | 130 | 0 | 0 | 0 | 0 | 1 | 131 |
| **P3 (oos)** | 34 | 0 | 3 | 0 | 0 | 29 | 66 |
| **total** | **296** | **0** | **3** | **0** | **0** | **31** | **330** |
| **P3 (oos)** | 34 | 0 | 6 | 1 | 0 | 29 | 70 |
| **total** | **296** | **0** | **6** | **1** | **0** | **31** | **334** |
</td><td valign='top' style='padding-left:2em'>
@ -26,7 +26,7 @@
| Team Lead | Remaining |
|---|---|
| [warcouncil](../team-leads/warcouncil.md) | 3 |
| [warcouncil](../team-leads/warcouncil.md) | 7 |
</td></tr></table>

View file

@ -4,7 +4,7 @@ title: Rail-1 turn unification — live game calls the Rust turn, delete GDScrip
priority: p3
scope: game1
owner: warcouncil
status: open
status: partial
updated_at: 2026-06-27
---
@ -26,6 +26,13 @@ The bridge already exists: `GdTurnProcessor::step(GdGameState)` (api-gdext/src/l
## Acceptance
**Steps 1-2 COMPLETE (headless-safe, 2026-06-27):** the Rust turn surfaces all granular UI
events (CityGrew 06c6e2547, CityBordersExpanded db808e477, FloraSuccession 7b6d24bde) AND
`GdTurnProcessor.step`'s result dict carries them as `result["events"]` (e165b4e6c, via
`replay::event_to_dict`). Also fixed a latent bug surfaced by the consolidation: B2 healing
healed besieged cities → siege-suppress (de68c9c10). All headless prep for the swap is done.
**Remaining = the live swap (steps 3-5, render-gated):**
- [ ] `turn_manager.gd` runs the turn via `GdTurnProcessor.step(GameState)` instead of the
per-player `proc._process_*` loop.
- [ ] The returned `TurnResult` is rendered to UI (EventBus signals: chronicle, combat log,

View file

@ -1,13 +1,13 @@
{
"generated_at": "2026-06-26T18:57:51Z",
"generated_at": "2026-06-27T07:58:32Z",
"totals": {
"partial": 3,
"missing": 0,
"done": 296,
"in_progress": 0,
"stub": 0,
"partial": 6,
"stub": 1,
"missing": 0,
"oos": 31,
"total": 330
"done": 296,
"total": 334
},
"objectives": [
{
@ -3309,6 +3309,46 @@
"owner": "warcouncil",
"updated_at": "2026-06-26",
"summary": "> **Owner directive (2026-06-26):** the /loop \"continue until Game-1 done\" is **not\n> finished until the SIMULATOR is complete** — the headless Rust sim\n> (`mc-turn::TurnProcessor`, driven via `GdPlayerApi`/`magic_civ_*`) must play a full\n> self-play game with ALL systems, not the reduced subset shipped so far. See\n> [[project_loop_done_means_simulator_complete]].\n\nThe headless `step()` ([processor.rs:392+](../../src/simulator/crates/mc-turn/src/processor.rs))\ncurrently runs: trade (p3-25), economy, city_production (single queue), culture+border\nexpansion, tech/science, fauna encounters, combat/siege, diplomacy. Verified live via\n`magic_civ_view` (e.g. border expansion fired turn 0→1: `owned_tiles [[1,6]]→[[1,6],[0,6]]`).\n\n**Gaps (each verified absent from the headless turn):**"
},
{
"id": "p3-27",
"title": "Biosphere in the headless sim — ecology population + flora succession + marine ecology",
"priority": "p3",
"status": "partial",
"scope": "game1",
"owner": "warcouncil",
"updated_at": "2026-06-26",
"summary": "Split from [[p3-26-complete-headless-simulator]]. The biological simulators exist as full\nRust crates — `mc-ecology` (`EcologyEngine`: per-tile fauna populations, predator/prey,\nevolution) and `mc-flora` (`FloraEngine`: per-tile vegetation + succession) — and the LIVE\ngame ticks them every turn (`EcologyState.tick` + `take_flora_transitions`,\nturn_manager.gd:315). But the **headless `mc-turn` step does NOT tick them** (verified: `0\nhits` for EcologyEngine/FloraEngine/flora in `processor.rs`). Only fauna *encounters*\n(combat) run headless. So the headless sim has no living biosphere for events/economy to\ninteract with."
},
{
"id": "p3-28",
"title": "Modular turn architecture — break dep cycle, phase registry, boot-config DRY",
"priority": "p3",
"status": "partial",
"scope": "game1",
"owner": "warcouncil",
"updated_at": "2026-06-26",
"summary": "The per-subsystem sprawl noticed while porting climate/events/happiness/healing/ecology\nrevealed three SOLID/DRY/DIP debts. \"Foundation first\" tackled the layering + phase pieces."
},
{
"id": "p3-29",
"title": "Rail-1 turn unification — live game calls the Rust turn, delete GDScript orchestration",
"priority": "p3",
"status": "partial",
"scope": "game1",
"owner": "warcouncil",
"updated_at": "2026-06-27",
"summary": "**The DRY / Rail-1 violation (verified 2026-06-27).** There are TWO turn orchestrations:\n- LIVE: `turn_manager.gd` → `turn_processor.gd::_process_*` (GDScript) + `EcologyState.tick` +\n `WorldsimState` — GDScript orchestrating the turn.\n- HEADLESS: `GdPlayerApi` → `mc_turn::TurnProcessor::step` (Rust).\n\nThe system *math* lives once in the Rust crates (DRY). The turn *orchestration* is duplicated —\nand the p3-26/p3-27 work this session added happiness/healing/improvements/recipes/equipment/\necology to `mc-turn` while the live game still runs its GDScript copies (e.g. `EcologyState.tick`\nduplicates the new Rust `ecology_phase`). This session BUILT `mc-turn::step` into the complete\nsingle source of truth; this objective is the capstone that makes it actually single.\n\nThe bridge already exists: `GdTurnProcessor::step(GdGameState)` (api-gdext/src/lib.rs:6354) runs\n`mc_turn::TurnProcessor::step` on the LIVE game's state. The live turn just doesn't call it."
},
{
"id": "p3-30",
"title": "Port wild-creature AI from GDScript to Rust (Rail-1 compliance)",
"priority": "p3",
"status": "stub",
"scope": "game1",
"owner": "warcouncil",
"updated_at": "2026-06-27",
"summary": "**Rail-1 gap surfaced during the p3-29 logic sweep (2026-06-27).** The live turn runs\nwild-creature AI **decision logic in GDScript**: `turn_processor.gd::_process_wild_creatures`\n(line 459) calls `wild_ai.process_wild_turn(game_map)` →\n`src/game/engine/src/modules/ai/wild_creature_ai.gd` (302 LOC — a guard / attack / roam state\nmachine over `owner == -1` units).\n\nThis is sim logic, not presentation, so it violates Rail-1 (\"GDScript is presentation only\";\n\"AI decision-making lives in Rust\"). It is **distinct from [[p0-26-ai-tactical-rust-port]]**,\nwhich ported *player* tactical AI (`simple_heuristic_ai.gd` / `ai_tactical.gd` / `ai_military.gd`)\nand explicitly did not touch wild-creature behaviour. It is also distinct from the fauna\n*population/rendering/stats* objectives (`g2-08`, `p3-12`, `p1-49`, `p2-58a`) — those model\necology; this is the per-creature **combat behaviour AI**.\n\nThe combat *resolution* for wilds already lives in Rust (`mc-combat::wilds`); only the\n*decision* layer (who to attack, when to roam, leash enforcement) is still GDScript."
}
]
}