From 8bf06decf3df8b5ac1d444bab2d00320cdfcd8e9 Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 28 Jun 2026 10:04:40 -0400 Subject: [PATCH] docs(objective): record p3-29 live-swap landed behind RUST_TURN flag (7475daa7) Steps 3-5 now implemented (default OFF): turn_manager runs whole-round GdTurnProcessor.step at round boundary under RUST_TURN=1, events[] -> EventBus. Remaining before done: whole-round render proof (new scene) + delete the gated GDScript orchestration once ON-path parity is proven. Co-Authored-By: Claude Opus 4.8 --- .../p3-29-rail1-turn-unification.md | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/.project/objectives/p3-29-rail1-turn-unification.md b/.project/objectives/p3-29-rail1-turn-unification.md index 51ea8ffd..c8ec2113 100644 --- a/.project/objectives/p3-29-rail1-turn-unification.md +++ b/.project/objectives/p3-29-rail1-turn-unification.md @@ -5,9 +5,25 @@ priority: p3 scope: game1 owner: warcouncil status: partial -updated_at: 2026-06-27 +updated_at: 2026-06-28 --- +## Progress 2026-06-28 — live swap implemented behind `RUST_TURN` flag (7475daa7) + +The render-gated steps 3-5 are now **implemented** (default OFF). `turn_manager.gd::end_turn` runs +the whole-round `GdTurnProcessor.step` on live state at the round boundary under `RUST_TURN=1` +(`_run_rust_round` + `_emit_rust_turn_events`), with the GDScript per-player `_process_*` loop and +the `next_player()` round-end sim glue gated off to avoid double-processing. Default path is +byte-for-byte the existing turn. Verified pre-commit: clean headless project load (no parse/script +errors, autoload registers); all referenced gdext methods/signals confirmed present +(`GdTurnProcessor.{step,load_authored_encounter_rates}`, `GdGameState.sync_{presentation_to_inner, +inner_to_presentation}`, EventBus `{tech,culture}_researched`/`golden_age_{started,ended}`/ +`worldsim_updated`). **Two acceptance items remain before `done`:** (1) the whole-round-`RUST_TURN` +render proof (a new proof scene — the existing 7k proof is the older fauna flag); (2) deleting the +gated GDScript orchestration once the proof confirms ON-path parity. The GDScript path is gated, +not deleted, so the fallback survives until the replacement is proven. Carve-out (worldsim/terraform) +documented + retained. + ## Summary **The DRY / Rail-1 violation (verified 2026-06-27).** There are TWO turn orchestrations: @@ -124,17 +140,38 @@ events (CityGrew 06c6e2547, CityBordersExpanded db808e477, FloraSuccession 7b6d2 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, - flora succession, notifications) — GDScript translates the result, emits no sim logic. -- [ ] `turn_processor.gd::_process_*` orchestration + the duplicate `EcologyState.tick` deleted - (or reduced to UI-only translation). +- [x] `turn_manager.gd` runs the turn via `GdTurnProcessor.step(GameState)` instead of the + per-player `proc._process_*` loop. **DONE behind a flag (7475daa7, 2026-06-28).** Under + `RUST_TURN=1`, `end_turn()` runs `_run_rust_round()` at the round boundary + (`is_last_in_round()`): `sync_presentation_to_inner` → `GdTurnProcessor.step` (whole round, all + players + sim glue, `state.turn` increment) → `sync_inner_to_presentation`; the per-player + `_process_*` loop and the `next_player()` round-end ecology/climate/wild/diplomacy passes are + gated OFF to avoid double-processing. +- [x] The returned `TurnResult` is rendered to UI (EventBus signals). **DONE (7475daa7):** + `_emit_rust_turn_events` translates `step`'s `events[]` (kind-tagged dicts from + `replay::event_to_dict`) into EventBus signals — `TechResearched`/`CultureResearched`/ + `GoldenAgeStarted`/`GoldenAgeEnded` now; entity-payload kinds (CityGrew/UnitCreated/…) deferred + (need a live entity lookup) — the board is already correct via the synced presentation slots, + so only the per-event signal is deferred. `worldsim_updated` emitted to nudge the fauna overlay. +- [~] `turn_processor.gd::_process_*` orchestration + the duplicate `EcologyState.tick` deleted + (or reduced to UI-only translation). **NOT deleted — GATED instead.** Deliberate: the swap ships + behind a default-OFF flag so both paths coexist for safe rollout + A/B parity verification. The + GDScript orchestration is gated off under `RUST_TURN=1` but not yet removed; deletion follows + once the render proof confirms ON-path parity (else we'd lose the fallback before proving the + replacement). - [ ] WorldsimState/terraform: either ported into `mc-turn` (preferred, completes Rail-1) or - kept as the one remaining GDScript-driven pass with a tracked carve-out. + kept as the one remaining GDScript-driven pass with a tracked carve-out. **Carve-out kept (and + documented in `next_player()`):** world-event dispatch / terraform drain / contamination tick / + `worldsim_updated` render hook stay GDScript-driven — `step` does NOT cover them (p3-26/p3-27 + boundary). The grid is still resolved across turns so this pass keeps running under the flag. - [ ] **Render proof**: a `scenes/tests/` proof scene + screenshot showing the live game plays - a turn correctly through the Rust step (UI parity with the old GDScript turn). -- [ ] GUT green; headless `mc-turn` already proven (it IS the step being adopted). + a turn correctly through the Rust step (UI parity with the old GDScript turn). **STILL OPEN — + the blocker to `done`.** The existing `iter_7k_turn_processor_gated_proof` covers the older + `RUST_FAUNA_ENCOUNTERS` flag, not the whole-round `RUST_TURN` path — a NEW proof scene is needed + that drives `end_turn()` through a full round with `RUST_TURN=1` and shows state advancing + + rendering. DO `dist:render` (software weston/Mesa) is the available render host (apricot/plum). +- [ ] GUT green; headless `mc-turn` already proven (it IS the step being adopted). Pre-commit + check: the flagged change loads clean headless (no parse/script errors, autoload registers). ## Notes