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.
66 lines
9.4 KiB
Markdown
66 lines
9.4 KiB
Markdown
# 全量修复执行状态
|
||
|
||
状态:ACTIVE
|
||
总目标:完成 REPAIR_BACKLOG.md 中所有待实施任务
|
||
|
||
## 调试铁律(跨压缩持久,每次恢复后必须遵守)
|
||
|
||
- **所有调试命令、测试与诊断脚本执行都必须设置显式的最长耗时预算(timeout)**。
|
||
- 若中途因超时跳出,先定位卡点,再允许延长预算重跑;**严禁不设超时让它无限卡死**。
|
||
|
||
当前任务:全部完成(P0-01 → P2-04)
|
||
当前阶段:COMPLETE(自动化部分;Windows 真机双渲染路径已验证;Linux 真机平台验证项见各任务 evidence)
|
||
最后完成动作:Windows 真机验证 —— ① `core/webview2.py` 加 byte[] 加载回落(本机 D: 卷 .NET “网络位置”怪癖),真桌面 WebView2 启动成功(Runtime 153.0.4234.32、controller ready、NavigationCompleted、截图像素证实聊天区真实 DOM 渲染);② `HAOCODE_FORCE_QTWEBENGINE=1` 真桌面回落验证(独立 per-instance profile、无 taskkill、渲染正常);③ P2-02 真机截图(diag_panel_scrollbar 真桌面 ALL PASS,三图已更新);④ T0 守卫真机交叉验证(diag 实例被锁自动回落,未误杀主程序 WebView2);证据更新 evidence/P1-03.md、evidence/P2-02.md、evidence/P2-03.md
|
||
下一步唯一动作:P2-03 人工清单 1–5 可在本机真实 WebView2 模式下走查(app 现可正常以 WV2 启动,当前前台实例为强制 QtWebEngine 模式,重开即默认 WV2);其余为 Linux 真机验证(P1-03/P1-04),需 Ubuntu 桌面主机
|
||
当前修改文件:core/webview2.py(byte[] 加载回落 + BaseException 防护)、docs/agent-handoff/evidence/{P1-03,P2-02,P2-03}.md、evidence/win_real_*_mainwindow.png(新)
|
||
最近测试结果:test_wv2_guard 10/10(修复后无回归);diag_panel_scrollbar 真桌面 ALL PASS;run_all 25/25(前次终态)
|
||
尚未验证的平台:Linux X11、Linux Wayland(P1-03 启动链、P1-04 X11 XGrabKey 真实注册/命中、Wayland portal 三态均需真机);Windows 真机双渲染路径(WebView2 + QtWebEngine 回落)已于 2026-09-17 验证,仅剩 P2-03 人工遮罩走查(1–5,可本机执行)
|
||
阻塞项:无
|
||
观察项:
|
||
- 【P1-01 教训·offscreen QtWebEngine 诊断】未 `window.resize()`+`show()` 前 `innerHeight=0`,锚点/滚动几何全废;诊断必须先 resize+show、等待 `innerHeight>0`、再显式 `load_messages_to_web` 重载
|
||
- 【P1-01 教训】`runJavaScript` 回调不能返回 DOM 元素(转换失败);`wait_until` 条件一律返回原语(`cond ? 1 : 0`)
|
||
- 【P1-01 教训】批次渲染守卫(`__rwPageRendering`)必须在**调度时刻**捕获,延迟回调(rAF+setTimeout)触发时批次已结束、标志已复位,届时再读会漏放 `scrollIntoView` 触发 'newer' 振荡
|
||
- 【P1-01 设计】页 = 半窗(`size//2`),非整窗:整窗页会使向上换页锚点必被裁出窗口,≤2px 锚点恢复不可达
|
||
- 【P1-02 教训】Windows 下 argv 列表形态 Popen 会被 `list2cmdline` 转义内部引号(`\"`),cmd.exe 不认 → 显式 cmd 契约必须用**字符串命令行** `cmd.exe /d /s /c "<cmd>"`
|
||
- 【P1-02 发现】旧 `shell=True` 对带引号程序名实为直接 CreateProcessW(不经 cmd);显式 cmd 后行为统一且可预测
|
||
- 【P1-03 教训】`QTimer.singleShot` 单位是**毫秒**(25 = 25ms),兜底预算要写 30000;事件循环启动前不触发
|
||
- 【P1-03 教训】QtWebEngine 顺序硬约束:QtWebEngineWidgets 必须先于 QApplication 导入;QWebEngineProfile 必须先于使用它的 page/view 创建,且 setPersistentStoragePath/setCachePath 要在 profile 使用前调
|
||
- 【P1-03 发现】PyQt6-WebEngine 6.10 未暴露 `QWebEnginePage.errorOccurred`(Qt 6.5+ API),加载失败诊断用 loadFinished(ok) + processEvents 循环
|
||
- 【P1-03 教训】改构造函数签名必须保旧式位置调用:`CustomWebPage(browser)` 的 view 会被新首形参误当 profile,按 `isinstance(QWebEngineProfile)` 分派
|
||
- 【P1-04 教训】PyQt6:`QThread.run()` 内任何未处理异常 = **abort 整个进程**(退出码 127、无 traceback、stdout 缓冲丢失,极难诊断)→ QThread.run() 必须顶层 try/except 全捕获 + 日志
|
||
- 【P1-04 教训】`ctypes.byref(x)` 返回 CArgObject:真实 CDLL 调用正常,传给测试替身(普通可调用对象)会 TypeError —— 替身端 `getattr(arg, "_obj", arg)` 还原原对象
|
||
- 【P1-04 教训】Windows 上 `os.pipe()` 的 fd 不能可靠用于 select()(10093/10038)——跨平台可 select 假 fd 用 `socket.socketpair()`;且 `a.send()` 的数据在 b 的接收缓冲(方向别写反)
|
||
- 【P1-04 发现】`file://` URI 解析用剥前缀+unquote 而非 urlparse().path:`file://D%3A%5Cx`(无第三斜杠)会被当 netloc;gdbus portal 的 FilePicked/Finished 信号都发在 request 对象上
|
||
- 【P2-01 教训】Qt:对刚展开、布局尚未 flush 的 QPlainTextEdit 立即设水平滚动值,随后的挂起 resize flush 会把滚动清零——真实用户无法在未渲染框上滚动,属测试时序伪影;测试设滚动前必须先 settle(N1×N2 探针矩阵证实 N2≥1 即稳定)
|
||
- 【P2-01 教训】offscreen harness 收尾用 `os._exit`:`sys.exit` 后 QtWebEngine 渲染/GPU 子进程可能不回收 → 解释器挂起至 timeout;管道执行时只看输出会误判通过(必须验 EXIT 码)
|
||
- 【P2-02 教训】offscreen 下 QPlainTextEdit 的 `grab()` 文档区不填充(黑图)但 `panel.grab()` 正常;像素断言一律走 `widget.render(painter)`(渲染到透明 QPixmap),且先做健全性检查(文本色/handle 色像素计数>0)防黑图假通过
|
||
- 【P2-02 教训】PyQt6:`Qt.Vertical`→`Qt.Orientation.Vertical`;`QStyleOptionSlider()` 无参构造(不接受 widget);`subControlRect(cc, opt, sc, widget)` 参数序 + 滚动条箭头子控件枚举名是 `SC_ScrollBarSubLine/AddLine`;`CC_ScrollBar` 属于 `QStyle.ComplexControl`
|
||
- 【P2-02 发现】裸控件(无样式表祖先)`style()` 是基础风格,空 option 下 subControlRect 返回 0 矩形——「原生参照」只能靠 sizeHint/实际几何对照(原生横条 14px vs 面板 8px)
|
||
- 【P2-03 发现】offscreen/无真实事件循环时 `processEvents()` **不处理 DeferredDelete**:`deleteLater()` 后必须显式 `QCoreApplication.sendPostedEvents(None, QEvent.Type.DeferredDelete)` 才会真正删除(offscreen 测试断言"已删除"前必须冲刷;生产事件循环常驻不受影响)
|
||
- 【P2-03 教训】PyQt6 `setGeometry` 无 `(QPoint, QSize)` 重载→构造 `QRect(tl,size)`;`QTest.mouseClick(widget, button, modifier, pos)` 第 3 参是 **modifier**;`QTest` 在独立模块 `PyQt6.QtTest`
|
||
- 【P2-03 发现】`core/webview2.py` `get_environment()` 含 `taskkill /F /IM msedgewebview2.exe` + 共享默认 profile 不可重定向 → 无人值守自动化不得走真实 WebView2 启动路径;真机验收走人工清单(拥有者顶层 Tool 窗 z 序上必盖 WebView2 原生子 HWND,机制与生产已验证的 AttachmentPreviewOverlay 同构)
|
||
- 【P2-03 结构判据】顶层窗可带 owner parent:`setWindowFlags(Tool|Frameless)` 后 `parentWidget()` 仍可非 None;判据是 `isWindow()` / `window() is self`,不是 `parentWidget() is None`
|
||
- 本机无系统级 Python 3.10;已用 uv 安装 CPython 3.10.21(用户目录托管)并重建项目内 `.venv`(Python 3.10.21),与文档基线对齐
|
||
- WSL 存在 Ubuntu-22.04(当前 Stopped,python3.12.3,未装 PyQt6);未检测到 X11/Wayland 桌面会话
|
||
- `tests/diag_live_agent.py:19`、`tests/tune_model_popup.py:155` 直接引用真实配置路径;属 live/tune 脚本,默认聚合排除,不属 P0-01 允许范围
|
||
- `readme.md` 提及的 `haocode.spec`/`pyi_rth_trace.py` 不在本快照内(打包属后续阶段,不影响源码运行)
|
||
|
||
环境探测(只读):
|
||
- OS:Windows 11 10.0.26200 x64
|
||
- Python:默认 3.13 / 3.12.10,无 3.10;`.venv` = **3.10.21**(uv 重建,P0-01)+ PyQt6 6.10.2 + PyQt6-WebEngine 6.10.0 + openai 2.26.0 + pythonnet 3.1.0 + PyMuPDF 1.28.0
|
||
- WebView2 Runtime:153.0.4234.32 存在
|
||
- WSL:Ubuntu-22.04(Stopped)、docker-desktop(Running)
|
||
|
||
任务状态表:
|
||
- P0-01: COMPLETE
|
||
- P0-02: COMPLETE
|
||
- P0-03: COMPLETE(2026-09-16 复核通过,文档一致性修订已固化)
|
||
- P1-01: COMPLETE
|
||
- P1-02: COMPLETE(Windows 侧自动化全绿;Linux 进程组用例 C3 待 Linux 环境运行生效)
|
||
- P1-03: COMPLETE(Windows 侧自动化 + offscreen 真实启动链全绿;Linux 真机与 root/容器 `--no-sandbox` 接受路径待对应环境验证)
|
||
- P1-04: COMPLETE(Windows 侧自动化全绿:路由矩阵/X11 替身/portal 替身/offscreen 真实启动链;Linux X11 XGrabKey 真实注册与 Wayland portal 授权/取消/无 portal 三态待真实 Linux 宿主手动验证,见 evidence/P1-04.md)
|
||
- P2-01: COMPLETE(两栏启动倒序 + 状态保持,offscreen 全量覆盖,无平台相关项;见 evidence/P2-01.md)
|
||
- P2-02: COMPLETE(offscreen 全量覆盖 + 无泄漏断言;Windows/Linux 真机截图待对应环境运行 diag_panel_scrollbar.py;见 evidence/P2-02.md)
|
||
- P2-03: COMPLETE(offscreen 结构+行为全量覆盖 34/34;审计确认唯一受影响遮罩;Windows 真机 WebView2 人工验收清单见 evidence/P2-03.md)
|
||
- P2-04: COMPLETE(聚合入口 tests/run_all.py:Windows 25/25 PASS;WSL/3.12 FAIL 0 + 21 有理由 SKIP;夹具演示非零退出码;独立/聚合输出一致;暴露并修复 T9 陈旧断言与 compaction.py 3.11+ dataclass 缺陷;见 evidence/P2-04.md)
|