Phase 12: Deep Systems Rework¶
Summary¶
Phase 12 resolves 16 MODERATE deficits from the Post-MVP Refinement Index and adds 2 new domains (civilian population, strategic air campaigns/IADS) through 6 sub-phases (12a–12f). All changes follow the backward-compatibility pattern: enable_* config flags defaulting to False, new parameters with MVP-preserving defaults.
Test count: 259 tests (30 + 53 + 22 + 66 + 41 + 47) across 6 test files. Total: 4,077 tests passing (up from 3,818).
New modules: 12 new source files (1 in c2/, 1 in logistics/, 7 in population/, 3 in combat/). Modified modules: ~25 existing source files across c2/, logistics/, combat/, morale/, detection/, terrain/, core/. No new dependencies.
What Was Built¶
12d: Morale & Psychology Depth (30 tests)¶
- Continuous-time Markov morale (
morale/state.py): Addeduse_continuous_time: bool = FalsetoMoraleConfig. Newcompute_continuous_transition_probs(params, dt)method usingP = 1 - exp(-λ·dt)for tick-rate-independent transitions.check_transition()gaineddt: float = 1.0parameter; when continuous-time enabled, uses rates instead of probabilities. Also enforcedtransition_cooldown_swhich was declared but never checked. - Enhanced PSYOP (
morale/psychology.py,morale/events.py): Newapply_psyop_enhanced()method with message_type × target_susceptibility × delivery_method scoring. AddedPsyopAppliedEventto events.py, published on EventBus. Config:message_type_multipliers,delivery_method_multipliers,training_resistance_weight. Originalapply_psyop()preserved unchanged.
12a: C2 Depth (53 tests)¶
- Multi-hop message propagation (
c2/communications.py): Addedenable_multi_hop: bool = False,max_relay_hops: int = 5to config. Injects optionalhierarchy: HierarchyTreefor relay path finding (LCA algorithm)._find_relay_path()returns chain; each hop has independent P(success) and additive delay. - Terrain-based comms LOS (
c2/communications.py): Injects optionallos_engine: LOSEngine. New_los_factor()returns 0.0 whenrequires_los=Trueand terrain blocks LOS; 1.0 otherwise. VHF/UHF/data_link require LOS; HF/satellite/wire unaffected. - Network degradation model (
c2/communications.py): Addedenable_network_degradation: bool = False, congestion params. Tracks per-band bandwidth utilization._congestion_factor(): <50% load = no effect; 50-90% = latency increase; >90% = message loss. Load decays viaexp(-rate * dt). - Arbitrary polyline FSCL (
c2/coordination.py): Replace east-west midpoint with ShapelyLineString.set_fscl()gainedwaypoints: list[Position] | Nonefor polyline.is_beyond_fscl()uses cross-product side-of-line test. AddedFireType.MISSILEto FSCL gating. - JTAC/FAC observer model (
c2/coordination.py): NewJTACObservationdataclass,register_jtac(),check_cas_feasibility(). JTAC must have LOS to target; reports position with Gaussian error inversely proportional to range. - JIPTL generation (
c2/coordination.py): NewTargetNomination,TargetAllocationdataclasses,JIPTLConfig.submit_target_nomination(),generate_jiptl(available_shooters)→ prioritized allocation via greedy assignment. - Network-centric COP (
detection/fog_of_war.py): NewDataLinkConfigwithenable_cop_sharing: bool = False.set_data_link_networks()registers Link 16/FBCB2 memberships. After sensor sweep inupdate(), share contacts laterally among data-linked units with track quality degradation. - Joint Task Force command (
c2/joint_ops.py, NEW):ServiceBranchenum,ComponentCommandenum,CoalitionCaveatconfig.JointOpsEngine: register units by service, assign liaisons, register coalition caveats.get_coordination_modifiers(): same service = 1.0; cross-service = ×1.5 delay, ×2.0 misinterpret; liaison reduces by 50%.check_caveat_compliance()per-nation mission/area restrictions. - ATO planning cycle (
c2/orders/air_orders.py): NewATOPlanningEngineclass withregister_aircraft(),submit_request(),generate_ato(). Priority: DCA/CAP first → JIPTL strikes → CAS → remaining. Sortie limits per aircraft, turnaround times, CAS reserve fraction.
12b: Logistics Depth (22 tests)¶
- Multi-echelon supply network + infrastructure coupling (
logistics/supply_network.py):SupplyNodegainedechelon_level,infrastructure_id,throughput_tons_per_hour.SupplyRoutegainedcurrent_flow_tons_per_hour,infrastructure_ids. Config flags:enable_capacity_constraints,enable_infrastructure_coupling,enable_min_cost_flow. New methods:sync_infrastructure(),find_supply_route_capacity_aware(),find_alternate_route(),sever_route(),compute_network_redundancy(). - Supply regeneration (
logistics/production.py, NEW):ProductionFacilityConfigpydantic model,ProductionEnginewithregister_facility(),update(). Production = rate × condition × dt. - Transport escort effects (
logistics/transport.py):update()gainedescort_strength: float = 1.0,threat_level: float = 0.0. Per-tick interdiction roll. - Erlang medical service (
logistics/medical.py): Addederlang_shape_k: int = 1to config. Treatment time:rng.gamma(k, mean/k)when k > 1. - Fuel gating wiring (
simulation/battle.py): Movement execution queriesctx.stockpile_managerfor Class III (fuel) and passesfuel_availableto movement.
12c: Combat Depth (66 tests)¶
- Air combat energy-maneuverability (
combat/air_combat.py): NewEnergyStatedataclass withspecific_energy = h + v²/2g. Addedenergy_advantage_weight: float = 0.0to config.resolve_air_engagement()gained optionalattacker_energy/defender_energy. Energy advantage modifies Pk: WVR/GUNS full effect, BVR ×0.3 effect, clamped ±0.3. - Naval compartment flooding (
combat/naval_surface.py): NewCompartmentConfigand flooding model.ShipDamageStategainedcompartment_flooding: list[float],capsized: bool. Methods:initialize_compartments(),apply_compartment_damage(),progressive_flooding(),counter_flood(),check_capsize().enable_compartment_model: bool = Falsepreserves MVP. - Submarine geometric evasion + patrol operations (
combat/naval_subsurface.py):SubmarineState,GeometricEvasionResultdataclasses for bearing rate model with thermocline crossing bonus.PatrolArea,PatrolResult,SubmarinePatrolConfigfor patrol operations with saturating exponential area coverage and Poisson contact detection. - Mine ship-signature + MCM operations (
combat/naval_mine.py):ShipMineSignaturedataclass for per-type trigger probability matching.MCMModeenum, geographic sweep bounding.update_mine_persistence()for battery decay.compute_minefield_density()for spatial query. - Amphibious operations depth (
combat/amphibious_assault.py):LandingCraftdataclass,compute_throughput(),check_tidal_window(),execute_wave_with_craft().enable_landing_craft_model: bool = Falsepreserves MVP.
12e: Civilian Population & COIN (41 tests)¶
- ModuleId additions (
core/types.py): AddedPOPULATIONandAIR_CAMPAIGNto ModuleId enum. - Package init (
population/__init__.py): New package. - Events (
population/events.py):DisplacementEvent,CollateralDamageEvent,DispositionChangeEvent,HumintTipEventfrozen dataclasses. - Civilian entity manager (
population/civilians.py):CivilianDispositionIntEnum,CivilianRegiondataclass,CivilianManagerwith spatial queries, displacement tracking, collateral tracking, state persistence. - Refugee displacement (
population/displacement.py):DisplacementConfig,DisplacementEnginedriving displacement from combat zones with distance falloff. Transport penalty computation. - Collateral damage tracking (
population/collateral.py):CollateralConfig,CollateralEnginewith cumulative tracking and escalation threshold. - Civilian HUMINT (
population/humint.py):HumintConfig,CivilianHumintEnginewith Poisson tip generation. FRIENDLY pop reports enemies to "blue", HOSTILE warns "red". - Population disposition dynamics (
population/influence.py):InfluenceConfig,InfluenceEnginewith Markov chain transitions driven by collateral damage/aid/psyop. - ROE escalation triggers (
c2/roe.py): Newevaluate_escalation()method. Collateral exceeds threshold → automatic ROE tightening (FREE→TIGHT→HOLD).
12f: Strategic Air Campaigns & IADS (47 tests)¶
- IADS model (
combat/iads.py, NEW):IadsSectordataclass with radar handoff chain.IadsConfigwith handoff timing and autonomous_effectiveness_mult.IadsEngine: register_sector(), process_air_track() (radar handoff with timing penalties), compute_sector_health() (radar × SAM × command compound), apply_sead_damage() with stochastic variation. - Air campaign management (
combat/air_campaign.py, NEW):CampaignPhaseIntEnum (AIR_SUPERIORITY → SEAD → INTERDICTION → CAS).PilotStatefatigue tracking.AirCampaignEngine: sortie capacity, pilot fatigue with performance degradation, weather day cancellation, fleet attrition/regeneration. - Strategic targeting (
combat/strategic_targeting.py, NEW):StrategicTargetwith health/repair_rate.TargetEffectChainmapping target_type → operational effect.StrategicTargetingEngine: TPL generation, strike with cascade to infrastructure/supply, BDA cycle with lognormal overestimate bias (historical ×3), target regeneration. - Strategic infrastructure nodes (
terrain/infrastructure.py): AddedHealthStateIntEnum,PowerPlant,Factory,Port,SupplyDepotmodels. ExtendedInfrastructureManagerwith new collections andget_feature_condition()method.
Design Decisions¶
- Backward-compatibility pattern maintained: All 12 sub-phases use
enable_*config flags or default parameter values that preserve MVP behavior. No existing test needed modification excepttests/unit/test_types.py(added new ModuleId members to expected set). - Population as overlay, not entities: Civilian population is a terrain-like overlay modulating other systems (detection via HUMINT, logistics via displacement, ROE via collateral), not combat entities in the engagement loop.
- IADS as compound health: Sector effectiveness computed as product of radar coverage × SAM availability × command connectivity — destruction of any component degrades the whole.
- BDA overestimate bias: Historical tendency to overestimate bomb damage (documented ×3 bias) modeled as lognormal distribution centered above true damage.
- CivilianDisposition IntEnum: FRIENDLY=0, NEUTRAL=1, HOSTILE=2. Note: FRIENDLY=0 is falsy — use
is Nonechecks, notorfallbacks.
Issues & Fixes¶
- NavalGunfireSupportEngine constructor: Test helper
_make_amphibious_enginepassed wrong keyword argument (naval_surface_engine=instead ofindirect_fire_engine=). Fixed by usingSimpleNamespacemock. - CivilianDisposition.FRIENDLY falsy:
disposition or defaultalways returned default because FRIENDLY=0 is falsy. Fixed withdisposition if disposition is not None else default. - ModuleId test assertion: Adding POPULATION and AIR_CAMPAIGN broke
test_types.py::test_all_memberswith hardcoded expected set. Fixed by adding new members.
Known Limitations / Post-MVP Refinements¶
- No YAML data files for population profiles, IADS sectors, or aircraft availability (structural only)
- COP sharing is lateral within data-link networks only — no hierarchical fusion
- JIPTL uses greedy allocation, not optimal assignment
- Air campaign not wired to ATO planning engine (structural connection deferred)
- No mine warfare campaign-level coordination (mine-laying + MCM are per-operation)
- Patrol operations use simplified Poisson model, not full ASW coordination
- Landing craft model does not include casualty evacuation via ship
- Insurgency dynamics not yet implemented (deferred to Phase 24)
- Supply regeneration production rates are per-facility, not network-optimized
Lessons Learned¶
- IntEnum zero values are falsy: Python truthiness means
IntEnum(0)is falsy. Always useis Nonechecks when an enum's first member is 0. - Background agent verification: Always verify background agent outputs before writing tests that depend on their changes.
- Test infrastructure mocks:
SimpleNamespacemocks are effective for avoiding heavy dependency chains in test helpers (e.g., mocking the indirect_fire_engine for amphibious tests). - Sub-phase ordering matters: Implementing 12d (smallest) first builds confidence; 12f (most dependencies) last ensures all foundations are in place.
- Parallel implementation effective: Using background agents for independent combat sub-items (12c-1 through 12c-5) saved significant time.
Deficits Resolved¶
| Deficit | Origin | Resolution |
|---|---|---|
| Submarine evasion simplified | Phase 4 | 12c — geometric evasion with bearing rate model |
| Mine trigger lacks ship signature | Phase 4 | 12c — ShipMineSignature per-type trigger matching |
| Naval damage control abstracted | Phase 4 | 12c — compartment flooding model with capsize |
| Air combat lacks energy-maneuverability | Phase 4 | 12c — EnergyState with specific energy advantage |
| Morale Markov discrete-time | Phase 4 | 12d — continuous-time Markov with P = 1 - exp(-λ·dt) |
| PSYOP simplified effectiveness | Phase 4 | 12d — enhanced PSYOP with message×susceptibility×delivery |
| No multi-hop C2 propagation | Phase 5 | 12a — relay chain via hierarchy LCA with per-hop P and delay |
| No terrain-based comms LOS | Phase 5 | 12a — LOSEngine integration for VHF/UHF |
| Simplified FSCL | Phase 5 | 12a — Shapely LineString polyline FSCL |
| No ATO planning cycle | Phase 5 | 12a — ATOPlanningEngine with sortie allocation |
| No JTAC/FAC observer | Phase 5 | 12a — JTAC LOS + position error model |
| No supply optimization solver | Phase 6 | 12b — min-cost flow via networkx |
| No multi-echelon supply chain | Phase 6 | 12b — echelon_level + throughput constraints |
| Simplified transport vulnerability | Phase 6 | 12b — escort effects on convoy survival |
| Medical M/M/c approximate | Phase 6 | 12b — Erlang-k service distribution |
| Fuel gating not wired to stockpile | Phase 11 | 12b — battle.py queries stockpile for Class III |