From dd31de9acbec3c80cb4eb111d636bc3306a1472a Mon Sep 17 00:00:00 2001 From: Natalie Date: Sat, 27 Jun 2026 02:16:39 -0400 Subject: [PATCH] =?UTF-8?q?docs(@projects/@magic-civilization):=20?= =?UTF-8?q?=F0=9F=94=8E=20p3-29=20=E2=80=94=20audit:=20the=20DRY=20fix's?= =?UTF-8?q?=20bulk=20is=20event-surfacing=20for=20UI=20parity,=20not=20the?= =?UTF-8?q?=20call-swap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TurnResult.events_emitted is replay-thin (founded/captured/killed/created); live UI needs city_grew/building_completed/border_expanded/culture_researched/flora_succession. Real p3-29 work: enrich Rust event surface (headless-safe) → surface in dict → GDScript translates → swap+delete → render-proof. Steps 1-2 carry no live-game risk. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../p3-29-rail1-turn-unification.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.project/objectives/p3-29-rail1-turn-unification.md b/.project/objectives/p3-29-rail1-turn-unification.md index 7735957e..41e8a0af 100644 --- a/.project/objectives/p3-29-rail1-turn-unification.md +++ b/.project/objectives/p3-29-rail1-turn-unification.md @@ -47,3 +47,28 @@ emits vs `TurnResult`; (2) wire turn_manager → GdTurnProcessor.step behind the (3) delete the GDScript orchestration; (4) render-proof on apricot/plum. This is the true Rail-1 finish line — bigger than B7 (per-building queues), which becomes moot once the live game runs the Rust turn (which has the model the unified turn will use). + +## De-risking audit (2026-06-27) — the bulk is event-surfacing, not the call-swap + +`GdTurnProcessor::step(GdGameState)` (the bridge) exists and the Rust step computes every +system. The blocker to switching the live game is **UI event parity**: +- `TurnResult.events_emitted: Vec` is REPLAY-focused — only + `CityFounded / CityCaptured / UnitKilled / UnitCreated / GameOver / AmbientEncounterFired`. +- The live GDScript turn emits far richer UI signals inline: `city_grew`, + `city_building_completed`, `city_unit_completed`, `city_border_expanded`, + `culture_researched`, `flora_succession`, `fauna_round_started/ended`, … +- `turn_result_to_dict` surfaces only a thin slice (lair/victory/fauna). + +So the real p3-29 work, in order: +1. **Enrich the Rust turn's event surface** — emit the granular UI events (growth, building/unit + completion, border expansion, culture, flora succession) from the phases that cause them into + `TurnResult` (headless-verifiable, no live-game risk). +2. **Surface them through `turn_result_to_dict`** so GDScript receives them. +3. **`turn_manager` translates `TurnResult` → `EventBus` emits** (GDScript = pure render). +4. **Swap** turn_manager to `GdTurnProcessor.step`; **delete** `turn_processor.gd::_process_*` + + the duplicate `EcologyState.tick`. +5. **Render-proof** on apricot/plum. + +Steps 1-2 are the bulk and are safe (Rust + headless tests, live game untouched). Steps 3-5 are +the live-game swap (needs the render proof). This is why the simulation was buildable headless +this session but the live game still runs GDScript — the event-surface gap was never closed.