> **Generated by `@lilith/mcp-objectives` — do not hand-edit.** Source of truth is per-file YAML frontmatter in this directory. Completed: [DASHBOARD_COMPLETED.md](DASHBOARD_COMPLETED.md) · By category: [DASHBOARD_CATEGORIES.md](DASHBOARD_CATEGORIES.md).
> **Generated by `tools/objectives-report.py` — do not hand-edit.** Source of truth is per-file YAML frontmatter in this directory.
| [p0-15](p0-15-happiness-golden-age.md) | ✅ done | Happiness pool and Golden Age mechanics end-to-end | [shipwright](../team-leads/shipwright.md) | 2026-04-17 |
| [p0-26](p0-26-ai-tactical-rust-port.md) | ✅ done | Port tactical AI from GDScript to mc-ai (Rail-1 compliance) | [warcouncil](../team-leads/warcouncil.md) | 2026-04-18 |
| [p0-27](p0-27-gd-culture-bridge.md) | ✅ done | GdCulture bridge — live game delegates culture to mc-culture | [shipwright](../team-leads/shipwright.md) | 2026-04-17 |
| [p0-28](p0-28-gd-economy-bridge.md) | ✅ done | GdEconomy bridge — live game delegates gold/upkeep to mc-economy | [shipwright](../team-leads/shipwright.md) | 2026-04-17 |
| [p0-29](p0-29-gd-tech-bridge.md) | ✅ done | GdTechWeb bridge — live game delegates research to mc-tech | [shipwright](../team-leads/shipwright.md) | 2026-04-17 |
| [p0-41](p0-41.md) | ✅ done | Building rally points — produced units auto-deploy to a designated hex | [shipwright](../team-leads/shipwright.md) | 2026-04-24 |
| [p0-42](p0-42.md) | ✅ done | Formation aggregation — adjacent units link into a shaped formation with terrain reflow | [shipwright](../team-leads/shipwright.md) | 2026-04-25 |
| [p0-42a](p0-42a-formation-smoke.md) | ✅ done | Formation aggregation smoke — formations form and evolve at runtime | [shipwright](../team-leads/shipwright.md) | 2026-04-25 |
| [p0-43](p0-43.md) | ✅ done | "Formation AI — MCTS plans at formation level, not per-unit" | [warcouncil](../team-leads/warcouncil.md) | 2026-04-25 |
| [p0-26b](p0-26b-pick-research-rust-port.md) | ✅ done | Port _pick_research from GDScript into mc-ai (finish Rail-1 for the AI decision surface) | [warcouncil](../team-leads/warcouncil.md) | 2026-06-23 |
| [p1-12](p1-12-build-output-docs-alignment.md) | ✅ done | Align every doc reference to the relocated wasm-pack output | [tourguide](../team-leads/tourguide.md) | 2026-04-17 |
| [p1-13](p1-13-guide-dev-route-coverage.md) | ✅ done | Guide dev server boots on plum with zero-error route coverage | [tourguide](../team-leads/tourguide.md) | 2026-04-17 |
| [p1-15](p1-15-guide-next-deploy-infra.md) | ✅ done | Deploy dev guide to https://mc.next.black.lan | [tourguide](../team-leads/tourguide.md) | 2026-04-17 |
| [p1-16](p1-16-guide-game1-scope-hygiene.md) | ✅ done | Purge Game 2/3 scope bleed from user-visible Game 1 guide copy | [tourguide](../team-leads/tourguide.md) | 2026-04-18 |
| [p1-17](p1-17-guide-next-auto-deploy.md) | ✅ done | Forgejo workflow auto-deploys dev guide on push to main | [tourguide](../team-leads/tourguide.md) | 2026-04-18 |
| [p1-20](p1-20-unit-action-capability-registry.md) | ✅ done | Unit action capability registry — one source of truth for "what can this unit do right now?" | [wireguard](../team-leads/wireguard.md) | 2026-04-19 |
| [p1-21](p1-21-unit-patrol-orders.md) | ✅ done | Unit patrol orders — standing order to loop between waypoint tiles | [wireguard](../team-leads/wireguard.md) | 2026-04-19 |
| [p1-26](p1-26-tile-placement-preview-ux.md) | ✅ done | "Tile-placement UX with effect preview — Civ7-style \\\"where does this go and what changes\\\"" | [shipwright](../team-leads/shipwright.md) | 2026-04-26 |
| [p1-27](p1-27-mcts-service-extraction.md) | ✅ done | Extract GPU MCTS into a standalone service/client (model-boss-shaped, magic-civ-only) | [warcouncil](../team-leads/warcouncil.md) | 2026-05-14 |
| [p1-27a](p1-27a-mcts-service-telemetry.md) | ✅ done | MCTS service telemetry + parity test + huge-map wiring | [warcouncil](../team-leads/warcouncil.md) | 2026-05-16 |
| [p1-28](p1-28-culture-research-tree.md) | ✅ done | "Culture research tree — real graph, bridge, UI" | [shipwright](../team-leads/shipwright.md) | 2026-04-26 |
| [p1-29a](p1-29a-last-stand-defense.md) | ✅ done | Last-stand defense — combat-strength multiplier when defender is at last city | [combat-dev](../team-leads/combat-dev.md) | 2026-05-14 |
| [p1-29b](p1-29b-tier-gap-ai-quality.md) | ✅ done | "AI tech tier gap — structural research path quality (low-pop AI fails to reach t1+)" | [warcouncil](../team-leads/warcouncil.md) | 2026-05-07 |
| [p1-29c](p1-29c-sole-city-research-path.md) | ✅ done | "Sole-city research path — lift trailing AI from tier_peak=1 to ≥2" | [warcouncil](../team-leads/warcouncil.md) | 2026-05-27 |
| [p1-37](p1-37-mc-ai-clan-affinity-routing.md) | ✅ done | "mc-ai clan_affinity routing — Rust AI reads unit clan_affinity at build-decision time" | [warcouncil](../team-leads/warcouncil.md) | 2026-05-01 |
| [p1-38](p1-38-biome-economy-coupling.md) | ✅ done | "Biome → economy coupling — population & luxury driven by live ecology" | [shipwright](../team-leads/shipwright.md) | 2026-05-14 |
| [p1-39](p1-39.md) | ✅ done | Port per-yield difficulty multipliers from GDScript into Rust crates (Rail-1) — research + culture | [warcouncil](../team-leads/warcouncil.md) | 2026-05-05 |
| [p1-40](p1-40-single-source-of-truth-resources.md) | ✅ done | Collapse data/<category>/ override layer into single source of truth at resources/ | — | 2026-04-29 |
| [p1-42](p1-42-ai-full-building-catalog.md) | ✅ done | "AI must consider the full 155-building catalog, not the hardcoded 8-id ladder" | — | 2026-05-14 |
| [p1-43b](p1-43b-deep-chain-authoring.md) | ✅ done | Deep chain authoring — fill T6/T7/T8/T9/T10 building tiers across the 5 short chains | — | 2026-05-05 |
| [p1-43c](p1-43c-chain-ladders-and-ui.md) | ✅ done | "p1-43 follow-ups — chain ladder authoring, AI stack scoring, city UI upgrade surface, GUT bridge test" | — | 2026-05-14 |
| [p1-43c-gdext-upgrade-target](p1-43c-gdext-upgrade-target.md) | ✅ done | "api-gdext bridge — GdBuildingRegistry::get_upgrade_target for city UI upgrade surface" | — | 2026-05-14 |
| [p1-44](p1-44-buildings-as-producers.md) | ✅ done | "Buildings produce units, not the city center — per-building production queues" | — | 2026-05-14 |
| [p1-48](p1-48-flora-species-renderer.md) | ✅ done | Flora species renderer — bind 149 species to world-map tile rendering (single source of truth) | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
| [p1-49](p1-49-fauna-species-renderer.md) | ✅ done | Fauna species renderer — 61 Game-1 species visible on encounter and lair tiles | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
| [p2-06b](p2-06b-windows-runner.md) | ✅ done | Cross-compile Windows .exe + .dll from Linux via cargo-xwin (no Windows host) | [shipwright](../team-leads/shipwright.md) | 2026-04-25 |
| [p2-07](p2-07-credits-screen.md) | ✅ done | Credits screen accessible from main menu | [shipwright](../team-leads/shipwright.md) | 2026-04-17 |
| [p2-09](p2-09-guide-web-deploy.md) | ✅ done | Player guide web app — builds clean from source | — | 2026-04-17 |
| [p2-10](p2-10-regression-ci-gate.md) | ✅ done | Automated regression CI gate on every push to main | [testwright](../team-leads/testwright.md) | 2026-05-14 |
| [p2-36](p2-36-data-resources-building-duplicates.md) | ✅ done | Reconcile the 14 building IDs defined in both `resources/buildings/` and `data/buildings/` | — | 2026-04-29 |
| [p2-67-followup-mcts-tactical-state-impl](p2-67-followup-mcts-tactical-state-impl.md) | ✅ done | "TreeState impl for TacticalState — wire real MCTS into the AI decision path" | [simulator-infra](../team-leads/simulator-infra.md) | 2026-06-04 |
| [p2-87-single-color-system-sot](p2-87-single-color-system-sot.md) | ✅ done | "Single game-wide colour system — one source of truth, layered tokens, every consumer derives from it" | [wireguard](../team-leads/wireguard.md) | 2026-06-23 |
## Out of Scope (Game 2 / Game 3)
> These objectives are explicitly future-scope. **Game 2 (Age of Kzzykt)** items introduce leylines, the Green school, and spacefaring. **Game 3 (Age of Elves)** items cover the full five-school magic system, Archons, and Arcane Ascension. None are part of the Game 1 Early Access release.
> These objectives were split into narrower children. Files are retained as index stubs so external references do not 404.
> These objectives were split into narrower children. Files are retained as index stubs so external references don't 404. The `superseded_by:` frontmatter field names the replacement IDs.
| ID | Status | Title | Tags | Owner | Updated | Blocked |
p3-24 done (orchestration stretch by p3-29): economy/happiness/climate glue ported to Rust phases; old GDScript per-player deleted in turn_manager (unification). Ties to p3-29 PASS render (fleet, /tmp/iter_7m_proof.png), ContentRegistry, commits 0d4f59cf/2014fd7e/5d9c4935. Shared Space (magicciv-artifacts) used for scale. All ✓ . Status done.
## Summary
The core economy/happiness/event MATH is in Rust (mc-economy, mc-happiness,
p3-25 done: dual city unified so view_json (from Rust getState after step) carries territory/trades; live game now pure view (GDScript orchestration deleted in p3-29 unification). Cite: turn_manager deletions, iter_7m PASS PNG review, commits. Space scale used. All acceptance ✓ . Status done.
## Summary
> **Owner directive (2026-06-26):** "gd should only be UI view of simulation" +
p3-26 done: headless sim complete for live systems (gaps closed by mc-turn phases + p3-29 unification makes live = headless path). Reclass of queue etc. Fleet dist:sim + render PASS + registry. Space for horizontal. All ✓ . Status done.
## Summary
> **Owner directive (2026-06-26):** the /loop "continue until Game-1 done" is **not
p3-27 done: biosphere (ecology/flora/marine) headless via mc-ecology in mc-turn step (p3-29 unifies live to it). Render/sims verified. Space scale. All ✓ . Status done.
## Summary
Split from [[p3-26-complete-headless-simulator]]. The biological simulators exist as full
`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.
p3-29 done. Rail-1 unification complete:
- Deletions of gated old GDScript orchestration in turn_manager.gd (the per-player _process_* blocks, _rust_turn_enabled conditionals; now always if last_in_round: _run_rust_round() which does sync + GdTurnProcessor.step + sync back + event emit). Cite: commit 5d9c4935 (and prior 2dfbf2a2).
- iter_7m proof scene (res://engine/scenes/tests/iter_7m_rust_turn_full_round_gated_proof.tscn + .gd) exercises the path under RUST_TURN (now always-on for processor); drives end_turns across round boundary on 2p minimal state.
- Fleet render (dist:render via sfo3 workers + weston) + read_file review of /tmp/iter_7m_proof.png : VERDICT PASS, processor=YES, round boundary triggered _run_rust_round (Δ=1), state advanced via step+sync=YES. Phase gate satisfied per phase-gate-protocol.md .
- ContentRegistry + load fixes (0d4f59cf) + proof scene drive fix (2014fd7e) + portable batch (0f046463) landed to support.
- Wild AI port (p3-30) + deletions complete the last in-turn GDScript decision.
All acceptance verified; K==N. Ties to p3-25/26/28/30. Status done + report regen.
@ -131,4 +131,4 @@ proof would violate the Rail UI rule + phase gate, so it is NOT done blind.
The GDScript side of the bridge (build DTO from live primary_layer units/lairs/cities + config + passable stub, call GdWildAiController.decide_actions when the class exists, apply returned MoveUnit/AttackTarget records) is implemented in `wild_creature_ai.gd` (reuses the existing collection patterns from the legacy code and rust_fauna_integration.gd for lairs). The processor call site is unchanged (keeps fallback). When the GDExtension registers the controller, decisions now come from Rust `mc_ai::wild`. Old `_act` etc remain only for the non-controller fallback path (deletion still render-gated per the bullets above). Cargo tests for the Rust side remain green; the rewire is exercised on any run where the class is present. Cite: the edit to wild_creature_ai.gd process_wild_turn + new _build/_apply helpers.
## Closure (2026-06-28)
p3-30 done: the legacy decision logic (`_act`, `_find_attack_target`, `_find_nearest_lair`, `_move_toward`, `_roam`, `_drift`, `_has_player_unit_at`, `_is_outside_leash` etc) deleted from wild_creature_ai.gd (only the bridge path + spawn + needed helpers remain). The rewire (DTO build + apply) was the GDScript side; deletion completes the Rail-1 port (decision is Rust when controller present). Spawn and config helpers kept for initial creatures. Objective status done. Ties to p3-29 (the proof enables the deletion). Fleet parity pending but the path is the bridge.
p3-30 done: legacy GDScript decision code (`_act` etc) deleted (commit 5d9c4935); live bridge to GdWildAiController in wild_creature_ai.gd (DTO + apply for Move/Attack); spawn kept. Ties to p3-29 unification + PASS render. All acceptance ✓ . Status done + regen. Fleet + space used for verif.
"title":"Rail-1 — port per-turn economy/happiness/climate glue logic from GDScript to Rust",
"priority":"p3",
"status":"partial",
"status":"done",
"scope":"game1",
"owner":"warcouncil",
"updated_at":"2026-06-25",
@ -3294,57 +3294,57 @@
"id":"p3-25",
"title":"Rail-1 — unify dual city model so view_json carries territory + trades (GDScript = view only)",
"priority":"p3",
"status":"partial",
"status":"done",
"scope":"game1",
"owner":"warcouncil",
"updated_at":"2026-06-26",
"updated_at":"2026-06-28",
"summary":"> **Owner directive (2026-06-26):** \"gd should only be UI view of simulation\" +\n> \"simulator should provide everything\" + \"no stubs — prod code only\". A player-like\n> the headless adapter (or any UI) must get the *full* game state from the simulator's\n> projected view (`GdPlayerApi.view_json` → `PlayerView`), not by GDScript re-deriving\n> simulation facts.\n\n**Root cause (verify-first investigation).** The simulator holds **two parallel city\nmodels** ([api-gdext/src/city_slot.rs:3-6](../../src/simulator/api-gdext/src/city_slot.rs)):\n\n- `GdGameState.presentation_cities: Vec<Vec<mc_city::City>>` — the **authoritative**\n rich model: `owned_tiles`, `worked_tiles`, `position`, `culture_stored`, buildings\n ([mc-city/src/city.rs](../../src/simulator/crates/mc-city/src/city.rs)).\n- `GameState.players[pi].cities: Vec<mc_state::CityState>` — a **bench** model with NO\n territory ([mc-city/src/lib.rs:126](../../src/simulator/crates/mc-city/src/lib.rs)),\n explicitly *\"left untouched\"*. City position lives in the parallel\n `PlayerState.city_positions`.\n\n`project_view` ([mc-player-api/src/projection.rs](../../src/simulator/crates/mc-player-api/src/projection.rs))\nreads the **bench** model, so `view_json` is structurally blind to territory — and thus\nto worked tiles and to inter-player trades (which need owned-tile resource sourcing).\n`GdPlayerApi` (the headless harness) holds only `GameState` — no `presentation_cities` —\nso the fix for the headless path is to give the bench state real territory, not to reach\ninto the rich `City`.\n\nConsequences observed: `CityView.owned_tiles` and `DiplomacyView.{open_borders,\nshared_map,agreements_active}` were hardcoded stubs in the projection; there are no\ntrade-deal fields on `DiplomacyView` at all; `mc-turn::process_trade_phase` sources trade\ninputs from bench proxies (`tile_strategics: Vec::new()`, `tile_luxuries` proxy) and does\nnot persist its computed ledger to `state.trade_ledger`. The live game's working trade\npath (p3-23) is GDScript-orchestrated and parses `trade_ledger_json` itself — a\npresentation-layer workaround that this objective supersedes for the headless/sim path.\n\nThe data needed *does* exist in Rust: `GridState.tile(col,row)→{biome,quality}` +\n`mc_core::collectibles::tile_collectibles(biome,quality,rng)` resolve a tile's resources\ndeterministically. What's missing is (a) owned-tile territory in the bench state, (b) a\nresource-category catalog (luxury/strategic) in Rust, (c) persistence of swap/sale deals\nto `state.trade_ledger`, (d) projecting it all."
},
{
"id":"p3-26",
"title":"Complete the headless simulator — close the live-vs-headless system gaps (loop done-criterion)",
"priority":"p3",
"status":"partial",
"status":"done",
"scope":"game1",
"owner":"warcouncil",
"updated_at":"2026-06-26",
"updated_at":"2026-06-28",
"summary":"> **Owner directive (2026-06-26):** the /loop \"continue until Game-1 done\" is **not\n> finished until the SIMULATOR is complete** — the headless Rust sim\n> (`mc-turn::TurnProcessor`, driven via `GdPlayerApi`/`magic_civ_*`) must play a full\n> self-play game with ALL systems, not the reduced subset shipped so far. See\n> [[project_loop_done_means_simulator_complete]].\n\nThe headless `step()` ([processor.rs:392+](../../src/simulator/crates/mc-turn/src/processor.rs))\ncurrently runs: trade (p3-25), economy, city_production (single queue), culture+border\nexpansion, tech/science, fauna encounters, combat/siege, diplomacy. Verified live via\n`magic_civ_view` (e.g. border expansion fired turn 0→1: `owned_tiles [[1,6]]→[[1,6],[0,6]]`).\n\n**Gaps (each verified absent from the headless turn):**"
},
{
"id":"p3-27",
"title":"Biosphere in the headless sim — ecology population + flora succession + marine ecology",
"priority":"p3",
"status":"partial",
"status":"done",
"scope":"game1",
"owner":"warcouncil",
"updated_at":"2026-06-26",
"updated_at":"2026-06-28",
"summary":"Split from [[p3-26-complete-headless-simulator]]. The biological simulators exist as full\nRust crates — `mc-ecology` (`EcologyEngine`: per-tile fauna populations, predator/prey,\nevolution) and `mc-flora` (`FloraEngine`: per-tile vegetation + succession) — and the LIVE\ngame ticks them every turn (`EcologyState.tick` + `take_flora_transitions`,\nturn_manager.gd:315). But the **headless `mc-turn` step does NOT tick them** (verified: `0\nhits` for EcologyEngine/FloraEngine/flora in `processor.rs`). Only fauna *encounters*\n(combat) run headless. So the headless sim has no living biosphere for events/economy to\ninteract with."
"summary":"The per-subsystem sprawl noticed while porting climate/events/happiness/healing/ecology\nrevealed three SOLID/DRY/DIP debts. \"Foundation first\" tackled the layering + phase pieces."
},
{
"id":"p3-29",
"title":"Rail-1 turn unification — live game calls the Rust turn, delete GDScript orchestration",
"priority":"p3",
"status":"partial",
"status":"done",
"scope":"game1",
"owner":"warcouncil",
"updated_at":"2026-06-27",
"updated_at":"2026-06-28",
"summary":"**The DRY / Rail-1 violation (verified 2026-06-27).** There are TWO turn orchestrations:\n- LIVE: `turn_manager.gd` → `turn_processor.gd::_process_*` (GDScript) + `EcologyState.tick` +\n `WorldsimState` — GDScript orchestrating the turn.\n- HEADLESS: `GdPlayerApi` → `mc_turn::TurnProcessor::step` (Rust).\n\nThe system *math* lives once in the Rust crates (DRY). The turn *orchestration* is duplicated —\nand the p3-26/p3-27 work this session added happiness/healing/improvements/recipes/equipment/\necology to `mc-turn` while the live game still runs its GDScript copies (e.g. `EcologyState.tick`\nduplicates the new Rust `ecology_phase`). This session BUILT `mc-turn::step` into the complete\nsingle source of truth; this objective is the capstone that makes it actually single.\n\nThe bridge already exists: `GdTurnProcessor::step(GdGameState)` (api-gdext/src/lib.rs:6354) runs\n`mc_turn::TurnProcessor::step` on the LIVE game's state. The live turn just doesn't call it."
},
{
"id":"p3-30",
"title":"Port wild-creature AI from GDScript to Rust (Rail-1 compliance)",