Phase 68: Consequence Enforcement¶
Status: Complete Block: 8 (Consequence Enforcement & Scenario Expansion) Tests: 67 new (7 test files)
Summary¶
Phase 68 converts the 7 highest-priority "log but don't act" patterns to actual behavioral enforcement, each gated behind an enable_* flag (default False) to prevent regressions.
What Was Built¶
68a: Fuel Consumption Enforcement¶
enable_fuel_consumptionflag in CalibrationSchema- Vehicles consume fuel proportional to distance moved in
_execute_movement() - Domain-specific rates: ground 0.0001/m, air 0.0005/m, naval 0.00005/m
- Per-unit
fuel_consumption_rateattribute override supported - Fuel exhaustion sets speed to 0, existing fuel gate at 0 prevents movement
- Infantry (max_speed <= 5) exempt
68b: Ammo Depletion Gate¶
enable_ammo_gateflag in CalibrationSchema_ammo_expendeddict tracks rounds fired perunit_id:weapon_name- Weapons with
magazine_capacityblocked after exhaustion - Weapons without
magazine_capacityfire unlimited (backward compat) - State checkpointed in
get_state()/set_state()
68c: Order Delay Enforcement¶
_pending_decisionsdict tracksunit_id → execute_at_elapsed_s- After
propagate_order(): positive delay queues decision deferral - Before
decide(): matured decisions proceed, pending decisions skip - Gated by existing
enable_c2_frictionflag - State checkpointed
68d: Order Misinterpretation Enforcement¶
_misinterpreted_ordersdict stores propagation results (transient, not checkpointed)- Four misinterpretation types enforced:
"position": offset movement target bymisinterpretation_radius_m(default 500m)"timing": re-queue with doubled delay"objective": swap ATTACK/DEFEND school adjustments"unit_designation": skip decide cycle entirely- Gated by
enable_c2_friction
68e: Fire Zone Damage¶
fire_damage_per_tickfield in CalibrationSchema (default 0.01)- Replaced log-only fire zone code with actual damage application
- Uses existing deferred damage pattern (
_apply_aggregate_casualties) - DUG_IN posture reduces fire damage by 50%
- Gated by existing
enable_fire_zonesflag
68f: Stratagem Expiry¶
stratagem_duration_ticksfield in CalibrationSchema (default 100)_activation_ticksdict on StratagemEngine tracks when each stratagem activatedexpire_stratagems(current_tick, duration)removes expired plansis_active(stratagem_id)public helper- Battle loop calls
expire_stratagems()before stratagem evaluation activate_stratagem()now acceptstickparameter (backward compat via default=0)- State checkpointed in
get_state()/set_state()
68g: Guerrilla Retreat Movement¶
retreat_distance_mfield in CalibrationSchema (default 2000m)- Guerrilla units that disengage physically move away from nearest enemy
- Retreat vector: opposite direction from nearest enemy, magnitude =
retreat_distance_m - Optional ROUTING status via blend probability + PRNG
- Gated by existing
enable_unconventional_warfareflag
Files Modified¶
| File | Changes |
|---|---|
simulation/calibration.py |
+6 fields: enable_fuel_consumption, enable_ammo_gate, fire_damage_per_tick, stratagem_duration_ticks, retreat_distance_m, misinterpretation_radius_m |
simulation/battle.py |
7 behavioral changes, +3 instance vars (_ammo_expended, _pending_decisions, _misinterpreted_orders), checkpoint support |
c2/ai/stratagems.py |
+_activation_ticks, expire_stratagems(), is_active(), tick param on activate_stratagem(), checkpoint update |
tests/unit/test_phase_60_structural.py |
Updated string match for renamed fire zone log |
tests/validation/test_phase_67_structural.py |
Excluded Phase 68 flags from scenario exercise check |
New Test Files¶
| File | Tests |
|---|---|
test_phase_68a_fuel.py |
8 |
test_phase_68b_ammo.py |
11 |
test_phase_68c_order_delay.py |
14 |
test_phase_68d_misinterpretation.py |
11 |
test_phase_68e_fire_damage.py |
8 |
test_phase_68f_stratagem_expiry.py |
8 |
test_phase_68g_guerrilla_retreat.py |
7 |
| Total | 67 |
Postmortem¶
Scope¶
On target. All 7 planned substeps delivered. 67 tests vs ~54 planned (+24%).
Integration¶
Fully wired — 1 minor finding:
- is_active() on StratagemEngine defined but not called from battle.py. Available as public helper for tests and future use. Not a gap — expiry removes plans from _active_plans, which prevents bonus application naturally.
- _misinterpreted_orders correctly NOT checkpointed — transient intra-tick buffer consumed in same OODA cycle.
Test Quality¶
Unit-level coverage strong, integration-level coverage moderate: - 68a (fuel) and 68f (stratagem expiry) have strong integration tests calling actual engine methods - 68b (ammo), 68c (delay), 68d (misinterpretation), 68g (guerrilla retreat) test logic in isolation rather than through full battle loop - Acceptable for Phase 68 — the behavioral code is structurally wired and gated behind flags that default to False
Deficits (accepted limitations)¶
- Hardcoded domain fuel rates — ground/air/naval rates baked into battle.py (0.0001/0.0005/0.00005). Per-unit
fuel_consumption_rateattribute provides override path. Schema-level domain rates deferred. - Hardcoded fire posture multiplier — DUG_IN takes 0.5x fire damage. Should be in CalibrationSchema if tuning needed.
- Hardcoded vehicle speed threshold — 5.0 m/s determines fuel eligibility. Consistent with Phase 58e gate.
- Phase 68 flags not in scenarios —
enable_fuel_consumptionandenable_ammo_gatenot enabled in any scenario YAML yet. Will be enabled in a future integration phase (like Phase 67 did for Block 7 flags).
Performance¶
67 new tests run in 0.62s. Full suite ~21 min, consistent with prior phases. No performance regression.