# Adversarial Review & Quality Handoff Report: Milestone M5

**Reviewer**: `reviewer_m5_2` (Reviewer & Adversarial Critic)  
**Parent**: `1cc48fc5-ce57-4f48-8964-24cab4bfcacc`  
**Working Directory**: `c:\Projects\FreeExile\.agents\teamwork\reviewer_m5_2`  
**Timestamp**: `2026-10-01T22:45:00Z`  
**Verdict**: `REQUEST_CHANGES` (2 Major Functional Defects in Adversarial Edge Cases; 0 Integrity Violations)

---

## 1. Observation

### 1.1. Line Limits & Code Hygiene Verification
All 9 required files were verified independently via line count tools:
- `client/webapp/js/engine/monster_system.js`: 476 lines (Hard cap $\le 490$, Soft $\le 350$, Target $\le 480$) — **PASS**
- `client/webapp/js/engine/grid_pathfinder.js`: 300 lines (Target $\le 320$, Soft $\le 350$, Hard $\le 500$) — **PASS**
- `client/webapp/js/engine/monster_pack_system.js`: 277 lines (Target $\le 350$, Soft $\le 350$, Hard $\le 500$) — **PASS**
- `client/webapp/js/data/wilderness_zone_packs.js`: 298 lines (Target $\le 350$, Soft $\le 350$, Hard $\le 500$) — **PASS**
- `client/webapp/js/engine/ambush_trigger_system.js`: 143 lines (Target $\le 300$, Soft $\le 350$, Hard $\le 500$) — **PASS**
- `client/webapp/js/engine/boss_gate_controller.js`: 197 lines (Target $\le 200$, Soft $\le 350$, Hard $\le 500$) — **PASS**
- `client/webapp/js/engine/world_renderer.js`: 475 lines (Target $\le 500$, Hard $\le 500$) — **PASS**
- `client/webapp/index.html`: 198 lines (Target $\le 200$, Soft $\le 200$, Hard $\le 400$) — **PASS**
- `tests/unit/test_encounter_zones.py`: 259 lines (Target $\le 300$, Soft $\le 350$, Hard $\le 500$) — **PASS**
- `python tools/lint/check_code_and_doc_hygiene.py --strict`: Exited with code `0`. All modified files strictly obey hard caps.

### 1.2. Baseline Test & Benchmark Execution
- `pytest tests/unit/test_encounter_zones.py -v`: 12/12 PASSED (Exit 0)
- `pytest tests/unit/test_monster_poise_and_leash.py -v`: 10/10 PASSED (Exit 0)
- `pytest tests/e2e/test_poe2_map_system_e2e.py -v`: 81/81 PASSED (Exit 0)
- `pytest tests/unit/test_waypoint_safe_radius.py tests/unit/test_fog_and_minimap.py tests/unit/test_tile_collision.py tests/unit/test_mobile_webapp_config.py -v`: 57/57 PASSED (Exit 0)
- `node tools/perf/map_render_benchmark.js`:
  - Stationary Test (30,30): 0 re-bakes over 50 frames
  - Average Frame Time: 0.007 ms (Equivalent FPS: 151,830.3 FPS >= 30.0 FPS)
  - Draw calls / frame: avg=6.41, max=8 (Target avg <= 6.5, max <= 8)
  - Active Canvas + Grid RAM: 16.010 MB / 16.50 MB budget

### 1.3. Integrity Audit
- Source code inspected for hardcoded test results, facade logic, or test bypasses: **NONE FOUND**.
- Implementation of Supercover DDA raycasting, flat typed-array binary min-heap A*, pack leashing, and boss gate controller represents authentic logic.

### 1.4. Adversarial Edge Case Defect 1: Rapid Monster Deaths Double-Count Cleared Packs
In `client/webapp/js/engine/monster_pack_system.js` lines 160-196:
```javascript
export function registerMonsterKill(monster, activeMonsters, zoneId) {
  if (!monster || monster.isDummy) return;
  const z = zoneId || monster.zoneId || root.currentZoneId || 'zone_tang_kiem_nhai';
  const prog = root.zoneEncounterProgress?.[z];
  if (!prog) return;

  prog.totalKills = (prog.totalKills || 0) + 1;

  if (root.bossGateController && typeof root.bossGateController.reportKill === 'function') {
    root.bossGateController.currentKills = prog.totalKills;
  }

  if (monster.packId && Array.isArray(activeMonsters)) {
    const packMobs = activeMonsters.filter(m => m.packId === monster.packId);
    const allDead = packMobs.every(m => m.hp <= 0);
    if (allDead) {
      prog.clearedPacks = (prog.clearedPacks || 0) + 1;
      prog.alivePacks = Math.max(0, (prog.totalPacks || 0) - prog.clearedPacks);
      ...
```
When an AoE skill kills multiple monsters in a pack within the same tick/window (or when sequential monster deaths occur while all monsters have `hp <= 0`), `registerMonsterKill` is invoked once per dying monster.
In a simulated pack of 3 monsters (`m1, m2, m3`) where all 3 die in an AoE blast, executing `registerMonsterKill` on each mob resulted in:
```json
{"zoneId":"zone_tang_kiem_nhai","totalPacks":2,"alivePacks":0,"clearedPacks":3,"totalKills":3,"requiredPacks":2}
```
`clearedPacks` became `3` after killing only 1 pack. `alivePacks` immediately dropped from `2` to `0`, and `'encounter:pack_cleared'` fired 3 separate times for the exact same `packId`.

### 1.5. Adversarial Edge Case Defect 2: POI Ambush Fallback Minions Spawn Inside Solid Wall Tiles
In `client/webapp/js/engine/ambush_trigger_system.js` lines 46-58:
```javascript
for (let k = 0; k < waveSize; k++) {
  const angle = (k / waveSize) * Math.PI * 2 + (Math.random() - 0.5) * 0.4;
  const radius = 1.6 + Math.random() * 0.6;
  let spawnWx = poiCenterX + Math.cos(angle) * radius;
  let spawnWy = poiCenterY + Math.sin(angle) * radius;

  if (typeof root.getTileAt === 'function') {
    const t = root.getTileAt(Math.floor(spawnWx), Math.floor(spawnWy));
    if (t === 2 || t === 0 || t === 9 || t === 10 || t === 19) {
      spawnWx = poiCenterX + Math.cos(angle) * 0.8;
      spawnWy = poiCenterY + Math.sin(angle) * 0.8;
    }
  }
```
When a POI is positioned near walls, in a single-tile corridor, or near a map boundary, the fallback coordinate `poiCenterX + Math.cos(angle) * 0.8` extends 0.8 units from the center `(poi.x + 0.5)`. This places the coordinate at `poi.x + 1.3`, which rounds down to `poi.x + 1`. If the adjacent tile is a WALL (code 2), the fallback tile is never re-tested for passability.
In our adversarial test with a single-tile floor clearing surrounded by walls:
All 5 spawned ambush minions landed directly inside solid WALL tiles (`tile = 2 (BLOCKED)`).
A similar risk exists in `monster_pack_system.js` line 99: `mWx = lWx + Math.cos(theta) * 0.6;` is not re-checked against `isPassableTile`.

---

## 2. Logic Chain

1. **Pathfinding Robustness**:
   - `hasLineOfSight(x0, y0, x1, y1)`: Tested axial trajectories (`dx == 0` or `dy == 0`) with integer and floating-point inputs. Setting `tMaxX = Infinity` (or `tMaxY = Infinity`) prevents `0 * Infinity = NaN`. Output verified: `no NaN trap`, returns correct boolean across obstacles.
   - Closed room / unreachable target: A* terminates in `1-3 ms` bounded by `MAX_EXPANDED_NODES` (600) and heap capacity without infinite loops or memory leaks.
   - Diagonal corner-cutting: Setting wall at `(5, 4)` forced A* from `(4, 4)` to `(5, 5)` to route via `(4.5, 5.5)` (orthogonal detour). Direct LoS across the diagonal was correctly rejected (`false`).

2. **Safe Haven & Waypoint Safe Radius**:
   - Server-side `ZoneEngine.validate_monster_spawn`: 100% rejection of hostile spawns in `zone_player_hideout` and `zone_boundless_sanctuary`. Rejection verified at waypoint coordinate and within `safe_radius - 0.5`.
   - Client-side `isInWaypointSafeRadius`: Verified at `0.0` (true), `7.5` (true), `8.5` (false). Leashing and immunity guards in `monster_system.js` engage immediately when player enters safe radius.

3. **POI Wave Ambush Execution Count**:
   - Multiple rapid traversals (entering, leaving, and re-entering POI radius over 10 iterations): Verified `poi.isTriggered = true` prevents re-triggering. Spawns triggered exactly once (4 minions).

4. **Root Cause of Defect 1 (Double-Counting in Rapid Deaths)**:
   - Observation 1.4 demonstrates that `registerMonsterKill` does not track whether `monster.packId` has already been cleared.
   - When a pack of size $M$ dies simultaneously, all $M$ calls to `registerMonsterKill` find `packMobs.every(m => m.hp <= 0) === true`.
   - `prog.clearedPacks` increments $M$ times instead of 1.
   - In any zone where $M \ge \text{requiredPacks}$, a single pack clear clears the entire zone and unlocks the boss gate prematurely.

5. **Root Cause of Defect 2 (Spawning Minions Inside Wall Tiles)**:
   - Observation 1.5 demonstrates that `ambush_trigger_system.js` applies a single fallback offset (`0.8 * cos(angle)`) without re-validating the resulting tile against `getTileAt`.
   - In tight spaces or near boundaries, the fallback offset lands in adjacent blocked tiles or out of bounds.

---

## 3. Caveats

- In headless Node.js testing environments, Web Audio API context (`sfxEngine`) and DOM are mocked as lightweight stubs.
- Pre-existing legacy script `tools/asset_pipeline/produce_med_low_assets.py` (884 lines) exceeds soft/hard cap in hygiene check, but is pre-existing and out of scope for Milestone M5.

---

## 4. Conclusion

Milestone M5 demonstrates high algorithmic engineering quality (zero-allocation A*, DDA raycasting without NaN traps, strict line limit compliance, 150k+ FPS rendering benchmark, and zero integrity violations).
However, **2 Major functional defects** directly fail the adversarial challenge criteria specified in the dispatch:
1. `zoneEncounterProgress` double/triple-counts cleared packs during rapid/AoE deaths (`monster_pack_system.js:176`).
2. POI ambush minions spawn inside solid wall tiles when POIs are located near walls or corridors (`ambush_trigger_system.js:55`).

**Verdict: REQUEST_CHANGES**

### Required Action Items for Worker / Implementer:
1. **Fix Pack Clear Idempotency (`monster_pack_system.js`)**:
   Ensure a pack is only counted as cleared once. For example:
   ```javascript
   if (!prog.clearedPackIds) prog.clearedPackIds = new Set();
   if (allDead && !prog.clearedPackIds.has(monster.packId)) {
     prog.clearedPackIds.add(monster.packId);
     prog.clearedPacks = (prog.clearedPacks || 0) + 1;
     prog.alivePacks = Math.max(0, (prog.totalPacks || 0) - prog.clearedPacks);
     ...
   ```
2. **Fix Passable Tile Guarantee on Ambush Minion Spawns (`ambush_trigger_system.js` & `monster_pack_system.js`)**:
   Verify the fallback spawn position. If the fallback tile is still blocked, clamp `(spawnWx, spawnWy)` directly to `(poiCenterX, poiCenterY)` (which is guaranteed walkable FLOOR) or find a walkable neighbor:
   ```javascript
   if (typeof root.getTileAt === 'function') {
     let t = root.getTileAt(Math.floor(spawnWx), Math.floor(spawnWy));
     if (t === 2 || t === 0 || t === 9 || t === 10 || t === 19) {
       spawnWx = poiCenterX + Math.cos(angle) * 0.4;
       spawnWy = poiCenterY + Math.sin(angle) * 0.4;
       t = root.getTileAt(Math.floor(spawnWx), Math.floor(spawnWy));
       if (t === 2 || t === 0 || t === 9 || t === 10 || t === 19) {
         spawnWx = poiCenterX;
         spawnWy = poiCenterY;
       }
     }
   }
   ```
3. **Add Regression Tests**:
   Update `tests/unit/test_encounter_zones.py` with:
   - A test where a pack with 3-4 minions all die simultaneously (AoE), verifying `clearedPacks` increments by exactly 1.
   - A test where POI ambush triggers near a wall, verifying 100% of spawned minions are located on passable tiles.

---

## 5. Verification Method

To independently verify these findings and reproduce the defects:

1. **Reproduce Rapid Monster Death Double-Counting**:
   ```bash
   node -e "
   import('./client/webapp/js/engine/monster_pack_system.js').then(({ registerMonsterKill }) => {
     const z = 'zone_tang_kiem_nhai';
     global.currentZoneId = z;
     global.zoneEncounterProgress = {
       [z]: { zoneId: z, totalPacks: 2, alivePacks: 2, clearedPacks: 0, totalKills: 0, requiredPacks: 2 }
     };
     const m1 = { id: 'm1', packId: 'pack_1', hp: 0 };
     const m2 = { id: 'm2', packId: 'pack_1', hp: 0 };
     const m3 = { id: 'm3', packId: 'pack_1', hp: 0 };
     const activeMonsters = [m1, m2, m3];
     for (const m of activeMonsters) registerMonsterKill(m, activeMonsters, z);
     console.log('Result:', JSON.stringify(global.zoneEncounterProgress[z]));
   });
   "
   # Defect Invalidation Condition: global.zoneEncounterProgress[z].clearedPacks === 1
   ```

2. **Reproduce Minion Spawn Inside Wall Tile**:
   ```bash
   node -e "
   import('./client/webapp/js/engine/ambush_trigger_system.js').then(({ updateAmbushTriggers }) => {
     global.currentMapMetadata = { pois: [{ x: 10, y: 10, type: 15 }] };
     global.player = { wx: 10.5, wy: 10.5, hp: 100 };
     global.getTileAt = (tx, ty) => (tx === 10 && ty === 10) ? 1 : 2;
     const spawned = [];
     updateAmbushTriggers(0.016, [], (cfg) => { spawned.push(cfg); return { ...cfg, hp: 1000 }; });
     const walls = spawned.filter(m => global.getTileAt(Math.floor(m.wx), Math.floor(m.wy)) === 2);
     console.log('Spawned inside walls:', walls.length, 'out of', spawned.length);
   });
   "
   # Defect Invalidation Condition: walls.length === 0
   ```

3. **Standard Regression & Hygiene Commands**:
   ```bash
   pytest tests/unit/test_encounter_zones.py -v
   pytest tests/unit/test_monster_poise_and_leash.py -v
   pytest tests/e2e/test_poe2_map_system_e2e.py -v
   node tools/perf/map_render_benchmark.js
   python tools/lint/check_code_and_doc_hygiene.py --strict
   ```
