/ INFIL_EXFIL_BOUNDARY.md
INFIL_EXFIL_BOUNDARY.md
  1  # Infil/Exfil Boundary Documentation
  2  
  3  ## Overview
  4  
  5  GUNRPG implements a strict architectural boundary between **combat (infil)** and **operator management (exfil)** to ensure data integrity, prevent state corruption, and maintain a clear separation of concerns.
  6  
  7  ## Key Concepts
  8  
  9  ### Infil (Combat)
 10  **Infil** refers to the combat phase where operators engage in tactical combat scenarios. During infil:
 11  - Operators use a **copy** of their stats (snapshot)
 12  - Combat state is transient and exists only for the duration of the session
 13  - No operator progression or permanent state changes occur
 14  - Combat produces **outcomes** but does not persist operator changes directly
 15  
 16  ### Exfil (Operator Management)
 17  **Exfil** refers to the out-of-combat phase where operator progression and management occur. During exfil:
 18  - Operators are represented as event-sourced aggregates
 19  - All state changes are recorded as **append-only events**
 20  - Events are hash-chained for tamper detection
 21  - This is the **ONLY** place where operator events may be committed
 22  
 23  ## Architecture
 24  
 25  ```
 26  ┌─────────────────────────────────────────────────────────────────┐
 27  │                         INFIL (Combat)                          │
 28  ├─────────────────────────────────────────────────────────────────┤
 29  │                                                                 │
 30  │  CombatSession                                                  │
 31  │  ├─ Operator (snapshot/copy)                                    │
 32  │  │  ├─ Health, Stamina, etc. (mutable during combat)           │
 33  │  │  └─ Equipment, Stats (read-only from exfil)                 │
 34  │  │                                                              │
 35  │  └─ Produces CombatOutcome                                      │
 36  │     ├─ Damage taken                                             │
 37  │     ├─ XP earned                                                │
 38  │     ├─ Victory/defeat status                                    │
 39  │     └─ Other combat results                                     │
 40  │                                                                 │
 41  └─────────────────────────────────────────────────────────────────┘
 42 43                             │ CombatOutcome
 44 45  ┌─────────────────────────────────────────────────────────────────┐
 46  │                        EXFIL (Operator Management)               │
 47  ├─────────────────────────────────────────────────────────────────┤
 48  │                                                                 │
 49  │  OperatorExfilService                                           │
 50  │  ├─ ProcessCombatOutcome(outcome)                              │
 51  │  │  ├─ Player reviews/confirms outcome                          │
 52  │  │  └─ Translates to operator events                           │
 53  │  │                                                              │
 54  │  └─ Exfil-Only Actions                                          │
 55  │     ├─ ApplyXp()                                                │
 56  │     ├─ TreatWounds()                                            │
 57  │     ├─ ChangeLoadout()                                          │
 58  │     └─ UnlockPerk()                                             │
 59  │                                                                 │
 60  │  OperatorAggregate (Event-Sourced)                              │
 61  │  ├─ State derived from events                                   │
 62  │  ├─ Events are append-only                                      │
 63  │  └─ Events are hash-chained                                     │
 64  │                                                                 │
 65  │  IOperatorEventStore (LiteDB)                                   │
 66  │  └─ Persists events with integrity checks                       │
 67  │                                                                 │
 68  └─────────────────────────────────────────────────────────────────┘
 69  ```
 70  
 71  ## Event Sourcing
 72  
 73  ### Why Event Sourcing?
 74  
 75  Operators are event-sourced to provide:
 76  1. **Complete audit trail** - Every change to an operator is recorded
 77  2. **Tamper detection** - Hash chaining prevents unauthorized modifications
 78  3. **Time-travel debugging** - Can replay events to any point in history
 79  4. **Offline-first design** - Events can be synced across devices (future)
 80  5. **Clear transactional boundaries** - Events are atomic and ordered
 81  
 82  ### Event Types
 83  
 84  All operator events inherit from `OperatorEvent` and include:
 85  
 86  - **OperatorCreatedEvent** - Initial operator creation (sequence 0)
 87  - **XpGainedEvent** - Experience points awarded
 88  - **WoundsTreatedEvent** - Health restoration
 89  - **LoadoutChangedEvent** - Equipment changes
 90  - **PerkUnlockedEvent** - Skill/perk unlocks
 91  - **CombatVictoryEvent** - Combat victory (clears ActiveCombatSessionId, does not affect streak)
 92  - **ExfilFailedEvent** - Failed exfil (resets streak)
 93  - **OperatorDiedEvent** - Operator death (marks IsDead=true, resets streak)
 94  
 95  ### Hash Chaining
 96  
 97  Each event contains:
 98  - `SequenceNumber` - Monotonically increasing counter
 99  - `PreviousHash` - Hash of the previous event
100  - `Hash` - SHA256 hash of event contents + previous hash
101  - `Timestamp` - When the event occurred
102  
103  This creates a tamper-evident chain:
104  ```
105  Event 0: Hash A (prev: "")
106  Event 1: Hash B (prev: A)
107  Event 2: Hash C (prev: B)
108  Event 3: Hash D (prev: C)
109  ```
110  
111  If any event is modified, the hash chain breaks and the corruption is detected immediately on load.
112  
113  ### Rollback on Corruption
114  
115  When hash chain verification fails during load:
116  1. The system stops at the first corrupted event
117  2. All events from that point onward are deleted from storage
118  3. The operator is restored to the last valid state
119  4. No attempt is made to repair or rewrite corrupted events
120  
121  This ensures deterministic recovery - operators always roll back to their last known-good state.
122  
123  ## The Boundary Contract
124  
125  ### Combat MUST NOT:
126  - ❌ Directly mutate operator aggregate state
127  - ❌ Append events to the operator event store
128  - ❌ Persist operator changes
129  - ❌ Grant XP, unlock perks, or modify loadout
130  
131  ### Combat MUST:
132  - ✅ Use a **snapshot** of operator stats
133  - ✅ Produce a **CombatOutcome** at completion
134  - ✅ Include all relevant combat results in the outcome
135  - ✅ Remain deterministic and reproducible
136  
137  ### Exfil MUST NOT:
138  - ❌ Access combat system internals
139  - ❌ Simulate or execute combat
140  - ❌ Modify combat state
141  
142  ### Exfil MUST:
143  - ✅ Be the **ONLY** place events are committed
144  - ✅ Validate all state changes
145  - ✅ Verify hash chain integrity on load
146  - ✅ Allow player confirmation before committing outcomes
147  
148  ## Usage Examples
149  
150  ### Exfil Semantics
151  
152  #### Exfil Streak
153  The `ExfilStreak` property tracks consecutive successful exfils:
154  - Increments by 1 on each successful `InfilEndedEvent` (`wasSuccessful=true`)
155  - Resets to 0 on:
156    - `ExfilFailedEvent` (retreat, abandon mission, etc.)
157    - `OperatorDiedEvent` (operator death)
158    - Event chain rollback past the last successful exfil
159  
160  The streak is informational only (no gameplay effects yet) but provides foundation for future features like bonus rewards or difficulty modifiers.
161  
162  #### Operator Death
163  Once an operator dies (`IsDead = true`):
164  - All mutating operations are rejected at the service level (via `OperatorExfilService`)
165  - Health is set to 0
166  - Exfil streak is reset to 0
167  - `OperatorExfilService` will reject any further attempts to append events for this operator
168  - The underlying event store does not enforce this constraint by itself (allows replay for audit)
169  - The aggregate can still replay historical events for audit purposes
170  
171  The `IsDead` flag is derived from events - it's not persisted separately, ensuring event sourcing integrity.
172  
173  ### Creating an Operator (Exfil)
174  ```csharp
175  var service = new OperatorExfilService(eventStore);
176  var result = await service.CreateOperatorAsync("Operative Alpha");
177  var operatorId = result.Value;
178  ```
179  
180  ### Starting Combat (Infil)
181  ```csharp
182  // Load operator from exfil
183  var loadResult = await exfilService.LoadOperatorAsync(operatorId);
184  var aggregate = loadResult.Value;
185  
186  // Create combat snapshot
187  var combatSnapshot = aggregate.CreateCombatSnapshot();
188  
189  // Start combat session with the snapshot
190  var session = CombatSession.Create(combatSnapshot);
191  ```
192  
193  ### Completing Combat (Boundary)
194  ```csharp
195  // Combat produces outcome
196  var outcome = CombatOutcome.FromSession(session);
197  
198  // Outcome flows to exfil for processing
199  var result = await exfilService.ProcessCombatOutcomeAsync(
200      outcome, 
201      playerConfirmed: true
202  );
203  ```
204  
205  ### Applying Exfil Actions
206  ```csharp
207  // Only in exfil - apply XP
208  await exfilService.ApplyXpAsync(operatorId, 150, "Victory");
209  
210  // Only in exfil - treat wounds
211  await exfilService.TreatWoundsAsync(operatorId, 50f);
212  
213  // Only in exfil - change loadout
214  await exfilService.ChangeLoadoutAsync(operatorId, "AK-47");
215  
216  // Only in exfil - unlock perk
217  await exfilService.UnlockPerkAsync(operatorId, "Fast Reload");
218  
219  // Only in exfil - complete successful exfil (increments streak)
220  await exfilService.CompleteExfilAsync(operatorId);
221  
222  // Only in exfil - fail exfil (resets streak)
223  await exfilService.FailExfilAsync(operatorId, "Retreat");
224  
225  // Only in exfil - operator death (permanent, resets streak)
226  await exfilService.KillOperatorAsync(operatorId, "Combat casualty");
227  ```
228  
229  ## Data Integrity
230  
231  ### Hash Verification
232  On load, the system verifies:
233  1. Each event's hash matches its computed hash
234  2. Each event's previous hash matches the prior event's hash
235  3. Sequence numbers are consecutive with no gaps
236  
237  If any check fails, the system:
238  1. Stops processing at the corrupted event
239  2. Rolls back (deletes) all events from that point onward
240  3. Returns only the valid events before the corruption
241  4. The aggregate is restored to the last known-good state
242  
243  ### Append Constraints
244  When appending events:
245  1. Sequence must be exactly CurrentSequence + 1
246  2. Previous hash must match the last event's hash
247  3. Event hash must verify correctly
248  4. No concurrent modifications (enforced by store)
249  
250  ## Future Enhancements
251  
252  ### Planned Features
253  - **Multi-player sync** - Events can be synced across clients
254  - **Replay system** - Rebuild operator state at any point in time
255  - **Digital signatures** - Add cryptographic signing for authentication
256  - **Conflict resolution** - Handle concurrent modifications gracefully
257  - **Event migrations** - Versioning and schema evolution
258  
259  ### Not Included Yet
260  - ❌ Cryptographic keys (hash-only for now)
261  - ❌ Authentication/authorization
262  - ❌ Network sync
263  - ❌ Conflict resolution for concurrent edits
264  
265  ## Testing
266  
267  The implementation includes comprehensive tests:
268  - `OperatorEventTests` - Event creation, hashing, chain verification, new event types
269  - `OperatorAggregateTests` - Event replay, state derivation, streak tracking, death handling, rollback
270  - `LiteDbOperatorEventStoreTests` - Persistence, integrity checks, rollback behavior
271  - `OperatorExfilServiceTests` - Service operations, validation, dead operator constraints
272  
273  All 77 operator tests pass, ensuring the boundary is maintained correctly.
274  
275  ## Summary
276  
277  The infil/exfil boundary provides:
278  - ✅ **Correctness** - Clear separation prevents state corruption
279  - ✅ **Integrity** - Hash chaining detects tampering
280  - ✅ **Clarity** - Explicit boundaries make the system easier to reason about
281  - ✅ **Testability** - Each side can be tested independently
282  - ✅ **Future-proof** - Event sourcing enables powerful future features
283  
284  This design favors **correctness, integrity, and clarity over convenience**, as specified in the requirements.