/ POST_VICTORY_EXFIL_FIX.md
POST_VICTORY_EXFIL_FIX.md
  1  # Post-Victory Exfil Fix & Combat-Free Exfil
  2  
  3  ## Problem
  4  
  5  After winning a combat during an active infil, players were unable to exfil. The issue occurred because:
  6  
  7  1. **Victory clears the active combat session** - This is intentional design to allow consecutive battles during a single infil
  8  2. **Exfil required an active combat session** - The `ProcessExfil()` method checked for `ActiveSessionId` and failed if none existed
  9  3. **Players got stuck in Infil mode** - They couldn't exfil despite having successfully completed combat
 10  
 11  Additionally, the original design incorrectly required players to complete combat before exfiling, but the intended design is to allow exfil at any time during an infil.
 12  
 13  ### User-Reported Symptoms
 14  
 15  - "After I won I got sent back to base, rather than infil" (confusing - may be a separate issue)
 16  - "I tried exfilling after infilling again, and I was still in infil mode"
 17  - Error message: "Cannot exfil without completing a combat. You must engage and complete a combat encounter to exfil."
 18  
 19  ## Root Cause
 20  
 21  The exfil flow was designed around a single-combat-per-infil model. When the multi-combat feature was added (allowing consecutive battles), the victory flow properly:
 22  - Emits `CombatVictoryEvent` (clears `ActiveCombatSessionId`)
 23  - Clears `ActiveCombatSessionId` (allows starting new combat)
 24  - Keeps operator in `Infil` mode (allows consecutive battles)
 25  
 26  However, the exfil logic still required an active combat session, creating a catch-22: players needed to complete combat to exfil, but completing combat cleared the session needed for exfil.
 27  
 28  ## Solution
 29  
 30  ### New Endpoint: `POST /operators/{id}/infil/complete`
 31  
 32  Added a new flow for completing infil successfully at any time, with or without combat.
 33  
 34  #### Server-Side Changes
 35  
 36  **OperatorExfilService.CompleteInfilSuccessfullyAsync**
 37  - Validates operator is in `Infil` mode
 38  - **No combat requirement** - Players can exfil immediately after starting infil
 39  - Emits `InfilEndedEvent` with `wasSuccessful: true`
 40  - Transitions operator to `Base` mode
 41  - Preserves loot and streak (if any combat was completed)
 42  
 43  **OperatorService.CompleteInfilAsync**
 44  - Thin wrapper for API layer
 45  - Delegates to `OperatorExfilService.CompleteInfilSuccessfullyAsync`
 46  
 47  **API Controller**
 48  - `POST /operators/{id}/infil/complete`
 49  - Returns 200 OK on success
 50  - Returns 400 BadRequest if validation fails (not in Infil mode)
 51  
 52  #### Client-Side Changes
 53  
 54  **ProcessExfil() in ConsoleClient/Program.cs**
 55  
 56  Modified to handle two distinct cases:
 57  
 58  1. **No active session + Infil mode** (NEW - post-victory or no-combat path)
 59     - Calls `POST /operators/{id}/infil/complete`
 60     - Shows "Exfil successful!" message
 61     - Returns to base
 62  
 63  2. **Active session exists** (LEGACY - in-progress combat path)
 64     - Validates session is completed
 65     - Calls `POST /infil/outcome` with sessionId
 66     - Processes combat outcome
 67  
 68  This maintains backward compatibility while enabling the new exfil flow.
 69  
 70  ### Validation Logic
 71  
 72  The fix includes minimal validation:
 73  
 74  ```csharp
 75  // Must be in Infil mode to complete infil
 76  if (aggregate.CurrentMode != OperatorMode.Infil)
 77      return ServiceResult.InvalidState("Cannot complete infil when not in Infil mode");
 78  ```
 79  
 80  No combat requirement - players can exfil at any time during an active infil.
 81  
 82  ## Testing
 83  
 84  ### New Test: `OperatorCanExfilImmediatelyWithoutCombat`
 85  
 86  Validates that players can exfil immediately after starting infil:
 87  1. Create operator and start infil
 88  2. Verify operator is in Infil mode with ExfilStreak = 0 (no combat)
 89  3. Call `CompleteInfilSuccessfullyAsync`
 90  4. Verify operator is in Base mode with ExfilStreak still 0
 91  
 92  ### Existing Test: `AfterVictory_OperatorCanCompleteInfilSuccessfully`
 93  
 94  Validates the complete post-victory exfil flow:
 95  1. Create operator and start infil
 96  2. Win combat (emits `CombatVictoryEvent`, clears `ActiveCombatSessionId`)
 97  3. Verify operator is in Infil mode with no active session
 98  4. Call `CompleteInfilSuccessfullyAsync`
 99  5. Verify operator is in Base mode with preserved `ExfilStreak` and XP
100  
101  ### Test Results
102  
103  All relevant test suites pass:
104  - ✅ 4 InfilVictoryFlowTests (including the new test)
105  - ✅ 39 OperatorExfilServiceTests
106  - ✅ 19 OperatorAggregateTests
107  - ✅ 112 Mode-related tests
108  
109  ## Flow Diagrams
110  
111  ### Victory Flow (Corrected)
112  
113  ```
114  Player wins combat
115116  ProcessCombatOutcome() → POST /infil/outcome
117118  Server emits CombatVictoryEvent
119120  ActiveCombatSessionId = null (but stays in Infil mode)
121122  MissionComplete screen: "MISSION SUCCESS"
123124  Player clicks OK → BaseCamp
125126  BaseCamp shows: [ENGAGE COMBAT] [EXFIL] [VIEW STATS]
127  ```
128  
129  ### Exfil Flow (NEW - No Combat Required)
130  
131  ```
132  Player starts infil
133134  BaseCamp shows: [ENGAGE COMBAT] [EXFIL] [VIEW STATS]
135136  Player clicks EXFIL (without engaging in combat)
137138  ProcessExfil() checks for active session
139140  No session found, CurrentMode = "Infil"
141142  POST /operators/{id}/infil/complete
143144  Server validates CurrentMode = Infil
145146  Emit InfilEndedEvent (wasSuccessful: true)
147148  Operator → Base mode
149150  Client shows "Exfil successful!" → BaseCamp
151152  BaseCamp shows: [INFIL] [CHANGE LOADOUT] [TREAT WOUNDS] etc.
153  ```
154  
155  ## Design Notes
156  
157  ### Why Allow Exfil Without Combat?
158  
159  The design allows players full agency over their infil experience:
160  - **Risk assessment** - Players can scout and decide to exfil if conditions are unfavorable
161  - **Resource management** - Exit before taking damage or using consumables
162  - **Time management** - Quick in/out without forced engagement
163  - **Learning/Training** - New players can practice infil mechanics without combat pressure
164  
165  This creates a more flexible and player-friendly system.
166  
167  ### Why Not Modify ProcessCombatOutcomeAsync?
168  
169  We could have modified the victory path to not clear `ActiveCombatSessionId`, but this would:
170  - Break the multi-combat feature (can't start new combat with active session)
171  - Complicate session management (sessions would persist after completion)
172  - Require changes to auto-resume logic
173  
174  The separate endpoint approach is cleaner and maintains clear separation of concerns.
175  
176  ### Backward Compatibility
177  
178  The legacy path (with active session) is preserved for edge cases:
179  - Old game states loaded from database
180  - Potential race conditions during combat completion
181  - Future features that might use different session management
182  
183  ## Future Considerations
184  
185  ### Potential Enhancements
186  
187  1. **Multiple combats before exfil** - Already supported! Players can win multiple combats and only exfil when ready
188  2. **Exfil bonus for longer streaks** - Could be added by checking `ExfilStreak` in `CompleteInfilSuccessfullyAsync`
189  3. **Time-based exfil restrictions** - Could validate minimum time in infil before allowing exfil
190  4. **Exfil cost/penalty for no combat** - Could apply a resource cost for exfiling without engaging
191  
192  ### Migration Path
193  
194  No migration needed - this is a pure addition. Existing game states will work correctly:
195  - Operators in Base mode: No change
196  - Operators in Infil with active session: Uses legacy path
197  - Operators in Infil after victory: Uses new path
198  - Operators in Infil without combat: Uses new path
199  
200  ## Related Issues
201  
202  This fix addresses the core exfil problem. If users still report being "sent back to base" after victory, that would be a separate issue to investigate (possibly related to auto-cleanup or session management).