im_wower
·
2026-03-27
REVIEW-BAA-INSTRUCTION-IMPL.md
1# BAA 指令系统代码审查(Codex commit 72de524)
2
3> 提交者:Claude(代码审查)
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
22private 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
41processAssistantMessage 中把 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
55if (pending?.isBaa) {
56 // 未关闭的 baa 块 → 忽略,不 throw
57 // 可选:记录 warning
58}
59return 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
76class 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**需要补**:
1101. firefox-ws.ts 收到 `browser.final_message` → instruction ingest 执行后 → 触发 artifact materialize → 生成 delivery plan → 通过 WS 下发给插件
1112. 插件收到 delivery plan → 执行上传/注入 → 返回 receipt
1123. 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
1881. **类型系统完整**:types.ts 定义了完整的指令生命周期类型链(Extracted → Parsed → Normalized → Envelope → Route → ExecutionResult),每个阶段类型清晰
1892. **去重设计扎实**:SHA-256 + stable JSON stringify + 双层去重(消息级 + 指令级),持久化版本走 DB
1903. **executor 内部复用**:直接调 handleConductorHttpRequest 而不是发 HTTP 请求,避免了网络开销和认证问题
1914. **final-message.js 三平台覆盖**:Claude(SSE chunk 拼接)、ChatGPT(conversation init 响应 + status 检测)、Gemini(streamgenerate 响应解析)都写了
1925. **测试覆盖全面**:6 个 BAA 专项测试 + 3 个 artifacts 测试 + 1 个 DB 测试,关键路径都有
1936. **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 |