# Handoff Report: Challenger M5 1 (Empirical Stress Testing of Grid Pathfinder)

**Agent**: `challenger_m5_1` (Critic / Empirical Challenger / Specialist)  
**Parent**: `1cc48fc5-ce57-4f48-8964-24cab4bfcacc`  
**Timestamp**: 2026-10-01T22:45:00Z  
**Verdict**: `REQUEST_CHANGES`

---

## 1. Observation

1. **DDA Raycaster Premature Loop Termination (`hasLineOfSight`)**:
   In `client/webapp/js/engine/grid_pathfinder.js`, lines 113–126:
   ```javascript
   let currX = tx0, currY = ty0, safety = 64;

   while ((currX !== tx1 || currY !== ty1) && --safety > 0) {
     if (Math.abs(tMaxX - tMaxY) < 1e-5) {
       if (isTileImpassable(currX + stepX, currY) || isTileImpassable(currX, currY + stepY)) return false;
       currX += stepX; currY += stepY; tMaxX += tDeltaX; tMaxY += tDeltaY;
     } else if (tMaxX < tMaxY) {
       currX += stepX; tMaxX += tDeltaX;
     } else {
       currY += stepY; tMaxY += tDeltaY;
     }
     if (isTileImpassable(currX, currY)) return false;
   }
   return true;
   ```
   Direct empirical test:
   ```bash
   node --input-type=module -e "
   import { hasLineOfSight } from './client/webapp/js/engine/grid_pathfinder.js';
   global.currentMapWidth = 120; global.currentMapHeight = 90;
   const grid = new Uint8Array(120 * 90); grid.fill(1);
   grid[10 * 120 + 75] = 2; // WALL at (75, 10)
   global.getTileAt = (x, y) => grid[y * 120 + x];
   console.log('Result:', hasLineOfSight(10.5, 10.5, 90.5, 10.5));
   "
   ```
   **Result**: `true` (Verbatim output: `Result: true`).
   A wall directly blocks the ray at tile $(75, 10)$, yet `hasLineOfSight` returned `true` because the ray exceeded 64 steps, the loop terminated when `--safety > 0` became false, and the function fell through to line 126 (`return true;`).

2. **A* Path Buffer Truncation & Waypoint Disconnection (`findPath`)**:
   In `client/webapp/js/engine/grid_pathfinder.js`, lines 193–208:
   ```javascript
   let curr = targetIdx, count = 0;
   while (curr !== -1 && count < MAX_PATH_STEPS) {
     RECONSTRUCT_X[count] = (curr % mapW) + 0.5;
     RECONSTRUCT_Y[count] = Math.floor(curr / mapW) + 0.5;
     count++;
     curr = cameFrom[curr];
   }

   let outCount = 0;
   for (let i = count - 1; i >= 0; i--) {
     if (i === count - 1 && Math.floor(RECONSTRUCT_X[i]) === startTx && Math.floor(RECONSTRUCT_Y[i]) === startTy && count > 1) continue;
     outX[outCount] = RECONSTRUCT_X[i];
     outY[outCount] = RECONSTRUCT_Y[i];
     outCount++;
   }
   return outCount;
   ```
   Direct empirical test on a 120x90 grid:
   ```bash
   node --input-type=module -e "
   import { findPath } from './client/webapp/js/engine/grid_pathfinder.js';
   global.currentMapWidth = 120; global.currentMapHeight = 90;
   const grid = new Uint8Array(120 * 90); grid.fill(1);
   global.getTileAt = (x, y) => grid[y * 120 + x];
   const outX = new Float32Array(64), outY = new Float32Array(64);
   const count = findPath(10, 10, 90, 10, outX, outY);
   console.log('Count:', count, 'First WP:', outX[0], outY[0], 'Dist:', Math.hypot(outX[0] - 10.5, outY[0] - 10.5));
   "
   ```
   **Result**:
   `Count: 64 First WP: 27.5 10.5 Dist: 17`
   The start was $(10, 10)$. The first waypoint returned in `outX` is $(27.5, 10.5)$, which is **17 tiles away** from the start. Backtracing begins from `targetIdx` (goal) and caps at 64 steps, discarding the initial path steps nearest the starting position.

3. **Empirical Stress Test Suite Results (`tools/perf/stress_test_grid_pathfinder.js`)**:
   Execution command:
   ```bash
   node --expose-gc tools/perf/stress_test_grid_pathfinder.js
   ```
   Verbatim output summary:
   - **Zero-Heap Allocation & GC**: 5,000 queries in 1,307.33 ms (3,824.6 qps). `gcEvents = 0` (Zero GC thrashing). Retained heap growth after GC is 0 bytes (`-27,160 bytes` measured in isolation).
   - **Obstacle Avoidance & Strict Corner-Cutting**:
     - L-Shape Layout (81 paths): 0 impassable tiles, 0 corner cuts (PASS).
     - U-Shape Layout (7 paths): 0 impassable tiles, 0 corner cuts (PASS).
     - Complex Maze Layout (167 paths): 0 impassable tiles, 0 corner cuts (PASS).
     - Passability by tile code (Standard 0, 2, 3, 9, 10, 19; Compact 1, 4, 5, 15): 100% blocked (PASS).
     - Boss gate dynamic unlock: Locked = blocked, Breached = passable (PASS).
   - **DDA Line-of-Sight Stress (10,000 queries)**:
     - 2,500 Pure Vertical (`dx=0`): 0 NaNs, 0 lockups.
     - 2,500 Pure Horizontal (`dy=0`): 0 NaNs, 0 lockups.
     - 2,500 Diagonal Lines: 0 NaNs, 0 lockups.
     - Short-Range Analytical (<64 steps): 7,638 queries tested against exact analytical Liang-Barsky line-box intersection oracle $\rightarrow$ 7,638 matches, **0 mismatches (100.00% agreement)**.
     - Long-Range ($\ge 64$ steps): **121 false-positive raycasts** where `hasLineOfSight` returned `true` through walls because the ray aborted at `safety = 64`.

4. **Non-Regression Test Suites**:
   - `pytest tests/unit/test_encounter_zones.py -v`: 12/12 PASSED (0.92s, Exit 0)
   - `pytest tests/unit/test_tile_collision.py -v`: 11/11 PASSED (0.16s, Exit 0)
   - `pytest tests/e2e/test_poe2_map_system_e2e.py -v`: 81/81 PASSED (1.21s, Exit 0)
   - `python tools/lint/check_code_and_doc_hygiene.py --strict`: Passed without hard cap violations; `tools/perf/stress_test_grid_pathfinder.js` is 270 lines ($\le 350$ lines soft cap).

---

## 2. Logic Chain

1. **Step 1 — Zero-Allocation & Corner-Cutting Verification**:
   Observation 3 confirms that within short-to-medium ranges, `GridPathfinder` achieves 0 GC thrashing (0 GC events over 5,000 queries) and strict corner-cutting avoidance (0 corner cuts across 255 paths across L-shape, U-shape, and complex maze layouts). The diagonal neighbor check in lines 173–174 (`isTileImpassable(cx + DIR_X[d], cy) || isTileImpassable(cx, cy + DIR_Y[d])`) is mathematically sound and strictly enforced.

2. **Step 2 — Root Cause of False Line-of-Sight (Observation 1 & 3)**:
   In `hasLineOfSight`, `safety = 64` acts as a step counter. On a 120x90 grid (the canonical wilderness map dimension), the Manhattan distance between opposite corners is $120 + 90 = 210$ steps, and Euclidean diagonal is $\approx 150$ tiles. When ray length $\ge 64$ steps, the while loop exits before `(currX === tx1 && currY === ty1)`. Instead of checking whether the ray reached the target, the function executes line 126: `return true;`. This falsely reports that line-of-sight exists through all obstacles beyond step 64.

3. **Step 3 — Blast Radius of False Line-of-Sight**:
   In `steerMonsterChase`:
   ```javascript
   if (hasLineOfSight(monster.wx, monster.wy, targetWx, targetWy)) {
     if (monster.pathLength > 0) { monster.pathLength = 0; monster.pathIndex = 0; }
     STATIC_STEER_RESULT.vx = (dx / distToPlayer) * stepSize;
     STATIC_STEER_RESULT.vy = (dy / distToPlayer) * stepSize;
     STATIC_STEER_RESULT.isDirectLoS = true; STATIC_STEER_RESULT.hasPath = true;
     return STATIC_STEER_RESULT;
   }
   ```
   When a player is $> 64$ steps away across the map, monsters will falsely detect direct line-of-sight through intervening maze walls, discard their A* path, and march directly into walls.

4. **Step 4 — Root Cause & Impact of Path Truncation (Observation 2)**:
   In `findPath`, path reconstruction traces backwards from `targetIdx` (goal) to `startIdx` using `cameFrom`. By terminating after `count < MAX_PATH_STEPS` (64), it captures the 64 steps nearest the goal and discards the steps closest to the monster. When reversed into `outX`, the first waypoint `outX[0]` is separated from the monster by $N - 64$ tiles (e.g. 17 tiles away). The monster is told to navigate directly toward this distant waypoint, bypassing local collision geometry.

---

## 3. Caveats

- **Existing Tests Scope**: Existing test suites (`test_encounter_zones.py`, `test_tile_collision.py`) did not catch these issues because they only test small grids (e.g. 10x10) or short distances ($\le 10$ tiles).
- **Short-Range Soundness**: Within 63 steps, `grid_pathfinder.js` exhibits 100.00% precision and zero NaN lockups. The defects manifest exclusively on long-range queries ($\ge 64$ steps) on full-scale 120x90 maps.

---

## 4. Conclusion

**Verdict: `REQUEST_CHANGES`**

While the core A* min-heap and short-range DDA math are high-performance and zero-allocation, two significant defects must be resolved before approval:
1. **Critical Defect (`hasLineOfSight`)**:
   - `safety` counter must scale to map dimensions or ray length: e.g. `const safetyLimit = Math.abs(tx1 - tx0) + Math.abs(ty1 - ty0) + 4;`
   - If the while loop terminates without reaching the target, it must return `false`:
     `return (currX === tx1 && currY === ty1);` (NOT unconditional `return true;`).
2. **High Defect (`findPath`)**:
   - Path reconstruction must retain the path segment starting at `startTx, startTy`, rather than truncating the beginning. Either allocate buffer space for full map paths (e.g. `MAX_PATH_STEPS = 256`), or reconstruct the chain and preserve the first 64 steps originating from the start.

---

## 5. Verification Method

To independently verify all findings and reproduce the exact failures:

```bash
# 1. Run empirical challenger stress harness (demonstrates 121 LoS false positives & 17-tile waypoint jump)
node --expose-gc tools/perf/stress_test_grid_pathfinder.js

# 2. Direct single-line reproduction of DDA safety abort bug
node --input-type=module -e "import { hasLineOfSight } from './client/webapp/js/engine/grid_pathfinder.js'; global.currentMapWidth = 120; global.currentMapHeight = 90; const g = new Uint8Array(10800).fill(1); g[10*120+75] = 2; global.getTileAt = (x,y) => g[y*120+x]; console.log('LoS past wall at 75:', hasLineOfSight(10.5, 10.5, 90.5, 10.5));"

# 3. Direct single-line reproduction of path truncation disconnect bug
node --input-type=module -e "import { findPath } from './client/webapp/js/engine/grid_pathfinder.js'; global.currentMapWidth = 120; global.currentMapHeight = 90; const g = new Uint8Array(10800).fill(1); global.getTileAt = (x,y) => g[y*120+x]; const ox = new Float32Array(64), oy = new Float32Array(64); findPath(10, 10, 90, 10, ox, oy); console.log('First WP:', ox[0], oy[0], 'Dist from start:', Math.hypot(ox[0]-10.5, oy[0]-10.5));"

# 4. Run baseline regression suites
pytest tests/unit/test_encounter_zones.py -v
pytest tests/unit/test_tile_collision.py -v
pytest tests/e2e/test_poe2_map_system_e2e.py -v

# 5. Strict code hygiene check
python tools/lint/check_code_and_doc_hygiene.py --strict
```
