baa-conductor

git clone 

commit
48d41ff
parent
88dab15
author
jiaozhiwang
date
2026-03-27 08:22:05 +0800 CST
docs: add BUG-014, FIX-BUG-014, OPT-001 and update bugs README
4 files changed,  +110, -63
M bugs/BUG-014-ws-reconnect-premature-completed.md
+21, -18
 1@@ -1,40 +1,43 @@
 2-# BUG-014: ws_reconnect 报 completed=true 但实际还没重连
 3+# BUG-014: ws_reconnect 报 completed=true 但实际还未重连
 4 
 5 ## 现象
 6 
 7-`POST /v1/browser/actions` 派发 `ws_reconnect` 后,conductor 收到 `action_result` 标记 `completed: true, failed: false`,但此时 WS 实际还没断开更没重连。真正的断开和重连在 80ms 后的 setTimeout 回调中才执行。
 8+执行 `ws_reconnect` 动作后,conductor 立即收到 `action_result` 且 `completed: true, failed: false`,但此时 WS 实际尚未断开,更没有重连。真正的断开和重连在 80ms 后的 `setTimeout` 回调中才发生。
 9 
10-- 哪个模块:`plugins/baa-firefox/controller.js`,`runPluginManagementAction` 函数,ws_reconnect 分支
11+- 哪个模块:`plugins/baa-firefox/controller.js`,`runPluginManagementAction` 中 `ws_reconnect` 分支
12 - 返回了什么:`completed: true`
13-- 预期:要么等重连完成后再报 completed,要么报 `completed: false` 表示异步执行中
14-- 复现条件:任何 ws_reconnect 调用
15+- 预期:要么等重连完成再报 `completed: true`,要么报 `completed: false` 表示异步完成
16+- 复现条件:任何 `ws_reconnect` 调用
17 
18 ## 触发路径
19 
20 ```text
21-POST /v1/browser/actions {action: "ws_reconnect"}
22-→ conductor dispatchWithActionResult
23-→ WS → Firefox controller.js
24-→ runPluginManagementAction("ws_reconnect")
25-→ setTimeout(() => { closeWsConnection(); connectWs(); }, 80)  ← 非阻塞
26-→ 函数立即返回 → .then() 发 action_result(completed: true)
27-→ 80ms 后才真正断开和重连
28+POST /v1/browser/actions {"action":"ws_reconnect"}
29+  → conductor dispatch → WS → Firefox controller.js
30+  → runPluginManagementAction("ws_reconnect")
31+  → setTimeout(() => { closeWsConnection(); connectWs(); }, 80)
32+  → 函数立即返回(setTimeout 非阻塞)
33+  → .then() → sendPluginActionResult({ completed: true })
34+  → conductor 收到 action_result,认为重连完成
35+  → 80ms 后 WS 才真正断开并重连
36 ```
37 
38 ## 根因
39 
40-`ws_reconnect` 的 `setTimeout(..., 80)` 是非阻塞的,`runPluginManagementAction` 在 setTimeout 注册后立刻返回,调用链的 `.then()` 把结果当作「已完成」发给 conductor。
41+`ws_reconnect` 用 `setTimeout` 延迟执行断开和重连,但 `runPluginManagementAction` 是同步返回的,不等 setTimeout 回调。上层 `.then()` 在函数返回后立刻发送 `completed: true` 的 action_result。
42 
43 ## 影响
44 
45-- conductor 侧收到 `completed: true` 后可能立即发后续请求,但 WS 此时正在断连过程中
46-- 自动化编排如果依赖 ws_reconnect 的 completed 语义来做顺序控制,会出问题
47-- 当前手动使用不易触发,但 task scheduler 上线后风险增大
48+- 语义不准确:conductor 侧依赖 `completed` 判断动作是否执行完毕
49+- 如果后续有自动化逻辑在收到 completed=true 后立即发送请求,可能在旧连接上发送
50+- 80ms 窗口内 WS 消息行为不可预期
51 
52 ## 严重度
53 
54 Low-Medium
55 
56-## 关联
57+## 建议修复方案
58 
59-无前置依赖
60+方案 A(推荐):让 `runPluginManagementAction` 的 ws_reconnect 分支返回一个带标记的结果,上层据此发送 `completed: false`,让 conductor 知道这是异步完成的动作。重连成功后 conductor 通过 `hello` 消息自然感知。
61+
62+方案 B:把 `setTimeout` 改为 `await sleep(80)` + 同步执行,等重连握手完成后再返回。但这会让 action 响应变慢。
M bugs/FIX-BUG-014.md
+35, -24
 1@@ -1,4 +1,4 @@
 2-# FIX-BUG-014: ws_reconnect completed 语义修复
 3+# FIX-BUG-014: ws_reconnect 过早报 completed=true
 4 
 5 ## 关联 Bug
 6 
 7@@ -6,44 +6,55 @@ BUG-014-ws-reconnect-premature-completed.md
 8 
 9 ## 目标
10 
11-让 ws_reconnect 的 action_result 正确反映重连是否完成。
12+让 ws_reconnect 的 action_result 语义正确:在重连尚未完成时不报 completed=true。
13 
14 ## 修改文件
15 
16-`plugins/baa-firefox/controller.js` — `runPluginManagementAction` 的 ws_reconnect 分支和调用链 `.then()` 处理
17+`plugins/baa-firefox/controller.js`
18 
19 ## 修改方案
20 
21-### 方案 A(推荐):先发 action_result 再执行重连
22+在 `runPluginManagementAction` 的 ws_reconnect case 中,给返回结果增加一个标记(例如 `deferred: true`),让上层的 `sendPluginActionResult` 调用时将 `completed` 设为 `false`。
23 
24-在 WS 消息处理的 `.then()` 和 `.catch()` 中,对 ws_reconnect 特殊处理:
25+具体改动:
26 
27-1. 在 `runPluginManagementAction` 返回后、`.then()` 调 `sendPluginActionResult` 时,把 ws_reconnect 的 `completed` 设为 `false`,表示「已接受,异步执行中」
28-2. 80ms 后 setTimeout 触发时,closeWsConnection + connectWs 正常执行
29-3. 重连成功后 conductor 通过 hello 消息感知新连接建立
30+### 1. runPluginManagementAction ws_reconnect 分支
31 
32-具体改动(connectWs 中的 `.then()` 回调,约第 3856 行附近):
33+在 break 前,给 results 加一个带 `deferred` 标记的条目,或者在返回对象上加 `deferred: true`:
34 
35 ```javascript
36-.then((result) => {
37+case "ws_reconnect":
38+  addLog("info", "正在重连本地 WS", false);
39+  setTimeout(() => {
40+    closeWsConnection();
41+    connectWs({ silentWhenDisabled: true });
42+  }, 80);
43+  // 标记为延迟完成
44+  return {
45+    action: methodName,
46+    platform: trimToNull(options.platform),
47+    results: normalizedResults,
48+    snapshot: buildPluginStatusPayload(),
49+    deferred: true
50+  };
51+```
52+
53+### 2. connectWs 中 action_result 发送逻辑
54+
55+在 `.then()` 中检查 deferred 标记:
56+
57+```javascript
58+}).then((result) => {
59   sendPluginActionResult(result, {
60-    action: pluginAction.action,
61-    commandType: pluginAction.commandType,
62-    platform: pluginAction.platform,
63-    requestId: pluginAction.requestId,
64-    // ws_reconnect 是异步执行的,completed 应为 false
65-    completed: pluginAction.action !== "ws_reconnect"
66+    ...,
67+    completed: result.deferred !== true
68   });
69 })
70 ```
71 
72-### 方案 B:把 ws_reconnect 改为同步等待
73-
74-把 setTimeout 改为 await promise,等 closeWsConnection + connectWs 都完成后再返回。需要把 connectWs 改成返回 Promise(当前是 void)。改动较大,不推荐首版。
75-
76 ## 验收标准
77 
78-1. `pnpm typecheck` 通过
79-2. `pnpm test` 通过
80-3. 手动测试:派发 ws_reconnect 后,action_result 返回 `completed: false`
81-4. WS 重连成功后 conductor 收到新的 hello 消息
82+1. 执行 ws_reconnect 后 action_result 的 `completed` 为 `false`
83+2. conductor 侧能正确识别异步完成的动作
84+3. 其他 action(tab_open、plugin_status 等)仍然报 `completed: true`
85+4. 不引入新的 lint 或类型警告
M bugs/OPT-001-action-result-code-quality.md
+51, -18
 1@@ -1,36 +1,69 @@
 2-# OPT-001: action_result 相关代码质量建议
 3+# OPT-001: action_result 闭环代码质量优化
 4 
 5 日期:2026-03-27
 6-来源:Claude 代码审查 782e4c7
 7-优先级:Low(均不影响功能)
 8+来源:Claude 代码审查 commit 782e4c7
 9 
10 ---
11 
12-## A. request_id 字段命名风格不一致
13+## OPT-A: request_id 字段命名风格不一致
14 
15-`BrowserBridgeActionResultSnapshot` 接口中使用 `request_id`(snake_case),但 Firefox 插件发送的 JSON 字段是 `requestId`(camelCase)。
16+### 问题
17 
18-当前 `handlePluginActionResult`(firefox-ws.ts)做了归一化,功能正常。
19+`BrowserBridgeActionResultSnapshot` 接口定义 `request_id`(snake_case),但 Firefox 插件 WS 发送的是 `requestId`(camelCase)。firefox-ws.ts 的 `handlePluginActionResult` 做了归一化(`readFirstString(message, ["requestId", "request_id", "id"])`),功能不受影响。
20 
21-建议:接口层统一为 snake_case(与其他 snapshot 接口一致),或在 browser-types.ts 的注释里说明 wire format 是 camelCase、内部 snapshot 是 snake_case。
22+但接口层 snake_case 和插件层 camelCase 混用,增加维护困惑。
23 
24-## B. test 文件 buildShellRuntime 定义了两次
25+### 建议
26+
27+统一为 snake_case(与 BrowserBridgeClientSnapshot 等其他快照接口一致)。插件侧发送时也改为 `request_id`,减少 conductor 侧的归一化分支。
28+
29+### 优先级
30+
31+Low
32+
33+---
34+
35+## OPT-B: test 文件中 buildShellRuntime 定义了两次
36+
37+### 问题
38 
39 `apps/conductor-daemon/src/index.test.js` 中:
40-- 模块级 `buildShellRuntime`(~第 498 行):tab_id=321
41-- `createBrowserBridgeStub` 内部 `buildShellRuntime`:tab_id=88
42 
43-两个函数功能基本相同,只是默认值不同。建议合并为一个,通过 overrides 参数区分。
44+1. 模块级 `buildShellRuntime(platform, overrides)` — tab_id=321
45+2. `createBrowserBridgeStub()` 内部 `buildShellRuntime(platform, overrides)` — tab_id=88
46+
47+两个函数功能几乎相同,只有默认值不同。内部那个遮蔽了外部那个。
48+
49+### 建议
50+
51+删除内部重复定义,统一使用模块级版本,通过 overrides 参数传入 stub 特有的默认值。
52 
53-## C. dispatchBrowserAction async 错误路径分工
54+### 优先级
55 
56-`dispatchBrowserAction` 现在是 async,内部有两种错误路径:
57-1. bridge 同步抛出(如 no client)→ 被内部 catch 转为 LocalApiHttpError
58-2. `await dispatch.result` rejection(如 action_timeout)→ 传播到 handleBrowserActions 的 catch
59+Low
60 
61-逻辑上没 bug,两层 catch 都能正确处理。建议在 `dispatchBrowserAction` 函数头部加一行注释说明:
62+---
63+
64+## OPT-C: dispatchBrowserAction async 错误路径可加注释
65+
66+### 问题
67+
68+`dispatchBrowserAction` 内有两层错误处理:
69+
70+1. 内层 `catch` — 捕获 bridge 同步抛出的错误(如 no_active_client、send_failed)
71+2. 外层 `handleBrowserActions` 的 `try/catch` — 捕获 `await dispatch.result` 的 rejection(如 action_timeout、client_disconnected)
72+
73+逻辑正确,两层分工合理,但没有注释说明。如果后续有人改动错误处理,可能不理解为什么有两层。
74+
75+### 建议
76+
77+在 `dispatchBrowserAction` 的 catch 块加一行注释:
78 
79 ```typescript
80-// Synchronous bridge errors (no client, send failed) are caught below.
81-// Asynchronous result errors (action_timeout, client_disconnected) propagate to the caller.
82+// 这里只捕获 dispatch 阶段的同步错误(bridge 不可用、发送失败)。
83+// action_result 超时或连接断开的异步错误由 await dispatch.result 传播到调用方。
84 ```
85+
86+### 优先级
87+
88+Low
M bugs/README.md
+3, -3
 1@@ -20,7 +20,7 @@
 2 | BUG-011 | `BUG-011-*.md` | writeHttpResponse drain handler 永久挂起 | Medium-High | FIX-BUG-011.md |
 3 | BUG-012 | `BUG-012-*.md` | browser-request-policy waiter 死锁 | Medium | FIX-BUG-012.md |
 4 | BUG-013 | `BUG-013-*.md` | stream session timer 未清除 | Low | FIX-BUG-013.md |
 5-| BUG-014 | `BUG-014-*.md` | ws_reconnect 报 completed=true 但还没重连 | Low-Medium | FIX-BUG-014.md |
 6+| BUG-014 | `BUG-014-*.md` | ws_reconnect 过早报 completed=true | Low-Medium | FIX-BUG-014.md |
 7 
 8 修复优先级:BUG-011 > BUG-012 > BUG-014 > BUG-013
 9 
10@@ -28,11 +28,11 @@
11 
12 | # | 文件 | 内容 | 优先级 |
13 |---|---|---|---|
14-| OPT-001 | `OPT-001-*.md` | action_result 命名一致性、test 去重、async catch 注释 | Low |
15+| OPT-001 | `OPT-001-*.md` | action_result 命名风格、test 重复定义、错误路径注释 | Low |
16 
17 ## 编号规则
18 
19 - BUG-XXX:bug 报告
20 - FIX-BUG-XXX:对应修复任务卡(给 Codex 执行)
21-- OPT-XXX:优化建议(非紧急,按需执行)
22+- OPT-XXX:优化建议(非紧急,可合并处理)
23 - 编号按发现顺序递增,不复用