# BÁO CÁO BÀN GIAO (HANDOFF REPORT) — MILESTONE 1 PROGRESSION

> **Người thực hiện**: `explorer_m1_progression_1` (Teamwork Preview Explorer)  
> **Người nhận**: `orchestrator_4` & `worker_m1`  
> **Loại Handoff**: Hard Handoff (Nhiệm vụ khảo sát hoàn tất đầy đủ)  

---

## 1. Observation (Quan Sát Trực Tiếp)

1. **Vị Trí Seeder Hiện Tại**:
   - Tệp [server/world/game_design_matrix_seeder.py](file:///c:/Projects/FreeExile/server/world/game_design_matrix_seeder.py) lines 241-256 sử dụng công thức đơn giản:
     ```python
     for lvl in range(1, 101):
         target_xp = int(500 * (lvl ** 1.85))
     ```
   - Đo đạc thực tế: `target_xp(1) = 500`, `target_xp(20) = 127607`, `target_xp(99) = 2459773`, `target_xp(100) = 2505936`.
   - Delta EXP cấp 99 $\to$ 100 hiện tại chỉ là $46.163$ EXP ($1.84\%$ tổng EXP), không tạo được bức tường Soft-Wall.
   - Tổng EXP cấp 1-20 chiếm $\approx 0.51\%$ tổng tích lũy, vi phạm điều kiện $< 0.1\%$.

2. **Cấu Trúc Bảng Hiện Tại**:
   - Bảng `progression_benchmarks` trong [server/world/game_design_matrix_schema.py](file:///c:/Projects/FreeExile/server/world/game_design_matrix_schema.py) lines 132-140 chỉ gồm 7 cột: `level`, `target_exp`, `player_base_hp`, `player_benchmark_dps`, `monster_base_hp`, `monster_base_dps`, `max_affix_tier_allowed`.
   - Ràng buộc: `target_exp INTEGER NOT NULL CHECK (target_exp > 0)`.

3. **Cơ Chế Kiểm Tra Đơn Điệu (Validator)**:
   - [server/world/game_design_matrix_service.py](file:///c:/Projects/FreeExile/server/world/game_design_matrix_service.py) lines 241-248:
     ```python
     cur.execute("SELECT level, target_exp FROM progression_benchmarks ORDER BY level ASC")
     prog_rows = cur.fetchall()
     prev_xp = 0
     for r in prog_rows:
         if r["target_exp"] <= prev_xp:
             violations.append(...)
         prev_xp = r["target_exp"]
     ```
   - Nếu cấp độ 1 có `target_exp = 0` và `prev_xp = 0`, điều kiện `0 <= 0` sẽ bị kích hoạt lỗi vi phạm đơn điệu.

4. **Kết Quả Tính Toán Mô Hình 7 Phân Đoạn**:
   - Thực thi mô phỏng qua Python CLI:
     - Tổng EXP cả đời (1 $\to$ 100): $23.925.692.466$ EXP ($\approx 23.93$ tỷ).
     - Tổng EXP cấp 1 $\to$ 20: $2.755.579$ EXP $\implies \mathbf{0.0115\%} < 0.1\%$.
     - Tổng EXP cấp 1 $\to$ 99: $17.989.242.456$ EXP.
     - Delta EXP cấp 99 $\to$ 100: $5.936.450.010$ EXP $\implies \mathbf{33.00\%}$ tích lũy 1-99 ($\ge 30\%$).
     - Tỷ trọng Delta 99 $\to$ 100 trên tổng cả đời: $\mathbf{24.81\%}$ ($25-35\%$).
     - Tính đơn điệu: 99 bước chuyển tiếp và 100 mốc tích lũy đều đơn điệu tăng ngặt ($100\%$ PASS).

5. **Giới Hạn Vệ Sinh Mã Nguồn (Hygiene Limits)**:
   - Tệp `game_design_matrix_seeder.py` hiện có $351$ dòng (ngay tại ngưỡng Soft Cap 350 dòng).

---

## 2. Logic Chain (Chuỗi Suy Luận)

1. Từ Observation 1 và 4: Công thức hiện tại ($500 \cdot L^{1.85}$) không đáp ứng bất kỳ tiêu chuẩn nào của bản đặc tả PoE2 Seasonal Progression. Công thức 7 phân đoạn giải quyết triệt để bài toán: cấp 1-20 siêu tốc ($0.0115\%$), cấp 60-80 tăng tuyến tính ($+12\%$ mỗi cấp), cấp 90-99 dựng đứng, và cấp 99 $\to$ 100 là bức tường cực hạn đúng $33\%$ của cả đời 1-99.
2. Từ Observation 2: Milestone 2 cần các thông số `death_penalty_ratio` (0%, 5%, 10%, 15%, 25%), `exp_to_next_level`, `level_gap_penalty_exp` (0.60). Việc mở rộng schema `progression_benchmarks` ngay tại Milestone 1 là điều kiện tiên quyết để Milestone 2 không phải sửa lại database.
3. Từ Observation 3: Cấp 1 đại diện cho nhân vật mới tạo (0 EXP). Để `cumulative_exp` hoặc `target_exp` cấp 1 có giá trị $0$ một cách hợp lệ, cần cập nhật `CHECK (target_exp >= 0)` trong schema và đổi `prev_xp = -1` trong validator của `GameDesignMatrixService`.
4. Từ Observation 5: Nếu viết toàn bộ thuật toán sinh 7 phân đoạn trực tiếp vào `game_design_matrix_seeder.py`, file này sẽ bị đội lên $> 400$ dòng. Do đó, việc tách thuật toán thuần túy sang [server/world/level_progression_curve.py](file:///c:/Projects/FreeExile/server/world/level_progression_curve.py) là giải pháp tối ưu, cho phép tái sử dụng ở M2, M4 và unit tests.

---

## 3. Caveats (Các Điểm Lưu Ý & Ngoại Lệ)

- **Số Làm Tròn Số Nguyên (Floor vs Round)**: Toàn bộ công thức chuyển tiếp sử dụng hàm sàn `int(...)` hoặc `math.floor(...)` để bảo đảm tính tất định và tương thích số nguyên lớn trên SQLite INTEGER (64-bit int).
- **Hệ Thống Client WebApp**: Cần lưu ý rằng khi hiển thị thanh EXP ở Client, giá trị tối đa của thanh EXP tại cấp $L$ chính là `exp_to_next_level(L)`. Tại cấp 100, `exp_to_next_level = 0` (thanh EXP hiển thị FULL hoặc "MAX").

---

## 4. Conclusion (Kết Luận & Đề Xuất Thực Thi)

Worker M1 cần thực hiện 5 bước nguyên tử:
1. **Tạo `server/world/level_progression_curve.py`**: Chứa hàm `calculate_piecewise_exp_curve() -> Dict[int, LevelExpBenchmark]` tính toán 7 phân đoạn và tỷ lệ phạt chết phân bậc.
2. **Cập nhật `server/world/game_design_matrix_schema.py`**: Mở rộng bảng `progression_benchmarks` với 6 cột mới (`exp_to_next_level`, `cumulative_exp`, `death_penalty_ratio`, `level_gap_safe_range`, `level_gap_penalty_exp`, `monster_benchmark_exp`).
3. **Cập nhật `server/world/game_design_matrix_types.py`**: Đồng bộ DTO `ProgressionBenchmarkRow`.
4. **Cập nhật `server/world/game_design_matrix_seeder.py`**: Gọi `calculate_piecewise_exp_curve()` và insert đầy đủ 13 trường.
5. **Cập nhật `server/world/game_design_matrix_service.py`**: Sửa `prev_xp = -1` và map đầy đủ các trường mới trong `get_level_progression_benchmark()`.

---

## 5. Verification Method (Phương Pháp Kiểm Chứng Độc Lập)

1. **Chạy Unit Test và Lint Ma Trận Thiết Kế**:
   ```bash
   pytest tests/unit/test_game_design_matrix.py
   python tools/lint/verify_game_design_matrix.py --sync
   ```
   *Điều kiện đạt*: Toàn bộ 8 unit tests PASS, tool sync thành công 100 cấp độ với 0 violations.

2. **Kiểm Tra Kiểm Toán Vệ Sinh Mã Nguồn**:
   ```bash
   python tools/lint/check_code_and_doc_hygiene.py --strict
   ```
   *Điều kiện đạt*: Toàn bộ file mới $\le 350$ dòng, không có hàm nào $> 50$ dòng.

3. **Kiểm Tra Chỉ Số Toán Học Trên SQLite**:
   ```bash
   python -c "
   import sqlite3
   conn = sqlite3.connect('data/game_design_matrix.db')
   cur = conn.cursor()
   cur.execute('SELECT level, cumulative_exp, exp_to_next_level FROM progression_benchmarks WHERE level IN (1, 20, 99, 100)')
   rows = dict((r[0], (r[1], r[2])) for r in cur.fetchall())
   assert rows[20][0] / rows[100][0] < 0.001, 'Lv 1-20 cumulative exp must be < 0.1%'
   assert rows[99][1] / rows[99][0] >= 0.30, 'Delta 99->100 must be >= 30% of Lv 1-99 cumulative'
   print('Mathematical bounds verified successfully!')
   "
   ```
