# Dispatch: Remediation Worker M5 Fix 1 (worker_m5_fix_1)

**Target Role**: `teamwork_preview_worker`
**Working Directory**: `c:\Projects\FreeExile\.agents\teamwork\worker_m5_fix_1`
**Authoritative User Request**: `c:\Projects\FreeExile\.agents\teamwork\ORIGINAL_REQUEST.md` (specifically section `## 2026-10-01T19:19:13Z` - R5)

---

## 🔒 MANDATORY INTEGRITY WARNING
DO NOT CHEAT. All implementations must be genuine. DO NOT hardcode test results, create dummy/facade implementations, or circumvent the intended task. A `teamwork_preview_auditor` will independently verify your work. Integrity violations WILL be detected and your work WILL be rejected.

---

## Adversarial Review Reports to Remediate:
- `c:\Projects\FreeExile\.agents\teamwork\reviewer_m5_2\handoff.md` (Pack clear idempotency, POI ambush minion wall fallback)
- `c:\Projects\FreeExile\.agents\teamwork\challenger_m5_1\handoff.md` (DDA raycast `safety = 64` abort returning false positive, A* path truncation disconnecting waypoints)

---

## 1. Concrete Remediation Tasks

### Task 1: Fix Pack Clear Idempotency in `client/webapp/js/engine/monster_pack_system.js`
In `registerMonsterKill(monster, activeMonsters, zoneId)` around lines 160–196:
- Prevent multiple increments to `prog.clearedPacks` when multiple monsters in the same pack die simultaneously (e.g., from an AoE attack).
- Use a `Set` or boolean tracking on `prog`:
  ```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);
    ...
  ```
- Also check line 99: `mWx = lWx + Math.cos(theta) * 0.6;`. Re-validate `isPassableTile(mWx, mWy)`: if blocked, fall back to `(lWx, lWy)`.
- Keep `monster_pack_system.js` strictly $\le 350$ lines.

### Task 2: Fix Ambush Minion Wall Fallback in `client/webapp/js/engine/ambush_trigger_system.js`
In `updateAmbushTriggers` lines 46–65:
- Re-check the fallback coordinate with `root.getTileAt`. If still blocked, clamp `(spawnWx, spawnWy)` directly to `(poiCenterX, poiCenterY)` (which is guaranteed walkable floor):
  ```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;
      }
    }
  }
  ```
- Keep `ambush_trigger_system.js` strictly $\le 300$ lines.

### Task 3: Fix DDA Raycaster Premature Loop Exit in `client/webapp/js/engine/grid_pathfinder.js`
In `hasLineOfSight(x0, y0, x1, y1)` around lines 113–126:
- Change loop safety limit from hardcoded 64 to dynamic size:
  `let safety = Math.max(256, (mapW + mapH) * 2);`
- In the while loop termination condition and return statement:
  Never return `true` if the ray did not reach the target tile!
  At the end of the function:
  `return (currX === tx1 && currY === ty1);`
  If the loop exited before reaching `(tx1, ty1)` (e.g. out of steps or safety limit reached), return `false`!

### Task 4: Fix Path Reconstruction Buffer Truncation in `client/webapp/js/engine/grid_pathfinder.js`
In `findPath` around lines 193–208:
- Increase static reconstruct buffers to 256:
  `const RECONSTRUCT_X = new Float32Array(256);`
  `const RECONSTRUCT_Y = new Float32Array(256);`
- Backtrace until `curr === -1 || count >= 256`.
- When outputting to `outX, outY`:
  Iterate from `count - 1` down to 0 (start to goal).
  Output the FIRST `Math.min(count, maxOut)` waypoints starting from adjacent to `(startTx, startTy)`:
  This ensures `outX[0], outY[0]` is ALWAYS the immediate next step adjacent to the monster, rather than 17+ tiles away!
- Keep `grid_pathfinder.js` strictly $\le 320$ lines.

### Task 5: Add Regression Tests in `tests/unit/test_encounter_zones.py`
Add unit tests verifying:
1. Simultaneous AoE death of 3 pack mobs increments `clearedPacks` by exactly 1.
2. POI ambush adjacent to a 1-tile wall spawns 100% of minions on passable tiles.
3. `hasLineOfSight` across a 75-tile line with a wall at step 70 returns `false`.
4. `findPath` on an 80-tile open line returns `outX[0]` adjacent to start ($\text{dist} \le 1.5$ tiles).
- Keep `tests/unit/test_encounter_zones.py` strictly $\le 300$ lines.

---

## 2. Verification Commands
Execute and verify:
1. `pytest tests/unit/test_encounter_zones.py -v`
2. `node --expose-gc tools/perf/stress_test_grid_pathfinder.js` (Must PASS with 0 false positives)
3. `pytest tests/unit/test_waypoint_safe_radius.py -v`
4. `pytest tests/unit/test_monster_poise_and_leash.py -v`
5. `pytest tests/unit/test_fog_and_minimap.py -v`
6. `pytest tests/unit/test_tile_collision.py -v`
7. `pytest tests/e2e/test_poe2_map_system_e2e.py -v`
8. `node tools/perf/map_render_benchmark.js` (Must PASS with 0 stationary re-bakes)
9. `pytest tests/unit/ -q` (all unit tests must PASS)
10. `python tools/lint/check_code_and_doc_hygiene.py --strict` (0 violations)

Write `handoff.md` in your working directory and notify parent when complete.

## 2026-10-01T22:42:01Z
[Message] timestamp=2026-10-01T22:42:01Z sender=1cc48fc5-ce57-4f48-8964-24cab4bfcacc priority=MESSAGE_PRIORITY_HIGH content=You are M5 Remediation Worker (worker_m5_fix_1).
Your working directory is: c:\Projects\FreeExile\.agents\teamwork\worker_m5_fix_1
Your dispatch assignment is: c:\Projects\FreeExile\.agents\teamwork\worker_m5_fix_1\DISPATCH.md
MANDATORY: Read the authoritative user request at:
c:\Projects\FreeExile\.agents\teamwork\ORIGINAL_REQUEST.md (specifically section ## 2026-10-01T19:19:13Z - R5).
Also read c:\Projects\FreeExile\GEMINI.md, c:\Projects\FreeExile\AGENTS.md, c:\Projects\FreeExile\.agents\teamwork\reviewer_m5_2\handoff.md, and c:\Projects\FreeExile\.agents\teamwork\challenger_m5_1\handoff.md.
