from pathlib import Path import hashlib import json folder = Path(__file__).resolve().parent names = [ 'wechat_gui_qt.py', 'gui_runtime.py', 'wechat_bot.py', 'review_state.py', 'assets/ui/console-app.js', 'assets/ui/zhenyang-ai-console.html', 'assets/ui/console-review.css', 'test_desktop_review_visibility.py', 'test_visual_review_visibility.py', 'test_visual_handoff_visibility.py', 'test_review_handoff.py', 'test_handoff_safety.py', 'test_handoff_evidence.py', 'test_queued_handoff_report.py', 'test_qt_console_bridge.py', 'test_queue_log.py', ] manifest = [] for root in (Path(r'C:\kefu\wechat_rpa'), Path(r'C:\wechat_rpa')): files = names + (['protocol_engine.py', 'test_protocol_review_visibility.py'] if root.name == 'wechat_rpa' and root.parent == Path('C:/') else []) for name in files: path = root / name if path.exists(): data = path.read_bytes() if path.suffix == '.py': compile(data, str(path), 'exec') manifest.append({'path': str(path), 'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}) (folder / 'final-file-manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding='utf-8') rows = [] for label, name in ( ('原版桌面、队列、运行器', 'desktop-source-final.json'), ('协议版桌面、队列、运行器、协议流水', 'desktop-target-final.json'), ('原版人工接管保护及证据', 'handoff-source-final.json'), ('协议版人工接管保护及证据', 'handoff-target-final.json'), ('原版视觉与回复安全', 'visual-source-final.json'), ('协议目录视觉与回复安全', 'visual-target-final.json'), ): report = json.loads((folder / name).read_text(encoding='utf-8')) assert not report['failures'] and not report['errors'], name assert not report['blocked_external_actions'], name passed = report['tests_run'] - report['skipped'] rows.append(f"| {label} | {passed} | {report['skipped']} | 0 | [{name}]({name}) |") frontend = json.loads((folder / 'qa/frontend-results.json').read_text(encoding='utf-8')) assert all(item['passed'] for item in frontend['results']) assert not frontend['errors'] rows.append(f"| 两目录浏览器交互与窄屏 | {len(frontend['results'])} | 0 | 0 | [结果](qa/frontend-results.json) |") content = '''# 桌面审核与人工处理可见性修复 日期:2026-09-16 已更新 C:\\kefu\\wechat_rpa 与 C:\\wechat_rpa 的本地源码。未重新打包安装程序,也未重启正在运行的客户端。 ## 问题及结果 - 协议 `awaiting_review` 原来未映射为待审核,队列缺少独立审核筛选,详情未完整显示草稿及审核原因;现在提供“待审核”“需人工”入口和计数。 - JS 更新判据遗漏审核标记/原因,任务仅改变审核状态时可能不刷新;现在审核状态变化会更新界面。 - 视觉队列保存/恢复时遗漏审核原因,部分旧任务被当作过期或旧标记清理;现在保留审核依据,未知旧原因继续等待人工核对。 - 实际发现的协议任务处于 `receipt_check + uncertain`,是发送结果待核对。现在明确显示原因,仅提供“核对后转人工”;不显示再次审批、重试或普通取消。 - 人工移交会在后台重新核对发送状态,发送中继续受保护;未知回执须显式确认。监听停止时也能处理。 - 接管保存原任务快照及操作时间,保留去重/发送证据,不把移交动作标记为送达确认;保存失败不宣称成功。 - 协议环境异步启动期间保留用户已经点击的审核请求,环境就绪后再核对会话处理。 ## 使用方式 1. 从更新后的目录重新启动客户端,进入“任务队列”。源码更新不会自动替换已安装的旧 EXE。 2. “待审核”:查看来源消息、审核原因和草稿,确认后点击“通过并发送”。 3. “需人工”:查看具体原因;发送待核对任务先去企业微信原会话检查,再点击“核对后转人工”。不要把人工接管当作重新发送。 视觉模式下,转人工的原消息不会因重启再次发送;只有识别到可靠的后续客户消息才恢复自动处理。无法证明出现新消息时,界面进度及运行日志提示继续人工跟进。 ## 验证 均使用临时数据、模拟模型和发送器;网络、桌面输入与真实发送受到隔离。包含真实 Qt 内嵌页面读取队列状态后的显示验证,以及 Playwright 浏览器交互验证。 | 检查范围 | 通过 | 跳过 | 失败 | 结果 | |---|---:|---:|---:|---| ''' + '\n'.join(rows) + ''' 跳过项是原版目录不具备的协议功能;不是失败。早期中间报告已被上述 `*-final.json` 替代。 截图:[待审核详情](qa/protocol-review-queue.png)、[发送待核对](qa/protocol-uncertain-delivery.png)。 没有批准、移交或删除真实客户任务,也没有向真实客户发送测试消息。真实模型问答联调的先前登录限制不属于本次隔离验证结果。 ## 回滚与文件 原文件备份:两个项目目录下 `backups/review-visibility-20260916/`,Qt 与前端备份位于对应子目录。当前文件与 SHA256 见 [清单](final-file-manifest.json)。只替换本次对应源码,勿还原真实队列或登录配置。 ''' (folder / 'TEST_REPORT.md').write_text(content, encoding='utf-8') print(json.dumps({'report': str(folder/'TEST_REPORT.md'), 'manifest_files': len(manifest)}, ensure_ascii=False))