# WP1 资产迁移 Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use subagent-driven-development to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 迁移 5 个遗漏的 TRM4 进化/动量模板到 TRM5，删除 2 个死字段，把加载器的静默空串兜底改为 fail-loud，解锁自进化引擎。

**Architecture:** 进化引导 prompt 是引擎的一部分（放项目根 `prompts/`，不参与版本化进化）。TRM4 五模板的输出 JSON 契约与 TRM5 解析代码已核实完全对齐，可直接拷贝。执行顺序：先迁移模板 → 删死字段（`consolidate_system`/`span_eval_user`，零消费）→ 加载器 fail-loud（顺序关键：先删死字段，fail-loud 才不会对不存在也不需要的模板报错）。

**Tech Stack:** Python 3.11、pytest、frozen dataclass（`core/evolution/types.py`）。

**设计源**：`research-wiki/designs/2026-07-16-preflight-fixes-design.md §4`

---

## 关键锚点（实现前必读）

| 用途 | 位置 |
|------|------|
| TRM4 源模板 | `/home/iomgaa/Projects/Video-Tree-TRM4/prompts/{evolve_skill,evolve_system,evolve_tool,evolve_rank,slow_momentum}.md` |
| evolve 加载器 | `app/harness/runner.py:2266-2280` `_load_evolve_prompts` |
| diagnose 加载器 | `app/harness/runner.py:2282-2299` `_load_diagnose_prompts` |
| 平行 diagnose 加载器 | `app/harness/video_split_cli.py:362-378` `_load_diagnose_prompts` |
| dataclass 定义 | `core/evolution/types.py:477-522`（`DiagnosePrompts` L494-501 / `EvolvePrompts` L518-522） |
| 死字段 `consolidate_system` 内联替代 | `core/evolution/evolve.py:997` `_CONSOLIDATE_SYSTEM`（消费点 L1036） |
| 死字段 `span_eval_user` 无消费 | diagnose 只用 `prompts.span_eval_system`（`core/evolution/diagnose.py:511`），user_prompt 内联构造 |
| 测试 fixture | `test_evolve.py:682` `consolidate_system="cons"`；`test_diagnose.py:753` `span_eval_user=""`；`test_evolution_types.py:352` `span_eval_user="p4"` / `:370` `consolidate_system="consolidate_tmpl"` |

## 核心算法保真校验

本计划不迁移/不改核心算法（ARCHITECTURE §6），只搬运模板文件 + 清理死字段 + 加 fail-loud。模板内容是 evolve 引擎（算法 #8）的输入数据，非算法逻辑本身；迁移已核实输出契约（`suggestions`/`edits`/`edits_extract`/`edits_verify`/`selected_indices`/`slow_update_content`）与 TRM5 解析代码逐字对齐。**保真检查点（Task 1 Step 4）**：加载 evolve_tool.md 后确认其要求 LLM 返回 `edits_extract`+`edits_verify` 双键（对齐 `evolve.py:1433-1435`）。

---

## Task 1: 迁移 5 个 TRM4 模板

**Files:**
- Create: `prompts/evolve_skill.md`、`prompts/evolve_system.md`、`prompts/evolve_tool.md`、`prompts/evolve_rank.md`、`prompts/slow_momentum.md`（从 TRM4 拷贝）
- Test: `tests/unit/test_evolve_prompts_present.py`

- [ ] **Step 1: 写契约冒烟测试（先失败）**

`tests/unit/test_evolve_prompts_present.py`：
```python
"""校验 5 个进化/动量模板存在且输出契约关键词与解析代码对齐。"""
from pathlib import Path

import pytest

_PROMPTS_DIR = Path("prompts")


@pytest.mark.parametrize(
    "name, required_tokens",
    [
        ("evolve_skill.md", ["suggestions", "edits"]),
        ("evolve_system.md", ["suggestions", "edits"]),
        ("evolve_tool.md", ["edits_extract", "edits_verify"]),
        ("evolve_rank.md", ["selected_indices"]),
        ("slow_momentum.md", ["slow_update_content"]),
    ],
)
def test_evolve_template_present_and_contract(name: str, required_tokens: list[str]) -> None:
    path = _PROMPTS_DIR / name
    assert path.exists(), f"缺模板: {path}"
    text = path.read_text(encoding="utf-8")
    assert text.strip(), f"模板为空: {path}"
    for token in required_tokens:
        assert token in text, f"{name} 缺输出契约关键词 {token!r}（与解析代码不对齐）"
```

- [ ] **Step 2: 运行确认失败（模板尚未迁移）**

Run: `conda run -n Video-Tree-TRM python -m pytest tests/unit/test_evolve_prompts_present.py -v`
Expected: 5 参数化用例全 FAIL（`AssertionError: 缺模板: prompts/evolve_skill.md` 等；TRM5 当前无这 5 个模板）。

- [ ] **Step 3: 拷贝 5 个模板**

Run:
```bash
cp /home/iomgaa/Projects/Video-Tree-TRM4/prompts/evolve_skill.md prompts/evolve_skill.md
cp /home/iomgaa/Projects/Video-Tree-TRM4/prompts/evolve_system.md prompts/evolve_system.md
cp /home/iomgaa/Projects/Video-Tree-TRM4/prompts/evolve_tool.md prompts/evolve_tool.md
cp /home/iomgaa/Projects/Video-Tree-TRM4/prompts/evolve_rank.md prompts/evolve_rank.md
cp /home/iomgaa/Projects/Video-Tree-TRM4/prompts/slow_momentum.md prompts/slow_momentum.md
```
Expected: 5 文件存在于 `prompts/`。

- [ ] **Step 4: 运行确认通过**

Run: `conda run -n Video-Tree-TRM python -m pytest tests/unit/test_evolve_prompts_present.py -v`
Expected: 5 参数化用例全 PASS。若 evolve_tool.md 缺 `edits_extract`/`edits_verify` 则契约不符——停止并逐行比对 TRM4 源。

- [ ] **Step 5: 保真检查点**

Run: `conda run -n Video-Tree-TRM python -c "print('edits_extract' in open('prompts/evolve_tool.md').read() and 'edits_verify' in open('prompts/evolve_tool.md').read())"`
Expected: `True`（对齐 `evolve.py:1433-1435` 的 `parsed["edits_extract"]`/`parsed["edits_verify"]`）。

- [ ] **Step 6: 提交**

```bash
git add prompts/evolve_skill.md prompts/evolve_system.md prompts/evolve_tool.md prompts/evolve_rank.md prompts/slow_momentum.md tests/unit/test_evolve_prompts_present.py
git commit -m "feat: migrate 5 evolve/momentum templates from TRM4 (algo #8)"
```

---

## Task 2: 删除 2 个死字段（consolidate_system / span_eval_user）

**Files:**
- Modify: `core/evolution/types.py:494-501,518-522`
- Modify: `app/harness/runner.py:2279,2294`
- Modify: `app/harness/video_split_cli.py:374`
- Modify: `tests/unit/test_evolve.py:682`、`tests/unit/test_diagnose.py:753`、`tests/unit/test_evolution_types.py:352,370`

- [ ] **Step 1: 删 dataclass 字段与 docstring**

`core/evolution/types.py` — `DiagnosePrompts` 删 `span_eval_user`：
- docstring 删行 `        span_eval_user: span 评估用户提示模板。`（L487）
- 字段删行 `    span_eval_user: str`（L497）

`EvolvePrompts` 删 `consolidate_system`：
- docstring 删行 `        consolidate_system: appendix 压缩系统提示。`（L515）
- 字段删行 `    consolidate_system: str`（L522）

- [ ] **Step 2: 删加载器对死字段的 `_read` 行**

`app/harness/runner.py`：
- `_load_evolve_prompts` 删行 `            consolidate_system=_read("consolidate_system.md"),`（L2279）
- `_load_diagnose_prompts` 删行 `            span_eval_user=_read("span_eval_user.md"),`（L2294）

`app/harness/video_split_cli.py`：
- `_load_diagnose_prompts` 删行 `        span_eval_user=_read("span_eval_user.md"),`（L374）

- [ ] **Step 3: 删测试 fixture 对死字段的赋值 + 更新 docstring**

- `tests/unit/test_evolve.py:684` 删行 `    consolidate_system="cons",`
- `tests/unit/test_diagnose.py:753` 删行 `        span_eval_user="",`
- `tests/unit/test_evolution_types.py:352` 删行 `        span_eval_user="p4",`
- `tests/unit/test_evolution_types.py:370` 删行 `        consolidate_system="consolidate_tmpl",`
- `tests/unit/test_evolution_types.py:347` 的文档字符串"DiagnosePrompts 8 个模板字段"改为"7 个"；`:364` 的"EvolvePrompts 5 个模板字段"改为"4 个"（删字段后数量变化）。

注：`test_evolution_types.py` 若有断言逐字段比对或字段计数，同步移除对两个死字段的断言（读该测试确认，删净引用）。上述行号以当前代码为准，实现前 `grep -n consolidate_system\|span_eval_user tests/unit/test_evolution_types.py` 复核。

- [ ] **Step 4: 运行相关测试确认通过**

Run: `conda run -n Video-Tree-TRM python -m pytest tests/unit/test_evolve.py tests/unit/test_diagnose.py tests/unit/test_evolution_types.py -q`
Expected: 全 PASS（无 `TypeError: unexpected keyword argument` / 无 `missing positional argument`）。

- [ ] **Step 5: 全库确认无残留引用**

Run: `conda run -n Video-Tree-TRM python -c "import subprocess; r=subprocess.run(['grep','-rn','consolidate_system\|span_eval_user','core/','app/','adapters/','tests/'],capture_output=True,text=True); print(r.stdout)"`
Expected: 空输出（`consolidate_appendix` 用内联 `_CONSOLIDATE_SYSTEM` 不算 `consolidate_system` 字段引用；若出现请确认非 dataclass 字段引用）。

- [ ] **Step 6: 提交**

```bash
git add core/evolution/types.py app/harness/runner.py app/harness/video_split_cli.py tests/unit/test_evolve.py tests/unit/test_diagnose.py tests/unit/test_evolution_types.py
git commit -m "refactor: drop dead prompt fields consolidate_system/span_eval_user"
```

---

## Task 3: 加载器 fail-loud（缺模板即报错）

**Files:**
- Modify: `app/harness/runner.py:2270-2272,2286-2288`
- Modify: `app/harness/video_split_cli.py:365-367`
- Test: `tests/unit/test_evolve_prompts_present.py`（追加）

- [ ] **Step 1: 追加 fail-loud 测试（直接调真实加载器）**

在 `tests/unit/test_evolve_prompts_present.py` 追加——直接驱动真实 loader（不复制 _read 逻辑），在无模板的空 cwd 下断言 `FileNotFoundError`：
```python
def test_video_split_loader_fail_loud_on_missing(tmp_path, monkeypatch):
    """video_split_cli 的真实 diagnose 加载器缺模板必须 FileNotFoundError。"""
    monkeypatch.chdir(tmp_path)  # 空目录，无 prompts/*.md
    from app.harness.video_split_cli import _load_diagnose_prompts

    with pytest.raises(FileNotFoundError, match="缺进化/诊断模板"):
        _load_diagnose_prompts()


def test_runner_evolve_loader_fail_loud_on_missing(tmp_path, monkeypatch):
    """runner 的真实 evolve 加载器缺模板必须 FileNotFoundError。"""
    monkeypatch.chdir(tmp_path)
    from app.harness.runner import Runner

    r = object.__new__(Runner)  # 绕过 __init__，仅测无状态加载器方法
    with pytest.raises(FileNotFoundError, match="缺进化/诊断模板"):
        r._load_evolve_prompts()


def test_runner_diagnose_loader_fail_loud_on_missing(tmp_path, monkeypatch):
    """runner 的真实 diagnose 加载器缺模板必须 FileNotFoundError。"""
    monkeypatch.chdir(tmp_path)
    from app.harness.runner import Runner

    r = object.__new__(Runner)
    with pytest.raises(FileNotFoundError, match="缺进化/诊断模板"):
        r._load_diagnose_prompts()
```
> 这三个测试直接调真实 loader（`_load_evolve_prompts`/`_load_diagnose_prompts` 无 self 状态依赖，`object.__new__` 可安全调用）；修复前静默返回空串不抛，故 Step 2 前必 FAIL。

- [ ] **Step 2: 三处 `_read` 闭包改 fail-loud**

`app/harness/runner.py` `_load_evolve_prompts`（L2270-2272）与 `_load_diagnose_prompts`（L2286-2288），以及 `app/harness/video_split_cli.py` `_load_diagnose_prompts`（L365-367），把：
```python
        def _read(name: str) -> str:
            p = Path("prompts") / name
            return p.read_text(encoding="utf-8") if p.exists() else ""
```
改为：
```python
        def _read(name: str) -> str:
            p = Path("prompts") / name
            if not p.exists():
                raise FileNotFoundError(f"缺进化/诊断模板: {p}（请从 TRM4 迁移或检查 prompts/）")
            return p.read_text(encoding="utf-8")
```
（runner.py 缩进 12 空格；video_split_cli.py 的 `_read` 缩进按其函数体，见 L365 为 8 空格——按各自现场缩进套用。）

- [ ] **Step 3: 运行测试确认通过**

Run: `conda run -n Video-Tree-TRM python -m pytest tests/unit/test_evolve_prompts_present.py -v`
Expected: 全 PASS。

- [ ] **Step 4: runner 其他行为回归（非 fail-loud 验证）**

fail-loud 已由 Step 3 的三个真实 loader 测试验证；此步仅确认模板迁移 + loader 改动未破坏 runner 其他行为。
Run: `conda run -n Video-Tree-TRM python -m pytest tests/unit/test_harness_runner.py -q`
Expected: 全 PASS（模板已迁移，真实加载走成功分支）。

- [ ] **Step 5: 提交**

```bash
git add app/harness/runner.py app/harness/video_split_cli.py tests/unit/test_evolve_prompts_present.py
git commit -m "fix: fail-loud on missing evolve/diagnose templates (no silent empty)"
```

---

## Self-Review（作者自查，执行者复核）

- [ ] 5 模板均已迁移且契约测试覆盖关键字段。
- [ ] `consolidate_system`/`span_eval_user` 在 core/app/tests 全库无残留字段引用。
- [ ] 三处 loader（runner 两处 + video_split_cli 一处）均已 fail-loud。
- [ ] 执行顺序正确：Task 2（删死字段）先于 Task 3（fail-loud），避免对不需要的模板报错。

## 验收标准

1. `pytest tests/unit/test_evolve_prompts_present.py tests/unit/test_evolve.py tests/unit/test_diagnose.py tests/unit/test_evolution_types.py tests/unit/test_harness_runner.py` 全绿。
2. `grep -rn 'consolidate_system\|span_eval_user' core/ app/ tests/` 无 dataclass 字段残留。
3. `prompts/` 下 5 个新模板存在且非空。
