Files
Haocode/docs/agent-handoff/evidence/P2-04.md
T
sorrow404null bc0b92bcdc docs: add agent handoff docs, verification guide, and repair evidence
Add docs/agent-handoff (backlog, execution state, verification, platform plan, per-task evidence), repo AGENTS.md, and architecture notes updated for the dual-renderer design.
2026-09-17 16:40:06 +08:00

105 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# P2-04 证据:跨平台聚合测试入口
状态:COMPLETE
日期:2026-07-17(本机执行时间)
平台:Windows 11 10.0.26200 x64(主)+ WSL Ubuntu-22.04 / CPython 3.12.3Linux 路径验证)
## 交付物
- `tests/run_all.py`(新增,唯一新文件):跨平台聚合测试入口。
- `python tests/run_all.py --group logic|offscreen|all`(默认 all
- `--list` 只列条目;`--only <id,子串>` 跑子集(调试);`--keep-logs` 保留全部子日志(默认只留失败/超时)。
- 设计:每个子测试 = 独立子进程 + 独立临时目录(`haocode_RUN_<id>`/`haocode_CONF_<id>`POSIX 走 TMPDIR、Windows 走 TEMP+ 显式秒级 timeout`subprocess.run(timeout=...)`);offscreen 组子进程注入 `QT_QPA_PLATFORM=offscreen``HAOCODE_RENDER=software``QTWEBENGINE_CHROMIUM_FLAGS=--disable-gpu`(均 setdefault,不覆盖外层)。
- 退出码语义:任一 FAIL/TIMEOUT → 退出码 1(并打印"失败命令"清单);SKIP 不影响退出码。
- SKIP 双闸门:①依赖探测(`importlib.util.find_spec`node/openai/PyMuPDF/PyQt6,缺失即 SKIP 并给理由);②平台令牌(`platform:win32` 等,平台不适用即 SKIP 并给理由)。
- 聚合鲁棒性:子进程崩溃/超时只记 FAIL/TIMEOUT,不中断后续;输出统一 UTF-8(`errors=replace`Windows cp936/cp1252 宿主安全);WSL 时钟回拨保护(耗时 clamp ≥0)。
- 不要求 pytest / npm / 网络;聚合器自身零第三方依赖。
## 默认聚合范围(25 条)与排除项
- logic 12 条:run_tests.pyagent core 41)、test_tool_params、test_bash_stream、test_copy_session、test_compaction_persist、test_file_attach、test_cross_platform_shell、test_global_hotkey_platforms、test_wv2_guard、test_pdf_reader、test_math_extract.js、test_render_window.js。
- offscreen 13 条:smoke_offscreen、smoke_mode、smoke_copy_session、smoke_bash_panel、smoke_timeline、smoke_midswitch、test_main_window_event_filter、test_config_isolation、test_error_persist、test_think_code_neutral、test_debug_window、test_renderer_matrix、test_screen_capture_platforms。
- 默认排除(有注释理由,不进默认聚合):
- `diag_*`rename_overlay / panel_scrollbar / render_scale):人工/像素诊断,需真机或交互。
- `verify_*`:人工验证脚本。
- `tune_model_popup.py`:调参实验。
- `smoke_offscreen.py` 之外的旧式 real-DB 冒烟(引用真实 `data/haocode.db`,未接临时库)。
- `diag_live_agent.py``probe_agent_loop.py`:需真实 API 凭据。
- `_probe_*``_test_env.py`:探针/工具模块(被其他测试 import,非独立用例)。
- `run_all.py` 自身:聚合器不入聚合。
## 汇总结果(硬约束要求:Windows/Linux 各一份)
### Windows 11 / CPython 3.10.21.venvPyQt6+openai+PyMuPDF 齐全)
| 组 | 结果 | 耗时 |
|---|---|---|
| `--group logic` | **PASS 12 / FAIL 0 / SKIP 0**EXIT=0 | 43.9s |
| `--group offscreen` | **PASS 13 / FAIL 0 / SKIP 0**EXIT=0 | 81.9s |
| `--group all` | 25/25 PASSEXIT=0,两组顺序执行) | ~126s |
- 日志样例:`D:/tmp/runall_win_logic2.log``D:/tmp/runall_win_offscreen.log`
- offscreen 明细:smoke_offscreen 8.3s、smoke_bash_panel 30.3s、smoke_midswitch 8.7s、test_think_code_neutral 9.3s 等 13 条全 PASS。
### WSL Ubuntu-22.04 / CPython 3.12.3(系统 python3,无 PyQt6/openai/PyMuPDF,离线环境)
| 组 | 结果 | 耗时 |
|---|---|---|
| `--group all` | **PASS 4 / FAIL 0 / SKIP 21**EXIT=0 | 0.9s |
- PASS 4 = test_copy_session、test_file_attach、test_math_extract.js、test_render_window.js(纯 stdlib/node)。
- SKIP 21 全部带明确理由:无 openai(5,禁止联网安装)、无 PyQt6(14offscreen 组整体)、无 PyMuPDF1)、平台不适用 win32-only1test_wv2_guardmsvcrt 单实例互斥是 WebView2 守卫的 Windows 专属机制,Linux 无 WebView2 链路)、无 PyQt6 的 GUI 逻辑(1test_global_hotkey_platforms 含 QShortcut 构造)。
- 证明:聚合器在 Linux/3.12 上可运行、隔离约定在 POSIX(TMPDIR)成立、诚实 SKIP 不伪装通过、JS 条目跨平台运行。
- 注:WSL 无 .venv 且离线,依赖型条目按硬约束 SKIP;在按 VERIFICATION 矩阵备齐依赖的 Linux 真机上同命令即可执行全部条目(offscreen 组仍需真实 Linux 桌面环境完成 P1-03/P1-04 的平台验证,状态不变)。
## 故意失败夹具演示(硬约束:崩溃/超时不阻断汇总、退出码反映失败)
- 临时夹具(用后即删,未提交):
- 崩溃夹具(`raise RuntimeError`)→ 聚合器记 **FAIL** 并继续跑后续健康测试。
- 超时夹具(`time.sleep(60)` + budget 5s)→ 聚合器记 **TIMEOUT** 并继续。
- 演示运行:`总计 3: PASS 1 FAIL 1 TIMEOUT 1`,退出码 **1**,完整汇总 + 失败命令清单正常打印。
- 演示日志:`D:/tmp/runall_fixture_demo.log`;夹具文件已删除,仓库无残留(已 rg 复核)。
## 等价性(硬约束:单文件命令仍可直接运行,输出与聚合子进程一致)
- `test_copy_session.py`
- 独立运行:`===== 54/54 PASS ===== / ALL PASS`
- 聚合子进程(保留子日志 tail):`===== 54/54 PASS ===== / ALL PASS` —— 逐字一致。
- 其余条目同理(聚合即 `subprocess.run([sys.executable, ...原命令...])`,命令形态未变)。
## 聚合入口暴露并修复的两个真实缺陷
1. **`tests/test_compaction_persist.py` T9 陈旧断言**(跨平台共同项,Windows 上 FAIL):
- 断言用 `assertIn('msg["role"] not in', src)` 硬编码变量名,而 `ui/views/main_window.py` 现行代码用 `m["role"] not in`P1-01/P2-01 期间变量名演化,测试未跟进)。
- 修复:改为变量名无关的语义断言(同时接受 `msg["role"] not in` / `m["role"] not in` 两种等价写法,检查"摘要标记行被过滤"这一语义本身)。
- 验证:单跑 41/41 PASS;聚合 logic 组 12/12 PASS。
2. **`core/agent/compaction.py` dataclass 不可哈希默认值(Python 3.11+ 崩溃,真跨平台 bug**
- `CompactionPreparation.settings: CompactionSettings = DEFAULT_COMPACTION_SETTINGS`:默认值是 eq-dataclass 实例(`__hash__=None`)。Python ≤3.10 的 dataclass 只拒 list/dict/set 默认值 → 合法;Python 3.11+ 追加"不可哈希默认值"检查 → **import 即 ValueError**
- 影响面:Ubuntu 24.04 自带 CPython 3.12VERIFICATION 矩阵明确支持的 Linux 目标)—— 任何 import `core.agent` 的模块/测试在 3.11+ 全灭。由聚合入口在 WSL/3.12 上首次系统性暴露。
- 修复(1 行,语义完全等价):`settings: CompactionSettings = field(default_factory=lambda: DEFAULT_COMPACTION_SETTINGS)` —— 仍返回同一共享默认实例,3.10 行为不变。
- 验证:3.10 本地 `test_compaction_persist` 41/41 + `run_tests` 41/41 无回归;WSL/3.12 上 `core.agent` 链 import 通过(3 个 openai 依赖条目越过 compaction 后才在 openai 处 SKIP,证明 3.12 兼容)。
## 注册表元数据修正(诚实 SKIP 的前提)
- `test_tool_params.py` / `test_bash_stream.py` / `test_cross_platform_shell.py`:补 `openai` 依赖(均 import `core.agent.tools` → 顶层 `from openai import OpenAI`)。
- `test_global_hotkey_platforms.py`:补 `pyqt6` 依赖(含 QShortcut/overlay 构造)。
- `test_wv2_guard.py`:标 `platform:win32`T4 msvcrt 跨进程互斥为 Windows 专属;非 win32 上 `acquire_instance_lock()` 按设计返回 None)。
## 边界遵守
- 未动任何独立测试命令与既有入口(`run_tests.py` 原样保留)。
- 未要求 pytest/npm/网络;聚合器零第三方依赖。
- 默认聚合未启动任何真实 API / 人工诊断 / 真实桌面 / 凭据脚本(见排除清单)。
- 未读取真实配置(`data/config.json` 零访问);未删除用户运行数据;夹具用后即删。
- 产品代码改动仅 2 处且均为聚合暴露的缺陷修复:`tests/test_compaction_persist.py`(测试断言更新)+ `core/agent/compaction.py`1 行 3.11+ 兼容)。
## 复现命令
```text
python tests/run_all.py --list
python tests/run_all.py --group logic
python tests/run_all.py --group offscreen
python tests/run_all.py --group all
python tests/run_all.py --only test_copy_session --keep-logs
```