# TECHNICAL INVESTIGATION & WORKER HANDOFF REPORT: MILESTONE 1 (SERVER ZONE GATING & SAFE HAVEN PURITY)

**Author**: `teamwork_preview_explorer` (M1 Server Registration & Gating Explorer)  
**Target Milestone**: Milestone 1 (Server Zone Gating & Safe Haven Purity)  
**Working Directory**: `c:\Projects\FreeExile\.agents\teamwork\explorer_m1_1`  
**Date**: 2026-09-30 (UTC)  
**Verified Patch**: `c:\Projects\FreeExile\.agents\teamwork\explorer_m1_1\m1_server_zone_gating.patch`  

---

## 1. Observation

### 1.1. Missing Canonical Hideout Registration in Zone Catalog
- **File**: `server/world/zone_catalog.py` (Lines 29-98)
- In `_register_zones(engine: ZoneEngine)`, the server registers `zone_boundless_sanctuary` as `ZoneType.SANCTUARY` (lines 31-98) followed immediately by 9 outer open-world zones (`zone_tang_kiem_nhai`, etc., lines 101-497).
- **Direct Observation**: `zone_player_hideout` is completely absent from `_register_zones()`.
- **Runtime Error**: When invoking `zone_engine.get_zone("zone_player_hideout")` or attempting to spawn a player into their hideout:
  ```python
  from server.world.zone_engine import ZoneEngine
  ze = ZoneEngine()
  ze.spawn_player("player_hero", "zone_player_hideout")
  ```
  Execution terminates verbatim with:
  ```
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "C:\Projects\FreeExile\server\world\zone_engine.py", line 152, in spawn_player
      zone = self.get_zone(zone_id)
             ^^^^^^^^^^^^^^^^^^^^^^
    File "C:\Projects\FreeExile\server\world\zone_engine.py", line 86, in get_zone
      raise KeyError(f"Zone '{zone_id}' not found in registry.")
  KeyError: "Zone 'zone_player_hideout' not found in registry."
  ```

### 1.2. Ad-hoc String Check in Zone Engine
- **File**: `server/world/zone_engine.py` (Lines 106-117)
- In `ZoneEngine.can_spawn_hostile_monsters(zone_id)`:
  ```python
  106:     def can_spawn_hostile_monsters(self, zone_id: str) -> bool:
  107:         """
  108:         PoE2 Rule: Safe Havens (Sanctuary Hub and Player Hideout) prohibit hostile mobs.
  109:         Only OPEN_WORLD, DUNGEON_INSTANCE, and SECRET_CHAMBER allow hostile encounters.
  110:         """
  111:         if zone_id == "zone_player_hideout":
  112:             return False
  113:         zone = self.zones.get(zone_id)
  114:         if zone is None:
  115:             return False
  116:         return zone.zone_type in (ZoneType.OPEN_WORLD, ZoneType.DUNGEON_INSTANCE, ZoneType.SECRET_CHAMBER)
  ```
- Line 111 contains a hardcoded workaround `if zone_id == "zone_player_hideout": return False` introduced specifically because `zone_player_hideout` was missing from `self.zones`. Once `zone_player_hideout` is registered as `ZoneType.SANCTUARY`, line 116 natively evaluates to `False`.

### 1.3. Duplicate Method Definition in Zone Engine
- **File**: `server/world/zone_engine.py` (Lines 89-94)
- Direct inspection reveals two identical method names defined contiguously:
  ```python
  89:     def get_zone_definition(self, zone_id: str) -> ZoneDefinition:
  90:         """Alias for get_zone for backward compatibility."""
  91:         return self.get_zone(zone_id)
  92: 
  93:     def get_zone_definition(self, zone_id: str) -> Optional[ZoneDefinition]:
  94:         return self.zones.get(zone_id)
  ```
- In Python, the second definition (`lines 93-94`) silently clobbers the first (`lines 89-91`) in the class dict at load time. `tests/unit/test_npc_system.py:338` depends on `get_zone_definition` returning `Optional[ZoneDefinition]` (`assert sandstorm_zone is not None`).

### 1.4. Data Class Schema in Zone Types
- **File**: `server/world/zone_types.py` (Lines 56-74)
- `ZoneDefinition` is defined as:
  ```python
  @dataclass(slots=True, frozen=True)
  class ZoneDefinition:
      zone_id: str
      name: str
      zone_type: ZoneType
      environment: ZoneEnvironment
      min_level: int
      max_players: int
      bounds_width: float
      bounds_height: float
      default_spawn_x: float
      default_spawn_y: float
      respawn_zone_id: str
      respawn_x: float
      respawn_y: float
      waypoints: Dict[str, Waypoint] = field(default_factory=dict)
      portals: Dict[str, ZonePortal] = field(default_factory=dict)
      npc_ids: List[str] = field(default_factory=list)
  ```
- Note that `ZoneDefinition` uses `name` (not `display_name`) and does not declare `max_level`.
- Passing unexpected keywords `display_name` or `max_level` raises `TypeError: ZoneDefinition.__init__() got an unexpected keyword argument`.
- By adding `max_level: int = 100` (after `respawn_y: float`) and `@property def display_name(self) -> str: return self.name`, `ZoneDefinition` supports both conventions without breaking existing positional or keyword callers.

### 1.5. File Length & Hygiene Thresholds
- Tool command: `python tools/lint/check_code_and_doc_hygiene.py --strict`
- Thresholds:
  - Static catalogs (`*_catalog.py`): Soft Cap $\le 700$ lines, Hard Cap $\le 1000$ lines.
  - Logic code (`zone_engine.py`): Soft Cap $\le 350$ lines, Hard Cap $\le 500$ lines.
- Current lengths:
  - `server/world/zone_catalog.py`: 570 lines (Adding ~45 lines $\rightarrow$ ~615 lines, well below 700 soft cap).
  - `server/world/zone_engine.py`: 452 lines (Removing duplicate method and hardcoded check removes 6 lines $\rightarrow$ 446 lines, comfortably under 500 hard cap).
  - `server/world/zone_types.py`: 150 lines (Adding ~6 lines $\rightarrow$ 156 lines).
  - `tests/unit/test_zone_monster_spawning_rules.py`: 94 lines (Adding ~70 lines $\rightarrow$ ~164 lines).

---

## 2. Logic Chain

1. **Premise 1 (PoE2 Safe Haven Purity)**:
   - According to `PROJECT.md` (§Feature 1) and `ORIGINAL_REQUEST.md` (§R1), Player Hideouts (`zone_player_hideout`) and Town Hubs (`zone_boundless_sanctuary`) must be 100% peaceful zones with zero hostile monsters.
2. **Premise 2 (Server Authority & Spatial Isolation)**:
   - In `ZoneEngine.add_zone()`, each registered zone receives its own dedicated `SpatialGrid(cell_size=64.0)`.
   - Players spawning into a zone (`spawn_player(player_id, zone_id)`) require `self.get_zone(zone_id)`.
3. **Deduction 1 (Root Cause of Hideout Instantiation Failure)**:
   - Because `zone_player_hideout` was missing in `_register_zones()` in `server/world/zone_catalog.py`, `ZoneEngine` never created a `SpatialGrid` for it, and any player teleport or spawn into Hideout resulted in `KeyError` (Observation 1.1).
4. **Deduction 2 (Remediation of Gating Architecture)**:
   - Registering `zone_player_hideout` canonically in `zone_catalog.py` as `ZoneType.SANCTUARY` solves the lookup failure.
   - Consequently, in `ZoneEngine.can_spawn_hostile_monsters(zone_id)`:
     ```python
     zone = self.zones.get(zone_id)
     if zone is None:
         return False
     return zone.zone_type in (ZoneType.OPEN_WORLD, ZoneType.DUNGEON_INSTANCE, ZoneType.SECRET_CHAMBER)
     ```
     For `zone_player_hideout`, `zone.zone_type` is `ZoneType.SANCTUARY`, so the expression naturally evaluates to `False`. The hardcoded string check `if zone_id == "zone_player_hideout": return False` is completely redundant and must be pruned (Observation 1.2).
5. **Deduction 3 (Behavior of `validate_monster_spawn`)**:
   - `validate_monster_spawn(zone_id, is_dummy)`:
     - When `is_dummy=True`: returns `(True, "Khôi Lỗi Luyện Võ (Target Dummy) được phép kích hoạt...")`. Target Dummy is permitted in any zone.
     - When `is_dummy=False`: evaluates `not self.can_spawn_hostile_monsters(zone_id)`. In `zone_player_hideout`, `can_spawn_hostile_monsters` returns `False`, so `not False` is `True`, rejecting hostile mobs with reason: `"Nghiêm cấm sản sinh quái vật trong khu vực An Toàn (Sanctuary/Hideout: [zone_player_hideout]) theo chuẩn PoE2!"`.
6. **Deduction 4 (Safe Deduplication of `get_zone_definition`)**:
   - Removing lines 89-91 and keeping lines 93-94 as `def get_zone_definition(self, zone_id: str) -> Optional[ZoneDefinition]: return self.zones.get(zone_id)` preserves the exact runtime lookup behavior required by `tests/unit/test_npc_system.py:338` while removing the syntax warning and duplicate definition (Observation 1.3).
7. **Deduction 5 (Database Seeder Compatibility)**:
   - In `server/world/game_design_matrix_seeder.py:288`, `_resolve_zone_act()` maps zones to Acts. Adding `"zone_player_hideout": "ACT_I_HUNGER"` ensures that running `python tools/lint/verify_game_design_matrix.py` synchronizes the new zone with zero warnings and 100% integrity.

---

## 3. Caveats

1. **Client-Side Entity Flusher & Telemetry (Milestone 2 Scope)**:
   - Milestone 1 strictly governs server-authoritative registration, spatial grid initialization, and spawn validation. The client-side Target Dummy rolling DPS telemetry (`TargetDummyTelemetry`) and entity array flushing on portal transit belong to Milestone 2.
2. **Backward Compatibility of `ZoneDefinition`**:
   - `ZoneDefinition` must keep all existing positional fields intact. `max_level: int = 100` is added with a default value after `respawn_y: float` so that all existing calls across `zone_catalog.py` continue to work without positional signature breakage.
3. **Portal Network Geometry**:
   - `zone_boundless_sanctuary` has portals at West (`-380, 0`), North (`0, 380`), and South (`0, -380`). The East coordinate (`380, 0`) is unoccupied; we designate East as `portal_sanctuary_to_hideout` and South (`0, -350`) in hideout as `portal_hideout_to_sanctuary`.

---

## 4. Conclusion & Actionable Implementation Plan

The Worker should apply the pre-verified patch file located at:  
`c:\Projects\FreeExile\.agents\teamwork\explorer_m1_1\m1_server_zone_gating.patch`  
or execute the changes as specified in the before/after snippets below.

### 4.1. File 1: `server/world/zone_types.py`
Add `max_level: int = 100` and `@property def display_name` to `ZoneDefinition`:
```python
# Before (lines 70-74):
    respawn_x: float
    respawn_y: float
    waypoints: Dict[str, Waypoint] = field(default_factory=dict)
    portals: Dict[str, ZonePortal] = field(default_factory=dict)
    npc_ids: List[str] = field(default_factory=list)

# After:
    respawn_x: float
    respawn_y: float
    max_level: int = 100
    waypoints: Dict[str, Waypoint] = field(default_factory=dict)
    portals: Dict[str, ZonePortal] = field(default_factory=dict)
    npc_ids: List[str] = field(default_factory=list)

    @property
    def display_name(self) -> str:
        """Alias for name attribute to maintain compatibility."""
        return self.name
```

### 4.2. File 2: `server/world/zone_catalog.py`
1. Add portal to hideout in `zone_boundless_sanctuary`:
```python
# Before (lines 89-91):
                    min_level=20,
                ),
            },

# After:
                    min_level=20,
                ),
                "portal_sanctuary_to_hideout": ZonePortal(
                    portal_id="portal_sanctuary_to_hideout",
                    name="Lối Vào: Tiên Phủ Động Thiên",
                    source_zone_id="zone_boundless_sanctuary",
                    source_x=380.0,
                    source_y=0.0,
                    target_zone_id="zone_player_hideout",
                    target_x=0.0,
                    target_y=0.0,
                    min_level=1,
                ),
            },
```

2. Register `zone_player_hideout` right after `zone_boundless_sanctuary`:
```python
    # 1b. Sanctuary: Player Hideout (Tiên Phủ Động Thiên)
    engine.add_zone(
        ZoneDefinition(
            zone_id="zone_player_hideout",
            name="Tiên Phủ Động Thiên (Hideout)",
            zone_type=ZoneType.SANCTUARY,
            environment=ZoneEnvironment.NORMAL,
            min_level=1,
            max_players=5000,
            bounds_width=800.0,
            bounds_height=800.0,
            default_spawn_x=0.0,
            default_spawn_y=0.0,
            respawn_zone_id="zone_player_hideout",
            respawn_x=0.0,
            respawn_y=0.0,
            max_level=100,
            waypoints={
                "wp_hideout_central": Waypoint(
                    waypoint_id="wp_hideout_central",
                    name="Trụ Đá Thần Hành: Tiên Phủ Động Thiên",
                    zone_id="zone_player_hideout",
                    x=0.0,
                    y=0.0,
                    is_unlocked_by_default=True,
                )
            },
            portals={
                "portal_hideout_to_sanctuary": ZonePortal(
                    portal_id="portal_hideout_to_sanctuary",
                    name="Lối Ra: Doanh Trại Bến Lưu Đày",
                    source_zone_id="zone_player_hideout",
                    source_x=0.0,
                    source_y=-350.0,
                    target_zone_id="zone_boundless_sanctuary",
                    target_x=360.0,
                    target_y=0.0,
                    min_level=1,
                )
            },
            npc_ids=[],
        )
    )
```

### 4.3. File 3: `server/world/zone_engine.py`
1. Clean duplicate method:
```python
# Before (lines 89-94):
    def get_zone_definition(self, zone_id: str) -> ZoneDefinition:
        """Alias for get_zone for backward compatibility."""
        return self.get_zone(zone_id)

    def get_zone_definition(self, zone_id: str) -> Optional[ZoneDefinition]:
        return self.zones.get(zone_id)

# After:
    def get_zone_definition(self, zone_id: str) -> Optional[ZoneDefinition]:
        """Safe lookup alias for zone definition; returns None if not found."""
        return self.zones.get(zone_id)
```

2. Prune hardcoded string check in `can_spawn_hostile_monsters`:
```python
# Before (lines 106-117):
    def can_spawn_hostile_monsters(self, zone_id: str) -> bool:
        """
        PoE2 Rule: Safe Havens (Sanctuary Hub and Player Hideout) prohibit hostile mobs.
        Only OPEN_WORLD, DUNGEON_INSTANCE, and SECRET_CHAMBER allow hostile encounters.
        """
        if zone_id == "zone_player_hideout":
            return False
        zone = self.zones.get(zone_id)
        if zone is None:
            return False
        return zone.zone_type in (ZoneType.OPEN_WORLD, ZoneType.DUNGEON_INSTANCE, ZoneType.SECRET_CHAMBER)

# After:
    def can_spawn_hostile_monsters(self, zone_id: str) -> bool:
        """
        PoE2 Rule: Safe Havens (Sanctuary Hub and Player Hideout) prohibit hostile mobs.
        Only OPEN_WORLD, DUNGEON_INSTANCE, and SECRET_CHAMBER allow hostile encounters.
        """
        zone = self.zones.get(zone_id)
        if zone is None:
            return False
        return zone.zone_type in (ZoneType.OPEN_WORLD, ZoneType.DUNGEON_INSTANCE, ZoneType.SECRET_CHAMBER)
```

### 4.4. File 4: `server/world/game_design_matrix_seeder.py`
Add `"zone_player_hideout": "ACT_I_HUNGER"` to `_resolve_zone_act()`:
```python
# Before (lines 288-294):
def _resolve_zone_act(zone_id: str) -> str:
    zone_act_map = {
        "zone_boundless_sanctuary": "ACT_I_HUNGER",
        "zone_tang_kiem_nhai": "ACT_I_HUNGER",
        "zone_ancient_sword_barrow": "ACT_II_MIASMA",
        "zone_boundless_sandstorm": "ACT_III_CRUCIBLE",
    }

# After:
def _resolve_zone_act(zone_id: str) -> str:
    zone_act_map = {
        "zone_boundless_sanctuary": "ACT_I_HUNGER",
        "zone_player_hideout": "ACT_I_HUNGER",
        "zone_tang_kiem_nhai": "ACT_I_HUNGER",
        "zone_ancient_sword_barrow": "ACT_II_MIASMA",
        "zone_boundless_sandstorm": "ACT_III_CRUCIBLE",
    }
```

### 4.5. File 5: `tests/unit/test_zone_monster_spawning_rules.py`
Add tests for:
- `test_hideout_zone_registration_and_metadata`
- `test_hideout_player_spawning_and_spatial_grid`
- `test_portal_traversal_sanctuary_hideout_roundtrip`
- `test_clean_get_zone_definition`
- Hideout hostile spawn rejection and target dummy acceptance in `test_validate_monster_spawn_enforcement`.

---

## 5. Verification Method

To independently verify the implementation:

### 5.1. Git Patch Dry-Run Verification
Run:
```pwsh
git apply --check -v .agents/teamwork/explorer_m1_1/m1_server_zone_gating.patch
```
*Expected Result*: Exits with code 0, reporting all 5 files checked successfully with zero hunk failures.

### 5.2. Unit Test Suite Execution
Run:
```pwsh
pytest tests/unit/test_zone_monster_spawning_rules.py tests/unit/test_map_instance_system.py -v
```
*Expected Result*: 100% of test cases pass (estimated 18 passed in $< 0.3s$).

### 5.3. Pre-Flight Game Design Matrix & Integrity Gate
Run:
```pwsh
python tools/lint/verify_game_design_matrix.py
```
*Expected Result*: Exits with code 0 (`SUCCESS: Code, Central Database, and Documentation are 100% IN SYNC.`).

### 5.4. Code Hygiene Threshold Check
Run:
```pwsh
python tools/lint/check_code_and_doc_hygiene.py --strict
```
*Expected Result*:
- `server/world/zone_catalog.py`: $\sim 615$ lines ($\le 700$ Soft Cap).
- `server/world/zone_engine.py`: $\sim 446$ lines ($< 500$ Hard Cap).
- Zero new Hard Cap violations.

### 5.5. Python Interactive Verification Script
```python
from server.world.zone_engine import ZoneEngine
from server.world.zone_types import ZoneType

ze = ZoneEngine()

# 1. Registration check
hideout = ze.get_zone("zone_player_hideout")
assert hideout.zone_type == ZoneType.SANCTUARY
assert hideout.name == "Tiên Phủ Động Thiên (Hideout)"
assert hideout.max_level == 100

# 2. Player spawn check & spatial grid
loc = ze.spawn_player("player_test", "zone_player_hideout")
assert loc.zone_id == "zone_player_hideout"
assert "zone_player_hideout" in ze.zone_spatial_grids

# 3. Gating check
assert ze.can_spawn_hostile_monsters("zone_player_hideout") is False
can_mob, _ = ze.validate_monster_spawn("zone_player_hideout", is_dummy=False)
assert can_mob is False
can_dummy, _ = ze.validate_monster_spawn("zone_player_hideout", is_dummy=True)
assert can_dummy is True

# 4. Clean get_zone_definition
assert ze.get_zone_definition("zone_player_hideout") is not None
assert ze.get_zone_definition("invalid_zone") is None
print("ALL M1 INVARIANTS VERIFIED!")
```
