cc-haha/docs/desktop/08-electron-migration-tasks.md
程序员阿江(Relakkes) 386a41e606 feat(desktop): enable Electron migration path
Introduce the Electron desktop shell alongside the existing React renderer and local Bun server boundary. The migration keeps the DesktopHost contract explicit across Tauri, Electron, and browser runtimes while adding Electron main/preload services for dialogs, shell, notifications, updates, tray/window lifecycle, terminal, preview WebContentsView, app mode, and release/package validation.

The commit also carries the latest local main desktop command updates, including agent slash entries and hidden-by-default markdown thinking details, so the packaged Electron build matches the current main UX surface.

Constraint: React renderer, local Bun server, REST/WebSocket, and sidecar boundaries must remain reusable during the migration
Constraint: macOS dev packages are ad-hoc signed and cannot prove Developer ID notarization or Gatekeeper release launch
Rejected: Browser-only smoke validation | it cannot exercise native dialogs, keychain prompts, notification behavior, or packaged app startup
Confidence: medium
Scope-risk: broad
Directive: Do not remove Tauri host support until signed Electron release artifacts pass native OS smoke on macOS, Windows, and Linux
Tested: bun run check:desktop
Tested: cd desktop && bun run check:electron
Tested: CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron📦dir
Tested: bun run test:package-smoke --platform macos --package-kind dir --artifacts-dir desktop/build-artifacts/electron
Tested: Computer Use read packaged Electron app window at desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app
Not-tested: Developer ID signed/notarized Gatekeeper launch
Not-tested: Real OS notification click-to-session action
Not-tested: Windows and Linux packaged app smoke on real hosts
2026-06-01 22:43:16 +08:00

84 KiB
Raw Permalink Blame History

Electron 迁移任务清单

For agentic workers: 按任务顺序执行。每个任务完成后先跑该任务列出的核对命令,再进入下一任务。不要跳过打包壳 smoke也不要把浏览器/Vite smoke 当作 Electron 壳验收。

目标: 将桌面端 host runtime 从 Tauri 2 迁移到 Electron同时保持 React 层、server/sidecar、自动更新、系统交互、通知、构建发布和 Computer Use 能力可用。 架构: 先抽象 desktopHost contract再并行实现 Tauri 与 Electron最后切换构建发布链路。保留本地 Bun server + REST/WebSocket 边界不把会话、聊天、workspace、team 业务协议改成 Electron IPC。 技术栈: React 18、Vite、Zustand、Bun、Electron、electron-builder/electron-updater、现有 sidecar、现有质量门禁。


当前同步状态

执行记录2026-06-01

  • 已按本地 main 重新同步当前迁移工作区:当前分支 feat/electron-migration-main-sync-2392 已快进到本地 main 的最新提交后再恢复 Electron 迁移改动。
  • 已在当前工作区内解决同步冲突,重点文件包括 desktop/package.jsondesktop/scripts/build-macos-arm64.shdesktop/src-tauri/resources/preview-agent.jsdesktop/src/components/workspace/WorkspaceFileOpenWith.test.tsxdesktop/src/lib/previewEvents.tsdesktop/src/lib/previewEvents.test.ts
  • 冲突解决后已重建 preview agent并运行 targeted tests 与 Electron package dir 构建;后续迭代均基于同步后的本地 main
  • packaged Electron renderer 使用 file://.../app.asar/dist/index.html,真实模型 smoke 暴露出 H5 token mode 下 WebSocket 会因 Origin: file:// 被拒绝;已在 server CORS/H5 access policy 中把 file:// 纳入本地桌面可信来源,并补回归测试。

Phase 0迁移前基线

1. 冻结当前 Tauri 行为基线

涉及文件:

  • 只读:desktop/src/**
  • 只读:desktop/src-tauri/**
  • 只读:scripts/quality-gate/**

步骤:

  • 运行 git status --short,确认当前工作区是否有无关改动。
  • 运行 bun run check:policy
  • 运行 bun run check:desktop
  • 运行 bun run check:server
  • 运行 bun run check:coverage
  • 有 live provider 时运行 bun run quality:gate --mode baseline --allow-live --provider-model <provider:model[:label]>
  • 记录 quality report 路径和失败项;失败时先修现有基线,不进入迁移。

执行记录2026-05-31

  • bun run check:policy 通过。
  • bun run check:desktop 通过;首次缺少 desktop 依赖,已在 desktop/ 执行 bun install 后复跑通过。
  • bun run check:server 通过。
  • bun run check:coverage 初次运行未通过;归因后在 adapters/ 执行 bun install 修复 grammy 缺包环境问题,并补足 changed-line 覆盖率。
  • 后续 bun run check:coverage 复跑曾因本机 WeChat 占用随机端口 24023 导致 src/server/__tests__/h5-access-auth.test.ts 超时;已把该测试的随机端口选择改为先探测可绑定端口。
  • 最新 bun run check:coverage 通过report 为 artifacts/coverage/2026-05-31T13-15-40-522Z/coverage-report.mdsummary 为 passed=5 failed=0changed-lines coverage 为 90.67% (204/225)

通过标准: 现有 Tauri 主干的 desktop/server/coverage/baseline 状态可复现。 阻断条件: 当前主干已有红灯且未归因。


Phase 1Host Adapter Contract

2. 新增 host adapter 类型与 browser fallback

创建:

  • desktop/src/lib/desktopHost/types.ts
  • desktop/src/lib/desktopHost/browserHost.ts
  • desktop/src/lib/desktopHost/index.ts
  • desktop/src/lib/desktopHost/contract.test.ts

步骤:

  • 定义 DesktopHost 接口,至少覆盖 runtime/server URLupdatesnotificationsdialogsshellwindowterminalpreviewappMode
  • browserHost 中保留 H5/browser fallback没有桌面能力时返回明确错误或 no-op。
  • 在 contract test 中固定每个方法的 payload 和错误语义。
  • 运行 cd desktop && bun run test -- src/lib/desktopHost/contract.test.ts --run

执行记录2026-05-31

  • 新增 desktop/src/lib/desktopHost/types.tsbrowserHost.tsindex.tscontract.test.ts
  • 先运行 contract test 得到预期红灯(缺少 browserHost 模块),补实现后 desktopHost/contract.test.ts 6 tests 通过。

通过标准: host adapter 类型能表达当前所有 Tauri command/plugin 能力。 阻断条件: 仍需要 renderer 直接 import Tauri API 才能表达某个能力。

3. 实现 Tauri host adapter

创建:

  • desktop/src/lib/desktopHost/tauriHost.ts

修改:

  • desktop/src/lib/desktopRuntime.ts
  • desktop/src/api/terminal.ts
  • desktop/src/stores/updateStore.ts
  • desktop/src/lib/desktopNotifications.ts
  • desktop/src/lib/previewBridge.ts
  • desktop/src/lib/previewEvents.ts
  • desktop/src/lib/appZoom.ts
  • desktop/src/stores/adapterStore.ts
  • desktop/src/stores/settingsStore.ts
  • dialog/shell 调用组件:DirectoryPicker.tsxSidebar.tsxSettings.tsxTerminalSettings.tsxComputerUseSettings.tsxWorkspaceFileOpenWith.tsx、聊天链接打开组件

步骤:

  • @tauri-apps/api/core.invoke 包进 tauriHost.commands.invoke 或具体方法。
  • 把 Tauri listen 包进 desktopHost.events
  • 把 dialog/shell/window/update/notification 调用全部迁到 tauriHost
  • 运行 rg "@tauri-apps|__TAURI_INTERNALS__|window\\.__TAURI__|from '@tauri" desktop/src
  • 确认生产代码只在 desktop/src/lib/desktopHost/tauriHost.ts 命中。
  • 运行 bun run check:desktop

通过标准: renderer 业务文件不再知道 Tauri API。 阻断条件: 任一 store/component 仍直接动态 import @tauri-apps/*

当前进度2026-05-31

  • 新增 desktop/src/lib/desktopHost/tauriHost.ts,先迁入 get_server_url 运行时调用。
  • desktop/src/lib/desktopRuntime.ts 已改为通过 desktopHost 选择 Electron preload host、Tauri host 或 browser fallback新增回归测试确保 window.desktopHost 优先于 H5/browser fallback。
  • 已迁移 desktop/src/api/terminal.tsdesktop/src/lib/previewBridge.tsdesktop/src/lib/previewEvents.tsdesktop/src/lib/appZoom.tsdesktop/src/lib/desktopNotifications.tsdesktop/src/stores/updateStore.tsdesktop/src/stores/adapterStore.tsdesktop/src/stores/settingsStore.ts 的 runtime/terminal/preview/zoom/notification/update/adapter restart/app mode 调用到 desktopHost
  • desktopHost 现在覆盖 app.getVersioncommands.invokeevents.listenwebview.onDragDropEventdialogs.open/saveshell.opennotifications.permission/request/send/onActionwindow.minimize/toggleMaximize/close/startDragging/focus/requestAttention/isMaximized/onResized/onNativeMenuNavigateupdates.check/prepare/cancel/relaunch 等 Tauri 能力。
  • 已运行 focused testsdesktopRuntime.test.tsdesktopHost/contract.test.tsterminal.test.tspreviewBridge.test.tspreviewEvents.test.tsappZoom.test.tsdesktopNotifications.test.tsupdateStore.test.tsadapterStore.test.tssettingsStore.test.tsAppShell.test.tsxWindowControls.test.tsxComputerUseSettings.test.tsx 全部通过。
  • 继续迁移了 dialog/shell/window/menu 组件层、file drop webview、official login/open-with 等打开路径:DirectoryPickercomposerAttachmentsTerminalSettingsComputerUseSettingsSettingsSidebarTabBarWindowControlsAppShellWorkspaceFileOpenWithAssistantMessageAssistantOutputTargetCardCurrentTurnChangeCard
  • 2026-05-31 扫描结果:业务 renderer 生产代码不再直接 import @tauri-apps/*;剩余 Tauri 命中为 desktopHost 自身、runtime detection 类型声明和测试 mock。生产路径排除 desktopHost 后扫描为空:rg -n "@tauri-apps|__TAURI_INTERNALS__|window\\.__TAURI__|from '@tauri" desktop/src --glob '!**/*.test.ts' --glob '!**/*.test.tsx' --glob '!**/lib/desktopHost/**' --glob '!src-tauri/**'
  • bun run check:desktop 通过desktop tsc --noEmit、Vitest 全量 146 test files / 1127 tests passedproduction build 成功;现有 React act(...) warnings 仍为非阻断输出。

Phase 2Electron 最小壳

4. 新增 Electron main/preload 骨架

创建:

  • desktop/electron/main.ts
  • desktop/electron/preload.ts
  • desktop/electron/ipc/channels.ts
  • desktop/electron/ipc/capabilities.ts
  • desktop/electron/tsconfig.json

修改:

  • desktop/package.json
  • package.json

步骤:

  • 新增 electron:develectron:buildcheck:electron 脚本。
  • BrowserWindow 使用 preload保持 contextIsolation,不启用 renderer Node。
  • preload 暴露 window.desktopHost,不暴露通用 ipcRenderer
  • capabilities.ts 枚举允许的 IPC channel 和 payload validator。
  • 运行 bun run check:electron

执行记录2026-05-31

  • 新增 desktop/electron/main.tspreload.tsipc/channels.tsipc/capabilities.tsipc/capabilities.test.tselectron/tsconfig.jsonscripts/electron-dev.ts
  • BrowserWindow 配置为 preload + contextIsolation: true + nodeIntegration: false + sandbox: truepreload 只通过 contextBridge.exposeInMainWorld('desktopHost', ...) 暴露 typed host contract。
  • check:electron 通过Electron IPC validator 测试 3/3 通过,electron-dist/main.cjselectron-dist/preload.cjs 构建成功。
  • electron:build 通过renderer desktop/dist 和 Electron CJS entrypoints 均构建成功。
  • 短时 Electron 启动 smoke 已尝试;当前机器首次运行 electron 会触发 Electron binary 下载,下载未在本轮完成,因此真实窗口启动验证保留到下一轮 runtime/server bridge smoke。

通过标准: Electron 壳能启动并加载 Vite/dev 或 desktop/dist阻断条件: renderer 需要 Node API 或 Electron API 才能工作。

5. 实现 Electron runtime/server bridge

创建:

  • desktop/electron/services/sidecarManager.ts
  • desktop/electron/services/serverRuntime.ts

步骤:

  • 复刻 start_server_sidecar保留动态端口、host、config、startup log、超时错误。
  • 复刻 adapter sidecar 启动和 restart_adapters_sidecar
  • 保留 CLAUDE_CONFIG_DIRCC_HAHA_APP_PORTABLE_DIRADAPTER_SERVER_URL 注入。
  • 提供 get_server_url 等价 IPC。
  • 确认 renderer 仍通过 desktop/src/api/client.ts 和 WebSocket manager 访问会话/聊天/workspace/team不新增这些业务的 IPC handler。
  • 运行 bun run check:electron
  • 打开 Electron dev app确认首页能连上 local server。

执行记录2026-05-31

  • 新增 desktop/electron/services/sidecarManager.tsserverRuntime.tsElectron main 在 ready 阶段启动 server sidecarruntime.getServerUrl() 通过 IPC 返回动态 loopback URL。
  • sidecarManager 复用现有 desktop/src-tauri/binaries/claude-sidecar-<triple>server 参数保持 server --app-root <path> --host 0.0.0.0 --port <dynamic>adapter 参数保持 adapters --app-root <path> --feishu/--telegram/--wechat/--dingtalk
  • 环境变量保留 CLAUDE_CONFIG_DIRXDG_CACHE_HOMECLAUDE_H5_AUTO_PUBLIC_URLCLAUDE_H5_DIST_DIRadapter 注入 ADAPTER_SERVER_URL=ws://<dynamic>
  • headless smoke 通过:bun run build:sidecars 后用 ElectronServerRuntime 启动 serverGET /health 返回 200随后 stopAll() 停止 server/adapters 子进程。
  • 2026-06-01 Electron dev 壳已由 Computer Use 多轮复测renderer 加载 localhost:1420/main process 自动启动 local server/sidecar首页、设置页和会话页均可通过 server API/WebSocket 工作。

通过标准: Electron app 不依赖外部手动启动 server。 阻断条件: 需要用户先运行 SERVER_PORT=... bun run src/server/index.ts 才能使用桌面端,或迁移方案开始绕过 local server 直接重写 session/chat IPC。


Phase 3系统能力迁移

6. 迁移文件选择、保存、外链打开

创建/修改:

  • desktop/electron/services/dialogs.ts
  • desktop/electron/services/shell.ts
  • desktop/src/lib/desktopHost/electronHost.ts

步骤:

  • openFileopenDirectorysaveFile 映射到 Electron dialog。
  • openExternal 限制 URL schemeopenPath 限制为明确来自文件选择或 workspace 的路径。
  • 更新 adapter contract tests。
  • 运行 bun run check:desktop && bun run check:electron
  • 手动 smoke选择工作目录、选择附件、选择 Python 路径、打开外链。

执行记录2026-05-31

  • 新增 desktop/electron/services/dialogs.tsshell.tsdialog options 映射到 Electron showOpenDialog/showSaveDialogshell URL 只允许 http: / https: / mailto:
  • DesktopHost.shell 拆分为 open(url)openPath(path)workspace/open-with 本地文件路径改走 openPath,普通网页/OAuth/通知设置等外链继续走 open
  • 新增 desktop/src/lib/desktopHost/electronHost.tspreload 改为复用该 typed host factoryIPC payload 在 preload 进入 ipcRenderer.invoke 前先校验。
  • Focused tests 通过:electron/services/shell.test.tsdialogs.test.tselectronHost.test.tsdesktopHost/contract.test.tsWorkspaceFileOpenWith.test.tsx
  • bun run check:electron 通过16 tests passedElectron main/preload 构建成功。
  • bun run check:desktop 通过,现有 React/Tauri 主干未被 openPath contract 破坏。
  • 2026-06-01 Electron dev 壳 Computer Use 复测附件选择:点击 composer 添加文件或图片 打开 macOS 原生 打开 面板,选择 /Users/nanmi/cc-haha-cua-attachment-smoke.txt 后回到应用composer 显示 cc-haha-cua-attachment-smoke.txt 附件 chip。
  • 2026-06-01 Electron dev 壳 Computer Use 复测 Python 路径选择:在 Computer Use 设置中点击 选择 打开 macOS 原生 选择 Python 解释器 面板,选择 /Users/nanmi/cc-haha-cua-python3-smoke 后应用解析并保存为 /opt/homebrew/Cellar/python@3.14/3.14.5/Frameworks/Python.framework/Versions/3.14/bin/python3.14;随后恢复自动检测,/api/computer-use/authorized-apps 返回 pythonPath: null
  • 2026-06-01 Electron dev 壳 Computer Use 复测外链:点击关于页 GitHub 项目卡片后,系统浏览器 Google Chrome 被拉到前台,地址栏为 github.com/NanmiCoder/cc-hahaElectron renderer 仍停留在 localhost:1420/,未在应用内导航外链。

通过标准: 所有原 plugin-dialogplugin-shell 用户路径可用。 阻断条件: 任一路径绕过 preload 或接受未校验目标。

7. 迁移窗口、托盘、菜单和单实例

创建/修改:

  • desktop/electron/services/windows.ts
  • desktop/electron/services/tray.ts
  • desktop/electron/services/menu.ts
  • desktop/electron/services/singleInstance.ts

步骤:

  • 实现 minimize/toggleMaximize/close/startDragging。
  • 实现关闭隐藏到托盘、托盘 show/quit。
  • 实现 macOS About/Settings 菜单并发送 native-menu-navigate 等价事件。
  • 实现窗口位置/尺寸持久化和 monitor 可见性保护。
  • 实现 app.requestSingleInstanceLock(),第二实例显示主窗口。
  • 运行 bun run check:electron
  • 打包 app smoke关闭窗口、托盘恢复、菜单打开设置页、重启后窗口仍可见。

执行记录2026-05-31

  • 新增 desktop/electron/services/windows.tstray.tsmenu.tssingleInstance.ts
  • Electron main 现在启动前申请 single-instance lock重复启动会 show/focus 主窗口。
  • 主窗口关闭默认隐藏到托盘,托盘菜单提供 Show Claude Code HahaQuit Claude Code Haha;显式 quit 才停止 sidecar 并退出。
  • 窗口位置/尺寸/maximized 状态写入 window-state.json,优先使用 CLAUDE_CONFIG_DIR,恢复前会验证尺寸和 display/workArea 可见性。
  • 原生菜单发送 desktop:window:native-menu-navigate,保持 renderer 现有 settings/about 路由入口。
  • Electron 先不声明自定义 Windows chrome 能力:windowControls=falserenderer 不渲染自定义窗口按钮,也不会把空白 tab/sidebar 区当成可拖拽窗口区域;后续若启用 frameless window需要同时实现真实拖拽。
  • bun run check:electron 通过26 tests passedElectron main/preload 构建成功。
  • 2026-06-01 Electron dev 壳 Computer Use 复测窗口关闭/恢复:点击 macOS 关闭按钮后 Electron 进程仍在 list_apps 中保持 running主窗口消失通过 macOS app activation 路径重新激活同一 Electron app 后Computer Use 再次看到 Claude Code Companion 主窗口和 localhost:1420/ renderer。Computer Use 当前不能稳定枚举 Dock因此本轮验证的是与 Dock 点击等价的 app activation/show 主窗口路径。
  • 2026-06-01 packaged app Computer Use 复测菜单与窗口生命周期:在 desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app 隔离实例中,通过 macOS app menu 点击 Settings... 后 renderer 打开设置页;点击关闭按钮后 Computer Use 返回 noWindowsAvailableSystem Events 显示同一 PID count=0 且进程仍存活;随后用 app activation 恢复Computer Use 再次读取到 Claude Code Companion 设置页窗口。继续通过 app menu Quit 退出后,隔离配置写入 window-state.json;用同一 CLAUDE_CONFIG_DIR--user-data-dir 重启Computer Use 成功读取新 PID 的 packaged file://.../app.asar/dist/index.html 主窗口System Events 显示 count=1 names=Claude Code Companion。该轮覆盖 packaged 菜单打开设置、关闭隐藏、activation 恢复和重启后窗口仍可见;真实 tray 图标点击仍需后续实机/可枚举 tray 环境复验。

通过标准: macOS/Windows/Linux 主窗口生命周期等价。 阻断条件: 关闭行为会误退出或窗口状态可能恢复到屏幕外。

8. 迁移通知

创建/修改:

  • desktop/electron/services/notifications.ts
  • desktop/src/lib/desktopNotifications.ts
  • desktop/src/lib/desktopHost/contract.test.ts

步骤:

  • 实现权限状态、请求权限、发送通知。
  • 实现通知点击回跳并传递 target。
  • Windows 保留打开系统通知设置入口。
  • macOS 若 Electron Notification 无法满足权限/点击语义,保留 native bridge 或补原生模块。
  • 运行 bun run check:electron
  • 运行 bun run check:desktop
  • 打包 app smoke发送通知、点击通知、跳回目标 session。

执行记录2026-05-31

  • 新增 desktop/electron/services/notifications.ts,使用 Electron main-process Notification 发送系统通知并监听 click
  • 通知点击通过 desktop:notification:action 发送 { id, extra, target, action: 'click' }renderer 继续复用现有 installDesktopNotificationClickListener target 解析和导航。
  • Electron commandInvoke 兼容现有 renderer 的 legacy 通知入口:macos_notification_permission_statemacos_request_notification_permissionmacos_send_notificationplugin:notification|is_permission_grantedplugin:notification|request_permission
  • Windows 通知设置入口通过 allowlisted ms-settings:notifications 打开macOS 系统通知设置 URL 仍限制在 allowlist。
  • 参考 Electron 官方 Notification 文档main process 使用 new Notification(...).show(),点击通过 notification.on('click', ...) 处理Electron 没有独立的桌面通知 request-permission API因此 Electron host 将 Notification.isSupported() 映射为 host permission state。
  • bun run check:electron 通过32 tests passedElectron main/preload 构建成功。
  • bun run check:desktop 通过141 test files / 1103 tests passedproduction build 成功;现有 React act(...) warnings 仍为非阻断输出。

执行记录2026-06-01

  • 新增 desktop/electron/services/notificationSmoke.ts,提供显式环境变量触发的 Electron 通知 smoke hook设置 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_SESSION_ID=<session-id>main process 会延迟发送带 { type: 'session', sessionId } target 的真实 Electron Notification。默认不启用,不影响正常启动和生产行为。
  • 新增 desktop/electron/services/notificationSmoke.test.ts锁定默认不发送、target payload 透传和 delay clampcd desktop && bun test electron/services/notificationSmoke.test.ts electron/services/notifications.test.ts electron/services/windows.test.ts 通过16 tests passed。
  • Computer Use 真实 OS 点击复测:带 smoke env 启动 Electron dev 壳并关闭主窗口后Electron 仍 running但本机 Computer Use 对 /System/Library/CoreServices/NotificationCenter.appSystemUIServer 均返回 timeoutReached,无法稳定枚举或点击 macOS 通知横幅。重新 get_app_state(Electron) 会触发 app activation 并恢复窗口,因此不能作为“点击通知回跳”的证据。该项保持未完成,需在可操作 Notification Center/横幅的 macOS 会话或签名 packaged app 上复验。
  • 2026-06-01 通知点击二次复测:用 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_DELAY_MS=30000 启动 Electron dev 壳Computer Use 确认主窗口可见后点击 macOS close buttonElectron 无主窗口且进程保持 running等待通知触发后Computer Use 对 SystemUIServer 与 Notification Center 仍 timeoutReached,前台 Chrome 截图/AX 树也没有出现可点击通知横幅。真实 OS 通知点击回跳仍未验收通过。
  • 2026-06-01 通知 smoke 可审计化:新增 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_LOG=<path>,仅在显式 smoke env 下把 scheduled/sent/action/send_failed 写入 JSONL方便真实 macOS/Windows runner 点击通知后确认 Electron main 是否收到 OS click。cd desktop && bun test electron/services/notificationSmoke.test.ts electron/services/notifications.test.ts electron/services/windows.test.ts 通过 17 testscd desktop && bun run check:electron 通过 85 tests。
  • 2026-06-01 packaged 通知 smoke 复测:重新运行 CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 后,用隔离配置启动 packaged app并设置 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_LOG=/tmp/.../notification-smoke.jsonl。Computer Use 确认 packaged file://.../app.asar/dist/index.html 主窗口可见JSONL 记录 scheduledsent:true,证明 Electron main 已向系统发出真实通知。但 Computer Use 仍无法读取 SystemUIServerNotificationCenter.app(均 timeoutReached),日志也未出现 action,所以“点击通知回到目标 session”仍未完成。
  • 2026-06-01 通知点击当前态再复测:用 canonical packaged app 启动隔离通知 smoke设置 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_SESSION_ID=notification-click-smoke-sessionCC_HAHA_ELECTRON_NOTIFICATION_SMOKE_DELAY_MS=8000 和 JSONL logComputer Use 关闭主窗口后 app 留在后台JSONL 记录 scheduledsent:true。Computer Use 对 /System/Library/CoreServices/UserNotificationCenter.app/ 返回安全策略拒绝,对 NotificationCenter.apptimeoutReached,所以仍无法执行真实 OS 通知点击。为后续实机复验,通知 smoke 日志已扩展记录 lifecycle: close|failed,可区分通知发出后被系统关闭、发送失败和用户点击;cd desktop && bun test electron/services/notificationSmoke.test.ts electron/services/notifications.test.ts 通过 10 testscd desktop && bun run check:electron 通过 90 tests。

通过标准: 通知提示、权限、点击回跳都能在真实打包 app 里证明。 阻断条件: 只在 unit test 中 mock 成功,未走系统通知。

9. 迁移自动更新

创建/修改:

  • desktop/electron/services/updater.ts
  • desktop/src/stores/updateStore.ts
  • desktop/package.json
  • .github/workflows/release-desktop.yml

步骤:

  • 选定 electron-builder + electron-updater,不要用 Electron 内置 updater 覆盖 Linux。
  • 保留 check -> download -> progress -> install -> relaunch 状态机。
  • install 前调用 sidecar manager 停止 server/adapters失败后恢复可重试状态。
  • 建立 mock update feed 测试。
  • macOS 签名/zip metadata、Windows NSIS metadata、Linux AppImage/deb update 策略写入 Electron build metadata。
  • 运行 bun run check:electron
  • 运行 bun run check:desktop
  • release workflow 切换到 Electron builder并在打包前验证签名/notarization/update metadata secrets 是否配置齐全。

通过标准: 自动更新全链路有 unit、mock feed、打包产物证据。 阻断条件: 只能检查到新版本,不能安全安装并 relaunch。

执行记录2026-05-31

  • 新增运行时依赖 electron-updater@6.8.3 和打包依赖 electron-builder@26.8.1
  • 新增 desktop/electron/services/updater.tsautoDownload=falsecheckForUpdates() 只返回 metadatadownloadUpdate() 将 electron-updater download-progress 转成现有 DesktopUpdateDownloadEventrelaunch() 阶段调用 quitAndInstall(false, true)
  • 扩展 Electron IPCdesktop:update:downloaddesktop:update:installdesktop:update:download-event
  • desktop/src/lib/desktopHost/electronHost.ts 将 Electron update metadata 包装成现有 DesktopUpdate 对象renderer updateStore 不需要改状态机。
  • desktop/package.json 已加入 Electron builder metadataappId=com.claude-code-haha.desktop、GitHub publish 到 NanmiCoder/cc-haha、macOS dmg/zip、Windows nsis、Linux AppImage/debpackaged runtime 使用 asar=true,并通过 asarUnpack 保留 node-pty 与 sidecar binaries。
  • bun run check:electron 通过36 tests passedElectron main/preload 构建成功。
  • bun run check:desktop 通过desktop tsc --noEmit、Vitest 全量 146 test files / 1127 tests passedproduction build 成功。

执行记录2026-06-01

  • 完整 macOS Electron Builder release packaging 暴露出两个自动更新发布问题:平台级 publish: ["github"] 会在 worktree/CI 中触发 git remote 自动探测并导致 update info 生成崩溃;默认含空格 artifact name 会让 latest-mac.yml 指向 Claude-Code-Haha-...,但实际上传文件是 Claude Code Haha-...
  • 已修复 desktop/package.json:保留顶层显式 GitHub publish 元数据,并移除 mac/win/linux 平台级 publish 覆盖;新增统一无空格 artifactName,避免 update metadata 与上传 asset 文件名漂移。
  • 已增强 scripts/quality-gate/package-smoke/index.ts:当存在 latest-mac.yml / latest.yml / latest-linux.yml 时,会解析 url / path 并确认引用的 artifact 文件实际存在;当发布 archive 存在但 update metadata 缺失时会失败。
  • 已新增回归测试:scripts/pr/release-workflow.test.ts 防止 Electron Builder 再依赖 git remote autodetectionscripts/quality-gate/package-smoke/index.test.ts 会捕获 latest-mac.yml 指向缺失 asset 的情况。
  • 2026-06-01 packaged app updater 缺失 channel metadata 修复:本地打包 app 启动后 electron-updater 可访问 GitHub release但当前 release 缺少 latest-mac.yml 时会抛 ERR_UPDATER_CHANNEL_FILE_NOT_FOUND。该错误现在仅在 message 指向 latest*.yml 时降级为“无更新”,继续保留其它 updater 错误抛出;cd desktop && bun test electron/services/updater.test.ts 通过 7 testscd desktop && bun run check:electron 通过 84 tests。
  • 2026-06-01 packaged app 当前态复验:重新运行 cd desktop && CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 后,从 desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app 以隔离 CLAUDE_CONFIG_DIR / --user-data-dir 启动Computer Use 确认主窗口加载 file://.../app.asar/dist/index.htmlsidecar 自动启动,未出现 Safe Storage 钥匙串弹窗,日志未再出现 ERR_UPDATER_CHANNEL_FILE_NOT_FOUND IPC handler 崩错。纯 --dir 包缺少 app-update.yml 的提示仍按无更新处理,不代表 signed release update feed 已验收。
  • 当前本机 .dmg 构建仍受 macOS DiskImages/hdiutil 状态阻塞:hdiutil: create failed - 操作超时,且当前 worktree 下残留的临时读写 disk image 无法正常 detach这属于本机磁盘镜像服务阻塞不应被 package-smoke 或浏览器 smoke 替代为发布通过。

10. 迁移 PTY 终端

创建/修改:

  • desktop/electron/services/terminal.ts
  • desktop/src/api/terminal.ts

步骤:

  • 明确 PTY 实现:保留 Rust sidecar command 或引入 Node PTY 方案。
  • 实现 spawn/write/resize/kill 和 output/exit event。
  • 保留 terminal shell 配置读写语义。
  • 运行 bun run check:desktop && bun run check:electron
  • 打包 app smoke打开终端、输入命令resize/kill 需随完整 packaged smoke 继续复验。

通过标准: 终端交互和事件流等价。 阻断条件: Windows shell 或自定义 shell path 行为不明确。

执行记录2026-05-31

  • 已选择 Node PTY 方案:新增运行时依赖 node-pty@1.1.0Electron main process 负责 pty.spawn()renderer 只通过 preload IPC 发送 spawn/write/resize/kill。Context7 /microsoft/node-pty 文档确认主进程集成方式为 spawn + onData/onExit + write/resize/kill
  • 新增 desktop/electron/services/terminal.ts,保留 Tauri 终端语义:CLAUDE_CONFIG_DIR/terminal-config.json 优先,其次 Electron userData/terminal-config.json;继续读取 ~/.claude/settings.jsondesktopTerminal Windows shell 配置;保留 Windows bash_pathCOMSPEC、POSIX SHELL/bin/zsh//bin/bash 默认 shell 选择。
  • terminal_spawn 等价实现最小尺寸限制:cols >= 20rows >= 8;默认 cwd 仍按 cwd -> CLAUDE_CONFIG_DIR -> HOME/USERPROFILE -> process.cwd();环境变量合并 login shell env并强制 UTF-8 locale补充 TERM=xterm-256colorCOLORTERM=truecolor
  • desktop/electron/main.ts 已注册 desktop:terminal:* IPCspawn/write/resize/kill/get-bash-path/set-bash-path,并通过 desktop:terminal:outputdesktop:terminal:exit 事件回传现有 renderer payload。
  • desktop/electron/ipc/capabilities.ts 收紧 terminalSpawn payload 校验,避免 renderer 传入非数字尺寸或非字符串 cwd/shell。
  • 新增 desktop/electron/services/terminal.test.ts覆盖配置路径、bash path 持久化、Windows shell 解析、UTF-8 env、spawn event 转发、write/resize/kill、exit 后 session 清理。
  • packaged macOS node-pty 直接从 .app/Contents/Resources/app.asar.unpacked 执行会在 spawn-helper 上触发 posix_spawnp failed;已改为打包时先执行 prepare:node-pty 修正 helper executable bit并在 packaged macOS runtime 将 node-pty 复制到 Electron userData/native/node-pty-<platform>-<arch>-<version> 后加载,避免嵌套 app bundle 资源执行限制。
  • bun run check:electron 通过58 tests passedElectron main/preload/preview-preload 构建成功。
  • bun run check:desktop 通过desktop tsc --noEmit、Vitest 全量 146 test files / 1127 tests passedproduction build 成功。
  • CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 通过并产出 unpacked macOS .appComputer Use packaged smoke 通过:启动 Claude Code Haha.app,点击“打开终端”后显示 运行中 /bin/zsh,在终端输入 printf electron-terminal-ok 并看到输出 electron-terminal-ok。终端 resize/close kill 仍需在完整 packaged smoke 中补齐。

11. 迁移预览面板

创建/修改:

  • desktop/electron/services/preview.ts
  • desktop/src/lib/previewBridge.ts
  • desktop/src/lib/previewEvents.ts

步骤:

  • 使用 WebContentsView,不要用已弃用的 BrowserView
  • 保留 http/https URL 白名单。
  • 保留 preview agent 注入和 JSON 消息校验。
  • 实现 open/navigate/setBounds/setVisible/close/eval/message。
  • 运行 bun run check:desktop && bun run check:electron
  • 打包 app smoke打开 workbench/browser panel导航 URL完成 preview/screenshot 回填 smoke。

通过标准: 预览面板功能等价且安全边界保留。 阻断条件: 允许 file:javascript: URL 进入 preview。

执行记录2026-05-31

  • Context7 /electron/electron 文档确认当前 API 使用 WebContentsView + contentView.addChildView() + setBounds() + webContents.loadURL(),不再新增 BrowserView
  • 新增 desktop/electron/services/preview.ts,使用 WebContentsView 实现 open/navigate/setBounds/setVisible/close/eval,继续拒绝空 URL、file:javascript: 等非 http/https URL。
  • 新增 desktop/electron/preview-preload.ts,只暴露 window.__DESKTOP_PREVIEW_POST__(raw);外部页面无法拿到 Node/Electron API只能把 JSON 字符串发回 main process。
  • desktop/src/preview-agent/index.ts 改为调用通用 __DESKTOP_PREVIEW_POST__Tauri 端在 desktop/src-tauri/src/webview_panel.rs 创建 child webview 时先注入该函数,再注入 preview agentElectron 端由 preview preload 注入同名函数。
  • Electron main process 增加内部 channel desktop:preview:message-from-view,只接受当前 preview webContents 发出的 raw JSON校验 JSON 后通过 desktop:preview:event 转发给 React。
  • desktop/package.jsonbuild:electron 增加 preview-preload.cjs 构建Electron builder files 增加 src-tauri/resources/preview-agent.js,保证 packaged app 能读取注入脚本。
  • 新增 desktop/electron/services/preview.test.ts,覆盖 URL 白名单、bounds 规范化、child view 复用、load 后注入 preview agent、JSON event 转发、close 清理。
  • bun run build:preview-agent 已重建 desktop/src-tauri/resources/preview-agent.js
  • bun run check:electron 通过49 tests passedElectron main/preload/preview-preload 构建成功。
  • bun run check:desktop 通过desktop tsc --noEmit、Vitest 全量 146 test files / 1127 tests passedproduction build 成功。
  • 2026-06-01 Computer Use packaged smoke 覆盖 preview/workbench临时 packaged app 中打开 browser panel导航 https://example.com/ 成功,并完成 screenshot 回填 composer完整 preview event 回归继续由 desktop/electron/services/preview.test.ts 覆盖。

12. 迁移 app mode、portable config 和 zoom

创建/修改:

  • desktop/electron/services/appMode.ts
  • desktop/electron/services/zoom.ts
  • desktop/src/stores/settingsStore.ts
  • desktop/src/lib/appZoom.ts

步骤:

  • 保留 get_app_modeset_app_modedetect_portable_dir 返回形状。
  • 保留同时写系统默认配置目录和 portable 目录的语义。
  • zoom 继续 clamp 到 0.5..2.0
  • 运行 bun run check:persistence-upgrade
  • 运行 bun run check:desktop && bun run check:electron

通过标准: 老配置可升级unknown fields 保留。 阻断条件: 写入 ~/.claude/settings.json 或受保护配置路径。

执行记录2026-05-31

  • 新增 desktop/electron/services/appMode.ts,复刻 Tauri app-mode.json 语义:启动时按 external CLAUDE_CONFIG_DIR -> default portable app-mode.json -> system app-mode.json -> default portable data auto-detect 判定是否设置 CLAUDE_CONFIG_DIR
  • Electron app.whenReady() 后、server sidecar 启动前调用 applyStartupPortableMode(app),保证 sidecar、window state、terminal config 都能看到 portable env同时设置 CC_HAHA_APP_PORTABLE_DIR=1WEBVIEW2_USER_DATA_FOLDER=<portable>/EBWebView
  • desktop:app-mode:get 返回现有 renderer 形状:modeportableDirdefaultPortableDiractiveConfigDirconfigDirSourceconfigDirSource 区分 system、外部环境变量 environment、应用切换产生的 portable
  • desktop:app-mode:set 在当前 active config、目标 portable dir、系统默认 userData 三处写入 app-mode.json,不写 ~/.claude/settings.json,也不移动/删除已有数据。
  • desktop:app-mode:restart 现在 relaunch() 后立即 quit(),与设置页“切换后重启”的语义一致;prepareRestart 继续先停止 server/adapters。
  • 新增 desktop/electron/services/zoom.tsnative zoom 统一 clamp 到 0.5..2.0,避免 Electron IPC 绕过 renderer 侧限制。
  • 新增 appMode.test.tszoom.test.ts,覆盖 portable 数据探测、启动模式解析、env 设置、返回形状、三处 app-mode 写入、zoom clamp。
  • bun run check:electron 通过56 tests passedElectron main/preload/preview-preload 构建成功。
  • bun run check:persistence-upgrade 通过server persistence 6 tests passeddesktop localStorage migration 8 tests passed。
  • bun run check:desktop 通过desktop tsc --noEmit、Vitest 全量 146 test files / 1127 tests passedproduction build 成功。

Phase 4Build、CI、Release

13. 替换本地构建脚本

修改:

  • desktop/package.json
  • package.json
  • desktop/scripts/build-macos-arm64.sh
  • desktop/scripts/build-windows-x64.ps1

步骤:

  • 保留 desktop build 作为 renderer build。
  • 保留 build:sidecars
  • 新增 Electron package 脚本,输出到 desktop/build-artifacts/<platform>
  • check:native 替换为 Electron main/preload/package config check或新增 check:electron 并让 quality:gate 使用它。
  • 运行 bun run check:desktop && bun run check:electron

通过标准: 本地 macOS/Windows 构建命令产出可启动 app。 阻断条件: sidecar 没有被打进产物或产物启动后仍读开发路径。

执行记录2026-05-31

  • desktop/package.json 保留 build 为 renderer build新增/调整 electron:buildelectron:packageelectron:package:dir,并将 build:electron 扩展为 main/preload/preview-preload 三个 bundle。
  • Electron builder 配置包含 dist/**electron-dist/**src-tauri/binaries/**src-tauri/resources/preview-agent.jsnode_modules/node-pty/**electron-updater 已打进 main bundlenode-pty 保持外部原生模块并通过 asarUnpack 解包。
  • check:native 已从 Tauri cargo check 改为 build:sidecars + check:electronquality:gate native lane 描述同步为 Electron host/package checks。
  • desktop/scripts/build-macos-arm64.sh 改为 Electron Builder构建 sidecar、renderer、Electron bundles执行 electron-builder --mac dmg zip --arm64 --publish never,输出复制到 desktop/build-artifacts/macos-arm64
  • desktop/scripts/build-windows-x64.ps1 改为 Electron Builder导入 MSVC 环境,构建 sidecar、renderer、Electron bundles执行 electron-builder --win nsis --x64 --publish never,输出复制到 desktop/build-artifacts/windows-x64
  • desktop/scripts/build-macos-arm64.sh 支持 MAC_TARGETS 覆盖 Electron Builder macOS target默认仍为 dmg zip;本机 DiskImages 异常时可用 MAC_TARGETS=zip 单独验证 zip/update metadata 链路。脚本会对当前 worktree 的 stale Electron Builder 临时 DMG 挂载 fail fast成功复制 canonical artifacts 后默认运行 package-smoke。
  • desktop/scripts/build-windows-x64.ps1 会把 installer、update metadata、blockmap 和 win-unpacked 复制到 canonical desktop/build-artifacts/windows-x64;成功复制后默认运行 bun run test:package-smoke --platform windows --package-kind release --artifacts-dir desktop/build-artifacts/windows-x64,可用 SKIP_PACKAGE_SMOKE=1 跳过静态验包。
  • desktop/scripts/build-linux.sh 支持 LINUX_ARCH=x64|arm64LINUX_TARGETS 覆盖,默认构建 AppImage/deb x64脚本会复制 .AppImage.deblatest-linux.yml、blockmap 和 linux-unpacked 到 canonical desktop/build-artifacts/linux-<arch>,并默认运行 bun run test:package-smoke --platform linux --package-kind release --artifacts-dir desktop/build-artifacts/linux-<arch>
  • node-pty 原生模块验证:bunx electron ./tmp-electron-node-pty-smoke.cjs 在 Electron 42.3.0 runtime 下成功输出 node-pty spawn type: function
  • electron-builder install-app-deps 在本机 @electron/rebuild node-gyp worker 上空转无输出;由于 node-pty Electron runtime smoke 已通过,默认配置设为 npmRebuild=false,脚本保留 REBUILD_NATIVE=1 作为 runner/调试开关。
  • packaged .app 首轮 smoke 暴露两个打包差异Vite 默认绝对 /assets/...file:// 下加载失败,已通过 vite.config.ts base: './'publicAssetPath() 和字体 URL 改造修复;asar=false 的全量 unpacked resources 会触发 macOS assessment Too many open files,已切换为 asar=true + asarUnpack
  • CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 通过并产出 desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.appcodesign --verify --deep --strict --verbose=2 通过packaged app 启动后 sidecar 从 app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin 运行renderer 从 app.asar/dist/index.html 加载。
  • bun run check:native 通过sidecar build 成功Electron host 56 tests passedmain/preload/preview-preload 构建成功。
  • 2026-06-01 已把 check:native 扩展为 build:sidecars + check:electron + electron:package:dir + test:package-smoke:current最新复跑通过Electron 77 tests passedElectron --dir 产出 desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app,当前平台 macOS package-smoke PASS。
  • bun run check:desktop 通过desktop tsc --noEmit、Vitest 全量 146 test files / 1127 tests passedproduction build 成功。
  • 生产 React 直接 Tauri 扫描无输出:rg -n "@tauri-apps|__TAURI_INTERNALS__|window\\.__TAURI__|from '@tauri" desktop/src --glob '!**/*.test.ts' --glob '!**/*.test.tsx' --glob '!**/lib/desktopHost/**' --glob '!src-tauri/**'

14. 替换 CI workflow

修改:

  • .github/workflows/build-desktop-dev.yml
  • .github/workflows/release-desktop.yml
  • .github/workflows/pr-quality.yml
  • scripts/quality-gate/modes.ts
  • scripts/pr/change-policy.ts
  • scripts/pr/check-pr.ts

步骤:

  • 移除 tauri-apps/tauri-action@v0
  • 移除 Linux WebKitGTK/Rust/Tauri 专用依赖,保留 Electron/Linux 打包需要的依赖。
  • 配置 macOS arm64/x64、Windows x64、Linux x64/arm64 matrix。
  • 配置 signing/notarization/update metadata secrets。
  • 让 PR/change policy 识别 desktop/electron/** 和 Electron build config。
  • 运行 bun run check:policy

通过标准: CI lane 名称、触发条件和产物路径都更新为 Electron。 阻断条件: PR 改 Electron host 文件但不会触发桌面/原生检查。

执行记录2026-05-31

  • .github/workflows/build-desktop-dev.yml 改为 Electron Builder matrixmacOS arm64/x64、Windows x64、Linux x64/arm64每个 job 安装 Bun/Node、构建 sidecars、构建 renderer/Electron bundles、运行 electron-builder 并上传 desktop/build-artifacts/electron 产物。
  • .github/workflows/release-desktop.yml 移除 tauri-apps/tauri-action@v0,改为 electron-builder --publish never 生成产物,再用 softprops/action-gh-release@v2 上传 release assets。
  • Linux CI 依赖删除 WebKitGTK/Rust/Tauri 专用包,仅保留 Electron packaging 与 AppImage 需要的基础构建工具和 libfuse2
  • PR desktop-native-checks 不再安装 Rust toolchain 或 Rust cachedesktop/scripts/build-sidecars.ts 改为用 process.platform / process.arch 推导 host triple避免 Electron check:native 继续依赖 rustc -vV
  • sidecar build target env 已改为 SIDECAR_TARGET_TRIPLEbuild-sidecars.ts 暂时兼容旧 TAURI_ENV_TARGET_TRIPLE,但 CI 和平台脚本不再使用 Tauri 命名。
  • scripts/pr/change-policy.tsdesktop/electron/tsconfig.json、Electron macOS/Windows build scripts 纳入 native/release pathsscripts/quality-gate/modes.tsscripts/pr/impact-report.ts 文案从 Tauri native 改为 Electron host/package。
  • bun run check:policy 通过64 tests passedquarantine review 0 expired。

15. 替换 release 脚本

修改:

  • scripts/release.ts
  • scripts/pr/release-workflow.test.ts

步骤:

  • 版本来源从 desktop/src-tauri/tauri.conf.json 改为 Electron package/build config。
  • 删除 Cargo.toml/Cargo.lock release 更新。
  • 保留 release note 校验、commit、tag 流程。
  • 运行 bun run scripts/release.ts <next-version> --dry
  • 运行 bun test scripts/pr/release-workflow.test.ts

通过标准: dry run 显示的版本文件全部属于 Electron 发布链。 阻断条件: release 仍依赖 Tauri 版本文件。

执行记录2026-05-31

  • scripts/release.ts 当前版本来源改为 desktop/package.json,版本更新文件只保留 desktop/package.json
  • release 脚本不再更新 desktop/src-tauri/tauri.conf.jsonCargo.tomlCargo.lock,也不再运行 cargo generate-lockfile
  • Git commit/tag 流程仍保留 release notes 校验、git add desktop/package.json release-notes/vX.Y.Z.md、commit、annotated tag。
  • bun run scripts/release.ts patch --dry 通过,显示 0.3.1 -> 0.3.2,只会更新 desktop/package.jsonrelease-notes/v0.3.2.md
  • bun test scripts/pr/release-workflow.test.ts scripts/pr/change-policy.test.ts 通过11 tests passed。

Phase 5最终验收

16. Electron packaged smoke

步骤:

  • macOS构建 .app/.dmg,启动 app验证聊天、附件、通知、托盘、更新 mock、终端、预览面板。
  • Windows构建 NSIS 安装器,安装后启动 app验证 sidecar 文件锁和更新前停进程。
  • Linux构建 deb/AppImage验证启动、tray、通知、更新策略。
  • 运行 bun run test:package-smoke --platform macos 作为包结构预检Windows/Linux 待对应平台产物。

通过标准: 每个平台至少一个真实发布格式完成 smoke。 阻断条件: 只有 dev build 或 Vite 页面通过。

执行记录2026-05-31

  • macOS unpacked .app smoke 已完成一部分:CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 成功Computer Use 启动 packaged app 后主界面正常渲染server sidecar 正常启动,终端面板可打开并执行 printf electron-terminal-ok
  • Review 后重新运行 cd desktop && CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 成功;bun run test:package-smoke --platform macos 通过,确认当前 .app 结构、app.asar、unpacked sidecar、unpacked node-pty native module 和 spawn-helper 存在。
  • 因本机已有正式 /Applications/Claude Code Haha.app 实例持有单实例锁Computer Use 最终 smoke 使用同一构建输出重新打包临时 Claude Code Haha Smoke.app(仅临时 appId/productName 不同执行packaged renderer 从 app.asar/dist/index.html 加载sidecar 自动启动,终端执行 electron-terminal-ok系统目录选择器可打开preview WebContentsView 可导航 https://example.com/,截图可回填 composer。
  • 注意:test:package-smoke 是 bundle-structure/static-artifact preflight不启动 GUI也不替代 Computer Use 或实机 smoke。
  • 2026-06-01 同步本地 main 后重新运行 cd desktop && CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 成功,产出 desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app
  • 2026-06-01 使用 Computer Use 操作真实 packaged app并把本机 cc-haha provider/model 配置复制到隔离临时 CLAUDE_CONFIG_DIR:模型选择器显示 gpt-5.5 Sub2API-ChatGPT,系统目录选择器选中 /private/tmp/cc-haha-electron-real-fixed-sRFUeO/project
  • 真实模型 smoke 会话 e0d7abc2-27db-4ff1-9954-7923f6c3385e 通过packaged renderer 从 file://.../app.asar/dist/index.html 加载server sidecar 建立 WebSocketCLI subprocess 使用 --model gpt-5.5 启动,模型读取 package.jsonsrc/greeting.ts 后经权限审批执行 bun test,结果为 1 pass / 0 fail,最终回复“测试通过。”。
  • 2026-06-01 重跑 SKIP_INSTALL=1 SIGN_BUILD=0 desktop/scripts/build-macos-arm64.sh:修复平台级 publish 后曾产出 .app/.zip/.dmg/latest-mac.yml,但 latest-mac.yml 与实际含空格 asset 文件名不一致;加入稳定 artifactName 后,清理旧 disk image 挂载前本机 DiskImages/hdiutil 在 .dmg 创建阶段超时zip-only Electron Builder 也曾在本机 7za 子进程处失败。
  • 2026-06-01 清理旧 Tauri/Electron disk image 挂载后,MAC_TARGETS=zip SKIP_INSTALL=1 SIGN_BUILD=0 desktop/scripts/build-macos-arm64.sh 通过canonical desktop/build-artifacts/macos-arm64 产出 .appClaude-Code-Haha-0.3.2-arm64.zip.zip.blockmaplatest-mac.yml;脚本自动运行 bun run test:package-smoke --platform macos --artifacts-dir desktop/build-artifacts/macos-arm64 并 PASS确认 latest-mac.yml 引用真实 zip 且 packaged resources 含 app-update.yml
  • 2026-06-01 .dmg target 最新复验通过:后续默认 SKIP_INSTALL=1 SIGN_BUILD=0 desktop/scripts/build-macos-arm64.sh 已完成 dmg zip targetcanonical desktop/build-artifacts/macos-arm64 包含 .app.dmg.zip、blockmap 和 latest-mac.yml,脚本自动 release package-smoke PASS。Gatekeeper/signed/notarized launch 仍保持 release blocker。
  • 2026-06-01 macOS build script hardening默认 dmg zip target 在当前 worktree 检测到 stale Electron Builder .temp...dmg 挂载时会提前失败并给出 MAC_TARGETS=zip 替代路径,避免继续进入会卡死的 DiskImages detach/create 分支。
  • 2026-06-01 Windows build script hardeningcanonical 输出现在保留 win-unpacked 供 package-smoke 检查 app.asarapp-update.yml、unpacked sidecar 和 node-pty 原生模块;脚本默认在 Windows runner 上执行 canonical package-smoke但本机 macOS 未执行 NSIS 真实构建/安装。
  • 2026-06-01 Linux build script hardening新增 desktop/scripts/build-linux.shbuild:linux-x64 / build:linux-arm64 scriptscanonical 输出现在保留 AppImage/deb、latest-linux.ymllinux-unpacked 供 package-smoke 检查;本机 macOS 不能执行 Linux 真实构建/安装。
  • 2026-06-01 Linux package-smoke 区分开发目录包和发布包:electron-builder --dir 只产出 linux-unpacked 时仍检查 app.asar、sidecar 和 node-pty 并通过 check:nativeAppImage/deb 发布产物存在时继续要求 latest-linux.ymlapp-update.yml
  • 2026-06-01 Linux arm64 updater metadata 修复:build-linux.sh 现在复制 latest-linux*.yml,避免 arm64 Electron Builder 产出 latest-linux-arm64.yml 时 canonical linux-arm64 目录丢失 update metadatapackage-smoke 同步识别 latest-linux.ymllatest-linux-arm64.yml,并检查 metadata 引用的 AppImage/deb artifact 是否真实存在。
  • 2026-06-01 package-smoke 增加显式 --package-kind dir|releasecheck:native 使用 dir + desktop/build-artifacts/electron,发布/dev packaging workflow 和平台 release 脚本使用 release + 精确 artifacts-dir,避免旧 installer/metadata 污染本轮验包。
  • 2026-06-01 Gatekeeper 诊断加固:--require-macos-gatekeeperspctl 失败时package-smoke 会同时记录 codesign --verify --deep --strict --verbose=2codesign -dv --verbose=4xcrun stapler validate 摘要方便区分未签名、签名链、bundle 格式和 notarization ticket 问题。
  • 2026-06-01 SubAgent Review 修复release workflow 在打包矩阵前新增 bun run verify 非 live 预检;quality:gate --mode release 会包含 PR checks并把 desktop-package-smoke:<platform> 改为 --package-kind release --artifacts-dir desktop/build-artifacts/<platform-arch>,避免验到旧的 --dir 包。
  • 2026-06-01 多架构 metadata 风险收敛release workflow 上传前会把 latest*.yml 改名为带 matrix label 后缀的唯一 asset避免 GitHub Release 中同名 metadata 互相覆盖或上传失败matrix 全部通过后由 scripts/release-update-metadata.ts 重新发布标准 updater channel metadata。macOS 会把 x64/arm64 zip entries 合并回 latest-mac.ymlLinux 会恢复 latest-linux.yml / latest-linux-arm64.ymlWindows 会恢复 latest.yml。完整自动更新 release 仍需在 signed/notarized artifact 上复验。
  • 2026-06-01 signing/notarization secrets preflightrelease workflow 新增非 matrix signing-preflight job在任何平台 artifact 上传前检查 MACOS_CERTIFICATEMACOS_CERTIFICATE_PASSWORDAPPLE_IDAPPLE_APP_SPECIFIC_PASSWORDAPPLE_TEAM_IDWINDOWS_CERTIFICATEWINDOWS_CERTIFICATE_PASSWORD。缺失时用 GitHub Actions error 早停,避免部分 matrix leg 先发布 partial release。真实 Developer ID signing/notarization 仍需在 CI secrets 存在时复验。
  • 2026-06-01 native/package 当前态复验:bun run check:native 通过,完成 sidecar 构建和 ad-hoc signing、check:electron 83 tests、desktop production build、electron-builder --dir,并运行 bun run test:package-smoke --platform macos --package-kind dir --artifacts-dir desktop/build-artifacts/electron PASS。该证据只覆盖 directory-only unpacked app bundle 结构、app.asar、sidecar、node-ptyspawn-helper,不替代 .dmg、Gatekeeper 或 signed release smoke。
  • 2026-06-01 DMG 当前态复验:默认 SKIP_INSTALL=1 SIGN_BUILD=0 desktop/scripts/build-macos-arm64.sh 已通过canonical desktop/build-artifacts/macos-arm64 恢复 .app.dmg.dmg.blockmap.zip.zip.blockmaplatest-mac.yml,并由 release package-smoke PASS。
  • 2026-06-01 跨平台 package-smoke 当前态复验:bun test scripts/quality-gate/package-smoke/index.test.ts scripts/pr/release-workflow.test.ts scripts/release-update-metadata.test.ts 通过 26 tests覆盖 Windows canonical output、Linux x64/arm64 update metadata、release workflow Gatekeeper/signing preflight 和 metadata republishbun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/macos-arm64 PASS确认当前 macOS zip/update canonical artifact set 仍完整。
  • 本轮尚未完成 .dmg 安装包、通知点击、signed/notarized Gatekeeper launch、Windows NSIS 和 Linux AppImage/deb 的真实发布格式 smoke因此本阶段保持未完成。附件选择、目录选择、外链、终端和 preview 已有 dev 或 unpacked packaged Computer Use 证据,但还不能替代最终 signed release artifact 的全量验收。

17. Release gate

步骤:

  • 运行 bun run verify
  • 有 live provider 时运行 bun run quality:gate --mode release --allow-live --provider-model <provider:model[:label]>
  • 检查 quality report 的 pass/fail/skip。
  • 记录 coverage report 路径。

通过标准: PR/release 门禁通过coverage 未退化。 阻断条件: 任一 release 必需 lane 失败。

执行记录2026-06-01

  • bun run verify 通过:最新为 artifacts/quality-runs/2026-05-31T21-42-57-279Z/report.mdpassed=9 failed=0 skipped=1
  • 首次重跑完整 release --allow-live 后,旧 agent-browser/Vite blocker 已消失;唯一失败为 coverage 中 h5-access-auth.test.ts 随机端口撞到本机 WeChat 的 127.0.0.1:24023
  • 已把 h5-access-auth.test.ts 端口选择改成 OS 分配 ephemeral portfocused bun test src/server/__tests__/h5-access-auth.test.ts 通过,bun run check:coverage 最新通过:artifacts/coverage/2026-05-31T21-46-15-873Z/coverage-report.mdpassed=5 failed=0
  • 历史 bun run quality:gate --mode release --allow-live --provider-model sub2api-chatgpt:main:sub2api-chatgpt-main 曾通过:artifacts/quality-runs/2026-05-31T20-29-45-758Z/report.mdpassed=15 failed=0 skipped=0。SubAgent Review 后 release gate 已收窄为必须检查 canonical release artifact当前仍需在 signed/notarized artifact 与可用 Computer Use 桌面会话上重新跑完整 release gate。

18. Computer Use 全面测试

步骤:

  • 用真实打包 app 运行 Computer Use 设置流程。
  • 验证 Python 路径选择、权限授权、屏幕录制/辅助功能授权提示、授权应用列表。
  • 验证点击、输入、滚动、系统快捷键、剪贴板。
  • 验证通知点击回到目标会话。
  • macOS 和 Windows 至少各完成一次Linux 如能力降级,写入 release note 和已知限制。

通过标准: Computer Use 在真实 Electron app 中完成端到端操作。 阻断条件: 只用浏览器/Vite 或 mock 路径证明。

验证清单: 迁移收口使用 docs/desktop/09-electron-migration-validation-checklist.md其中区分自动化证据、macOS Computer Use smoke、Windows 待实机和 Linux 待实机。

执行记录2026-06-01

  • 尝试继续 Computer Use GUI 验收时Computer Use 工具层对 list_appsget_app_state(Finder) 均返回 NSOSStatusErrorDomain Code=-600 procNotFound。该结果说明当前 macOS 自动化桥未拿到可用进程,不是 Electron app 单点问题剩余附件、Python 路径、外链、Dock/tray、通知点击和完整 update feed 验收保持未完成。
  • 2026-06-01 后续复测 Computer Use 仍返回同一 procNotFound,这是连续第二个 goal turn 复现;目标仍可在代码和发布门禁层继续推进,因此暂不标记 goal blocked。
  • 2026-06-01 再次复测后 Computer Use list_apps 已恢复,但 get_app_state(Electron) 对 Electron dev 壳超时,get_app_state(Finder)cgWindowNotFound;系统 screencapture 同时返回 could not create image from display。这说明当前桌面会话没有可用截图/窗口捕获能力Computer Use 交互验收仍不能推进。
  • 2026-06-01 continuation 复测Computer Use list_apps 仍可列应用,get_app_state(Finder) 仍为 cgWindowNotFoundscreencapture 仍失败为 could not create image from display当前仍不能完成附件、Python 路径、外链、Dock/tray、通知点击等 GUI 验收。
  • 2026-06-01 packaged app 启动被 macOS AppleSystemPolicy 拦截:spctl -a -t execute desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.appbundle format unrecognized, invalid, or unsuitable,系统日志显示 ad-hoc/unknown-chain app 被终止。本轮已新增 bun run test:package-smoke --platform macos --require-macos-gatekeeper 作为可选 release-readiness 检查当前该检查失败release-ready 需要 Developer ID signing/notarization 或真实签名产物复验。
  • 2026-06-01 release workflow 已补强macOS release artifact 上传前强制运行 bun run test:package-smoke --platform macos --require-macos-gatekeeper,防止未通过 macOS 启动策略的 .app/.dmg/.zip 被发布dev build 仍保留结构型 package-smoke。
  • 2026-06-01 修复 Electron dev launcher 代理问题:electron:dev 默认 renderer URL 改为 http://localhost:1420并对当前进程、Vite 与 Electron 子进程补齐 NO_PROXY/no_proxy=localhost,127.0.0.1,::1,避免本机代理把 localhost renderer 等待请求打成 502。
  • 2026-06-01 主窗口显示逻辑加固:创建 BrowserWindow 后立即 show/focusrenderer load 完成后再次 show/focus,避免隐藏窗口在 load 等待期间被托盘/单实例/Computer Use 路径视为无窗口。
  • 2026-06-01 SubAgent gap follow-uprenderer 生产代码不再用 isTauriRuntime() 判断通用桌面能力,新增 isDesktopRuntime(),更新弹窗、附件 native picker、设置 app mode、移动布局分支均识别 Electron/Tauri desktop host。
  • 2026-06-01 Electron updater proxy contract 已补齐renderer 传入的 { proxy } 会进入 Electron main检查更新前应用到 Electron app/session proxy切回系统代理时会清理手动代理避免更新检查继续沿用旧 proxy。
  • 2026-06-01 package-smoke 增加发布型包的 installed updater metadata 检查:当 artifact set 含 .zip/.dmg/latest-mac.yml、Windows installer/latest.yml 或 Linux package/latest-linux.yml 时,安装后 resources 目录必须包含 app-update.yml;纯 --dir 开发包会记录 note 但不失败。
  • 2026-06-01 真实 provider smoke 使用本机 ~/.claude/cc-haha/providers.json selector sub2api-chatgpt:main:sub2api-chatgpt-main 通过:artifacts/quality-runs/2026-05-31T21-28-07-154Z/report.mdpassed=1 failed=0 skipped=0。该路径复用 cc-haha provider 配置,不走 QUALITY_GATE_PROVIDER_* 明文 env-only 分支。
  • 2026-06-01 check:native 已补上打包后验包:根脚本现在会在 Electron --dir 打包后运行 test:package-smoke:current。最新 bun run check:native 通过package-smoke notes 明确当前纯 --dir macOS artifact set 不强制 app-update.yml,也不代表 GUI 启动或 Gatekeeper approval。
  • 2026-06-01 final-definition 复测Computer Use list_apps 正常,但 get_app_state(Finder) 仍返回 cgWindowNotFound;系统 screencapture -x /tmp/cc-haha-cua-retake-final.png 仍失败为 could not create image from display。当前机器继续不能承载真实 GUI 操作验收。
  • 2026-06-01 cross-platform-script 复测Computer Use list_apps 正常且可见 /Applications/Claude Code Haha.appget_app_state(Finder) 仍为 cgWindowNotFoundget_app_state(Claude Code Haha) 返回 remoteConnection,系统 screencapture -x /tmp/cc-haha-computer-use-check.png 仍失败为 could not create image from display。当前仍不能完成点击、输入、附件选择、托盘恢复、通知点击等 GUI 验收。
  • 2026-06-01 package-kind 复测Computer Use list_apps 正常且显示 /Applications/Claude Code Haha.app 正在运行,get_app_state(Claude Code Haha)get_app_state(Finder) 均返回 cgWindowNotFound,系统 screencapture -x /tmp/cc-haha-computer-use-check-2.png 仍失败为 could not create image from display。当前机器仍不能承载真实 GUI 操作验收。
  • 2026-06-01 Gatekeeper 诊断加固release-readiness package-smoke 在 spctl 失败时会补充 codesign verify/details 摘要,后续 signed/notarized artifact 的失败可以直接从 quality log 判断是签名链、bundle 格式还是 notarization policy 问题。
  • 2026-06-01 Gatekeeper 诊断复验:当前 ad-hoc .app 仍被 spctl 拒绝为 bundle format unrecognized, invalid, or unsuitable,但 package-smoke 已输出 codesign verification exited with status 0 与 bundle identifier/signature detail 摘要证明诊断链路可用release blocker 仍是 Developer ID signing/notarization 或真实签名产物复验。
  • 2026-06-01 Gatekeeper 诊断后 Computer Use 复测:list_apps 可见运行中的 /Applications/Claude Code Haha.app,但 get_app_state(Claude Code Haha)get_app_state(Finder) 均仍为 cgWindowNotFound,系统 screencapture -x /tmp/cc-haha-computer-use-check-3.png 仍失败为 could not create image from display。当前机器继续无法执行点击、输入、文件选择器、托盘/通知等 GUI 验收。
  • 2026-06-01 Security/Runtime Review 修复Electron 移除了 renderer 可调用的 previewEval 任意 JS 注入通道,预览截图/元素选择改走结构化 preview.messageshell.openPath 改为只允许已存在的普通文件/目录,并拒绝 .app、脚本/安装器/Windows 可执行扩展和 POSIX executable 文件Electron 暂不声明自定义 window controls避免 Windows 原生标题栏与自定义标题栏双渲染。
  • 2026-06-01 release metadata 合并后 Computer Use 复测:list_apps 可见运行中的 /Applications/Claude Code Haha.app,但 get_app_state(Claude Code Haha)get_app_state(Finder) 均仍为 cgWindowNotFound,系统 screencapture -x /tmp/cc-haha-computer-use-check-4.png 仍失败为 could not create image from display。当前机器继续无法执行点击、输入、文件选择器、托盘/通知等 GUI 验收。
  • 2026-06-01 mock update feed UI 流程补强:UpdateChecker 集成测试通过 Electron desktopHost mock update feed 驱动真实 updateStore,覆盖 check/download/install/relaunch 状态流、下载完成弹窗、点击安装后 prompt 退出。signed/release feed 仍需真实 signed/notarized artifact 复验。
  • 2026-06-01 mock update feed UI 补强后 Computer Use 复测:list_apps 可见运行中的 /Applications/Claude Code Haha.app,但 get_app_state(Claude Code Haha)get_app_state(Finder) 均仍为 cgWindowNotFound,系统 screencapture -x /tmp/cc-haha-computer-use-check-5.png 仍失败为 could not create image from display。当前机器继续无法执行点击、输入、文件选择器、托盘/通知等 GUI 验收。
  • 2026-06-01 macOS Keychain 弹窗修复:用户截图显示 Electron/Chromium 反复请求 claude-code-desktop Safe Storage。原因是 Chromium profile safe storage 会访问 macOS Keychain本项目 OAuth token 已走 sidecar file-backed storage不依赖 Chromium cookie/password store。Electron main 现在在启动早期对 macOS 设置 use-mock-keychain,并把 ModelSelector 的官方 OAuth status 从挂载即请求改为 runtime 下拉打开时按需请求一次,避免启动/重渲染时反复触发敏感存储路径。验证:cd desktop && bun run test -- --run src/components/controls/ModelSelector.test.tsx electron/services/keychain.test.ts 12 tests passedcd desktop && bun run check:electron 80 tests passed 并完成 main/preload bundle。
  • 2026-06-01 Keychain 修复后 Computer Use 复测:bun run electron:dev 启动当前 Electron dev 壳,get_app_state(Electron) 成功返回 localhost:1420/ 主界面和模型选择器 gpt-5.5 Sub2API-ChatGPT;截图/AX 树中未再出现 claude-code-desktop Safe Storage 钥匙串授权弹窗。随后已终止 dev Electron/Vite 进程。
  • 2026-06-01 Keychain 当前态复验:cd desktop && bun run test -- --run electron/services/keychain.test.ts src/components/controls/ModelSelector.test.tsx 通过 12 testscd desktop && bun run check:electron 通过 83 tests 并完成 Electron main/preload/preview-preload bundle。Computer Use get_app_state(Electron) 返回 Claude Code Companion 主窗口和 localhost:1420/ renderer未出现 Safe Storage 钥匙串授权弹窗。
  • 2026-06-01 原生附件文件选择补强Electron dialog IPC 改为使用当前 BrowserWindow 作为 parent windowcomposer 打开原生选择器前先关闭 plus 菜单,避免无 owner sheet 返回后菜单状态残留。验证:bun test electron/services/dialogs.test.ts 3 tests passedbun run test -- --run src/lib/composerAttachments.test.ts src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx 33 tests passedbun run check:electron 80 tests passedComputer Use 在 Electron dev 壳中成功选择 /Users/nanmi/cc-haha-cua-attachment-smoke.txt 并显示附件 chip未出现 Safe Storage 弹窗。
  • 2026-06-01 packaged updater 噪音修复Electron updater service 在 packaged app-update.yml 缺失时直接返回“无更新”,并将 electron-updater 内置 logger 设为 null,避免目录包或 GitHub release 暂缺 latest-mac.yml 时在启动日志里打印 404 stack非 metadata 错误仍会抛出。验证:cd desktop && bun test electron/services/updater.test.ts electron/services/singleInstance.test.ts 13 tests passedcd desktop && bun run check:electron 89 tests passed。
  • 2026-06-01 single-instance 验证绕过:新增显式 CC_HAHA_ELECTRON_DISABLE_SINGLE_INSTANCE_LOCK=1 供本地验证启动同 bundle 的 worktree/canonical app不影响默认生产单实例行为避免需要杀掉用户正在使用的 /Applications/Claude Code Haha.app
  • 2026-06-01 canonical macOS release Computer Use 复测:SKIP_INSTALL=1 SIGN_BUILD=0 desktop/scripts/build-macos-arm64.sh 通过默认 dmg ziprelease package-smoke PASS用隔离配置和 CC_HAHA_ELECTRON_DISABLE_SINGLE_INSTANCE_LOCK=1 启动 desktop/build-artifacts/macos-arm64/Claude Code Haha.appserver sidecar 启动Computer Use get_app_state 成功读取 Claude Code Companion 主窗口和 packaged renderer。启动日志没有 app-update.ymllatest-mac.yml updater stack。
  • 2026-06-01 canonical packaged 系统交互复测:在同一个 desktop/build-artifacts/macos-arm64/Claude Code Haha.app 隔离实例中Computer Use 确认项目选择器已选中系统原生目录选择结果 /private/tmp/cc-haha-electron-real-fixed-sRFUeO/project;点击 打开终端 后终端面板显示 运行中 /bin/zsh / /tmp/cc-haha-canonical-cua-flow.xTxSmt/claude-config,输入 printf 'canonical-terminal-ok\n'; pwd 后画面可见 canonical-terminal-ok 和实际工作目录输出。该轮未出现 claude-code-desktop Safe Storage 钥匙串弹窗。
  • 2026-06-01 canonical packaged Computer Use 设置流程复测:用隔离 CLAUDE_CONFIG_DIR 启动 desktop/build-artifacts/macos-arm64/Claude Code Haha.appComputer Use 打开设置页后进入 Computer Use tab。首次状态请求曾因 30s timeout 显示 Failed to check status,点击 重试 后页面正常显示 Python 3.14.5、venv 未创建、依赖未安装。通过页面 安装环境 按钮完成真实 server/setup 链路,随后 UI 显示 venv 已就绪、依赖包已安装、辅助功能权限已授权、屏幕录制权限未授权;curl /api/computer-use/status 返回 venv.created=truedependencies.installed=truepermissions.accessibility=truepermissions.screenRecording=false。点击 打开屏幕录制设置 后 Computer Use 成功读取 macOS 系统设置窗口 录屏与系统录音,并看到 Claude Code Haha 权限项。
  • 2026-06-01 canonical packaged Computer Use 授权应用复测:复用隔离 packaged appComputer Use 在设置页搜索 Terminal,应用列表返回 Terminal / com.apple.Terminal;点击该项后 UI 显示 check 状态,隔离配置 cc-haha/computer-use-config.jsonGET /api/computer-use/authorized-apps 均返回 authorizedApps[0].bundleId=com.apple.TerminalgrantFlags.clipboardRead=truegrantFlags.clipboardWrite=truegrantFlags.systemKeyCombos=true。该轮覆盖了真实 UI 点击、输入、滚动、应用枚举和授权配置持久化。
  • 2026-06-01 notarization 诊断复验:bun test scripts/quality-gate/package-smoke/index.test.ts 15 tests passedbun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/macos-arm64 --require-macos-gatekeeper 预期失败,但输出现在同时包含 spctlcodesignnotarization ticket validation 诊断,当前 ad-hoc app 被分类为没有 stapled notarization ticket。
  • 2026-06-01 Gatekeeper 当前态复跑:bun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/macos-arm64 通过,确认 release artifact 结构、latest-mac.yml 引用 zip、app-update.yml、sidecar 和 node-pty 完整;追加 --require-macos-gatekeeper 后仍失败,codesign verify/details 通过但 spctl 返回 bundle format unrecognized, invalid, or unsuitablestapler 返回 does not have a ticket stapled to it。package-smoke 现已在 spctlToo many open files 时用 raised file descriptor limit 自动重试,避免该临时诊断掩盖真实 Gatekeeper 结果。release-ready 仍需要 Developer ID signing/notarization 或真实签名产物复验。
  • 2026-06-01 Gatekeeper 诊断补强后 Computer Use 复测:用隔离 CLAUDE_CONFIG_DIR--user-data-dir 启动 desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.appComputer Use 成功读取 Claude Code Companion 主窗口和 packaged file://.../app.asar/dist/index.html renderer证明 package-smoke 诊断补强未破坏 packaged app 运行路径。
  • 2026-06-01 release gate 口径收紧:quality:gate --mode release 的当前平台 desktop-package-smoke:macos lane 现在会追加 --require-macos-gatekeeper,与 GitHub release workflow 上传前 Gatekeeper 检查保持一致。验证:bun test scripts/quality-gate/runner.test.ts scripts/quality-gate/package-smoke/index.test.ts scripts/pr/release-workflow.test.ts 通过 39 testsbun run quality:gate --mode release --dry-run --only 'desktop-package-smoke:*' 报告命令包含 --require-macos-gatekeeper;实际运行 bun run quality:gate --mode release --only 'desktop-package-smoke:*' 失败于 Gatekeeper报告为 artifacts/quality-runs/2026-06-01T12-16-06-442Z/report.md
  • 2026-06-01 当前 release provider smoke 复验:bun run quality:gate --mode release --allow-live --only 'provider-smoke:*' --provider-model sub2api-chatgpt:main:sub2api-chatgpt-main 通过,报告为 artifacts/quality-runs/2026-06-01T12-18-47-144Z/report.md;随后用 Computer Use 启动隔离 packaged app成功读取 Claude Code Companion 主窗口和 packaged file://.../app.asar/dist/index.html renderer。
  • 2026-06-01 governance gate 复验:bun run check:policy 通过 77 tests并执行 check:quarantine,覆盖 release workflow、quality gate runner、provider/desktop smoke、change policy、quality contract 和 quarantine governance。随后用 Computer Use 再次启动隔离 packaged app首次读取遇到 ScreenCaptureKit stream error但 System Events 显示同一 PID 有 Claude Code Companion 窗口,重试后 Computer Use 成功读取 packaged file://.../app.asar/dist/index.html renderer。
  • 2026-06-01 本地 main 同步后复验:已把本地 main 最近 10 个提交的非重叠改动应用到当前迁移工作区,并手动合并 ChatInput.tsx / previewEvents.ts / previewEvents.test.ts 中的 append prefill 与 Electron desktopHost.preview 改造。验证:cd desktop && bun run test -- --run src/components/chat/ChatInput.test.tsx src/lib/previewEvents.test.ts 通过 24 testsbun test src/cli/__tests__/structuredIO.test.ts src/services/api/withRetry.test.ts src/utils/__tests__/imageResizer.test.ts src/utils/shell/powershellDetection.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/conversation-attachments.test.ts src/server/__tests__/websocket-handler.test.ts 通过 124 testscd adapters && bun test feishu/__tests__/streaming-card.test.ts 通过 36 testscd desktop && bun run check:electron 通过 94 testsCSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 重新生成 packaged dirbun run test:package-smoke --platform macos --package-kind dir --artifacts-dir desktop/build-artifacts/electron 通过Computer Use 成功读取新打包的 desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app 主窗口和 packaged renderer。
  • 2026-06-01 release update blockmap 验证补强:package-smoke 的 release 模式现在强制检查 macOS .zip/.dmg.blockmap、Windows .exe.blockmap 和 Linux .AppImage.blockmap,避免只有 channel metadata 和安装包但缺少 electron-updater 差分更新文件。验证:bun test scripts/quality-gate/package-smoke/index.test.ts 通过 17 testsbun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/macos-arm64 通过,并确认当前 macOS release artifact set 含 .dmg.blockmap.zip.blockmap
  • 2026-06-01 packaged update smoke 补强:新增 CC_HAHA_ELECTRON_UPDATE_SMOKE_VERSION 驱动的 Electron main updater stub仅在显式 smoke env 下启用;app-update.yml 缺失不会阻断该 smoke stub生产 electron-updater 仍保持缺失 metadata 时 no-op。验证cd desktop && bun run check:electron 通过 94 testsCSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 生成新的 packaged dir复制为唯一 bundle id 的 smoke app 后packaged renderer file://.../app.asar/dist/index.html 通过 preload 调用 desktopHost.updates.check/download/prepareInstall/install/relaunchJSONL 记录 checkdownload-startdownload-finishquit-and-install。同轮 Computer Use list_apps 可见唯一 smoke appget_app_state 对该 app 仍超时,故真实“点击安装”仍不能声明通过。
  • 2026-06-01 packaged window smoke 诊断:新增 CC_HAHA_ELECTRON_WINDOW_SMOKE_LOGmain process 在 after-createafter-initial-showdid-finish-loadafter-final-show 写入窗口快照。早前同一 packaged dir 启动日志显示 Electron BrowserWindowvisible:truebounds 为 1280x820URL 为 packaged file://.../app.asar/dist/index.html,但 System Events 曾读取到 windows count=0 且 Computer Use get_app_state 超时;随后用完整 .app 路径复测Computer Use 成功读取 Claude Code Companion 主窗口和 packaged rendererSystem Events 返回 front=true count=1 names=Claude Code Companion。当前结论是之前的卡点在 macOS AX/窗口捕获状态波动,不是 renderer、server、更新 preload smoke 或关闭智能体流程。
  • 2026-06-01 packaged notification click 复测:直接执行 app binary 时 smoke env 未进入最终 Electron app 进程,改用 launchctl setenv + open -n ... --args --user-data-dir=<tmp>packaged app 的 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_LOG 正常写入 scheduledsent:true。Computer Use 对 /System/Library/CoreServices/NotificationCenter.appSystemUIServer 仍返回 timeoutReachedFinder 状态读取返回 ScreenCaptureKit stream errorJSONL 未出现 action;因此“点击通知回到目标 session”仍保持未完成。
  • 2026-06-01 通知点击再次复测:用 launchctl setenv 启动 worktree packaged app 后JSONL 正常写入 scheduledsent:true,但 Computer Use 对该 worktree app 连续返回 ScreenCaptureKit stream errorlist_apps 能看到 worktree app running。随后复制当前 packaged app 为唯一 bundle id smoke app 并 ad-hoc 重签名LaunchServices 能注册该 smoke app但应用立即退出系统日志显示 appDeath因此本轮仍不能把真实 OS 通知点击回跳勾选为完成。
  • 2026-06-01 当前全量桌面/服务端门禁复验:cd desktop && bun run check:desktop 通过 desktop lint、151 个 Vitest 文件、1199 个测试和 production buildbun run check:server 通过 88 files / 969 tests。通知真实点击、signed/notarized Gatekeeper、Windows/Linux 实机 release smoke 仍保持未完成项。
  • 2026-06-01 通知点击 renderer ack 补强:新增 Electron desktop:notification:action-ack IPCrenderer 处理通知 action 并打开目标 session 后,会把 { target, payload } 写入 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_LOGrenderer_ack 事件。该补强不模拟 OS 点击,只让后续可点击通知的 macOS/Windows runner 能证明 main action 已进入 UI 导航链路。验证focused notification/desktopHost tests 42 passedcd desktop && bun run check:electron 通过 97 testsCSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir 重新生成 packaged dirbun run test:package-smoke --platform macos --package-kind dir --artifacts-dir desktop/build-artifacts/electron PASS。
  • 2026-06-01 renderer ack 补强后 Computer Use 复测:用隔离 CLAUDE_CONFIG_DIR、隔离 --user-data-dir 和 notification smoke env 启动当前 worktree packaged app 时Computer Use 首次 get_app_state 返回 timeoutReached,直接执行也曾退出 137。随后用隔离 CLAUDE_CONFIG_DIR / --user-data-dir 重新直接运行同一 .app 后保持运行Computer Use 成功读取 Claude Code Companion 主窗口renderer URL 为当前 worktree 的 packaged file://.../app.asar/dist/index.html;点击设置入口后进入 packaged 设置页。该轮确认当前 Electron build 可被 Computer Use 读取和交互;spctl -a -t execute desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app 仍返回 bundle format unrecognized, invalid, or unsuitablecodesign --verify --deep --strict 通过,因此真实 OS 通知点击和 signed/notarized launch 仍未完成。
  • 2026-06-01 packaged synthetic notification action 复测:新增显式 smoke-only 开关 CC_HAHA_ELECTRON_NOTIFICATION_SMOKE_TRIGGER_ACTION=1,只在测试环境中由 main process 对刚发送的通知 action 触发同一条 renderer ack 链路。用 launchctl setenv + open -n desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app --args --user-data-dir=<tmp> 启动当前 worktree packaged app 后Computer Use 成功读取 Claude Code Companionrenderer URL 为 file:///Users/nanmi/.codex/worktrees/2392/claude-code-haha/desktop/build-artifacts/electron/mac-arm64/Claude%20Code%20Haha.app/Contents/Resources/app.asar/dist/index.htmlJSONL 记录 scheduledsent:truesynthetic_actionrenderer_ack。该 synthetic action 仅证明 packaged main -> renderer action -> ack 链路,不替代真实 OS 通知点击;真实“点击通知后回到目标 session”仍未完成。

Review 收口记录

2026-05-31 SubAgent Review

  • Security Review收紧 packaged renderer entrypackaged app 不再接受 ELECTRON_RENDERER_URLdev 模式只允许 local HTTP renderer。
  • Security Reviewpreview bridge 增加大小上限、事件 allowlist、字段 schema、data:image/* 校验,外部页面不能向 host 注入无限大或未知事件。
  • Security Reviewpackaged macOS node-pty runtime cache 增加 manifest 校验、tamper 后重建、cache/helper 权限收紧。
  • Code ReviewmacOS “打开通知设置”改走 native command 和 allowlisted system settings URL。
  • Code ReviewElectron preview.message() 改为转发到 injected preview bridge不再 silent no-op。
  • Code Reviewdev/release desktop workflows 在 Electron builder 后运行 test:package-smoke,避免上传缺少 sidecar 或 node-pty 关键资源的产物。
  • Verification Review新增 docs/desktop/09-electron-migration-validation-checklist.md,明确 package-smoke 只是结构预检,最终完成仍需要真实 packaged app + Computer Use/实机 smoke。
  • Release gate follow-updesktop-smoke:agent-browser-chat:* 保留为 baseline/browser confidence lane不再作为 release 必需 lanerelease mode 新增 desktop-package-smoke:<platform>,用当前平台 Electron packaged artifact 结构检查配合 Computer Use 真实 app 验收,避免用浏览器/Vite open 结果代表桌面构建可用性。

最终完成定义

  • desktop/src 生产代码不再直接依赖 @tauri-apps/*
  • Electron main/preload IPC 有 capability registry 和 payload validation。
  • 本地 Bun server + REST/WebSocket contract 保留renderer 没有把 session/chat/workspace/team 主链改成 IPC。
  • 自动更新、通知、文件选择、外链打开、窗口/托盘/菜单、sidecar、terminal、preview、app mode、zoom 全部有测试或 smoke 证据。
  • macOS、Windows、Linux 发布产物可构建并启动。
  • bun run verify 通过。
  • release gate 通过,或 live provider blocker 被明确记录。
  • Computer Use 对真实打包 app 完成全面测试。

最终定义复核2026-06-01

  • 生产 Tauri 边界扫描通过:rg -n "@tauri-apps|__TAURI_INTERNALS__|window\\.__TAURI__|from ['\\\"]@tauri" desktop/src --glob '!**/*.test.ts' --glob '!**/*.test.tsx' --glob '!**/lib/desktopHost/**' --glob '!src-tauri/**' 无输出。
  • Electron IPC 复核通过:desktop/electron/ipc/capabilities.ts 为每个 ELECTRON_IPC_CHANNELS channel 定义 validatordesktop/electron/main.tsregisterHandler() 在进入 handler 前执行 isElectronIpcChannel()validateElectronIpcPayload()renderer electronHost 也在 preload bridge 调用前执行相同 payload validation。
  • REST/WebSocket 主干复核通过session/workspace/team 仍在 desktop/src/api/sessions.tsdesktop/src/api/teams.tsdesktop/src/api/websocket.ts 调用 /api/**/ws/:sessionIdElectron IPC 命中仅覆盖 host/system 能力、terminal 和 preview不承载 chat/session/workspace/team 主业务。
  • 系统能力证据复核通过Electron service/API tests 覆盖 updater、notifications、dialogs、shell、windows/tray/menu/single-instance、sidecar、terminal、preview、app mode、zoommacOS .dmg/.zip/latest-mac.yml canonical release artifact set 已通过 package-smokecanonical .app 已通过 Computer Use 窗口可见性 smoke完整完成仍受 signed/notarized Gatekeeper、通知真实点击、Windows/Linux 实机发布格式 smoke 阻塞。