diff --git a/.project/objectives/p0-26b-pick-research-rust-port.md b/.project/objectives/p0-26b-pick-research-rust-port.md new file mode 100644 index 00000000..05e956d7 --- /dev/null +++ b/.project/objectives/p0-26b-pick-research-rust-port.md @@ -0,0 +1,65 @@ +--- +id: p0-26b +title: "Port _pick_research from GDScript into mc-ai (finish Rail-1 for the AI decision surface)" +priority: p1 +status: missing +scope: game1 +owner: warcouncil +tags: [ai, rust, rail-1, tech-debt] +updated_at: 2026-05-27 +relates_to: [p0-26, p0-29, p1-29c] +blocked_by: [] +--- + +## Context + +p0-26 ported the tactical AI (movement, production, combat, settle, promotion) +from GDScript into `mc-ai` and is marked done. But **research selection is the +one AI decision still made in inline GDScript**: `auto_play.gd::_pick_research` +(line ~1216) scores techs with hand-written per-pillar personality multipliers, +and its own comment admits the gap: + +> "Research scoring belongs in mc-ai::ScoringEvaluator::pick_tech (Rail-1). +> This test-harness path reads axes inline; wiring through GdAiController +> requires the tactical bridge to emit research actions (tracked in p0-26)." + +This violates Rail 1 (Rust is the simulation source of truth for AI decisions) +and creates a concrete correctness hazard: `tier_peak` — the metric the entire +p1-29 cluster optimises — is *defined by researched techs* (`auto_play.gd:2585` += max era across `researched_techs`). The lever that moves the headline AI gate +is currently chosen by GDScript the Rust controller never sees. p1-29c's +"Research-priority uplift" lives in `mc-ai::policy.rs` but the live research +decision is made elsewhere, so the two cannot be reconciled. + +## Acceptance + +- [ ] **`pick_tech` in mc-ai.** Implement research scoring in + `mc-ai::ScoringEvaluator` (or the tactical scorer), data-driven from tech JSON + + personality `strategic_axes` (Rail 2), reproducing the current per-pillar + multiplier intent (military/metallurgy/agriculture/civics/scholarship/ecology) + plus the p1-29 catch-up dynamics. +- [ ] **Bridge emits a research action.** `GdAiController` / the tactical bridge + surfaces the chosen tech as an action the engine applies, replacing the + inline `_pick_research` call in `auto_play.gd` and the live `turn_processor` + path. +- [ ] **Delete the GDScript path.** Remove `_pick_research` and its inline + helpers once the Rust path is live — no dual source, no `_unused` shim + (Zero Tech Debt rail). +- [ ] **Parity test.** A headless test pins that Rust `pick_tech` reproduces the + expected tech ordering for at least the 5 clan personalities (analogous to the + existing `clan_policy_priors` / production-axis tests). +- [ ] **No regression batch.** A 10-seed T300 autoplay batch shows tier_peak + distributions unchanged-or-better vs the pre-port baseline (research ordering + must not silently regress clan divergence). + +## Non-goals + +- Changing research *balance* (costs, pacing) — owned by p0-07. +- The catch-up science multipliers themselves — those are p1-29/p1-29b; this is + a port of *where the decision is made*, not *what it decides*. + +## Notes + +- Prerequisite-quality enabler for p1-29g's clean attribution: once research is + in the Rust controller, the action-priority uplift and the actual research + decision share one source and can be measured together.