magicciv/.project/objectives/g2-07-flora-lifecycle-transitions.md
Natalie a94c0f18e5 feat(@projects/@magic-civilization): add flora transition chronicle events
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 22:55:29 -07:00

9.3 KiB
Raw Permalink Blame History

id title priority status scope updated_at blocked_by
g2-07 Flora succession — wire the existing flora lifecycle engine into the playable turn p1 done game1 2026-06-09
p2-80

Summary

Climate-driven flora succession (forests advancing/retreating, riparian belts dying back) is the most visible expression of the living-world USP. The engine already exists and is tested — the work is integration, persistence, determinism, and presentation, NOT building a consumer.

  • Flora succession engine: mc-flora/src/engine.rs:352 tick_tiers (tier transitions, T9 event gating) + mc-flora/src/dynamics.rs:26 tick_populations.
  • Tier-advancement is already wired into the ecology step: EcologySim::process_step runs run_tier_advancement + run_seed_dispersal each tick (mc-ecology/src/engine.rs:276), and WorldSim::step already calls process_step (mc-worldsim/src/lib.rs:161).

Species data is fully authored: 149/149 flora carry lifecycle.transforms[] (e.g. european_beech.json::lifecycle.transforms[0]climate_field: temperature, climate_op: >=, climate_value: 0.62, climate_sustained_turns: 200). What is missing is that this engine runs only at worldgen / in benches today — it does not advance in the playable game turn (that goes through bare TurnProcessor::step).

This objective lands once mc-worldsim (p2-80) drives the step in the playable game: confirm flora succession ticks per played turn, persists, is deterministic, and is rendered.

Acceptance

  • Flora succession runs each played turn via WorldSim::step — DONE (2026-06-09). Tier advancement is wired into the played turn: WorldSim::stepecology.process_step (engine.rs:347) → run_tier_advancement (engine.rs:428) → tier::tick_tiers_capped. Proven by mc-worldsim::tests::flora_tier_advances_over_played_turns — a hardy Producer-diet flora seeded at tier 1 strictly advances tier over 60 WorldSim::step calls (not worldgen). Verified on apricot (2026-06-09, isolated CARGO_TARGET_DIR): cargo test -p mc-worldsim → 12 passed, 0 failed.
  • Sustained-turns / per-(tile, species) succession state persists — DONE (2026-06-09). EcologyContinuationState.tile_populations clones the full PopulationSlot (incl. tier + stability_ticks) and round-trips losslessly (#[serde(default)] on the new save fields, via p2-80's worldsim_state payload). Proven by mc-worldsim::tests::flora_succession_state_persists — the advanced tier survives a continuation_state() → serialize → deserialize → restore_continuation_state() round-trip. Green on apricot.
  • Succession transitions emit a chronicle event surfaced in the playable game log — DONE (2026-06-09). Authored mc_ecology::FloraTransition { col, row, species_id, from_tier, to_tier }. run_tier_advancement snapshots each slot's tier before tick_tiers_capped and collects the Producer-diet (flora) tier crossings, returned up through process_step (engine.rs). WorldSim::step pushes one ChronicleEntry::WorldEvent { category: "biological", kind: "flora_transition", col, row, severity_milli: to_tier*1000 } per transition (mc-worldsim/src/lib.rs), mirroring dispatch_world_events; the list is deterministically ordered ((col,row,species_id)). Live game path: GdFaunaEcology::tick_populations captures the transitions; take_flora_transitions drains them; ecology_state.gd + turn_manager.gd emit EventBus.flora_succession(turn, transitions) for the chronicle/game-log panel. Verified on apricot (2026-06-09): crate test mc-worldsim::tests::flora_transition_emits_chronicle_event (a flora_transition chronicle entry is produced at the seeded tile with tier-encoded severity when flora advances over played turns) green; GUT test_flora_succession_transition_surfaces_through_bridge (in test_worldsim_playable_path.gd) drives the EXACT live bridge tick_populations + take_flora_transitions calls and confirms a transition surfaces exactly once — green.
  • Existing cargo test -p mc-flora / -p mc-ecology invariants hold — DONE (2026-06-09). Verified on apricot: mc-flora 65 passed / 0 failed; mc-ecology 332+8+6 passed / 0 failed. Transitions remain additive; canopy/undergrowth evolution unchanged.
  • Determinism: same seed → identical succession sequence — DONE (2026-06-09). Proven by mc-worldsim::tests::flora_succession_is_deterministic — two 60-turn runs at the same seed produce an identical per-turn tier sequence (and the sequence shows real advancement, so the test is non-vacuous). Rides the SeedDomain::WorldsimDynamics stream. Green on apricot.
  • Render: succession visible on the world map over N played turns — DONE (2026-06-09). The visible flora the renderer draws (Layer 2 flora-cover: canopy / undergrowth) evolves each played turn via mc-climate::EcologyPhysics::process_step (Climate.process_turn — the live turn loop's flora succession). Proof scene src/game/engine/scenes/tests/flora_succession_proof.{gd,tscn} stands up a REAL worldgen game (production MapGeneratorGameMap, real players) and runs the exact production worldsim turn pair (Climate.process_turn then EcologyState.tick) for 150 played turns, capturing the vegetation layer at turn 5 vs turn 150. Captured + self-reviewed on apricot (weston, seed 5, deterministic): 98 tiles' vegetation rose (mean 0.00047→0.00280, ~6×; max canopy 0.060, undergrowth 0.107); the final frame shows distinct green canopy patches emerging against the brown pioneer background — bare→pioneer→canopy succession visibly advancing. Screenshots: .local/proof/flora_succession_proof_{early_t5,final_t150}_2026-06-09.png.
  • ECOLOGY_BINDING.md "Lifecycle ticks" section documents the playable-turn integration — DONE (2026-06-09). §11b "Flora lifecycle ticks (g2-07)" subsection documents the played-turn tier-advancement path, persistence, determinism, the pinning tests, the chronicle-emission wiring (crate + live bridge), and the render proof.
  • cargo test green, headless GUT green, proof-scene screenshot of succession over N played turns reviewed — DONE (2026-06-09). Verified on apricot: cargo test -p mc-flora -p mc-ecology -p mc-worldsim -p mc-save -p mc-climate -p mc-sim all green; GUT test_worldsim_playable_path.gd 11/11 (incl. the pinned test_worldsim_trajectory_golden_vector — the chronicle change is determinism-transparent — and test_flora_succession_transition_surfaces_through_bridge), test_climate_tile_sync.gd 8/8. Proof-scene screenshot reviewed (see Render bullet).

Verification note (2026-06-09, apricot — CLOSED)

Two-pass close on apricot (current plum working tree, isolated build worktree + CARGO_TARGET_DIR):

Pass 1 (deferred-verification): wired/confirmed the played-turn tier advancement, persistence, and determinism — 3 crate tests authored in crates/mc-worldsim/src/lib.rs (flora_tier_advances_over_played_turns, flora_succession_state_persists, flora_succession_is_deterministic). 5/8 bullets ✓.

Pass 2 (the 2 remaining bullets):

  1. Chronicle event — authored mc_ecology::FloraTransition; run_tier_advancement now collects Producer-diet tier crossings (pre/post snapshot in the parallel pass, sorted (col,row,species_id)), returned through process_step; WorldSim::step pushes a ChronicleEntry::WorldEvent { category:"biological", kind:"flora_transition", … } per transition. Live bridge: GdFaunaEcology::take_flora_transitionsEcologyStateturn_managerEventBus.flora_succession. Tests: crate flora_transition_emits_chronicle_event + GUT test_flora_succession_transition_surfaces_through_bridge (drives the real bridge).
  2. Render proofflora_succession_proof.{gd,tscn} (real worldgen, 150 played turns, weston). Self-reviewed: turn-5 uniform pioneer-brown vs turn-150 differentiated green canopy patches; 98 tiles' vegetation rose. .local/proof/flora_succession_proof_*.png.

Determinism guard: the run_tier_advancement change is purely additive observation (tier snapshot + transition collection); the pinned worldsim golden vector (test_worldsim_trajectory_golden_vector) still passes byte-identically (11/11 GUT).

Result: all 8 acceptance bullets ✓ with cited evidence → status done. No regression: cargo test -p mc-flora -p mc-ecology -p mc-worldsim -p mc-save -p mc-climate -p mc-sim green; GUT worldsim-path 11/11, climate-tile-sync 8/8.

Non-goals

  • Building the succession engine — it exists (mc-flora).
  • Fauna equivalents (g2-08 population, g2-10 migration).
  • Soil-driven growth_rate modulation — couples with g2-06 (the one worldsim engine that is genuinely unbuilt).

Risks

  • Save-format growth: per-(tile, species) succession counter is new persisted state; #[serde(default)].
  • Determinism: succession evaluation must stay order-stable across the api-gdext bridge (p2-80 risk), not just in the crate test.
  • Yield invariants: succession mutates canopy/undergrowth feeding city yield (p0-19 / p1-38); transitions stay additive, golden-vector gated.

Dependencies

  • Blocked-by: p2-80 (mc-worldsim drives the step in the playable game).
  • Couples with: g2-08 (flora habitat drives fauna), g2-09 (tolerance gating of the pool a tile may succeed into), g2-06 (soil → growth rate).