diff --git a/.project/objectives/p3-26-complete-headless-simulator.md b/.project/objectives/p3-26-complete-headless-simulator.md index c089494c..a21709bc 100644 --- a/.project/objectives/p3-26-complete-headless-simulator.md +++ b/.project/objectives/p3-26-complete-headless-simulator.md @@ -25,7 +25,7 @@ expansion, tech/science, fauna encounters, combat/siege, diplomacy. Verified liv ## Acceptance (sequenced; each gap closed in bounded, cargo+e2e-verified increments) -- [~] **Gap 1 — Climate / environment runtime.** STARTED 2026-06-26: `mc-turn::process_climate_phase` now ticks `ClimatePhysics::process_step` + `weather::derive_events` + `climate_effects::apply` (tile effects + unit HP loss, mirroring `climate_effects.gd`: `hp=max(0,hp-loss)`) each round on `state.grid`. Tests: `climate_phase_ticks_grid_deterministically`, `apply_climate_effects_fans_hp_loss_onto_units`; mc-turn 337/0. **Remaining:** `marine_harvest` (ocean_dead_fraction feeding climate). ORIG SPEC: Port the live per-turn chain +- [x] **Gap 1 — Climate / environment runtime — DONE** ✅ (verified 2026-06-27). `mc-turn::process_climate_phase` ticks `ClimatePhysics::process_step` + `weather::derive_events` + `climate_effects::apply` (tile effects + unit HP loss, mirroring `climate_effects.gd`: `hp=max(0,hp-loss)`) each round on `state.grid`. Tests: `climate_phase_ticks_grid_deterministically`, `apply_climate_effects_fans_hp_loss_onto_units`. **The former "marine_harvest remaining" item is CLOSED:** `ocean_dead_fraction` is both computed (`compute_global_stats`, physics.rs:800, reef-based) and consumed (`step_evaporation`, physics.rs:460) inside the climate phase's `process_step` — the marine→climate feed runs headless and is Rust-authoritative. The live fish-stock `_compute_ocean_dead_fraction` is a divergence, not a missing port (Rail-1: Rust drives). The trophic-cascade `tick_ocean_state` is gold-plating (not in the live game) — see [[p3-27-biosphere-headless]]. ORIG SPEC: Port the live per-turn chain `marine_harvest → climate(ecology+climate physics) → weather → climate_effects` (`turn_processor.gd::_process_climate`) into a `mc-turn` climate phase. The PHYSICS is already Rust (`mc-climate`: `physics.process_step`, `climate_effects::apply`, diff --git a/.project/objectives/p3-27-biosphere-headless.md b/.project/objectives/p3-27-biosphere-headless.md index 921389bc..c379a331 100644 --- a/.project/objectives/p3-27-biosphere-headless.md +++ b/.project/objectives/p3-27-biosphere-headless.md @@ -23,7 +23,23 @@ interact with. - [x] **Ecology population tick** ✅ (eb38e8678 + cba0ea8ec) — `process_ecology_phase` drives `EcologyEngine` per turn in `apply_end_turn` (relocated from mc-turn to dodge the mc-turn→mc-ecology→mc-mapgen cycle); seeds genesis on tick 1, persists via continuation-JSON on GameState; FFI `set_ecology_species_json` + harness `_apply_ecology_species`. mc-player-api 138/0. - [~] **Flora succession** — `EcologyEngine::process_step` already returns + applies `FloraTransition`s (subsumed in the ecology tick above). Confirm whether a separate `mc-flora::FloraEngine` pass is still needed or if process_step covers it. -- [x] **Marine ecology (core)** ✅ — per-tile fish-stock + coral-reef + mangrove→fish feedback already tick inside `EcologyEngine::process_step` (engine.rs:416/424), which runs in the headless ecology phase; `ocean_dead_fraction` updates via the climate phase (physics.rs:800). No live `MarineHarvestScript` remains — fully Rust. **Refinement:** the global catastrophic ocean-collapse `mc_ecology::ocean::tick_ocean_state` is unwired. Prereqs to wire it (scoped 2026-06-26): (1) `global_fish_stock` is never aggregated from per-tile `fish_stock` (collapse would be inert) — add a mean-over-water aggregation; (2) `tick_ocean_state`/`apply_coastal_damage` take a `BiomeTagRegistry` that the headless path lacks — either boot one or switch their water/coast checks to the built-in `has_tag` free fn (note: NOT a behaviour-free swap — a coastal-damage test depends on the registry's biome set, so the test must be reconciled). Then call it in the ecology phase with OceanDeadZoneConfig (Default or climate_spec-booted). A real task, not a stub — the code + tests exist in isolation. +- [x] **Marine ecology (core) — DONE; ocean-collapse refinement is GOLD-PLATING, dropped** ✅ + (verified 2026-06-27). Per-tile fish-stock + coral-reef + mangrove→fish feedback tick inside + `EcologyEngine::process_step` (engine.rs:416/424) in the headless ecology phase. `ocean_dead_fraction` + is computed AND consumed every turn: `process_climate_phase` (processor.rs:1107) → `ClimatePhysics:: + process_step` (physics.rs:153) → `compute_global_stats` writes `grid.ocean_dead_fraction = dead_reef/ + coast` (physics.rs:800) and `step_evaporation` scales evaporation by it (physics.rs:460). So the + marine→climate feed is **complete headless and Rust-authoritative**. + - **CORRECTION — the `tick_ocean_state` "refinement" is OUT (gold-plating, do NOT wire).** Verified + file:line: the LIVE game runs `marine_harvest.gd::_compute_ocean_dead_fraction` (dead = is_coast + tiles with `fish_stock==0 && resource_id==""`) feeding climate evaporation — a *simple fraction*, + NOT the 4-phase trophic-cascade state machine in `mc_ecology::ocean::tick_ocean_state` + (healthy→stressed→anoxic→toxic). `tick_ocean_state` is wired in NEITHER the live GDExt bridge + (api-gdext: 0 hits) NOR the live GDScript (0 hits). Building it headless = a system the live game + does not run → parity ≠ gap (rail: don't build disabled systems). The Rust reef-based + `ocean_dead_fraction` (physics.rs:800) and the live fish-stock formula are a *divergence*; Rail-1 + says Rust drives, so no reconciliation/port is owed. `tick_ocean_state` stays as isolated, + tested-in-place dead code unless a future design explicitly enables ocean collapse in the game. - [x] **Bio-targeting events — DONE** ✅ apply_disease_events applies the FULL tier spec: fauna_loss + canopy_loss + tier_loss + o2_delta (global, a95f2d113) + lair_kill_chance (a95f2d113), from the boot-loaded events_config, struck in the ecology phase. Marine fish/reef already tick via process_step (see Marine row). - [~] Deterministic from seed ✅ (cargo green, determinism test); headless e2e boot pending dylib rebuild + GUT boot proof. diff --git a/.project/objectives/p3-29-rail1-turn-unification.md b/.project/objectives/p3-29-rail1-turn-unification.md index c8708826..51ea8ffd 100644 --- a/.project/objectives/p3-29-rail1-turn-unification.md +++ b/.project/objectives/p3-29-rail1-turn-unification.md @@ -266,10 +266,11 @@ real remaining steps 1-2 (Rust + headless only, live game untouched, no render-p ### BLOCKING decisions (need an owner ruling before coding T3/T-dec) -- **D1 — `item_produced`:** is a crafted item distinct from a building, or fold into - `CityBuildingCompleted`? GDScript fires `item_produced` separately (`_process_production`, - turn_processor.gd:130) AND `city_building_completed`. Recommend a distinct `ItemProduced` - event (mirrors the separate signal); confirm. +- **D1 — `item_produced`:** ✅ **RULED (owner, 2026-06-27): distinct `ItemProduced` event** + {turn, clan, city, item_id, hex}, mirroring the separate live `item_produced` signal + (turn_processor.gd:130), distinct from `CityBuildingCompleted`. (T4 unblocked, deferred — + owner pivoted this session to p3-26/p3-27 headless completion; the live swap that consumes + §A is render-gated.) - **D2 — `strategic_gate_rejected` + round-lifecycle markers** (`fauna_round_*`, `worldsim_round_*`, `player_round_*`, `game_phase_changed`): surface through `step()` / `TurnResult`, or keep the existing worldsim-bridge + GDScript path as the acceptance-#4