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.
9.4 KiB
全量修复执行状态
状态: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.pyget_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)