diff --git a/.project/FINISH_GAME1_PLAN.md b/.project/FINISH_GAME1_PLAN.md index 12543d0e..52222ba9 100644 --- a/.project/FINISH_GAME1_PLAN.md +++ b/.project/FINISH_GAME1_PLAN.md @@ -47,7 +47,7 @@ Built logic that's dead-ended on a missing thin wrapper, plus cheap unblocks. Rail-1 hygiene: make the existing UI read game state from Rust. **Game renders fine via GDScript today β€” this is post-demo correctness, but required for "complete."** Chain order: | Order | Objective | Detail | |---|---|---| -| 1 | p2-65 | Extract `GameState`+pending-queue β†’ `mc-state` crate (Phase 0c groundwork done; Phase 1+3 per its resume note β€” relocate 6 mc-turn data types, then move game_state.rs behind a `pub use` shim; save round-trip is the gate). | +| 1 | p2-65 | 🟑 **partial (~5/9, green @ `6de3c67a8`)** β€” `mc-state` crate created + `GameState` moved into it behind `pub use` shims (Phases 1/3a/3b landed 2026-06-04; save-format round-trip byte-identical + parity trio green). **Remaining:** Phase 4 (sweep 20+ `mc_turn::game_state` consumer sites + mc-turn internals + the `lib.rs:62` re-export to `mc_state::`, delete shims β€” bullet 6 must hit zero hits), Phase 5 (Cargo downgrades), Phase 7 (p2-55f `SimConfig`). See p2-65 resume note. | | 2 | p2-72a-building-entity-port | Rust surface done (5/11); remove the parallel `npc_buildings` array + spatial index from `game_state.gd`, route 6 readers through `GdGameState`. | | 3 | **p2-72a** (canonical render source) | The **5–8 day milestone**: convert PlayerScript/UnitScript/GameMap fat classes β†’ thin views over `GdGameState`. | | 4 | p2-72b β€” **Path 2** | `Vec>` on `GdGameState` (operator-chosen; blocker cleared). | diff --git a/.project/objectives/p2-65-extract-mc-state-crate.md b/.project/objectives/p2-65-extract-mc-state-crate.md index 916a9316..eea743f2 100644 --- a/.project/objectives/p2-65-extract-mc-state-crate.md +++ b/.project/objectives/p2-65-extract-mc-state-crate.md @@ -2,7 +2,7 @@ id: p2-65 title: "Extract `GameState` and pending-queue data types into a dedicated `mc-state` crate" priority: p2 -status: stub +status: partial scope: game1 category: architecture owner: simulator-infra @@ -469,3 +469,67 @@ with `GameState` in mc-turn; nothing user-visible changes. Zero demo dependency. **Effort:** L β€” 2+ focused sessions (Phase 3a is the long pole: 6 sibling-type relocations, each with audit + green commit, then the mechanical game_state move + 30-site consumer sweep). + +## 2026-06-04 Wave-D session β€” Phases 1 / 3a / 3b LANDED (status stub β†’ partial, Kβ‰ˆ5/9) + +**Green SHA to resume from: `6de3c67a8`** (workspace `--no-run` clean; save-format gate + +parity trio all green β€” evidence below). The `mc-state` crate now EXISTS and `GameState` lives +in it. Acceptance recount: ~5/9 met β€” crate exists (βœ“), data-only modules moved (βœ“ ransom, +capture, combat_event, patrol, game_state), ScoringWeights cycle resolved (βœ“ via Phase-0c +`mc_core::tactical_types` + `mc_core::ScoringWeights`), save-format round-trip byte-identical +(βœ“ proven). **NOT yet done (status stays `partial`, NOT `done`):** bullet 6 (`grep +"mc_turn::game_state\|mc_turn::GameState"` must return ZERO β€” currently 20+ external hits + the +mc-turn internals + the `lib.rs:62` re-export), bullet 5 (consumer Cargo.toml downgrades), +bullet 4 (mc-turn becomes logic-only β€” still carries `mc-ai` dep + the shims), bullet 9 (p2-55f +`SimConfig` wiring). The shims are deliberate and green; mid-sweep is the WORST resume point, so +this session banked the clean fully-shimmed boundary. + +### What landed (commits, in order β€” each green-between) + +| Phase | Commit | What | +|---|---|---| +| 1 + 3a-ransom | `380619f89` | New `mc-state` crate (workspace member; data-shape deps only, NO mc-ai/mc-turn/rayon/GPU β€” verified no cycle: mc-state deliberately omits mc-vision, which deps mc-turn). `RansomOffer`/`RansomQueue` (+mechanics) β†’ `mc_state::ransom`; mc-turn `ransom.rs` = `pub use` shim. | +| 3a-rest | `9745f59e3` | `CapturePosture`+`Default`+`TryFrom<_> for PostureResolution`+`PromptUnresolved` β†’ `mc_state::capture` (the `TryFrom` MUST move with the enum β€” orphan-rule). 6 capture/PvP event structs β†’ `mc_state::combat_event`. `PatrolOrder`+`PatrolMode`+pure self-methods β†’ `mc_state::patrol`. mc-turn keeps `resolve_posture`, `issue/cancel/edit/PatrolError/validate_waypoints/advance_on_turn` (now FREE FNS β€” 3 call sites in action_handlers/mod.rs updated `PatrolOrder::` β†’ `patrol::`), `TurnResult`+Fauna/Pvp/Siege events. | +| 3b | `6de3c67a8` | `git mv game_state.rs β†’ mc-state`; mc-turn `game_state.rs` = `pub use mc_state::game_state::*;` shim. Re-paths: `crate::combat_balance::CombatBalance`β†’`mc_core::CombatBalance` (only non-sibling code ref); broken `[crate::…]` doc-links demoted. `PendingCaptureEvents::drain_into` peeled into mc-turn-local `DrainCaptureEvents` trait (`capture_drain.rs`) β€” it embeds `mc_replay::TurnEvent`+`TurnResult`, un-movable without a cycle; 4 call sites add the trait `use`. | + +### Evidence (apricot, this box = the RUN host; `cargo` runs directly in CWD) + +- `cargo test --workspace --no-run` exit 0 at `6de3c67a8`. +- **Save-format gate (byte-identical proof):** `mc-turn` `serde_roundtrip` 6/6, `full_turn_golden` 3/3. +- **Parity trio:** `mc-turn` lib 234/234 (1 ignored, pre-existing `five_players_overflow`; was 238 + pre-move β€” the 4 game_state unit tests moved WITH the file); `mc-ai` 268/268; `mc-player-api` 126/126. +- `mc-state` lib 12/12 (ransom 2 + capture 2 + combat_event 1 + patrol 3 + the 4 moved game_state tests). + +### Phase 4 path (the next session β€” start here; the load-bearing rule) + +**Phase 4 (bullet 6) β€” retarget ALL `mc_turn::game_state`/`mc_turn::GameState` sites to `mc_state::`, +then DELETE both shims.** This is bigger than the 20-file external grep β€” completeness requires THREE +surfaces, all driven to zero: +1. **External consumers (20+ files):** mc-player-api (src+tests), mc-sim, api-gdext, mc-core/derived_stats, + mc-save, mc-vision, tests/integration. Sweep `use mc_turn::game_state` β†’ `use mc_state::game_state` + and `mc_turn::{GameState,…}` β†’ `mc_state::game_state::{…}`. +2. **mc-turn internals:** `crate::game_state::…` across processor.rs / action_handlers/ β€” keep as + `crate::game_state` ONLY if the shim stays; to delete the shim, retarget these to `mc_state::game_state`. +3. **The `mc-turn/lib.rs:62` re-export** `pub use game_state::{GameState,…}` β€” this is what PRODUCES + `mc_turn::GameState` (bullet 6 also drives THAT to zero). Delete it (or re-point consumers). + Then delete `mc-turn/src/game_state.rs` (shim). + +**THE DEV-DEP RULE (advisor-pinned, prevents the only cycle trap):** any crate that mc-state +*depends on* (mc-core, mc-units, mc-city, mc-combat, mc-trade, mc-tech, mc-culture, mc-civics, +mc-comms, mc-replay, mc-observation) CANNOT take a normal mc-state dep. From the 20, only +`mc-core/src/derived_stats.rs` is in that set, and its `mc_turn::game_state` use is **test-only** +(a normal mc-coreβ†’mc-turn dep is already impossible β€” Cargo rejects it β€” so it's a dev-dep today). +In Phase 4 it becomes a `[dev-dependencies] mc-state` entry (dev-dep cycles are legal). Everything +DOWNSTREAM of mc-state (incl. mc-vision β€” mc-state does NOT dep it) gets a normal dep. + +**Phase 5 (bullet 5):** `cargo tree -p ` each consumer; where its only `mc-turn` use was state +shape, drop `mc-turn` from its Cargo.toml. **Phase 4-bonus (bullet 4):** once internals are off +`crate::game_state`, check whether mc-turn still needs its `mc-ai` dep (Phase-0c left it; may now be +removable). **Phase 7 (bullet 9):** `mc_state::SimConfig` carrying `combat_balance`, wired once at +api-gdext registration β†’ `GdGameState::init` populates `self.inner.combat_balance` (closes p2-55f bullet 2). + +**Gate each phase:** `cargo test --workspace --no-run` + `serde_roundtrip` + `full_turn_golden` + +the mc-turn/mc-ai/mc-player-api parity trio. Do NOT split `game_state.rs` (>500 lines) β€” out of scope, +flagged debt. Pre-existing unrelated failure `mc-turn::abstract_projection:: +five_players_overflow_truncates_to_max_players` (MAX_PLAYERS 4β†’12 staleness) fails on HEAD, touches +none of the moved types β€” NOT a regression.