- commit
- b2f6ea7
- parent
- c87af13
- author
- jiaozhiwang
- date
- 2026-03-26 20:25:59 +0800 CST
docs: add FIX-BUG-011/012/013 fix cards for codex
4 files changed,
+185,
-5
+56,
-0
1@@ -0,0 +1,56 @@
2+# FIX-BUG-011: writeHttpResponse drain handler 永久挂起
3+
4+## 关联 Bug
5+
6+BUG-011-writeHttpResponse-drain-handler-hangs.md
7+
8+## 目标
9+
10+修复 `writeHttpResponse()` 中 drain 等待无 close 监听导致协程挂死的问题。
11+
12+## 修改文件
13+
14+`apps/conductor-daemon/src/index.ts` — `writeHttpResponse()` 函数
15+
16+## 修改方案
17+
18+在每个 `await new Promise(resolve => writableResponse.on("drain", resolve))` 处,同时监听 `close` 事件,任一触发即 resolve 并清理另一个 listener。
19+
20+有两处需要修改:
21+
22+### 第一处:初始 body 写入的 drain 等待
23+
24+将:
25+```typescript
26+if (!writableResponse.write(payload.body)) {
27+ await new Promise<void>((resolve) => {
28+ writableResponse.on?.("drain", resolve);
29+ });
30+}
31+```
32+
33+改为:
34+```typescript
35+if (!writableResponse.write(payload.body)) {
36+ await new Promise<void>((resolve) => {
37+ const onDrain = () => { cleanup(); resolve(); };
38+ const onClose = () => { cleanup(); resolve(); };
39+ const cleanup = () => {
40+ writableResponse.off?.("drain", onDrain);
41+ writableResponse.off?.("close", onClose);
42+ };
43+ writableResponse.on?.("drain", onDrain);
44+ writableResponse.on?.("close", onClose);
45+ });
46+}
47+```
48+
49+### 第二处:streamBody 循环内的 drain 等待
50+
51+同样模式,替换 streamBody `for await` 循环中的 drain 等待。
52+
53+## 验收标准
54+
55+1. `pnpm typecheck` 通过
56+2. `pnpm test` 通过
57+3. 不引入新的 lint 或类型警告
+75,
-0
1@@ -0,0 +1,75 @@
2+# FIX-BUG-012: browser-request-policy waiter 死锁
3+
4+## 关联 Bug
5+
6+BUG-012-browser-request-policy-waiter-deadlock.md
7+
8+## 目标
9+
10+防止 `acquireTargetSlot` 和 `acquirePlatformAdmission` 中的 waiter Promise 永久挂起。
11+
12+## 修改文件
13+
14+`apps/conductor-daemon/src/browser-request-policy.ts`
15+
16+## 修改方案
17+
18+### 方案 A(推荐):给 waiter 加超时
19+
20+在 `acquireTargetSlot` 中:
21+
22+```typescript
23+private async acquireTargetSlot(state: BrowserRequestTargetState): Promise<void> {
24+ if (
25+ state.inFlight < this.config.concurrency.maxInFlightPerClientPlatform
26+ && state.waiters.length === 0
27+ ) {
28+ state.inFlight += 1;
29+ return;
30+ }
31+
32+ await new Promise<void>((resolve, reject) => {
33+ const timer = this.setTimeoutImpl(() => {
34+ const index = state.waiters.indexOf(onReady);
35+ if (index !== -1) state.waiters.splice(index, 1);
36+ reject(new BrowserRequestPolicyError(
37+ "waiter_timeout",
38+ "Timed out waiting for a browser request slot.",
39+ { timeoutMs: WAITER_TIMEOUT_MS }
40+ ));
41+ }, WAITER_TIMEOUT_MS);
42+ const onReady = () => {
43+ clearTimeout(timer);
44+ resolve();
45+ };
46+ state.waiters.push(onReady);
47+ });
48+}
49+```
50+
51+超时常量建议 `WAITER_TIMEOUT_MS = 120_000`(2 分钟)。
52+
53+同样模式应用到 `acquirePlatformAdmission`。
54+
55+### 方案 B(防御性补充):releaseTargetSlot 防御检查
56+
57+在 `releaseTargetSlot` 中增加防御:
58+
59+```typescript
60+private releaseTargetSlot(state: BrowserRequestTargetState): void {
61+ const waiter = state.waiters.shift();
62+ if (waiter != null) {
63+ waiter();
64+ return;
65+ }
66+ state.inFlight = Math.max(0, state.inFlight - 1);
67+}
68+```
69+
70+当前已有此逻辑,确认 `Math.max(0, ...)` 防御到位。
71+
72+## 验收标准
73+
74+1. `pnpm typecheck` 通过
75+2. `pnpm test` 通过
76+3. 新增单元测试:模拟 lease 未 complete 的场景,验证后续请求在超时后返回错误而非永久挂起
+41,
-0
1@@ -0,0 +1,41 @@
2+# FIX-BUG-013: stream session timer 未清除
3+
4+## 关联 Bug
5+
6+BUG-013-stream-session-timer-not-cleared.md
7+
8+## 目标
9+
10+在 stream session 关闭时清除 openTimer 和 idleTimer,避免 GC 延迟和多余回调。
11+
12+## 修改文件
13+
14+`apps/conductor-daemon/src/firefox-bridge.ts` — `FirefoxBridgeApiStreamSession` 类
15+
16+## 修改方案
17+
18+在 `finishWithEvent` 方法中,在设置 `this.closed = true` 之后,立即清除两个 timer:
19+
20+```typescript
21+private finishWithEvent(event: FirefoxBridgeStreamEvent): boolean {
22+ this.closed = true;
23+
24+ // 清除未触发的 timer
25+ if (this.openTimer != null) {
26+ this.clearTimeoutImpl(this.openTimer);
27+ this.openTimer = null;
28+ }
29+ if (this.idleTimer != null) {
30+ this.clearTimeoutImpl(this.idleTimer);
31+ this.idleTimer = null;
32+ }
33+
34+ // ... 现有的 enqueue 和 onClose 逻辑
35+}
36+```
37+
38+## 验收标准
39+
40+1. `pnpm typecheck` 通过
41+2. `pnpm test` 通过
42+3. 不引入新的 lint 或类型警告
+13,
-5
1@@ -6,15 +6,23 @@
2 - 一个通用 `BUG-TEMPLATE.md`
3 - 对应 bug 的修复任务卡
4
5+## 已关闭
6+
7 按当前工作区状态,这 3 个 bug 都已完成修复,但仍保留文档作为根因与回归记录:
8
9-1. `BUG-008` — 已随 `BUG-010` 一并修复;如果后续 recent events 出现 `app-server.turn.completed.missing` / `failureClass=transport_closed_before_turn_completed`,应作为新的 child / transport bug 记录,而不是 reopen 旧尾包问题
10-2. `BUG-009` — 已修复;如果后续测试绕开 `withRuntimeFixture(...)` 或关闭路径本身阻塞,仍需新开专项问题
11-3. `BUG-010` — 已修复;如果后续还有 `app-server.transport.closed` 且同一 turn 没有合法 `app-server.turn.completed`,应按新 bug 跟踪
12+1. `BUG-008` — 已随 `BUG-010` 一并修复
13+2. `BUG-009` — 已修复
14+3. `BUG-010` — 已修复
15+
16+## 待修复
17
18-修复任务:
19+| # | 文件 | 问题 | 严重度 | 修复卡 |
20+|---|---|---|---|---|
21+| BUG-011 | `BUG-011-*.md` | writeHttpResponse drain handler 永久挂起 | Medium-High | FIX-BUG-011.md |
22+| BUG-012 | `BUG-012-*.md` | browser-request-policy waiter 死锁 | Medium | FIX-BUG-012.md |
23+| BUG-013 | `BUG-013-*.md` | stream session timer 未清除 | Low | FIX-BUG-013.md |
24
25-- FIX-BUG-008:`/Users/george/code/baa-conductor/bugs/FIX-BUG-008.md`
26+修复优先级:BUG-011 > BUG-012 > BUG-013
27
28 ## 编号规则
29