# 62. Đặc Tả Kiến Trúc Khắc Phục Lỗ Hổng Tự Hành & Chuẩn Hóa Vòng Lặp Khép Kín (Autonomy Gap Resolution Architecture)

- **Mã tài liệu**: `SPEC-DOC-62-AUTONOMY-GAP-RESOLUTION-20260915`
- **Mốc thời gian**: 15/09/2026 (Bối cảnh Path of Exile 2 v0.5.5+, client build `6AA2213C`, AutoPOE2 IPC `PROTOCOL_VERSION = 0x00050005`)
- **Tài liệu căn cứ SSoT**: [`docs/development/61_autonomy_gap_audit_and_next_development_basis.md`](./61_autonomy_gap_audit_and_next_development_basis.md) và [`.agents/skills/poe2-human-controls/SKILL.md`](../../.agents/skills/poe2-human-controls/SKILL.md)
- **Trạng thái**: `[SSoT THIẾT KẾ & TRIỂN KHAI]` — Khắc phục triệt để các lỗ hổng GAP-1 đến GAP-6

---

## 1. Mục Đích & Phạm Vi Kiến Trúc

Tài liệu này xác lập hợp đồng kỹ thuật và đặc tả chi tiết các giải pháp kiến trúc khắc phục triệt để 6 lỗ hổng gốc (Root Gaps) được chỉ ra trong tài liệu thẩm định 61, nhằm đưa AutoPOE2 từ một **Co-pilot 120Hz + macro hở vòng** trở thành một **hệ thống tự hành hoàn chỉnh mô phỏng chính xác hành vi người chơi thật**:

```
[Hideout Idle / Stash Check]
            │
            ▼ (WASD giữ phím ≥300ms, cự ly < 18u)
[Map Device Approach]
            │
            ▼ (Phím F - không click đất mù)
[Mở Map Device UI]
            │
            ▼ (MoveMouseSmooth Bézier tuyệt đối, không gửi phím U)
[Chọn Atlas Node + Nạp Waystone + TRAVERSE]
            │
            ▼ (Phím Space đóng panel -> WASD tiếp cận portal -> Phím F)
[Vào Combat Map Instance]
            │
            ▼ (Packing địa hình thật qua TerrainGrid resample + Combat có hoạt ảnh)
[Chiến Đấu & Nhặt Đồ (Walk-to-Loot)]
            │
            ▼ (Tiêu chí Map hoàn tất -> Phím '.' mở portal về)
[Trở Về Hideout -> Stash/Vendor Gate -> Lặp lại]
```

---

## 2. Đặc Tả Chi Tiết 6 Giải Pháp Gốc (GAP Remediation)

### 2.1. GAP-1: Chuột Tuyệt Đối + Cấm Phím U + Tương Tác Phím F
- **Bất biến áp dụng**: `INV-INPUT-ABS-MOUSE`, `INV-WASD-NO-BLIND-LMB`, `INV-KEY-NO-U-FOR-MAP-DEVICE`.
- **Đặc tả giải pháp**:
  1. Trong `MapDeviceHandler` và `NpcInteractionHandler`: Tích hợp `HumanCurveGenerator` để tạo đường cong chuyển động sinh học Bézier mượt mà. Thay thế hoàn toàn `KMBoxNet::MoveMouse(targetScreenX, targetScreenY)` bằng `KMBoxNet::MoveMouseSmooth(targetX, targetY, m_curveGen, steps, stepDelayMs)`.
  2. Tương tác với thực thể bệ đá Map Device: Khi nhân vật tiếp cận trong cự ly `interactRadius` (< 18u), gửi phím tương tác `F` (`0x09` HID). Tuyệt đối CẤM click chuột trái (LMB) xuống đất hoặc chân nhân vật trong chế độ WASD.
  3. Chuột trái chỉ được nhấp sau khi con trỏ chuột đã nằm chính xác trên các phần tử giao diện UI (Atlas Node, nút TRAVERSE, slot Waystone trong túi đồ).
  4. Loại bỏ 100% phím `VK_U` (`0x55`) khỏi `tools/autonomous_mapping_coordinator.py` và mọi quy trình Map Device.

### 2.2. GAP-2: Nhịp Điệu WASD Dwell Thời Gian Thực
- **Bất biến áp dụng**: `INV-WASD-MIN-DWELL` (Dwell bước ngắn 300–500ms; đường dài 1.0–2.0s; cấm micro-tap < 150ms).
- **Đặc tả giải pháp**:
  1. Trong `QuestNavigator`: Thiết kế cơ chế **Hold-Until-Waypoint**. Duy trì danh sách phím đang giữ (`m_currentlyHeldKeys`). Khi hướng di chuyển không đổi, duy trì trạng thái `KeyDown`. Chỉ gửi `KeyUp` khi chuyển sang hướng mới hoặc khi đã đến đích.
  2. Loại bỏ khối `sleep_for(milliseconds(85))` rồi `KeyUp` ngay lập tức.
  3. Trong `MapDeviceHandler`: Tiếp cận bệ đá với dwell time tối thiểu 300ms.
  4. Trong `UnstuckHandler`: Nâng thời gian giữ phím bước gỡ kẹt Radial WASD từ 180ms lên 350ms.

### 2.3. GAP-3: Địa Hình Gốc, Hiệu Chuẩn Gốc Tọa Độ & Cấm Lưới Trống
- **Bất biến áp dụng**: `INV-NAV-TERRAIN-COMMERCIAL`, `INV-TERRAIN-ORIGIN`.
- **Đặc tả giải pháp**:
  1. Trong `TerrainGrid::RecenterIfNeeded`: Khi cửa sổ lưới trượt theo vị trí người chơi, nếu `m_hasNativeTerrain` là true, đối với các ô mới ngoài vùng giao nhau cũ, bắt buộc phải nạp lại từ `m_nativeTerrain.IsWalkableWorld(wx, wy)` thay vì gán mặc định `CELL_WALKABLE`.
  2. Trong `TerrainReader::ExtractFromTerrainStruct`: Gán tọa độ gốc thế giới thực tế `worldOriginX` và `worldOriginY` dựa trên dữ liệu cấu trúc địa hình hoặc hiệu chuẩn từ nhân vật.
  3. Trong `MapDeviceHandler`: Kết nối con trỏ `TerrainGrid*`. Trong `ComputeTangentSlide`, truyền con trỏ địa hình thực tế thay vì `nullptr`.
  4. Trong `main.cpp`: Khi bật F8/F6 (`allowPhysicalMove = true`), nếu `!terrainGrid.HasNativeTerrain()`, chủ động kích hoạt `gameSession.AutoDetectTerrain(&terrainGrid, ...)` để nạp địa hình tức thì ngay trong Hideout hoặc Map mà không phụ thuộc vào `LoadingFinished`.

### 2.4. GAP-4: Combat Trung Thực Có Hoạt Ảnh
- **Đặc tả giải pháp**:
  1. Trong `SkillEngine` và `ComboManager`: Kích hoạt gọi `OnSkillCastStarted(vkCode, nowMs)` trước mỗi lần bắt đầu tung chiêu để nạp chính xác các pha hoạt ảnh `Windup`, `Active`, `Recovery`, loại bỏ hoàn toàn hiện tượng trơ hoạt ảnh (recovery stub).
  2. Đọc tài nguyên thực tế hoặc fail-closed an toàn, không tự tăng combo/rage bằng cách đếm số lần bấm phím.
  3. Phân loại quái vật chuẩn xác từ LifeComponent, không tính tất cả entity là quái.

### 2.5. GAP-5: Phân Loại Radar Đầy Đủ & Di Chuyển Nhặt Đồ (Walk-To-Loot)
- **Đặc tả giải pháp**:
  1. Trong `EntityManager::ScanProximityMonsters`: Bảo tồn phân loại thực thể thực tế, không ép cứng `type = 1` cho toàn bộ thực thể khi chạy chế độ fallback.
  2. Trong `LootController`: Đọc cấu hình `moveMode`. Trong chế độ WASD, đối với các món đồ ở cự ly xa (> 25u) hoặc trong phạm vi radar 35–150u, đề xuất hành vi di chuyển tiếp cận (`Walk-To-Loot`) thông qua `QuestNavigator` trước khi nhấp nhặt.

### 2.6. GAP-6: Một Máy Trạng Thái Chủ, Khóa Chặn F8 & Cổng Stash
- **Bất biến áp dụng**: `INV-F8-MUTES-BRAIN`, `INV-FSM-SINGLE-OWNER`.
- **Đặc tả giải pháp**:
  1. Trong `main.cpp`: Khóa chặn thực thi `botBrain.Process(samplePacket, kmbox, tickMs)` khi `!allowPhysicalMove`. Khi F8 ở trạng thái PAUSE, cấm mọi hành động vật lý (Move, Click, Cast), giữ nguyên các phản xạ thụ động của Co-Pilot.
  2. Mở rộng chuyển trạng thái của `CharacterFSM`: `InMapExploring` chuyển sang `InMapCombat`, `InMapLooting`, `InMapPortalExit` theo diễn biến thực tế của map.
  3. Bổ sung cổng kiểm tra túi đồ (Inventory Gate) tại `HideoutIdle`: Ưu tiên Stash/Vendor khi túi đầy trước khi kích hoạt Map Device.
  4. Xuất `CharacterState` lên Telemetry để Python Companion nhận diện chính xác trạng thái từ C++ Core.

### 2.7. GAP-7: Triệt Tiêu Xung Đột Hai Não (Anti-Brain-Conflict Interlock) & Portal Retry Polling
- **Bất biến áp dụng**: `INV-BRAIN-CONFLICT-GATEKEEPER`, `INV-MAPDEVICE-RETRY-POLL`.
- **Bối cảnh phát hiện (15/09/2026)**:
  Khi người dùng bấm F8, C++ Core đang thực hiện `MapDeviceHandler` (tiếp cận, chọn node Atlas, nạp Waystone và click TRAVERSE). Đột nhiên Python Companion (`AutonomousLifecycleManager`) do nhận định nhầm nhân vật ở Hideout là `CAMPAIGN_GRIND` (player_level kẹt ở 1), đã phát lệnh `TRIGGER_WORLD_MAP_TRAVEL` (Opcode 96) về Act 1 The Riverbank. Lệnh này khiến `TownQuestEngine` trong C++ cướp quyền, bấm phím `U` mở World Map đè bẹp Map Device và đóng băng `MapDeviceHandler` suốt 30 giây gây timeout 45s. Đồng thời, `MapDeviceHandler` fail-closed quá sớm (sau 1 tick tại ms 2500) khi hoạt ảnh portal POE2 chưa kịp spawn.
- **Đặc tả giải pháp**:
  1. **Gatekeeper Interlock tại C++ Core (`main.cpp`)**: Khi nhận `MacroOpCode::TRIGGER_WORLD_MAP_TRAVEL`, nếu `mapDeviceHandler->IsActive()` hoặc nhân vật đang ở Hideout (`CurrentSceneName` chứa "hideout") và đích đến là Act 1-3, Core **REJECT** lệnh ngay lập tức, không cho phép phím `U` chen ngang.
  2. **Phòng vệ tại `TownQuestEngine::StartTravel`**: Tự động từ chối mở phím `U` nếu đang ở Hideout.
  3. **Đồng bộ Pha Python Companion (`autonomous_lifecycle.py` & `auto_quester.py`)**:
     - Bổ sung `is_hideout_area(area_code, area_name)`.
     - Trong `evaluate_phase()`: Nếu ở Hideout -> 100% là `ENDGAME_EARNING` và cập nhật `player_level >= 65`.
     - Trong `_execute_campaign_grind_step()`: Nếu ở Hideout -> Tự động chuyển pha sang `ENDGAME_EARNING`, tuyệt đối không gọi `send_trigger_world_map_travel`.
     - Trong `AutoQuester::resolve_active_objective()`: Tại Hideout, trả về mục tiêu `MAP_DEVICE` thay vì Act 1 The Riverbank.
  4. **Retry Polling trong `MapDeviceHandler::WaitForPortals`**:
     - Sau khi click TRAVERSE (`activateDelayMs = 2500ms`), thiết lập cửa sổ retry polling tối đa `maxPortalWaitMs = 8000ms`.
     - Nếu chưa tìm thấy thực thể portal (type=3) trong tick hiện tại nhưng chưa quá 8000ms, tiếp tục chờ ở các tick sau thay vì fail-closed ngay lập tức. Chỉ khi quá 8000ms mà không có portal mới chuyển sang `MapDeviceState::Failed`.

---

## 3. Hợp Đồng Kiểm Thử Nghiệm Thu (Definition of Done)

1. **Harness Kiểm Thử Độc Lập (User-Mode)**:
   - Reproduction test: Dwell WASD < 150ms bị từ chối; dwell ≥ 300ms được chấp thuận.
   - Kiểm tra `MapDeviceHandler` và `NpcInteractionHandler` dùng `MoveMouseSmooth` với tọa độ màn hình đích thực tế.
   - Kiểm tra `autonomous_mapping_coordinator.py` không còn lệnh gửi `VK_U`.
   - Kiểm tra `TerrainGrid::RecenterIfNeeded` nạp đúng các ô blocked từ native terrain.
   - Kiểm tra F8 PAUSE tắt hoàn toàn các tác vụ vật lý của `botBrain`.
2. **Kiểm Thử Toàn Diện Bộ Suite**:
   - `ctest --test-dir build -C Release --output-on-failure` đạt 100% PASS.
   - `pytest` các module Python liên quan đạt 100% PASS.
