| 1 | # 桌面壳迁移:从 Wails 到 Electron |
| 2 | |
| 3 | [English](DESKTOP_SHELL_MIGRATION.md) |
| 4 | |
| 5 | 本文保留 Wails 到 Electron 的架构决策、迁移证据和待完成验收项。当前实现使用 |
| 6 | Electron、Go 桌面服务和 React;Wails 入口及构建依赖已经移除。下文的迁移阶段和 |
| 7 | 基线命令描述这次转换,不作为日常开发流程。当前开发请参阅 |
| 8 | [贡献指南](../CONTRIBUTING.md)、[宿主协议](DESKTOP_HOST_PROTOCOL.zh-CN.md)和 |
| 9 | [生成的入口清单](desktop-migration/INVENTORY.md)。移除旧壳并不表示所有平台验收 |
| 10 | 均已通过;下文仍明确保留已记录的待验收项。 |
| 11 | |
| 12 | ## 决策 |
| 13 | |
| 14 | Reasonix Desktop 从 Wails v2(macOS WebKit、Windows WebView2、Linux WebKitGTK)迁移到 |
| 15 | 使用 Chromium 渲染的 Electron,原因是产品需要一个用户与 Agent 共同操作的原生浏览器, |
| 16 | 而没有任何系统 webview 能在四个发行目标上提供第二个隔离、可编程、引擎稳定的网页 |
| 17 | 表面。Go 桌面层成为独立服务进程,通过一条私有 JSON-RPC 连接与壳相接。开发分支直接 |
| 18 | 替换 Wails,不维护双壳产品;在本文所有验收门槛通过之前,该分支不发布。 |
| 19 | |
| 20 | 考虑并否决的替代方案: |
| 21 | |
| 22 | - **保留 Wails,通过 CDP 嵌入系统 Chrome。** 依赖外部浏览器安装,无法安全共享登录 |
| 23 | 分区,也无法控制应用窗口内的表面几何。 |
| 24 | - **Wails v3 多窗口。** 每个平台仍是一个系统引擎,没有 `WebContentsView` 对应物,促成 |
| 25 | 恢复代码的 WebKitGTK/WebView2 粗糙边缘依旧存在。 |
| 26 | - **用 TypeScript 重写桌面层。** 抛弃 CLI、Serve 和 bot 前端共享的 controller、租约、 |
| 27 | 恢复和远程逻辑。 |
| 28 | |
| 29 | 接受的后果:更高的固定内存与包体占用,按完整进程树测量并如实公布;两套运行时必须 |
| 30 | 保持同一版本单元;Linux 上需要 Chromium sandbox。 |
| 31 | |
| 32 | ## 基线 |
| 33 | |
| 34 | 迁移基线是 `main-v2` 的 `7717f3eeab47f66560ea85cc7dbe27426c3adf47`,在建分支时冻结。 |
| 35 | `e2298bd78` 上的原型成果(独立的 Electron+Go 浏览器实验和 ACP MCP 交互转发)随分支 |
| 36 | 保留。两者之间的修复(欢迎布局中的会话恢复可见、全局新建会话工作区目标、设置搜索 |
| 37 | 与保存栏重叠)属于基线,必须保留。 |
| 38 | |
| 39 | Wails 指标用 `scripts/desktop-shell-metrics.sh` 在同一台机器上采集,保存在 |
| 40 | `docs/desktop-migration/baseline/`。Electron 构建用同一脚本测量,保证对照口径一致。 |
| 41 | |
| 42 | ## 架构 |
| 43 | |
| 44 | ```text |
| 45 | React 界面 ──preload 类型化 IPC──▶ Electron 主进程 ──stdio JSON-RPC──▶ Go 桌面服务 |
| 46 | │ │ |
| 47 | ├─ WebContentsView(网站) └─ control.Controller、会话、 |
| 48 | ├─ 远程 Serve 窗口 工具、租约、恢复、计费 |
| 49 | └─ 菜单、托盘、对话框、剪贴板 |
| 50 | 远程 Reasonix Agent ◀── 经现有 SSH 通道的受限 Host RPC ──▶ Go 桌面服务 |
| 51 | ``` |
| 52 | |
| 53 | | 层 | 负责 | |
| 54 | | --- | --- | |
| 55 | | React 界面 | 展示、意图、布局、状态投影;不访问 Electron 或 Go 全局对象 | |
| 56 | | Electron 主进程 | 窗口、浏览器视图、菜单、托盘、对话框、剪贴板、通知、原生生命周期 | |
| 57 | | Go 桌面服务 | 全部桌面业务命令、controller 所有权、审批、设置、终端、SSH、扩展、更新协调 | |
| 58 | | Go 内核 | Agent、provider、工具、持久化、租约、恢复与计费语义不变 | |
| 59 | | 远程适配器 | 转发经会话授权的宿主能力;不建立第二套浏览器实现 | |
| 60 | |
| 61 | 契约(线路形状见协议文档): |
| 62 | |
| 63 | - `DesktopContract`:对 Go `App` 值反射得到的命令注册表,生成 TypeScript 命令表和 |
| 64 | DTO 声明,握手校验其摘要。 |
| 65 | - `DesktopEvent`:统一封装(`seq`、`generation`、`name`、`args`),原样携带现有事件载荷。 |
| 66 | - `NativeHost`:替代 Go 中直接壳工具包调用的接口;由 Electron 宿主通过 `host/*` 请求实现。 |
| 67 | - `BrowserExecutor`:本地与远程共享的浏览器读取/动作/截图/文件接口(阶段 D)。 |
| 68 | - `HostCapabilityRegistry`:宿主能力发现、版本协商和会话授权;浏览器工具接入现有 |
| 69 | capability 与 tool registry。 |
| 70 | - `DesktopLifecycle`:两进程共享的启动、就绪、隐藏、恢复、退出与更新交接状态。 |
| 71 | |
| 72 | ## 阶段与状态 |
| 73 | |
| 74 | 状态取值:`implemented`(代码在分支上)、`locally tested`(开发机上的测试或人工检查)、 |
| 75 | `externally verified`(CI 或其他平台)、`blocked`(附原因)。只有在每个发行目标上满足 |
| 76 | 退出条件,阶段才算关闭。 |
| 77 | |
| 78 | ### A. 冻结基线,建立完整入口清单 |
| 79 | |
| 80 | - 从冻结基线建立 `feature/electron-desktop-shell`,携带原型与 ACP 成果:implemented。 |
| 81 | - `tools/desktopinventory` 生成命令、原生调用、事件、前端桥接用法、CSS 标记、持久化 |
| 82 | 文件、旧壳专用 Go 文件、发布产物和 CI 任务的清单,每项恰有一个分类;`-check` 在 |
| 83 | 漂移或未分类时失败:implemented,locally tested。 |
| 84 | - Wails 基线指标:见 `docs/desktop-migration/baseline/`。 |
| 85 | - 本记录、协议文档与清单的中英文版本:implemented。 |
| 86 | |
| 87 | 退出条件:每个现有入口都有归属与验收用例。清单已满足;验收用例见下文门槛。 |
| 88 | |
| 89 | ### B. 抽离桌面服务,建立统一桥接 |
| 90 | |
| 91 | - `nativeHost` 接口及其 Wails 实现;Go 业务代码不再直接调用壳工具包:implemented, |
| 92 | locally tested(`desktop/native_host*.go`,`go test -short .` 通过)。 |
| 93 | - `desktop/internal/hostrpc`:反射注册表、契约摘要、TypeScript 生成器、基于 `rpcwire` |
| 94 | 的严格 JSON-RPC 服务、事件封装、反向宿主请求:implemented,locally tested;注册表 |
| 95 | 接受全部 575 个命令。 |
| 96 | - `reasonix-desktop --host-rpc`:一个 Go 服务进程管理全部会话与标签;`-emit-contract` |
| 97 | 输出生成的 TypeScript 与 JSON;RPC 原生宿主、托盘与退出钩子经壳连接工作: |
| 98 | implemented,locally tested。 |
| 99 | - `desktop/` 下统一的 pnpm workspace 管理前端与壳:implemented。 |
| 100 | - 根 Go 模块保持纯静态构建;桌面模块保留自己的构建。 |
| 101 | |
| 102 | 退出条件:服务可脱离 Wails 启动和测试;所有命令由契约映射;业务代码没有直接壳调用。 |
| 103 | |
| 104 | ### C. Electron 承载完整现有桌面 |
| 105 | |
| 106 | 主窗口、可信 preload、错误恢复页、服务监督器、带授权媒体转发的 `reasonix://app` |
| 107 | 资源 scheme、窗口状态、主题、标题栏拖动、快捷键、文件拖放、剪贴板、对话框、远程 |
| 108 | Serve 窗口、菜单、托盘、后台关闭与恢复。TranscriptKernel、稳定消息身份和单一滚动 |
| 109 | 写入者不动。 |
| 110 | |
| 111 | 状态:壳(`desktop/electron`)、前端宿主适配层(`src/lib/desktopHost.ts`、边界门禁、单一 |
| 112 | 样式表加拖动区域重写)以及托盘、远程窗口和重启的 host 模式路由已实现,并在 macOS arm64 |
| 113 | 本地测试通过:`pnpm --dir electron smoke` 在一次性数据目录中启动真实服务,12/12 通过 |
| 114 | (握手、invoke、未知命令拒绝、窗口边界、渲染进程无 Node 与 Wails 全局对象、两进程干净 |
| 115 | 退出);桌面 Go 完整测试通道与前端门禁均通过。用同一脚本对照 Wails 基线 |
| 116 | (`docs/desktop-migration/baseline/README.md`):前端就绪时间在噪声范围内不变,进程树 |
| 117 | 内存高约 280 MiB,SIGTERM 现在能干净退出。Windows 与 Linux 上的壳运行属于外部验证项。 |
| 118 | |
| 119 | 退出条件:完整现有桌面流程在 Electron 上可用,无 mock 兜底、无空按钮、无遗漏事件; |
| 120 | 快速切换会话不串台。 |
| 121 | |
| 122 | ### D. 生产浏览器与本地/远程统一执行器 |
| 123 | |
| 124 | 右侧工作区的浏览器面板(任务内多标签、地址栏、历史、刷新、缩放、加载错误、下载、 |
| 125 | DevTools),由 `BrowserSurfaceManager` 管理;Agent 能力(结构快照、截图、导航、点击、 |
| 126 | 输入、按键、滚动、标签、文件)接入现有 capability、审批、取消和证据体系;用户接管 |
| 127 | 撤销待执行动作;写操作先记录操作身份再执行,结果区分已执行/未执行/未知;远程 |
| 128 | Agent 通过 SSH 承载的 Host RPC 使用同一执行器,授权绑定世代。 |
| 129 | |
| 130 | 状态:已实现,并在 macOS arm64 上完成本地验证。Electron 浏览器表面 |
| 131 | (`desktop/electron/src/main/browser/`:WebContentsView 表面、快照/引用、可信 |
| 132 | 输入动作、绑定世代的授权与 stale/taken-over/no-grant 错误码、下载、截图) |
| 133 | 单测 81/81 通过,壳冒烟真实打开 example.com 并端到端校验标签标题(15/15); |
| 134 | 渲染端 API 固定在 `window.reasonixDesktop.browser`。前端浏览器面板 |
| 135 | (`BrowserPanel`、dock 标签、地址栏、缩放、DevTools、下载、接管横幅、覆盖层 |
| 136 | 门控)整体收进单个 lazy chunk,initial 预算按实测 ratchet(raw 2408.2 → |
| 137 | 2408.8 KiB,token 级 diff 证明 initial chunk 零泄漏)。远程 Agent 经 |
| 138 | 127.0.0.1 loopback broker(`desktop/browser_broker.go`)使用同一执行器: |
| 139 | 按主机连接世代铸造、重连即失效的 token,会话作用域路由与跨会话 `no_grant` |
| 140 | 拒绝,截图/下载经 SFTP 中转回流,serve 能力协商保证旧远程端继续可用;以上由 |
| 141 | `-race` 测试覆盖,含真实 SFTP 往返。未闭合项:真实 SSH 主机的远程端到端 |
| 142 | 验收、`browser_upload` 的远程→桌面反向 staging(wire 已透传 `files`,broker |
| 143 | 尚未实现中转),以及验收门槛表中的远程浏览器各行。 |
| 144 | |
| 145 | 退出条件:本地与远程 Agent 通过相同工具完成真实网页任务,接管、审批、文件归属与 |
| 146 | 恢复行为一致。 |
| 147 | |
| 148 | ### E. 平台功能、安装与更新 |
| 149 | |
| 150 | Electron 菜单、托盘、通知、文件关联、窗口恢复、单实例呈现;产品名称、安装位置、 |
| 151 | 快捷方式、卸载身份、数据目录和产物名称不变;Electron 打包接入现有 NSIS、nfpm 与 |
| 152 | 签名步骤;Go 更新协调器继续负责版本解析、签名校验、布局与恢复,Electron 提供准备 |
| 153 | 退出与重启;壳、服务、资源与辅助程序为同一版本单元;macOS Universal 并公证;Linux |
| 154 | Chromium sandbox 不使用 `--no-sandbox`;minisign 与摘要校验不变。 |
| 155 | |
| 156 | 状态:已实现,并在开发机允许的范围内完成本地验证。下文所述安装布局成员、 |
| 157 | payload schema 2、shell bootstrap 与 macOS 交接均已合入分支,desktop 模块测试 |
| 158 | 全绿、Windows/Linux 交叉编译通过。发布管线现已端到端打包 Electron 壳: |
| 159 | `desktop/packaging/` 用 @electron/packager(macOS 走 universal)组装 `app/` 树; |
| 160 | `scripts/desktop-build.sh` 先做契约漂移核对再驱动打包,不再调用 `wails build`; |
| 161 | NSIS 以 `File /r` 安装 `app/` 树;deb 安装到 `/usr/lib/reasonix/app` 并在 |
| 162 | postinstall 置 `chrome-sandbox` 为 root 4755;SignPath 配置覆盖树内 PE 集合, |
| 163 | 保留安装器二阶段签名;CI/release workflow 对打包产物运行 |
| 164 | `packaging/smoke.mjs`(`desktop-linux-webkit41` job 已删除;钉住旧流程的契约 |
| 165 | 测试已改写为新入口并加入 `wails build` 负向守卫)。未闭合项:四平台安装/升级 |
| 166 | 矩阵、真实签名与公证流程、SignPath preflight 重新 attestation(artifact |
| 167 | configuration 指纹已变化),以及 Windows/Linux runner 验证。 |
| 168 | |
| 169 | 退出条件:四类产物均可安装、启动、卸载,并通过 Wails→Electron 升级、Electron→Electron |
| 170 | 升级和安装失败恢复测试。 |
| 171 | |
| 172 | 版本化安装布局(Windows 与 Linux)的设计说明:`installlayout` 激活器只允许 |
| 173 | `versions/<v>/` 内的扁平常规文件。Electron 载荷新增一个树成员 `app/` 承载 Electron |
| 174 | 包;Windows 载荷清单升级到 schema 2,列出 `app/` 下每个文件及其摘要,激活器在移动 |
| 175 | `current.json` 之前校验整棵树。`reasonix-desktop(.exe)` 仍是瘦启动器启动的活动桌面 |
| 176 | 可执行文件:不带 `--host-rpc` 时它引导 `app/Reasonix(.exe)` 后退出,Electron 再以 |
| 177 | `--host-rpc` 启动同一二进制作为服务。因此启动器、`current.json`、单实例身份与重启 |
| 178 | 逻辑保持现状。macOS 上 bundle 的主可执行文件是 Electron,Go 服务位于 |
| 179 | `Contents/MacOS/`;`.app` 替换路径不变。实现说明:`installlayout.Member` 的名字是 |
| 180 | 版本目录下的正斜杠路径,要么是白名单内的文件名,要么是 `app/...`(不允许 `..`、绝对 |
| 181 | 路径、反斜杠与符号链接);清单读取端同时接受 schema 1(扁平列表)和 schema 2(扁平 |
| 182 | 列表加 `app/`);迁移期的 `REASONIX_DESKTOP_SHELL=wails` 进程内回退已随阶段 F 删除; |
| 183 | 在 shell 下,macOS 交接子进程等待的是 Electron 进程(服务的父进程, |
| 184 | 通过 `-owner-pid` 传入),替换后用 `open -n` 重新打开 bundle,shell 本身只退出。 |
| 185 | |
| 186 | #### 首次从 Wails 升级 |
| 187 | |
| 188 | 从 v1.38.x 首次升级 Electron 时,需要**手动安装完整安装包**。 |
| 189 | 已发布的客户端会复制并执行旧安装中的更新助手,无法携带新的 `app/` 目录。 |
| 190 | 因此发布资产标记 `install_layout: "electron-v1"`:v1.38.x 现有的清单校验会在 |
| 191 | 下载和替换任何文件前拒绝未知布局,保留可用的旧安装;更新错误界面仍提供官方下载页 |
| 192 | 入口。退出旧应用后,通过该页面安装完整 Windows 安装器、macOS 应用或 Linux 包。 |
| 193 | 便携版应完整解压到新目录,不能只替换 Go 可执行文件。配置、会话及数据目录名称与 |
| 194 | 格式保持不变。由于旧客户端校验整个跨平台清单,macOS 首次迁移也采用手动安装。 |
| 195 | |
| 196 | 完成首次迁移后,Electron 客户端接受 `electron-v1`,先发布同一版本的 Go 服务、 |
| 197 | CLI 与完整壳资源,再移动 `current.json`。Linux 原生包继续由包管理器管理。 |
| 198 | Windows 更新先等待 Electron 所属进程退出,再通过按数据目录命名的管道验证新 |
| 199 | Go 服务;管道服务端 PID 由 Windows 内核提供,旧 Wails 端点检测仍保留。 |
| 200 | 所有镜像和发布清单必须保留此边界;改回 `versioned-v1` 会重新启用不安全的旧版 |
| 201 | 自动更新路径。 |
| 202 | |
| 203 | ### F. 全矩阵验收并删除旧实现 |
| 204 | |
| 205 | CI 切换到新构建、契约生成和原生测试入口;删除 Wails 入口、依赖、生成绑定、WebView2 |
| 206 | 恢复与壳补丁;原型故障用例进入正式测试;删除迁移别名、重复 DTO 和临时适配。 |
| 207 | |
| 208 | 状态:删除已实现并通过本地测试。Wails 入口(`wails.Run`、`native_host_wails.go`、 |
| 209 | `wails.json`、生成的 `wailsjs` 绑定、进程内远程窗口子进程)已删除,随之删除的还有 |
| 210 | WebView2/WebKitGTK 恢复协调器、诊断观察者、原生冒烟工具(`cmd/transcript-native-smoke`、 |
| 211 | `cmd/transcript-selection-smoke`)、vendored go-webview2 分支、`webkit2_41` 构建标签和 CI |
| 212 | 的 WebKitGTK 工具链步骤。desktop 模块的 `go list -m all` 已无 Wails;前端只访问 |
| 213 | `window.reasonixDesktop`(由 `check-desktop-host-boundary.mjs` 强制),测试桩改为 |
| 214 | Electron 宿主 stub。`REASONIX_DESKTOP_SHELL=wails` 已不存在:未安装壳时直接启动会以 |
| 215 | 安装提示退出。原型的崩溃故障用例(派发前崩溃取消动作、派发后崩溃按已执行结算且不重放、 |
| 216 | 恢复保留登录分区)已成为 `desktop/electron/src/main/browser/` 的正式测试。有意保留: |
| 217 | `startNativeShellSupport` 下的 fyne systray 进程内回退(壳下不可达,但仍是裸服务路径)、 |
| 218 | 旧崩溃报告解码字段、`com.wails.reasonix-desktop` 包标识、更新助手的 `wails-app-` |
| 219 | 单实例查找(用于从 Wails 版升级的检测)。待办:四平台验收矩阵、与 Wails 基线的交互 |
| 220 | p95 对比、Windows/Linux CI runner 验证。 |
| 221 | |
| 222 | 退出条件:最终构建图中没有 Wails;业务代码没有旧桥接全局对象;全部矩阵项与门槛闭合。 |
| 223 | |
| 224 | ## 能力矩阵 |
| 225 | |
| 226 | 生成的清单列出每个入口。下表是验收执行遵循的产品级视图;每行映射到清单分类和下文 |
| 227 | 门槛。 |
| 228 | |
| 229 | | 能力 | 现状(Wails) | 目标(Electron) | 分类 | |
| 230 | | --- | --- | --- | --- | |
| 231 | | 会话:发送、停止、模型/effort 切换、历史、恢复、租约 | `App` 方法经 Wails 绑定 | 同一方法经 `desktop/invoke` | keep-business | |
| 232 | | 项目、工作树、文件预览、工作区监听 | Go+资源中间件 | Go+`reasonix://app` 转发到资源源 | keep-business | |
| 233 | | 终端 | Go PTY/ConPTY,事件 | 经 `desktop/event` 不变 | keep-business | |
| 234 | | 设置、MCP、MCP Apps、技能、插件 | Go | 不变;MCP Apps 保留各自回环源 | keep-business | |
| 235 | | 远程工作区与远程 Serve 窗口 | SSH 管理器+每窗口一个 Wails 子进程 | SSH 管理器不变;每主机一个隔离分区的 `BrowserWindow` | migrate-host | |
| 236 | | 窗口几何、主题、拖动区域、快捷键、缩放 | Wails runtime | `host/window.*`、preload 窗口接口、`-webkit-app-region` | migrate-host | |
| 237 | | 文件拖放、剪贴板、外部链接、对话框 | Wails runtime | preload 原生接口与 `host/dialog.*` | migrate-host | |
| 238 | | 菜单、托盘、后台关闭、第二实例 | Wails 菜单、fyne systray、Wails 锁 | Electron 菜单、`Tray`、按规范数据目录键控的 `requestSingleInstanceLock` | migrate-host | |
| 239 | | 更新器 | Go 协调器+Wails 重启 | Go 协调器+`host/app.relaunch` | migrate-host | |
| 240 | | 渲染进程恢复(WebView2/WebKitGTK) | Go 恢复协调器 | Electron `render-process-gone` 处理 | delete-shell | |
| 241 | | Agent 原生浏览器 | 仅原型 | `WebContentsView` 面板+`BrowserExecutor` | 新增 | |
| 242 | |
| 243 | ## 数据兼容 |
| 244 | |
| 245 | - 会话、配置、项目、任务、计费与租约格式不变;不修改 transcript schema。 |
| 246 | - 浏览器元数据与操作日志是旧壳从不读取的新增带版本文件。 |
| 247 | - 网站登录存放于 Chromium 持久分区;Cookie 值从不进入配置、日志或模型上下文。 |
| 248 | - 恢复的浏览器标签只保留安全的导航条目;不持久化密码、表单状态或可重放提交。 |
| 249 | - 文件化设置优先于旧 webview 本地偏好。唯一允许的重置是旧 webview 存储中的渲染 |
| 250 | 进程本地外观偏好(字体、字号、面板宽度、排版);旧 webview 数据保留在原处并在 |
| 251 | 迁移说明中列明。 |
| 252 | - 降级:停止 Electron 构建,运行上一个 Wails 构建;新增浏览器状态不得破坏其对会话 |
| 253 | 和配置的读取。 |
| 254 | |
| 255 | ## 验收门槛 |
| 256 | |
| 257 | | 领域 | 必须覆盖的场景 | |
| 258 | | --- | --- | |
| 259 | | 契约 | Go/TS 签名一致、空数组、可选字段、错误映射、取消、乱序回答、协议不匹配、大资源 | |
| 260 | | 会话与所有权 | 发送、停止、模型/effort 切换、快速切换项目与会话、后台重挂、租约冲突、controller 替换失败保留旧会话 | |
| 261 | | 事件恢复 | 渲染进程重载、事件积压、订阅断开与重新快照;无重复、无旧世代写入 | |
| 262 | | 桌面能力 | 终端输入输出与 resize、文件拖放、媒体预览、MCP Apps、设置、自动任务、远程连接与窗口 | |
| 263 | | 浏览器 | iframe、动态 DOM、受控输入、弹窗、上传下载、历史、临时分区、登录共享与隔离 | |
| 264 | | 接管与未知写入 | 审批前接管、审批后派发前接管、执行后回执丢失、崩溃后重启、重复 operation ID | |
| 265 | | 远程浏览器 | SSH 断连、重连世代变化、旧 token、跨会话误路由、远程上传下载、远程进程恢复 | |
| 266 | | 原生体验 | macOS、Windows、Linux 的真实中文 IME、焦点、选择复制、快捷键、标题栏、分栏、跨屏 DPI、托盘恢复 | |
| 267 | | 安装升级 | 旧版运行中升级、不同数据目录并存、相对数据目录、签名损坏、安装中断、重启失败与回滚 | |
| 268 | | 隔离 | 网站与 iframe 无桥接;伪造 IPC、过期资源 token、越界文件请求、外部协议调用被正确处理 | |
| 269 | |
| 270 | 真实任务验收:登录后的 GitHub PR 评审草稿并带来源;文档网站跨页检索并本地保存; |
| 271 | 可控测试网站的表单提交、上传与下载,完整经过审批与接管;同样的任务从远程工作区 |
| 272 | 执行,浏览器在本机、结果归属远程任务;提交已发生但回执未知时中断,证明恢复后不会 |
| 273 | 自动重复提交。 |
| 274 | |
| 275 | 资源与性能采样遵循 `scripts/desktop-shell-metrics.sh`(完整进程树;启动、空闲、1/5 |
| 276 | 标签、长会话、流式、一小时),另加 30 次标签与会话开关循环,证明进程、监听器、 |
| 277 | `WebContents` 与会话资源被释放。交互 p95(会话切换、停止反馈、输入延迟)不超过同机 |
| 278 | Wails 基线的 `max(1.2 倍基线,基线+50ms)`。包体、启动与内存增量如实公布;固定占用 |
| 279 | 本身不判失败,持续泄漏必须修复。 |
| 280 | |
| 281 | 最终证据绑定同一候选 SHA:根模块与桌面模块测试、变更并发路径的 race 测试、完整 |
| 282 | 前端 CI 套件,以及四类产物的原生验收。 |
| 283 |