docs(simulation-report): 📝 Update technical debt audit report documentation with latest findings and fixes
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
66fb634d58
commit
5ab6167857
1 changed files with 122 additions and 0 deletions
122
.project/simulation-report/tech-debt-audit.md
Normal file
122
.project/simulation-report/tech-debt-audit.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Architecture Tech Debt Audit — GDScript Simulation Logic
|
||||
|
||||
**Date:** 2026-04-09 | **Auditor:** code-explorer agent | **Scope:** Full engine/src/ scan
|
||||
|
||||
> **Architecture rule:** "Rust is the simulation source of truth. GDScript is the presentation layer — rendering, UI, input, signals, and thin wrappers that delegate to Rust GDExtension."
|
||||
|
||||
---
|
||||
|
||||
## Critical Findings (ranked by impact)
|
||||
|
||||
### 1. CRITICAL — Ecology simulation runs TWICE per turn
|
||||
|
||||
- `src/game/engine/src/modules/climate/climate.gd:83` → Rust `GdEcologyPhysics::process_step` (correct)
|
||||
- `src/game/engine/src/modules/management/turn_processor.gd:403-406` → GDScript `EcosystemOrchestrator::process_turn`
|
||||
|
||||
Same tile data, two mutation passes per turn. Flora canopy/undergrowth accumulates at ~2× intended rate. The GDScript ecology (ecosystem.gd ~308 lines + flora.gd ~405 lines) was originally a "transpiler target" — the transpiler was deleted, the functions were never ported, and they're now the live simulation alongside the Rust pass.
|
||||
|
||||
**Fix:** Remove the GDScript ecology pass. `GdEcologyPhysics::process_step` is the canonical Rust path.
|
||||
|
||||
### 2. CRITICAL — 4 stub modules called by turn_processor.gd produce SILENT no-ops
|
||||
|
||||
| Call site | Stub | Method called | Effect |
|
||||
|---|---|---|---|
|
||||
| `turn_processor.gd:268` | `culture.gd` (2 lines) | `CultureScript.process_turn(city, game_map, player)` | Culture = always 0, no border expansion |
|
||||
| `turn_processor.gd:273` | `happiness.gd` (2 lines) | `HappinessScript.process_turn(player, game_map)` | Happiness never updates |
|
||||
| `turn_processor.gd:386` | `government.gd` (2 lines) | `GovernmentScript.process_anarchy(player)` | Anarchy never decays |
|
||||
| `turn_processor.gd:299,305` | `spell_system.gd` (2 lines) | `sys.tick_overworld_casts()`, `sys.tick_enchantments()` | All spells dead |
|
||||
|
||||
**BONUS:** `HappinessScript.UNHAPPY_THRESHOLD` referenced at `turn_processor.gd:172` resolves to null/0 (stub has no constants), so the happiness gate for city growth is permanently disabled. Any happiness value passes the check.
|
||||
|
||||
**Fix per module:** Either bridge from Rust crate (mc-culture, mc-happiness, mc-magic exist) or port the reference implementation.
|
||||
|
||||
### 3. HIGH — turn_processor.gd has ~500 lines of simulation logic
|
||||
|
||||
The following phases are pure GDScript game logic, not Rust delegation:
|
||||
|
||||
| Phase | Lines | Should be |
|
||||
|---|---|---|
|
||||
| `_process_production` | L51-102 (52 lines) | mc-turn or mc-city |
|
||||
| `_process_research` | L104-155 (52 lines) | mc-tech (crate exists, has TechWeb + ResearchResult) |
|
||||
| `_process_growth` | L166-228 (40 lines) | mc-city |
|
||||
| `_process_healing` | L180-228 (40 lines) | mc-turn |
|
||||
| `_process_mana` | L231-255 (25 lines) | mc-magic |
|
||||
| `_process_improvements` | L477-495 (18 lines) | mc-economy |
|
||||
| `_apply_dead_zone_enchantment_decay` | L409-453 (45 lines) | mc-magic |
|
||||
|
||||
mc-turn::TurnProcessor::step() has its OWN Rust implementations of most of these phases (used by the bench). Two parallel pipelines exist — the bench uses Rust, the game uses GDScript.
|
||||
|
||||
### 4. HIGH — AI is full GDScript, bypasses mc-ai entirely
|
||||
|
||||
- `ai_tactical.gd` (406 lines): movement decisions, combat prediction (`_predict_combat` L292 uses simplified `30 * atk / def` — drifted from GdCombatResolver), city founding
|
||||
- `ai_military.gd` (234 lines): threat scoring, army composition analysis, garrison assignment
|
||||
- `ai_player.gd` (2 lines): stub, now deleted from turn_manager imports
|
||||
|
||||
`mc-ai` crate has `evaluator::ScoringWeights`, `mcts` module — never called from GDScript.
|
||||
|
||||
### 5. HIGH — ecology_db.gd is a full GDScript creature database (416 lines)
|
||||
|
||||
In-memory relational store (species, creatures, water_bodies, food_web) with hand-rolled CRUD, spatial indexes, and save/load serialization. `mc-ecology` crate exists but doesn't wrap this.
|
||||
|
||||
### 6. MEDIUM — AI combat prediction has drifted from actual resolver
|
||||
|
||||
`ai_tactical.gd::_predict_combat` (L292-311): `damage = 30 * atk / def_val`, ignores keywords, D20 attributes, fortification
|
||||
`GdCombatResolver::resolve`: full CombatParams with keywords, D20, flanking, all modifiers
|
||||
|
||||
AI attack decisions use wrong damage estimates.
|
||||
|
||||
### 7. MEDIUM — Rust fauna pipeline is complete but disabled
|
||||
|
||||
`RUST_FAUNA_ENCOUNTERS` env flag is not set in `.env.production`. The entire iter 7h-7k bridge (GdTurnProcessor + GdGameState + step_encounters_only) runs zero encounters in the actual game. Comment says "iter 7l will decide which to keep" — unresolved.
|
||||
|
||||
---
|
||||
|
||||
## Rust Crate ↔ GDExtension Bridge Gap
|
||||
|
||||
| Crate | Has Gd* class? | GDScript consumer wired? |
|
||||
|---|---|---|
|
||||
| mc-core | GdGridState | YES |
|
||||
| mc-climate | GdClimatePhysics, GdAtmosphericChemistry, GdClimateSpecEval | YES |
|
||||
| mc-mapgen | GdMapGenerator | YES |
|
||||
| mc-combat | GdCombatResolver (iter 7o) | YES (but combat_resolver.gd has Unit field drift) |
|
||||
| mc-city | GdCity | PARTIAL (item production wired, other city methods not) |
|
||||
| mc-items | GdItemSystem, GdLootRoller, GdStockpile, GdTreasury | YES |
|
||||
| mc-turn | GdTurnProcessor, GdGameState | ENV-GATED OFF |
|
||||
| mc-ecology | GdEcologyPhysics | YES (but duplicated by GDScript ecosystem.gd) |
|
||||
| mc-flora | (via mc-ecology) | YES |
|
||||
| mc-tech | **NONE** | Tech research runs in GDScript turn_processor.gd |
|
||||
| mc-ai | **NONE** | AI runs in GDScript ai_tactical.gd / ai_military.gd |
|
||||
| mc-economy | **NONE** | economy.gd is a 2-line stub |
|
||||
| mc-culture | **NONE** | culture.gd is a 2-line stub |
|
||||
| mc-happiness | **NONE** | happiness.gd is a 2-line stub |
|
||||
| mc-magic | **NONE** | spell_system.gd is a 2-line stub |
|
||||
| mc-observation | **NONE** | GdObservationStore was referenced but doesn't exist |
|
||||
| mc-balance | (bench-only) | N/A |
|
||||
| mc-compute | (GPU acceleration) | N/A |
|
||||
|
||||
**6 crates have no bridge at all.** These represent the bulk of the architecture gap.
|
||||
|
||||
---
|
||||
|
||||
## Recommended Priority Sequence
|
||||
|
||||
### Phase 1 — Stop the bleeding (CRITICAL fixes)
|
||||
1. **Remove duplicate ecology pass** — delete `turn_processor.gd`'s `_process_ecology` call. The Rust `GdEcologyPhysics::process_step` in climate.gd is the canonical path.
|
||||
2. **Enable Rust fauna encounters** — set `RUST_FAUNA_ENCOUNTERS=true` in `.env.production` and `.env.development`. The bridge was proven across 10 iterations.
|
||||
|
||||
### Phase 2 — Bridge the unbridged (HIGH — per-crate, parallelizable)
|
||||
Each crate gets a Gd* class in api-gdext following the GdCombatResolver pattern:
|
||||
3. **GdTechWeb** — bridge mc-tech, replace `_process_research` GDScript with delegation
|
||||
4. **GdAiController** — bridge mc-ai, add `decide_actions(state, player_index) -> Array[Dictionary]`
|
||||
5. **GdEconomy** — bridge mc-economy, replace `_process_economy` stub
|
||||
6. **GdCulture** — bridge mc-culture, replace culture.gd stub
|
||||
7. **GdHappiness** — bridge mc-happiness, replace happiness.gd stub
|
||||
8. **GdSpellSystem** — bridge mc-magic, replace spell_system.gd stub
|
||||
|
||||
### Phase 3 — Retire GDScript simulation
|
||||
9. **Replace turn_processor.gd phases** with per-phase Rust delegation (similar to how _process_rust_fauna_encounters delegates to RustFaunaIntegration)
|
||||
10. **Retire ai_tactical.gd + ai_military.gd** — fully replaced by GdAiController
|
||||
11. **Retire ecology_db.gd** — replaced by mc-ecology's creature system
|
||||
|
||||
### Phase 4 — Unify pipelines
|
||||
12. **Make GdTurnProcessor.step() the canonical turn** — GDScript turn_manager calls ONE Rust method per player turn, receives results as dicts, dispatches EventBus signals. The 500-line turn_processor.gd becomes a 50-line signal dispatcher.
|
||||
Loading…
Add table
Reference in a new issue