baa-conductor

git clone 

commit
3283b54
parent
72de524
author
im_wower
date
2026-03-27 22:19:16 +0800 CST
docs: code review of BAA instruction system — BUG-018/019/020 + improvements
4 files changed,  +274, -0
A bugs/BUG-018-preflight-batch-fail-closed.md
+21, -0
 1@@ -0,0 +1,21 @@
 2+# BUG-018: preflight 整批失败 — 一条坏指令杀死所有并行指令
 3+
 4+## 现象
 5+
 6+AI 发 3 个 baa 代码块,其中 1 个 target 不支持,preflight 对第一个不支持的指令 throw,导致整批 3 条指令全部失败。
 7+
 8+## 文件
 9+
10+`apps/conductor-daemon/src/instructions/loop.ts` preflight()
11+
12+## 根因
13+
14+preflight 用 `.map()` + throw,一个失败整个 map 中断。
15+
16+## 修复
17+
18+改为 filter-and-collect:合法指令继续执行,不合法的收集到 denied 数组。processAssistantMessage 结果中包含 denied 信息。
19+
20+## 严重度
21+
22+High
A bugs/BUG-019-unterminated-baa-block-throws.md
+25, -0
 1@@ -0,0 +1,25 @@
 2+# BUG-019: 未关闭的 baa 代码块导致整条消息处理失败
 3+
 4+## 现象
 5+
 6+AI 回复中有一个未关闭的 ` ```baa ` 块时,extractBaaInstructionBlocks throw,已关闭的合法 baa 块也不会被处理。
 7+
 8+## 文件
 9+
10+`apps/conductor-daemon/src/instructions/extract.ts`
11+
12+## 根因
13+
14+```javascript
15+if (pending?.isBaa) {
16+  throw new BaaInstructionExtractError("Unterminated ```baa code block.");
17+}
18+```
19+
20+## 修复
21+
22+未关闭的块忽略(warn log),不 throw。已关闭的合法块正常返回。
23+
24+## 严重度
25+
26+Medium
A bugs/BUG-020-inmemory-deduper-unbounded.md
+18, -0
 1@@ -0,0 +1,18 @@
 2+# BUG-020: InMemory 去重器无上限,长期运行内存泄漏
 3+
 4+## 现象
 5+
 6+InMemoryBaaInstructionDeduper 和 InMemoryBaaLiveInstructionMessageDeduper 的 Set 只 add 不清理,没有 TTL 或 maxSize。
 7+
 8+## 文件
 9+
10+- `apps/conductor-daemon/src/instructions/dedupe.ts` InMemoryBaaInstructionDeduper
11+- `apps/conductor-daemon/src/instructions/ingest.ts` InMemoryBaaLiveInstructionMessageDeduper
12+
13+## 修复
14+
15+加 maxSize 限制(默认 10000),超出时清理最旧的 20%。或用 Map<string, number> 记录时间戳做 LRU。
16+
17+## 严重度
18+
19+Low-Medium — 短期无影响,但 conductor 是常驻进程
A plans/discuss/REVIEW-BAA-INSTRUCTION-IMPL.md
+210, -0
  1@@ -0,0 +1,210 @@
  2+# BAA 指令系统代码审查(Codex commit 72de524)
  3+
  4+日期:2026-03-27
  5+审查范围:instructions/(11 文件)、artifacts/(5 文件)、final-message.js、content-script.js 扩展、firefox-ws.ts 扩展、local-api.ts 扩展、DB migration 0003
  6+
  7+---
  8+
  9+## BUG
 10+
 11+### BUG-018: preflight 整批失败 — 一条坏指令杀死所有并行指令
 12+
 13+**文件**:`instructions/loop.ts` preflight()
 14+
 15+**现象**:AI 发 3 个 baa 代码块,其中 1 个 target 不支持(比如 `@browser.chatgpt::send`),preflight 对第一个不支持的指令 throw BaaInstructionCenterError,导致整批 3 条指令全部失败,包括 2 条完全合法的 `@conductor::exec`。
 16+
 17+**根因**:preflight 用 `.map()` + throw,一个失败整个 map 中断。
 18+
 19+**修复**:改为 filter-and-collect 模式——合法指令继续执行,不合法的收集到 `policyDenied` 数组返回:
 20+
 21+```typescript
 22+private preflight(instructions) {
 23+  const routed = [];
 24+  const denied = [];
 25+  for (const inst of instructions) {
 26+    const decision = evaluateBaaInstructionPolicy(inst);
 27+    if (!decision.ok) {
 28+      denied.push({ instruction: inst, reason: decision.message });
 29+      continue;
 30+    }
 31+    try {
 32+      routed.push({ instruction: inst, route: routeBaaInstruction(inst) });
 33+    } catch (error) {
 34+      denied.push({ instruction: inst, reason: error.message });
 35+    }
 36+  }
 37+  return { routed, denied };
 38+}
 39+```
 40+
 41+processAssistantMessage 中把 denied 也放进结果,让调用方知道哪些被拒了。
 42+
 43+**严重度**:High — 直接影响并行执行的核心场景
 44+
 45+
 46+### BUG-019: 未关闭的 baa 代码块导致整条消息处理失败
 47+
 48+**文件**:`instructions/extract.ts` extractBaaInstructionBlocks()
 49+
 50+**现象**:如果 AI 回复的最终文本中有一个未关闭的 ` ```baa ` 块(比如 AI 在写示例时忘了关闭),extractBaaInstructionBlocks 会 throw BaaInstructionExtractError("Unterminated ```baa code block."),导致整条消息的所有已关闭的合法 baa 块也不会被处理。
 51+
 52+**修复**:未关闭的块应该被忽略(warn log),不应该 throw。已经关闭的合法块应该正常返回:
 53+
 54+```typescript
 55+if (pending?.isBaa) {
 56+  // 未关闭的 baa 块 → 忽略,不 throw
 57+  // 可选:记录 warning
 58+}
 59+return blocks;
 60+```
 61+
 62+**严重度**:Medium — AI 偶尔会输出未关闭的代码块
 63+
 64+
 65+### BUG-020: InMemoryBaaInstructionDeduper 无上限,长期运行内存泄漏
 66+
 67+**文件**:`instructions/dedupe.ts` InMemoryBaaInstructionDeduper
 68+
 69+**现象**:Set 只 add 不清理,没有 TTL 也没有 maxSize。长时间运行后 Set 无限增长。
 70+
 71+**同类问题**:`ingest.ts` InMemoryBaaLiveInstructionMessageDeduper 同样无上限。
 72+
 73+**修复**:加 LRU 或 maxSize 限制 + 定期清理:
 74+
 75+```typescript
 76+class InMemoryBaaInstructionDeduper {
 77+  private readonly keys = new Map<string, number>(); // key → timestamp
 78+  private readonly maxSize = 10000;
 79+
 80+  add(instruction) {
 81+    if (this.keys.size >= this.maxSize) {
 82+      // 删最旧的 20%
 83+      const entries = [...this.keys.entries()].sort((a, b) => a[1] - b[1]);
 84+      for (let i = 0; i < entries.length * 0.2; i++) {
 85+        this.keys.delete(entries[i][0]);
 86+      }
 87+    }
 88+    this.keys.set(instruction.dedupeKey, Date.now());
 89+  }
 90+}
 91+```
 92+
 93+**严重度**:Low-Medium — 短期无影响,但 conductor 是常驻进程
 94+
 95+
 96+---
 97+
 98+## 功能缺失
 99+
100+### MISSING-001: 执行结果没有回注到 AI 对话
101+
102+**现状**:instructions/ 模块完成了 提取 → 解析 → 去重 → 权限 → 路由 → 执行 的完整链路,artifacts/ 模块有 materialize、manifest、delivery-plan、upload-session 的完整代码。但**从执行结果到实际注入 AI 对话框**的最后一步没有接通。
103+
104+具体来说:
105+- `upload-session.ts` 有 `this.bridge.injectMessage()` 和 `this.bridge.uploadFile()` 调用
106+- 但 `this.bridge` 的接口是抽象的,没有看到和 firefox-bridge 的实际 wiring
107+- conductor 执行完指令后,结果停留在 `BaaInstructionProcessResult` 中,没有触发 delivery plan → 插件上传 → 注入
108+
109+**需要补**:
110+1. firefox-ws.ts 收到 `browser.final_message` → instruction ingest 执行后 → 触发 artifact materialize → 生成 delivery plan → 通过 WS 下发给插件
111+2. 插件收到 delivery plan → 执行上传/注入 → 返回 receipt
112+3. conductor 收到 receipt → 发送索引文本
113+
114+这是端到端闭环的最后一环。
115+
116+**优先级**:Phase 1 关键路径
117+
118+
119+### MISSING-002: 插件侧没有 delivery plan 执行器
120+
121+**现状**:controller.js 有 `relayObservedFinalMessage` 把最终消息发给 conductor,但没有处理 conductor 返回的 delivery plan 的代码——不会上传文件、不会注入结果文本。
122+
123+**需要补**:controller.js 中加处理 WS 消息 `type: "delivery_plan"` 的分支。
124+
125+
126+### MISSING-003: browser.* target 在 Phase 1 被完全拒绝
127+
128+**文件**:`instructions/policy.ts`
129+
130+**现状**:SUPPORTED_TARGETS 只有 `conductor` 和 `system`。根据 BAA_INSTRUCTION_SYSTEM.md,Phase 1 应该包含 `browser.claude` 的 send/current 能力。
131+
132+**建议**:至少把 `browser.claude` 加入 Phase 1 的 policy 白名单,router 中加对应的路由到 `/v1/browser/request`。
133+
134+
135+---
136+
137+## 改进建议
138+
139+### OPT-002: executor 缺少超时保护
140+
141+**文件**:`instructions/executor.ts` executeBaaInstruction()
142+
143+**现状**:直接调 `handleConductorHttpRequest` 无超时。如果 exec 命令卡住(比如死循环的 shell 脚本),整个 instruction processing pipeline 会无限阻塞。
144+
145+**建议**:加 AbortController 或 Promise.race 超时(默认 30s,exec 工具可配 60s)。
146+
147+
148+### OPT-003: policy 应支持配置化
149+
150+**文件**:`instructions/policy.ts`
151+
152+**现状**:SUPPORTED_TARGETS 和 SUPPORTED_TOOLS 硬编码为 Set 常量。
153+
154+**建议**:改为从配置读取,或者至少让 BaaInstructionCenter 构造时可以传入自定义 policy。这样 Phase 2 加新 target/tool 时不需要改代码。
155+
156+
157+### OPT-004: final-message.js 三平台观察器 — Claude SSE 拼接需要验证
158+
159+**文件**:`plugins/baa-firefox/final-message.js`
160+
161+**现状**:692 行代码,实现了 Claude / ChatGPT / Gemini 三个平台的最终消息提取。Claude 的逻辑依赖 SSE chunk 中的 `type: "completion"` 和 `completion` 字段拼接全文。
162+
163+**风险**:如果 Claude 的 SSE 格式变化(比如新版 API 用 `content_block_delta`),拼接会断。建议加一个 fallback:如果 SSE 拼接失败,降级到从 DOM 提取最终文本。
164+
165+
166+### OPT-005: normalize 阶段 parse 错误丢失了 blockIndex
167+
168+**文件**:`instructions/loop.ts` normalize()
169+
170+**现状**:`.map()` 遍历 blocks 解析,如果中间某个 block 解析失败(比如格式错误),throw 会中断整个 map。后面的合法 blocks 不会被处理。
171+
172+**和 BUG-018 同类**:应该改为 `flatMap` + try/catch,解析失败的 block 跳过并记录错误,不影响其他 blocks。
173+
174+
175+### OPT-006: DB migration 缺少表大小限制
176+
177+**文件**:`ops/sql/migrations/0003_baa_execution_persistence.sql`
178+
179+**现状**:三张表(baa_message_dedupes、baa_instruction_dedupes、baa_execution_journal)没有自动清理策略。
180+
181+**建议**:packages/db 中加定期清理(保留最近 N 天或最近 M 条),避免 SQLite 文件无限增长。看代码 store.ts 中 `listBaaExecutionJournal` 有 limit 参数但只影响查询,不影响存储。
182+
183+
184+---
185+
186+## 做得好的地方
187+
188+1. **类型系统完整**:types.ts 定义了完整的指令生命周期类型链(Extracted → Parsed → Normalized → Envelope → Route → ExecutionResult),每个阶段类型清晰
189+2. **去重设计扎实**:SHA-256 + stable JSON stringify + 双层去重(消息级 + 指令级),持久化版本走 DB
190+3. **executor 内部复用**:直接调 handleConductorHttpRequest 而不是发 HTTP 请求,避免了网络开销和认证问题
191+4. **final-message.js 三平台覆盖**:Claude(SSE chunk 拼接)、ChatGPT(conversation init 响应 + status 检测)、Gemini(streamgenerate 响应解析)都写了
192+5. **测试覆盖全面**:6 个 BAA 专项测试 + 3 个 artifacts 测试 + 1 个 DB 测试,关键路径都有
193+6. **DB schema 干净**:三张表职责分离,索引合理
194+
195+---
196+
197+## 总结优先级
198+
199+| # | 类型 | 内容 | 优先级 |
200+|---|---|---|---|
201+| MISSING-001 | 功能缺失 | 执行结果没有回注到 AI 对话 | **最高** |
202+| MISSING-002 | 功能缺失 | 插件侧没有 delivery plan 执行器 | **最高** |
203+| BUG-018 | Bug | 一条坏指令杀死整批并行指令 | High |
204+| BUG-019 | Bug | 未关闭 baa 块导致整条消息失败 | Medium |
205+| MISSING-003 | 功能缺失 | browser.* target Phase 1 被拒绝 | Medium |
206+| OPT-002 | 改进 | executor 缺少超时保护 | Medium |
207+| BUG-020 | Bug | 内存去重器无上限 | Low-Medium |
208+| OPT-003 | 改进 | policy 应支持配置化 | Low |
209+| OPT-004 | 改进 | final-message.js Claude SSE 拼接 fallback | Low |
210+| OPT-005 | 改进 | normalize parse 错误不应中断整批 | Low |
211+| OPT-006 | 改进 | DB 表缺少自动清理 | Low |