- commit
- 8eb6ff1
- parent
- 8eb6ff1
- author
- im_wower
- date
- 2026-03-21 19:19:23 +0800 CST
Initial scaffold and collaboration docs
72 files changed,
+4706,
-0
+14,
-0
1@@ -0,0 +1,14 @@
2+node_modules/
3+dist/
4+.DS_Store
5+
6+state/
7+runs/
8+worktrees/
9+logs/
10+tmp/
11+
12+.env
13+.env.*
14+.wrangler/
15+
+2652,
-0
1@@ -0,0 +1,2652 @@
2+# BAA Conductor 实施设计说明
3+
4+## 0. 文档状态
5+
6+本文档是新 conductor 系统的实施基线。
7+
8+它的细节深度是按“多个 Codex worker 读完就能并行开工”来写的。
9+
10+本文档定义了:
11+
12+- 系统目标
13+- 拓扑结构
14+- 域名与转发布局
15+- `mini` 与 `mac` 的主备切换行为
16+- Cloudflare D1 控制平面设计
17+- task 与 step 模型
18+- `planner`、`conductor`、`worker` 的职责边界
19+- Codex 执行约定
20+- 日志、checkpoint 与恢复机制
21+- Firefox 插件集成方式
22+- 建议的仓库结构
23+- 建议的并行开发拆分
24+
25+如果代码与本文档不一致,要么更新代码以符合本文档,要么明确修订本文档。
26+
27+## 1. 总体目标
28+
29+构建一个稳定的 AI 执行系统,具备以下特征:
30+
31+- `mini` 是主 conductor。
32+- `mac` 是备用 conductor。
33+- 两台机器都可以运行 Codex worker。
34+- 人类只使用一个可见的 Claude `control` 对话。
35+- 自动化使用一个隐藏的 Claude `dispatch` 通道。
36+- task 真相存储在共享数据库中。
37+- 进度恢复依赖 checkpoint,而不是试图复活已死亡的进程。
38+
39+整个系统围绕一个简单原则展开:
40+
41+**恢复 task 进度,而不是恢复 Codex 进程内存。**
42+
43+## 2. 现有基础组件
44+
45+当前环境已经有三个关键基础组件。
46+
47+### 2.1 `baa-hand`
48+
49+当前职责:
50+
51+- AI 路由
52+- `POST /ask`
53+- `POST /chat`
54+- `POST /plan`
55+
56+当前优点:
57+
58+- 能调 Claude
59+- 能调 Codex
60+- Codex 和 Claude 网页版都能用它
61+
62+当前限制:
63+
64+- 现在的 Codex 集成是面向进程的,不是面向 task 的
65+- 它会等到进程退出后再返回
66+- 它还不是一个可持久恢复的 step worker runtime
67+
68+结论:
69+
70+- 继续用 `baa-hand` 走 planner 和 review 路径
71+- 不要把当前 `baa-hand -> codex exec -> 等进程退出` 这条路径当成最终的 durable worker 模型
72+
73+### 2.2 `baa-shell`
74+
75+当前职责:
76+
77+- shell 命令执行
78+- 文件读写
79+- task 报告
80+
81+结论:
82+
83+- 它仍然适合远程操作流和外部自动化
84+- conductor 在 HTTP 执行比直连 SSH 或本地 exec 更方便时,可以调用它
85+
86+### 2.3 Firefox 插件
87+
88+当前职责:
89+
90+- 拦截 Claude 网页流量
91+- 已有常驻 controller 概念
92+- 可以承载可见的自动化状态与控制按钮
93+
94+结论:
95+
96+- 用它接可见的 `control` 会话
97+- 增加全局 `pause`、`resume`,可选 `drain`
98+- 不要让浏览器状态成为真相来源
99+- 浏览器按钮必须写入 conductor 控制平面
100+
101+## 3. 设计原则
102+
103+### 3.1 Durable 真相必须在 AI 进程之外
104+
105+真相不能放在:
106+
107+- 浏览器标签页
108+- Codex 进程
109+- 临时 Claude 会话
110+- Node.js 进程内存中的 map
111+
112+真相必须放在:
113+
114+- Cloudflare D1
115+- 本地 run 目录
116+
117+### 3.2 Codex 是 Worker,不是 Conductor
118+
119+Codex 可以:
120+
121+- 在被要求时参与规划任务
122+- 执行某个 step
123+- 总结结果
124+
125+Codex 不可以:
126+
127+- 持有 lease
128+- 持有队列真相
129+- 持有调度真相
130+- 直接改全局状态
131+
132+### 3.3 Planner 是抽象角色
133+
134+`planner` 是角色,不是固定实现方。
135+
136+planner 可以是:
137+
138+- 确定性的模板
139+- Claude dispatch
140+- Codex
141+- 未来单独的 planner 模块
142+
143+是否使用 planner、是否接受 planner 输出,由 conductor 决定。
144+
145+### 3.4 Conductor 必须是确定性基础设施
146+
147+conductor 是 daemon,不是聊天会话。
148+
149+conductor 必须负责:
150+
151+- task 创建归一化
152+- step 拆分结果验收
153+- lease 逻辑
154+- worker 分配
155+- heartbeat 检查
156+- timeout 处理
157+- checkpoint 写入
158+- 恢复
159+
160+### 3.5 Step 边界就是恢复边界
161+
162+每个 task 都要拆成多个 step。
163+
164+每个 step 都必须:
165+
166+- 有边界
167+- 可观察
168+- 可 checkpoint
169+- 可重试或可终止
170+
171+## 4. 系统角色
172+
173+## 4.1 Human
174+
175+人只需要做这些事:
176+
177+- 使用一个可见的 Claude `control` 对话
178+- 查看当前状态
179+- 暂停、恢复或 drain 自动化
180+- 做高层决策
181+
182+人不应该需要盯着 Codex 终端界面。
183+
184+## 4.2 `control`
185+
186+这是人类交互使用的可见 Claude 对话。
187+
188+职责:
189+
190+- 讨论策略
191+- 创建 task
192+- 查看状态
193+- 请求暂停或恢复
194+- 审查进展
195+
196+规则:
197+
198+- `control` 是交互界面
199+- `control` 不是任务队列
200+- `control` 不是 durable 状态存储
201+
202+## 4.3 `dispatch`
203+
204+这是隐藏的 Claude 自动化通道。
205+
206+职责:
207+
208+- 提供 planning 支持
209+- 提供 review 支持
210+- 输出结构化结果
211+
212+规则:
213+
214+- `dispatch` 不面向用户
215+- `dispatch` 不能做 durable 真相来源
216+- `dispatch` 只能给 proposal,不能直接做最终状态迁移
217+
218+## 4.4 `planner`
219+
220+职责:
221+
222+- 把目标转换成一个建议的 step plan
223+
224+输入:
225+
226+- task 目标
227+- repo
228+- 约束
229+- 验收条件
230+- 可选的当前 repo 状态
231+
232+输出:
233+
234+- 结构化 plan JSON
235+- 拆分理由
236+- 风险标记
237+
238+规则:
239+
240+- planner 输出只是建议
241+- conductor 在持久化之前必须校验 planner 输出
242+
243+## 4.5 `conductor`
244+
245+职责:
246+
247+- leader lease 管理
248+- task 归一化
249+- plan 验收
250+- step 调度
251+- worker 监管
252+- timeout 执行
253+- 日志索引
254+- checkpoint
255+- 故障切换与恢复
256+
257+## 4.6 `worker`
258+
259+职责:
260+
261+- 执行且只执行一个 step
262+- 输出本地日志
263+- 返回结果
264+- 退出
265+
266+默认 AI worker 是 Codex。
267+
268+Shell 和 Git 类 step 可以由 shell runner 执行,而不是由 Codex 进程执行。
269+
270+## 5. 拓扑结构
271+
272+## 5.1 节点
273+
274+### `mini`
275+
276+角色:
277+
278+- 主 conductor
279+- 本地 worker 宿主机
280+
281+运行:
282+
283+- conductor daemon
284+- worker runner
285+- 可选本地 status API
286+- 可选 Firefox 自动化栈
287+
288+### `mac`
289+
290+角色:
291+
292+- 备用 conductor
293+- 本地 worker 宿主机
294+
295+运行:
296+
297+- standby conductor daemon
298+- worker runner
299+- 可选本地 status API
300+
301+### `vps`
302+
303+角色:
304+
305+- 入口与反向代理
306+
307+运行:
308+
309+- Nginx
310+- TLS 终止,或 Cloudflare origin 代理支持
311+
312+VPS 不持有 leader 真相。
313+
314+### `cloudflare`
315+
316+角色:
317+
318+- D1 数据库
319+- 可选 Worker 形式的 control API
320+- DNS 与自定义域名
321+
322+## 5.2 数据路径
323+
324+### 控制平面
325+
326+- human -> 可见 Claude `control`
327+- `control` -> conductor control API
328+- conductors -> D1
329+- workers -> conductors
330+
331+### 执行平面
332+
333+- conductor -> 本地 worker 进程
334+- conductor -> 本地 shell 与 git 命令
335+- conductor -> `baa-hand` 做 planning 或 review
336+- conductor -> `baa-shell` 在 HTTP 远程执行更方便时使用
337+
338+### 浏览器平面
339+
340+- Firefox 插件 -> control API 做 pause 与 resume
341+- Firefox 插件 -> 可见状态 badge
342+- Firefox 插件 -> 如有需要,访问隐藏 `dispatch`
343+
344+## 6. 域名与二级域名布局
345+
346+本节定义推荐的二级域名布局。
347+
348+## 6.1 必需的公网域名
349+
350+### `conductor.makefile.so`
351+
352+用途:
353+
354+- conductor API 的统一公网入口
355+- 指向 VPS Nginx
356+- Nginx 再转发到 `mini` 主、`mac` 备
357+
358+使用方:
359+
360+- 人类工具
361+- Firefox 插件控制动作
362+- 诊断请求
363+
364+### `mini-conductor.makefile.so`
365+
366+用途:
367+
368+- 经 VPS 直达 mini,便于调试和维护
369+
370+### `mac-conductor.makefile.so`
371+
372+用途:
373+
374+- 经 VPS 直达 mac,便于调试和维护
375+
376+### `control-api.makefile.so`
377+
378+用途:
379+
380+- 绑定到 D1 的 Cloudflare Worker 自定义域
381+- durable 控制状态的规范写入口
382+
383+名字可以不同,但设计上要求有一个稳定的、前置 D1 的 HTTP API 域名。
384+
385+## 6.2 现有域名
386+
387+这些域名已经存在,继续沿用:
388+
389+- `led.makefile.so` -> `baa-hand`
390+- `s.makefile.so` -> `baa-shell`
391+
392+## 6.3 推荐 DNS 策略
393+
394+- `conductor.makefile.so` -> VPS 公网 IP
395+- `mini-conductor.makefile.so` -> VPS 公网 IP
396+- `mac-conductor.makefile.so` -> VPS 公网 IP
397+- `control-api.makefile.so` -> Cloudflare Worker 自定义域
398+
399+之后由 VPS 把节点专属域名再转发到 Tailscale 地址。
400+
401+## 6.4 建议的 DNS 记录
402+
403+推荐 DNS 记录如下:
404+
405+| Hostname | Type | Target | Purpose |
406+| --- | --- | --- | --- |
407+| `conductor.makefile.so` | `A` 或 `AAAA` | VPS 公网 IP | conductor 统一公网入口 |
408+| `mini-conductor.makefile.so` | `A` 或 `AAAA` | VPS 公网 IP | 经 VPS 直达 mini |
409+| `mac-conductor.makefile.so` | `A` 或 `AAAA` | VPS 公网 IP | 经 VPS 直达 mac |
410+| `control-api.makefile.so` | Worker 自定义域 | Cloudflare Worker | durable 控制平面 API |
411+| `led.makefile.so` | 现有 | 现有 origin | `baa-hand` |
412+| `s.makefile.so` | 现有 | 现有 origin | `baa-shell` |
413+
414+说明:
415+
416+- 如果 VPS 走 Cloudflare 代理,所有相关 host 的代理模式要保持一致
417+- `control-api.makefile.so` 最好留在 Cloudflare 内部,让 D1 访问保持本地化
418+- mini 和 mac 不应该直接暴露在公网,节点专属域名仍然应该经过 VPS
419+
420+## 6.5 TLS 策略
421+
422+推荐 TLS 模式:
423+
424+- 公网访问的域名尽量通过 Cloudflare 代理
425+- VPS 上给 `conductor.makefile.so`、`mini-conductor.makefile.so`、`mac-conductor.makefile.so` 申请 Let’s Encrypt 证书
426+- Worker 自定义域使用 Cloudflare 托管证书
427+
428+如果 conductor 域名启用了 Cloudflare 橙云:
429+
430+- VPS 上配置 origin certificate 或普通 Let’s Encrypt 证书
431+- 尽量限制对 origin 的直接访问
432+
433+如果 conductor 域名不走 Cloudflare 代理:
434+
435+- 保持 VPS 上 Let’s Encrypt 自动续期
436+- 对直连节点域名加鉴权保护
437+
438+## 7. VPS 与 Nginx 转发
439+
440+## 7.1 为什么需要 Nginx
441+
442+VPS 上的 Nginx 提供:
443+
444+- 稳定的公网入口
445+- TCP 与 HTTP 层的机器切换
446+- 到 mini 和 mac 的直连调试路由
447+
448+它不负责决定真实 leader。
449+
450+真实 leader 由 D1 lease 决定。
451+
452+## 7.2 Nginx Upstream 设计
453+
454+推荐 upstream:
455+
456+```nginx
457+upstream conductor_primary {
458+ server 100.71.210.78:4317 max_fails=2 fail_timeout=5s;
459+ server 100.112.239.13:4317 backup;
460+ keepalive 32;
461+}
462+
463+upstream mini_conductor_direct {
464+ server 100.71.210.78:4317;
465+ keepalive 16;
466+}
467+
468+upstream mac_conductor_direct {
469+ server 100.112.239.13:4317;
470+ keepalive 16;
471+}
472+```
473+
474+本例中:
475+
476+- `100.71.210.78` 是 `mini`
477+- `100.112.239.13` 是 `mac`
478+- `4317` 是 conductor 本地 HTTP 端口
479+
480+## 7.3 Nginx Server Block
481+
482+### 统一 conductor 入口
483+
484+```nginx
485+server {
486+ listen 443 ssl http2;
487+ server_name conductor.makefile.so;
488+
489+ ssl_certificate /etc/letsencrypt/live/conductor.makefile.so/fullchain.pem;
490+ ssl_certificate_key /etc/letsencrypt/live/conductor.makefile.so/privkey.pem;
491+
492+ location / {
493+ proxy_pass http://conductor_primary;
494+ proxy_http_version 1.1;
495+ proxy_set_header Host $host;
496+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
497+ proxy_set_header X-Forwarded-Proto $scheme;
498+ proxy_set_header X-Request-Id $request_id;
499+ proxy_connect_timeout 3s;
500+ proxy_read_timeout 60s;
501+ proxy_send_timeout 60s;
502+ }
503+}
504+```
505+
506+### 直达 mini 的路由
507+
508+```nginx
509+server {
510+ listen 443 ssl http2;
511+ server_name mini-conductor.makefile.so;
512+
513+ ssl_certificate /etc/letsencrypt/live/mini-conductor.makefile.so/fullchain.pem;
514+ ssl_certificate_key /etc/letsencrypt/live/mini-conductor.makefile.so/privkey.pem;
515+
516+ location / {
517+ proxy_pass http://mini_conductor_direct;
518+ proxy_http_version 1.1;
519+ proxy_set_header Host $host;
520+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
521+ proxy_set_header X-Forwarded-Proto $scheme;
522+ proxy_set_header X-Request-Id $request_id;
523+ }
524+}
525+```
526+
527+### 直达 mac 的路由
528+
529+```nginx
530+server {
531+ listen 443 ssl http2;
532+ server_name mac-conductor.makefile.so;
533+
534+ ssl_certificate /etc/letsencrypt/live/mac-conductor.makefile.so/fullchain.pem;
535+ ssl_certificate_key /etc/letsencrypt/live/mac-conductor.makefile.so/privkey.pem;
536+
537+ location / {
538+ proxy_pass http://mac_conductor_direct;
539+ proxy_http_version 1.1;
540+ proxy_set_header Host $host;
541+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
542+ proxy_set_header X-Forwarded-Proto $scheme;
543+ proxy_set_header X-Request-Id $request_id;
544+ }
545+}
546+```
547+
548+## 7.4 健康检查与就绪检查
549+
550+每个 conductor 都应暴露:
551+
552+- `GET /healthz`
553+- `GET /readyz`
554+- `GET /rolez`
555+
556+推荐语义:
557+
558+- `/healthz`: 进程活着
559+- `/readyz`: 节点已准备好提供 conductor 服务
560+- `/rolez`: 返回 `leader` 或 `standby`
561+
562+重要说明:
563+
564+- standby 可以健康,但不是 leader
565+- 只要节点不是 leader,写 API 仍然必须拒绝调度类操作
566+
567+## 7.5 建议的端口暴露方式
568+
569+VPS 只应对公网暴露:
570+
571+- `80/tcp`
572+- `443/tcp`
573+
574+mini 和 mac 不应该直接对公网开放 conductor 端口。
575+
576+推荐访问模型:
577+
578+- VPS 通过 Tailscale 或 WireGuard 访问 mini 和 mac
579+- 只有 VPS 去访问 conductor HTTP API
580+- 运维人员要访问 mini/mac 时,通过 SSH 或受保护的直连节点域名
581+
582+## 7.6 完整的 Nginx 模式
583+
584+推荐整体模式:
585+
586+1. `:80` 全部跳转到 HTTPS
587+2. `conductor.makefile.so` 走主加备 upstream
588+3. `mini-conductor.makefile.so` 直转 mini upstream
589+4. `mac-conductor.makefile.so` 直转 mac upstream
590+5. 直连节点域名额外加鉴权
591+
592+### HTTP 跳转
593+
594+```nginx
595+server {
596+ listen 80;
597+ listen [::]:80;
598+ server_name conductor.makefile.so mini-conductor.makefile.so mac-conductor.makefile.so;
599+ return 301 https://$host$request_uri;
600+}
601+```
602+
603+### 直连节点域名的 Basic Auth
604+
605+对 `mini-conductor.makefile.so` 与 `mac-conductor.makefile.so`,增加:
606+
607+```nginx
608+auth_basic "Restricted";
609+auth_basic_user_file /etc/nginx/.htpasswd-baa-conductor;
610+```
611+
612+除非你希望整个系统都放在认证后面,否则不要把这段加到统一入口域名上。
613+
614+## 7.7 Nginx 加固建议
615+
616+建议在每个 TLS server block 中增加:
617+
618+```nginx
619+proxy_set_header X-Real-IP $remote_addr;
620+proxy_set_header X-Forwarded-Host $host;
621+proxy_set_header X-Forwarded-Port $server_port;
622+proxy_buffering off;
623+client_max_body_size 10m;
624+```
625+
626+如果你需要日志串联请求:
627+
628+```nginx
629+add_header X-Request-Id $request_id always;
630+```
631+
632+如果后续要走状态流或长连接:
633+
634+```nginx
635+proxy_read_timeout 3600s;
636+proxy_send_timeout 3600s;
637+```
638+
639+## 7.8 Nginx Failover 的边界
640+
641+Nginx failover 只解决:
642+
643+- 网络路径故障
644+- TCP 连接失败
645+- 简单 upstream 不可达
646+
647+Nginx failover 不能解决:
648+
649+- split-brain
650+- 过期 leader 继续写
651+- task lease 冲突
652+- 安全恢复逻辑
653+
654+因此所有写接口仍然必须校验 D1 lease 状态。
655+
656+## 7.9 建议的 Nginx 文件布局
657+
658+建议在 VPS 上使用:
659+
660+```text
661+/etc/nginx/nginx.conf
662+/etc/nginx/sites-available/baa-conductor.conf
663+/etc/nginx/sites-enabled/baa-conductor.conf
664+/etc/nginx/.htpasswd-baa-conductor
665+```
666+
667+未来仓库中的文件应尽量镜像这个结构:
668+
669+```text
670+ops/nginx/baa-conductor.conf
671+ops/nginx/includes/common-proxy.conf
672+ops/nginx/includes/direct-node-auth.conf
673+```
674+
675+## 8. 主备与故障切换模型
676+
677+## 8.1 真相来源
678+
679+leader 真相在 D1,不在 Nginx。
680+
681+Nginx 只负责网络可达性和入口层 failover。
682+
683+## 8.2 推荐行为
684+
685+- `mini` 是首选 leader
686+- `mac` 是 standby
687+- 任意时刻只能有一个有效 leader lease
688+
689+推荐 lease 参数:
690+
691+- TTL: 30 秒
692+- 续租周期: 每 5 秒一次
693+- 连续 2 次续租失败后自我降级
694+
695+## 8.3 切换规则
696+
697+### 正常状态
698+
699+- mini 持有 lease
700+- mac 只监听,不调度
701+
702+### Mini 故障
703+
704+- mini heartbeat 停止
705+- lease 过期
706+- mac 抢到 lease
707+- mac 成为 active scheduler
708+
709+### Mini 恢复
710+
711+- mini 恢复上线
712+- mini 注册为 standby
713+- mini 不自动抢占
714+
715+### 手动切回
716+
717+管理员可以显式执行:
718+
719+- drain mac
720+- promote mini
721+- demote mac
722+
723+这样可以避免主备来回抖动。
724+
725+## 8.4 防止 Split-Brain
726+
727+规则:
728+
729+- 所有写动作都要求有效 lease
730+- claim task 与 claim step 前都要检查 lease
731+- 每条写路径都要校验当前 leader term
732+- standby 节点必须拒绝调度类写入
733+
734+## 9. Cloudflare D1 控制平面
735+
736+## 9.1 为什么使用 D1
737+
738+使用 D1 作为共享 durable 协调存储,是因为它适合承载:
739+
740+- mini 与 mac 之间的共享真相
741+- SQLite 事务语义
742+- 队列、lease、元数据与索引
743+
744+D1 不适合存储:
745+
746+- 长期保存的完整原始日志
747+- 大型二进制产物
748+- 高频逐 token 流式数据
749+
750+## 9.2 访问模式
751+
752+推荐模式:
753+
754+- 机器本身不要直接写 D1
755+- 全部通过统一的 HTTP control API
756+- control API 跑在 Cloudflare Worker 上,并绑定 D1
757+
758+这样可以集中管理:
759+
760+- 鉴权
761+- schema 访问
762+- 事务约束
763+- 版本演进
764+
765+## 9.3 读一致性
766+
767+控制路径上的读尽量使用主一致读。
768+
769+规则:
770+
771+- lease 获取与 task claim 必须保证主一致逻辑
772+- 状态查看类页面可以容忍轻微陈旧
773+
774+## 10. 数据库 Schema
775+
776+本节定义最小 D1 schema。
777+
778+## 10.1 `leader_lease`
779+
780+用途:
781+
782+- 全局唯一 active leadership lease
783+
784+```sql
785+CREATE TABLE IF NOT EXISTS leader_lease (
786+ lease_name TEXT PRIMARY KEY,
787+ holder_id TEXT NOT NULL,
788+ holder_host TEXT NOT NULL,
789+ term INTEGER NOT NULL,
790+ lease_expires_at INTEGER NOT NULL,
791+ renewed_at INTEGER NOT NULL,
792+ preferred_holder_id TEXT,
793+ metadata_json TEXT
794+);
795+```
796+
797+约定:
798+
799+- 只保留一行,`lease_name = 'global'`
800+
801+## 10.2 `controllers`
802+
803+用途:
804+
805+- 注册 conductors 及其 heartbeat
806+
807+```sql
808+CREATE TABLE IF NOT EXISTS controllers (
809+ controller_id TEXT PRIMARY KEY,
810+ host TEXT NOT NULL,
811+ role TEXT NOT NULL,
812+ priority INTEGER NOT NULL,
813+ status TEXT NOT NULL,
814+ version TEXT,
815+ last_heartbeat_at INTEGER NOT NULL,
816+ last_started_at INTEGER,
817+ metadata_json TEXT
818+);
819+```
820+
821+## 10.3 `workers`
822+
823+用途:
824+
825+- 注册 worker slot
826+
827+```sql
828+CREATE TABLE IF NOT EXISTS workers (
829+ worker_id TEXT PRIMARY KEY,
830+ controller_id TEXT NOT NULL,
831+ host TEXT NOT NULL,
832+ worker_type TEXT NOT NULL,
833+ status TEXT NOT NULL,
834+ max_parallelism INTEGER NOT NULL DEFAULT 1,
835+ current_load INTEGER NOT NULL DEFAULT 0,
836+ last_heartbeat_at INTEGER NOT NULL,
837+ capabilities_json TEXT,
838+ metadata_json TEXT
839+);
840+```
841+
842+## 10.4 `tasks`
843+
844+用途:
845+
846+- durable task 队列
847+
848+```sql
849+CREATE TABLE IF NOT EXISTS tasks (
850+ task_id TEXT PRIMARY KEY,
851+ repo TEXT NOT NULL,
852+ task_type TEXT NOT NULL,
853+ title TEXT NOT NULL,
854+ goal TEXT NOT NULL,
855+ source TEXT NOT NULL,
856+ priority INTEGER NOT NULL DEFAULT 100,
857+ status TEXT NOT NULL,
858+ planning_strategy TEXT,
859+ planner_provider TEXT,
860+ branch_name TEXT,
861+ base_ref TEXT,
862+ target_host TEXT,
863+ assigned_controller_id TEXT,
864+ current_step_index INTEGER NOT NULL DEFAULT -1,
865+ constraints_json TEXT,
866+ acceptance_json TEXT,
867+ metadata_json TEXT,
868+ result_summary TEXT,
869+ result_json TEXT,
870+ error_text TEXT,
871+ created_at INTEGER NOT NULL,
872+ updated_at INTEGER NOT NULL,
873+ started_at INTEGER,
874+ finished_at INTEGER
875+);
876+```
877+
878+索引:
879+
880+```sql
881+CREATE INDEX IF NOT EXISTS idx_tasks_status_priority
882+ON tasks(status, priority, created_at);
883+
884+CREATE INDEX IF NOT EXISTS idx_tasks_repo
885+ON tasks(repo, created_at);
886+```
887+
888+## 10.5 `task_steps`
889+
890+用途:
891+
892+- 持久化恢复边界
893+
894+```sql
895+CREATE TABLE IF NOT EXISTS task_steps (
896+ step_id TEXT PRIMARY KEY,
897+ task_id TEXT NOT NULL,
898+ step_index INTEGER NOT NULL,
899+ step_name TEXT NOT NULL,
900+ step_kind TEXT NOT NULL,
901+ status TEXT NOT NULL,
902+ assigned_worker_id TEXT,
903+ assigned_controller_id TEXT,
904+ timeout_sec INTEGER NOT NULL,
905+ retry_limit INTEGER NOT NULL DEFAULT 0,
906+ retry_count INTEGER NOT NULL DEFAULT 0,
907+ lease_expires_at INTEGER,
908+ input_json TEXT,
909+ output_json TEXT,
910+ summary TEXT,
911+ error_text TEXT,
912+ created_at INTEGER NOT NULL,
913+ updated_at INTEGER NOT NULL,
914+ started_at INTEGER,
915+ finished_at INTEGER,
916+ UNIQUE(task_id, step_index)
917+);
918+```
919+
920+索引:
921+
922+```sql
923+CREATE INDEX IF NOT EXISTS idx_task_steps_task_status
924+ON task_steps(task_id, status, step_index);
925+```
926+
927+## 10.6 `task_runs`
928+
929+用途:
930+
931+- 记录 step 的执行尝试
932+
933+```sql
934+CREATE TABLE IF NOT EXISTS task_runs (
935+ run_id TEXT PRIMARY KEY,
936+ task_id TEXT NOT NULL,
937+ step_id TEXT NOT NULL,
938+ worker_id TEXT NOT NULL,
939+ controller_id TEXT NOT NULL,
940+ host TEXT NOT NULL,
941+ pid INTEGER,
942+ status TEXT NOT NULL,
943+ lease_expires_at INTEGER,
944+ heartbeat_at INTEGER,
945+ log_dir TEXT NOT NULL,
946+ stdout_path TEXT,
947+ stderr_path TEXT,
948+ worker_log_path TEXT,
949+ checkpoint_seq INTEGER NOT NULL DEFAULT 0,
950+ exit_code INTEGER,
951+ result_json TEXT,
952+ error_text TEXT,
953+ created_at INTEGER NOT NULL,
954+ started_at INTEGER,
955+ finished_at INTEGER
956+);
957+```
958+
959+索引:
960+
961+```sql
962+CREATE INDEX IF NOT EXISTS idx_task_runs_task
963+ON task_runs(task_id, created_at);
964+
965+CREATE INDEX IF NOT EXISTS idx_task_runs_step
966+ON task_runs(step_id, created_at);
967+```
968+
969+## 10.7 `task_checkpoints`
970+
971+用途:
972+
973+- 恢复 step 中途进度的快照
974+
975+这一张表很关键,因为某些 Codex step 很长,在 step 完成前就可能已经产生了有价值的部分进度。
976+
977+```sql
978+CREATE TABLE IF NOT EXISTS task_checkpoints (
979+ checkpoint_id TEXT PRIMARY KEY,
980+ task_id TEXT NOT NULL,
981+ step_id TEXT NOT NULL,
982+ run_id TEXT NOT NULL,
983+ seq INTEGER NOT NULL,
984+ checkpoint_type TEXT NOT NULL,
985+ summary TEXT,
986+ content_text TEXT,
987+ content_json TEXT,
988+ created_at INTEGER NOT NULL,
989+ UNIQUE(run_id, seq)
990+);
991+```
992+
993+checkpoint 类型可以包括:
994+
995+- `heartbeat`
996+- `log_tail`
997+- `git_diff`
998+- `summary`
999+- `test_result`
1000+
1001+## 10.8 `task_logs`
1002+
1003+用途:
1004+
1005+- 存结构化日志尾部与生命周期事件
1006+
1007+```sql
1008+CREATE TABLE IF NOT EXISTS task_logs (
1009+ log_id INTEGER PRIMARY KEY AUTOINCREMENT,
1010+ task_id TEXT NOT NULL,
1011+ step_id TEXT,
1012+ run_id TEXT NOT NULL,
1013+ seq INTEGER NOT NULL,
1014+ stream TEXT NOT NULL,
1015+ level TEXT,
1016+ message TEXT NOT NULL,
1017+ created_at INTEGER NOT NULL
1018+);
1019+```
1020+
1021+## 10.9 `system_state`
1022+
1023+用途:
1024+
1025+- 全局自动化状态
1026+
1027+```sql
1028+CREATE TABLE IF NOT EXISTS system_state (
1029+ state_key TEXT PRIMARY KEY,
1030+ value_json TEXT NOT NULL,
1031+ updated_at INTEGER NOT NULL
1032+);
1033+```
1034+
1035+约定:
1036+
1037+- `state_key = 'automation'`
1038+- value 中包含 `mode = running | draining | paused`
1039+
1040+## 10.10 `task_artifacts`
1041+
1042+可选但推荐。
1043+
1044+用途:
1045+
1046+- 保存 durable 输出的结构化引用
1047+
1048+```sql
1049+CREATE TABLE IF NOT EXISTS task_artifacts (
1050+ artifact_id TEXT PRIMARY KEY,
1051+ task_id TEXT NOT NULL,
1052+ step_id TEXT,
1053+ run_id TEXT,
1054+ artifact_type TEXT NOT NULL,
1055+ path TEXT,
1056+ uri TEXT,
1057+ size_bytes INTEGER,
1058+ sha256 TEXT,
1059+ metadata_json TEXT,
1060+ created_at INTEGER NOT NULL
1061+);
1062+```
1063+
1064+## 11. Control API
1065+
1066+推荐的生产设计是:
1067+
1068+- 一个 Cloudflare Worker
1069+- 一个 D1 binding
1070+- 所有 durable 状态迁移都经过这个 Worker
1071+
1072+## 11.1 Base URL
1073+
1074+推荐:
1075+
1076+- `https://control-api.makefile.so`
1077+
1078+## 11.2 必需接口
1079+
1080+### `POST /v1/controllers/heartbeat`
1081+
1082+由 mini 和 mac conductor 调用。
1083+
1084+Body:
1085+
1086+```json
1087+{
1088+ "controller_id": "mini-main",
1089+ "host": "mini",
1090+ "role": "primary",
1091+ "priority": 100,
1092+ "status": "alive",
1093+ "version": "0.1.0"
1094+}
1095+```
1096+
1097+### `POST /v1/leader/acquire`
1098+
1099+由 conductors 用来获取或续租 lease。
1100+
1101+Body:
1102+
1103+```json
1104+{
1105+ "controller_id": "mini-main",
1106+ "host": "mini",
1107+ "preferred": true,
1108+ "ttl_sec": 30
1109+}
1110+```
1111+
1112+Response:
1113+
1114+```json
1115+{
1116+ "ok": true,
1117+ "holder_id": "mini-main",
1118+ "term": 7,
1119+ "lease_expires_at": 1760000000000,
1120+ "is_leader": true
1121+}
1122+```
1123+
1124+### `POST /v1/tasks`
1125+
1126+创建 task。
1127+
1128+Body:
1129+
1130+```json
1131+{
1132+ "repo": "/Users/george/code/event-fabric",
1133+ "task_type": "feature_impl",
1134+ "title": "Add conductor D1 schema",
1135+ "goal": "Implement the initial D1 schema and migration scripts.",
1136+ "priority": 50,
1137+ "constraints": {
1138+ "target_host": "mini"
1139+ },
1140+ "acceptance": [
1141+ "SQL schema committed",
1142+ "migration runner stub added"
1143+ ],
1144+ "metadata": {
1145+ "requested_by": "control",
1146+ "branch_prefix": "feat"
1147+ }
1148+}
1149+```
1150+
1151+### `POST /v1/tasks/:task_id/plan`
1152+
1153+持久化已通过校验的 step plan。
1154+
1155+该接口由 conductor 在完成 planner 输出校验后调用。
1156+
1157+### `POST /v1/tasks/claim`
1158+
1159+领取待规划 task 或下一个可运行 step。
1160+
1161+也可以拆成:
1162+
1163+- `POST /v1/tasks/claim-planning`
1164+- `POST /v1/steps/claim`
1165+
1166+只要事务语义清晰,这两种形式都可以。
1167+
1168+### `POST /v1/steps/:step_id/heartbeat`
1169+
1170+Body 包含:
1171+
1172+- `run_id`
1173+- `worker_id`
1174+- `lease_expires_at`
1175+- 可选 `checkpoint`
1176+
1177+### `POST /v1/steps/:step_id/checkpoint`
1178+
1179+Body:
1180+
1181+```json
1182+{
1183+ "run_id": "run_001",
1184+ "seq": 3,
1185+ "checkpoint_type": "git_diff",
1186+ "summary": "Current worktree diff after route refactor",
1187+ "content_text": "diff --git ..."
1188+}
1189+```
1190+
1191+### `POST /v1/steps/:step_id/complete`
1192+
1193+### `POST /v1/steps/:step_id/fail`
1194+
1195+### `POST /v1/system/pause`
1196+
1197+### `POST /v1/system/resume`
1198+
1199+### `POST /v1/system/drain`
1200+
1201+### `GET /v1/system/state`
1202+
1203+### `GET /v1/tasks/:task_id`
1204+
1205+### `GET /v1/tasks/:task_id/logs`
1206+
1207+### `GET /v1/runs/:run_id`
1208+
1209+## 11.3 鉴权
1210+
1211+最低要求:
1212+
1213+- conductor、worker 与 control API 之间使用 HMAC token 或共享密钥
1214+- 修改系统状态的操作使用单独的 admin token
1215+
1216+推荐后续演进:
1217+
1218+- 签名 service token
1219+- 按角色拆 scope
1220+
1221+## 11.4 Control API 授权模型
1222+
1223+推荐角色:
1224+
1225+- `controller`
1226+- `worker`
1227+- `browser_admin`
1228+- `ops_admin`
1229+- `readonly`
1230+
1231+推荐权限划分:
1232+
1233+| Role | Allowed Actions |
1234+| --- | --- |
1235+| `controller` | 获取与续租 lease、claim tasks、更新 run 状态 |
1236+| `worker` | heartbeat、checkpoint、完成已分配 step |
1237+| `browser_admin` | pause、resume、drain、查看队列 |
1238+| `ops_admin` | promote、demote、维护操作 |
1239+| `readonly` | 状态查看、日志、面板 |
1240+
1241+推荐实现:
1242+
1243+- `Authorization: Bearer <token>`
1244+- token 元数据包含角色与可选主机身份
1245+- 修改接口既校验角色,也校验资源归属
1246+
1247+## 11.5 Control API 运维约束
1248+
1249+Worker 应提供:
1250+
1251+- 尽量做到幂等写入
1252+- 对陈旧 term 明确返回冲突
1253+- 记录 request id
1254+- 输出结构化错误体
1255+
1256+推荐错误格式:
1257+
1258+```json
1259+{
1260+ "ok": false,
1261+ "error": "not_leader",
1262+ "message": "This node does not hold the active leadership lease.",
1263+ "request_id": "req_123"
1264+}
1265+```
1266+
1267+## 12. Task 模型
1268+
1269+## 12.1 Task 生命周期
1270+
1271+Task 状态:
1272+
1273+- `queued`
1274+- `planning`
1275+- `running`
1276+- `paused`
1277+- `done`
1278+- `failed`
1279+- `canceled`
1280+
1281+状态流转:
1282+
1283+- `queued -> planning`
1284+- `planning -> running`
1285+- `planning -> failed`
1286+- `running -> paused`
1287+- `paused -> running`
1288+- `running -> done`
1289+- `running -> failed`
1290+- `running -> canceled`
1291+
1292+## 12.2 Task 输入 Schema
1293+
1294+推荐归一化格式:
1295+
1296+```json
1297+{
1298+ "task_id": "task_001",
1299+ "repo": "/Users/george/code/event-fabric",
1300+ "task_type": "feature_impl",
1301+ "title": "Implement task scheduler",
1302+ "goal": "Add the initial scheduler and step claim logic.",
1303+ "priority": 50,
1304+ "constraints": {
1305+ "target_host": "mini",
1306+ "write_scope": [
1307+ "services/conductor-daemon/**",
1308+ "packages/db/**"
1309+ ]
1310+ },
1311+ "acceptance": [
1312+ "claim endpoint exists",
1313+ "scheduler handles one runnable step",
1314+ "tests added"
1315+ ],
1316+ "metadata": {
1317+ "branch_prefix": "feat",
1318+ "requested_by": "control"
1319+ }
1320+}
1321+```
1322+
1323+## 12.3 分支策略
1324+
1325+一个 task 对应一个分支。
1326+
1327+规则:
1328+
1329+- 分支由 conductor 创建
1330+- worker 不自己发明分支名
1331+- 分支命名应尽量确定性
1332+
1333+推荐命名:
1334+
1335+- `feat/T-001-d1-schema`
1336+- `feat/T-002-step-claim`
1337+- `fix/T-003-checkpoint-recovery`
1338+
1339+## 13. Step 模型
1340+
1341+## 13.1 Step 状态
1342+
1343+- `pending`
1344+- `running`
1345+- `done`
1346+- `failed`
1347+- `timeout`
1348+
1349+## 13.2 Step 类型
1350+
1351+推荐 step kind:
1352+
1353+- `planner`
1354+- `codex`
1355+- `shell`
1356+- `git`
1357+- `review`
1358+- `finalize`
1359+
1360+## 13.3 Step 合约
1361+
1362+每个 step 都必须具备:
1363+
1364+- 明确的目标边界
1365+- 输入 payload
1366+- timeout
1367+- retry 策略
1368+- 完成后的 summary
1369+
1370+## 13.4 恢复边界
1371+
1372+step 完成是主要恢复边界。
1373+
1374+但因为 Codex step 可能很长,所以系统还必须维护 step 中途的 checkpoint。
1375+
1376+## 14. Planning 模型
1377+
1378+## 14.1 Planner 所有权
1379+
1380+task 拆 step 的所有权属于 conductor。
1381+
1382+含义是:
1383+
1384+- planner 只负责提案
1385+- conductor 决定接受并持久化
1386+- worker 只负责执行
1387+
1388+## 14.2 规划策略选择
1389+
1390+conductor 需要决定:
1391+
1392+- `template_first`
1393+- `planner_assisted`
1394+- `manual`
1395+
1396+例如:
1397+
1398+- 标准 bugfix -> `template_first`
1399+- 模糊的大范围重构 -> `planner_assisted`
1400+- 紧急运维变更 -> `manual`
1401+
1402+## 14.3 Planner 输出 Schema
1403+
1404+推荐 planner 输出:
1405+
1406+```json
1407+{
1408+ "task_type": "feature_impl",
1409+ "strategy": "planner_assisted",
1410+ "reasoning": "The task touches scheduler, persistence, and recovery logic. Split into deterministic steps.",
1411+ "steps": [
1412+ {
1413+ "step_name": "prepare_branch",
1414+ "step_kind": "git",
1415+ "timeout_sec": 120,
1416+ "retry_limit": 0,
1417+ "input": {}
1418+ },
1419+ {
1420+ "step_name": "inspect_context",
1421+ "step_kind": "codex",
1422+ "timeout_sec": 600,
1423+ "retry_limit": 1,
1424+ "input": {
1425+ "goal": "Read the codebase and summarize where scheduler logic belongs."
1426+ }
1427+ },
1428+ {
1429+ "step_name": "implement_scheduler",
1430+ "step_kind": "codex",
1431+ "timeout_sec": 1800,
1432+ "retry_limit": 1,
1433+ "input": {
1434+ "goal": "Implement scheduler claim loop and state transition logic."
1435+ }
1436+ },
1437+ {
1438+ "step_name": "run_tests",
1439+ "step_kind": "shell",
1440+ "timeout_sec": 1200,
1441+ "retry_limit": 1,
1442+ "input": {
1443+ "command": "npm test"
1444+ }
1445+ },
1446+ {
1447+ "step_name": "commit_push",
1448+ "step_kind": "git",
1449+ "timeout_sec": 300,
1450+ "retry_limit": 0,
1451+ "input": {}
1452+ }
1453+ ]
1454+}
1455+```
1456+
1457+## 14.4 Planner 校验规则
1458+
1459+conductor 校验规则:
1460+
1461+- step 名称必须已知,或被明确允许
1462+- step 顺序必须合法
1463+- timeout 不能无上限
1464+- step 输入不能为空缺
1465+- 不允许 planner 指令直接做状态迁移
1466+
1467+## 15. 推荐的 Task 模板
1468+
1469+## 15.1 `feature_impl`
1470+
1471+推荐默认步骤:
1472+
1473+1. `prepare_branch`
1474+2. `inspect_context`
1475+3. `implement`
1476+4. `run_tests`
1477+5. `review_fix`
1478+6. `commit_push`
1479+7. `finalize`
1480+
1481+## 15.2 `bugfix`
1482+
1483+推荐默认步骤:
1484+
1485+1. `prepare_branch`
1486+2. `reproduce`
1487+3. `implement_fix`
1488+4. `run_targeted_tests`
1489+5. `commit_push`
1490+6. `finalize`
1491+
1492+## 15.3 `review_only`
1493+
1494+推荐默认步骤:
1495+
1496+1. `prepare_context`
1497+2. `analyze_diff`
1498+3. `write_review`
1499+4. `finalize`
1500+
1501+## 15.4 `ops_change`
1502+
1503+推荐默认步骤:
1504+
1505+1. `prepare_branch`
1506+2. `inspect_ops_context`
1507+3. `edit_ops_files`
1508+4. `validate_config`
1509+5. `commit_push`
1510+6. `finalize`
1511+
1512+## 15.5 `infra_bootstrap`
1513+
1514+推荐默认步骤:
1515+
1516+1. `prepare_branch`
1517+2. `inspect_current_ops_state`
1518+3. `edit_dns_or_nginx_docs`
1519+4. `validate_nginx_config`
1520+5. `commit_push`
1521+6. `finalize`
1522+
1523+## 16. Conductor 详细职责
1524+
1525+conductor 必须承担以下职责。
1526+
1527+### 16.1 启动时
1528+
1529+- 在 `controllers` 中注册自身
1530+- 启动 heartbeat 循环
1531+- 如果有资格则尝试获取 lease
1532+- 加载本地未完成 runs
1533+- 对本地过期 runs 做对账
1534+
1535+### 16.2 Leader Loop
1536+
1537+如果是 leader:
1538+
1539+- 规划 queued tasks
1540+- claim runnable steps
1541+- 分配 workers
1542+- 监管 runs
1543+- 持久化 checkpoints
1544+
1545+如果是 standby:
1546+
1547+- 只发 heartbeat
1548+- 不调度新工作
1549+- 随时准备在当前 leader 过期后接管 lease
1550+
1551+### 16.3 Worker 分配
1552+
1553+conductor 负责选择:
1554+
1555+- host
1556+- worker slot
1557+- worktree 路径
1558+- run 目录
1559+- timeout 预算
1560+
1561+### 16.4 Timeout 执行
1562+
1563+conductor 负责:
1564+
1565+- step timeout
1566+- lease 过期
1567+- 从 `SIGTERM` 升级到 `SIGKILL`
1568+
1569+### 16.5 恢复
1570+
1571+重启后:
1572+
1573+- 扫描本地 run 目录
1574+- 标记 orphaned runs
1575+- 对比本地状态与 D1 状态
1576+- 视情况重新入队或恢复
1577+
1578+## 17. Worker 模型
1579+
1580+## 17.1 为什么 Worker 要短生命周期
1581+
1582+短生命周期 worker 更容易:
1583+
1584+- 隔离故障
1585+- 收集日志
1586+- 管理资源
1587+- 在崩溃后恢复
1588+
1589+## 17.2 Step 执行合约
1590+
1591+worker 合约是:
1592+
1593+1. 接收一个 step
1594+2. 执行一个 step
1595+3. 输出本地日志
1596+4. 输出结构化结果
1597+5. 退出
1598+
1599+worker 不负责:
1600+
1601+- 管理队列
1602+- 持有 lease
1603+- 选择下一个 step
1604+
1605+## 17.3 Worker 类型
1606+
1607+### Codex Worker
1608+
1609+用于:
1610+
1611+- 阅读上下文
1612+- 修改代码
1613+- 输出总结
1614+
1615+### Shell Worker
1616+
1617+用于:
1618+
1619+- 测试
1620+- lint
1621+- 文件检查
1622+- build 命令
1623+
1624+### Git Worker
1625+
1626+用于:
1627+
1628+- 建分支
1629+- commit
1630+- push
1631+- 重置 per-task worktree
1632+
1633+## 18. Codex Worker 合约
1634+
1635+## 18.1 关键约束
1636+
1637+Codex 不以进程恢复为目标。
1638+
1639+系统恢复的是:
1640+
1641+- task
1642+- step
1643+- 最新 checkpoint
1644+
1645+系统不恢复:
1646+
1647+- 已死亡 Codex 进程内部的精确推理状态
1648+
1649+## 18.2 Codex Step 行为
1650+
1651+对于 Codex step:
1652+
1653+- conductor 准备 prompt 与上下文文件
1654+- conductor 启动 Codex
1655+- Codex 把输出写入本地日志
1656+- conductor 周期性抓取 checkpoint
1657+- step 完成后 Codex 退出
1658+
1659+## 18.3 为什么这比当前 `baa-hand` 的 Codex 模式更好
1660+
1661+当前 `baa-hand` 的 Codex 集成会一直等到进程退出,然后才返回输出。
1662+
1663+对于 durable worker 系统,这不够,因为:
1664+
1665+- 它不会把进度持续写成 durable 状态
1666+- 它不支持 checkpoint 恢复
1667+- 它没有把 task 状态和进程状态分离
1668+
1669+因此:
1670+
1671+- 当前 `baa-hand` 的 Codex 路径继续保留给临时用途
1672+- 生产自动化要走 conductor 自己持有的 Codex worker 路径
1673+
1674+## 18.4 Codex 结果 Schema
1675+
1676+推荐最终 step 输出:
1677+
1678+```json
1679+{
1680+ "ok": true,
1681+ "summary": "Implemented scheduler claim loop and added tests.",
1682+ "needs_human": false,
1683+ "blocked": false,
1684+ "artifacts": [],
1685+ "metrics": {
1686+ "duration_sec": 812
1687+ }
1688+}
1689+```
1690+
1691+可选 hint:
1692+
1693+```json
1694+{
1695+ "ok": false,
1696+ "summary": "Need failing test output from package runtime.",
1697+ "blocked": true,
1698+ "suggested_followup": [
1699+ {
1700+ "step_name": "collect_runtime_test_output",
1701+ "step_kind": "shell"
1702+ }
1703+ ]
1704+}
1705+```
1706+
1707+这些 hint 只是 proposal,不是直接状态修改。
1708+
1709+## 19. 日志模型
1710+
1711+## 19.1 本地完整日志
1712+
1713+每个 run 都有一个本地目录:
1714+
1715+```text
1716+<repo>/.baa-conductor/runs/<task-id>/<run-id>/
1717+ meta.json
1718+ worker.log
1719+ stdout.log
1720+ stderr.log
1721+ checkpoints/
1722+ 0001-summary.json
1723+ 0002-git-diff.patch
1724+```
1725+
1726+本地完整日志是详细取证记录。
1727+
1728+## 19.2 D1 日志索引
1729+
1730+D1 中只存:
1731+
1732+- run 元数据
1733+- 生命周期事件
1734+- 周期性 tail chunk
1735+- 最新 checkpoint 指针
1736+
1737+不应该把全部字节都长期塞进 D1。
1738+
1739+## 19.3 日志事件
1740+
1741+关键生命周期事件包括:
1742+
1743+- task created
1744+- planner selected
1745+- plan accepted
1746+- step claimed
1747+- worker started
1748+- checkpoint persisted
1749+- timeout
1750+- step done
1751+- task done
1752+- failover occurred
1753+
1754+## 20. Checkpoint 模型
1755+
1756+这是稳定性的关键部分。
1757+
1758+## 20.1 为什么需要 Checkpoint
1759+
1760+如果 Codex 改了 20 分钟代码,机器突然死掉,只按 step 边界恢复,就会丢掉所有未提交进度。
1761+
1762+因此,长 AI step 需要 step 中途 checkpoint。
1763+
1764+## 20.2 Checkpoint 类型
1765+
1766+推荐类型:
1767+
1768+- `summary`
1769+- `git_diff`
1770+- `log_tail`
1771+- `test_output`
1772+
1773+## 20.3 Git Diff Checkpoint
1774+
1775+对于 Codex 编辑类 step,conductor 应周期性快照:
1776+
1777+- `git status --short`
1778+- `git diff --binary`
1779+- 可选 `git diff --stat`
1780+
1781+推荐频率:
1782+
1783+- 每 30 到 60 秒一次
1784+- 或在检测到有意义的文件变化后执行
1785+
1786+## 20.4 Checkpoint 存储策略
1787+
1788+MVP 期的策略:
1789+
1790+- 最新 checkpoint summary 存入 D1
1791+- patch 文本大小合理时,一并写入 D1
1792+- 完整 checkpoint 文件保存在本地
1793+
1794+如果 patch 太大:
1795+
1796+- D1 里只保留截断 summary
1797+- 完整 patch 只保留在本地
1798+- 后续可选接入 R2
1799+
1800+## 20.5 从 Checkpoint 恢复
1801+
1802+如果 mini 在执行 Codex step 时宕机:
1803+
1804+1. mac 成为 leader
1805+2. mac 发现 run lease 已过期
1806+3. mac 创建新的 worktree
1807+4. mac checkout task branch 或 base ref
1808+5. 如果存在 checkpointed diff,则回放最新 diff
1809+6. 从最近一个可接受 checkpoint 继续,或直接重跑该 step
1810+
1811+这不是 bit-perfect 的进程恢复。
1812+
1813+这是 durable 的 task 恢复,并支持部分 patch 回放。
1814+
1815+## 21. Timeout 与 Retry 规则
1816+
1817+## 21.1 Timeout 所有权
1818+
1819+timeout 由 conductor 负责。
1820+
1821+worker 不对整个 task 自己做 timeout 判定。
1822+
1823+## 21.2 进程处理
1824+
1825+推荐升级顺序:
1826+
1827+1. 发送 `SIGTERM`
1828+2. 等待 5 秒
1829+3. 发送 `SIGKILL`
1830+4. 标记 run 为 `timeout`
1831+5. 根据策略决定 retry 或 fail
1832+
1833+## 21.3 Retry 规则
1834+
1835+推荐默认值:
1836+
1837+- planner steps: 1 次重试
1838+- codex inspect steps: 1 次重试
1839+- codex implement steps: 1 次重试
1840+- test steps: 基础设施失败时 1 次重试,确定性失败时 0 次
1841+- git commit/push: 默认 0 次,除非 push 因瞬时网络原因失败
1842+
1843+## 21.4 失败分类
1844+
1845+需要明确区分:
1846+
1847+- `timeout`
1848+- `worker_crash`
1849+- `infra_unreachable`
1850+- `deterministic_failure`
1851+- `blocked`
1852+
1853+这个分类会直接影响 retry 策略。
1854+
1855+## 22. Pause、Drain 与 Resume
1856+
1857+## 22.1 全局模式
1858+
1859+全局自动化模式:
1860+
1861+- `running`
1862+- `draining`
1863+- `paused`
1864+
1865+## 22.2 语义
1866+
1867+### `running`
1868+
1869+- 正常运行
1870+
1871+### `draining`
1872+
1873+- 不再启动新的 step
1874+- 已启动的 run 可以自然结束
1875+- 适合计划内切换或维护
1876+
1877+### `paused`
1878+
1879+- 不再启动新的 step
1880+- conductor 调度暂停
1881+- 已运行任务是否继续或终止,由策略决定
1882+
1883+## 22.3 真相来源
1884+
1885+全局模式存储在 D1 的 `system_state` 中。
1886+
1887+浏览器 UI 只是控制面板,不是真相来源。
1888+
1889+## 23. Firefox 插件集成
1890+
1891+## 23.1 浏览器职责
1892+
1893+Firefox 插件应负责:
1894+
1895+- 展示当前全局自动化模式
1896+- 提供 `pause`、`resume`,可选 `drain`
1897+- 展示当前 leader host
1898+- 如有需要,展示队列深度与 active task 数
1899+
1900+## 23.2 浏览器控制流
1901+
1902+可见的 `control` 对话仍然只用于人机交互。
1903+
1904+插件不应混用:
1905+
1906+- 你的交互式 `control` 对话
1907+- 自动化 task dispatch
1908+
1909+推荐模型:
1910+
1911+- 一个可见 `control` 对话
1912+- 一个隐藏 `dispatch` 通道
1913+
1914+## 23.3 浏览器按钮
1915+
1916+最少需要这些按钮:
1917+
1918+- `Pause`
1919+- `Resume`
1920+- 可选 `Drain`
1921+
1922+按钮应调用:
1923+
1924+- `POST /v1/system/pause`
1925+- `POST /v1/system/resume`
1926+- `POST /v1/system/drain`
1927+
1928+## 23.4 浏览器状态显示
1929+
1930+最低显示项:
1931+
1932+- 当前 mode
1933+- 当前 leader
1934+- active runs
1935+- queued tasks
1936+
1937+## 24. `baa-hand` 与 `baa-shell` 集成
1938+
1939+## 24.1 `baa-hand`
1940+
1941+`baa-hand` 适用于:
1942+
1943+- planner 调用
1944+- review 调用
1945+- 非 durable 的临时 AI 工作
1946+
1947+推荐:
1948+
1949+- planner 与 review steps 调 `led.makefile.so`
1950+- execution steps 不依赖当前 `baa-hand` 的 Codex 模式来保证 durability
1951+
1952+## 24.2 `baa-shell`
1953+
1954+`baa-shell` 适用于:
1955+
1956+- 当 HTTP 比 SSH 更方便时,执行远程 shell
1957+- 让外部自动化读写文件
1958+- 提供状态页面
1959+
1960+对于同机操作,本地 conductor 代码依然可以直接用 local exec。
1961+
1962+## 25. mini 与 mac 上的本地目录布局
1963+
1964+推荐根路径:
1965+
1966+```text
1967+/Users/george/code/baa-conductor
1968+```
1969+
1970+推荐 runtime 目录:
1971+
1972+```text
1973+/Users/george/code/baa-conductor/state/
1974+/Users/george/code/baa-conductor/runs/
1975+/Users/george/code/baa-conductor/worktrees/
1976+/Users/george/code/baa-conductor/logs/
1977+/Users/george/code/baa-conductor/tmp/
1978+```
1979+
1980+推荐的 per-run 目录:
1981+
1982+```text
1983+/Users/george/code/baa-conductor/runs/<task-id>/<run-id>/
1984+ meta.json
1985+ state.json
1986+ stdout.log
1987+ stderr.log
1988+ worker.log
1989+ checkpoints/
1990+ artifacts/
1991+```
1992+
1993+推荐的 worktree 目录:
1994+
1995+```text
1996+/Users/george/code/baa-conductor/worktrees/<task-id>/
1997+```
1998+
1999+规则:
2000+
2001+- 一个 task 恰好一个 worktree
2002+- worktree 路径应确定性
2003+- 陈旧 worktree 只能由 conductor 清理,worker 不得自行清理
2004+
2005+## 25.1 本地文件约定
2006+
2007+建议由 conductor 写入的文件:
2008+
2009+- `meta.json`: 不变的 run 元数据
2010+- `state.json`: 当前本地状态镜像
2011+- `worker.log`: 当前 run 的 conductor 生命周期消息
2012+- `stdout.log`: worker 原始 stdout
2013+- `stderr.log`: worker 原始 stderr
2014+
2015+建议作为 checkpoint 写入的文件:
2016+
2017+- `checkpoints/0001-summary.json`
2018+- `checkpoints/0002-git-diff.patch`
2019+- `checkpoints/0003-test-output.txt`
2020+
2021+## 26. 建议的仓库结构
2022+
2023+此仓库最终建议长成这样:
2024+
2025+```text
2026+apps/
2027+ control-api-worker/
2028+ conductor-daemon/
2029+ status-api/
2030+ worker-runner/
2031+packages/
2032+ db/
2033+ planner/
2034+ schemas/
2035+ step-templates/
2036+ logging/
2037+ git-tools/
2038+ checkpointing/
2039+ops/
2040+ nginx/
2041+ sql/
2042+ launchd/
2043+ scripts/
2044+docs/
2045+ decisions/
2046+```
2047+
2048+## 26.1 建议最先创建的文件
2049+
2050+为了方便并行开发,初始仓库至少应包含:
2051+
2052+```text
2053+README.md
2054+DESIGN.md
2055+ops/sql/schema.sql
2056+ops/sql/migrations/0001_init.sql
2057+ops/nginx/baa-conductor.conf
2058+ops/launchd/so.makefile.baa-conductor.plist
2059+ops/launchd/so.makefile.baa-worker-runner.plist
2060+apps/control-api-worker/src/index.ts
2061+apps/conductor-daemon/src/index.ts
2062+apps/worker-runner/src/index.ts
2063+packages/schemas/src/index.ts
2064+packages/db/src/index.ts
2065+packages/planner/src/index.ts
2066+packages/step-templates/src/index.ts
2067+packages/logging/src/index.ts
2068+packages/checkpointing/src/index.ts
2069+```
2070+
2071+## 27. mini 与 mac 上的 launchd
2072+
2073+因为 mini 和 mac 都是 macOS,推荐用 `launchd` 做进程守护。
2074+
2075+建议服务:
2076+
2077+- `so.makefile.baa-conductor`
2078+- `so.makefile.baa-worker-runner`
2079+- 可选 `so.makefile.baa-status-api`
2080+
2081+建议行为:
2082+
2083+- 开机或登录自动启动
2084+- 失败自动拉起
2085+- 日志写文件
2086+
2087+conductor 的示例参数:
2088+
2089+```xml
2090+<array>
2091+ <string>/usr/bin/env</string>
2092+ <string>node</string>
2093+ <string>/Users/george/code/baa-conductor/apps/conductor-daemon/dist/index.js</string>
2094+ <string>--host</string>
2095+ <string>mini</string>
2096+ <string>--role</string>
2097+ <string>primary</string>
2098+</array>
2099+```
2100+
2101+## 27.1 建议的 launchd 路径
2102+
2103+推荐安装路径:
2104+
2105+- repo 中的 plist 源文件:`ops/launchd/*.plist`
2106+- 用户级安装路径:`~/Library/LaunchAgents/`
2107+- 如果需要开机登录前启动,可改用系统级 `/Library/LaunchDaemons/`
2108+
2109+推荐 label:
2110+
2111+- `so.makefile.baa-conductor`
2112+- `so.makefile.baa-worker-runner`
2113+- `so.makefile.baa-status-api`
2114+
2115+## 27.2 环境变量
2116+
2117+建议 conductor 与 worker 服务使用:
2118+
2119+```text
2120+BAA_CONDUCTOR_HOST=mini
2121+BAA_CONDUCTOR_ROLE=primary
2122+BAA_CONTROL_API_BASE=https://control-api.makefile.so
2123+BAA_CONDUCTOR_LOCAL_API=http://127.0.0.1:4317
2124+BAA_RUNS_DIR=/Users/george/code/baa-conductor/runs
2125+BAA_WORKTREES_DIR=/Users/george/code/baa-conductor/worktrees
2126+BAA_LOGS_DIR=/Users/george/code/baa-conductor/logs
2127+BAA_TMP_DIR=/Users/george/code/baa-conductor/tmp
2128+BAA_NODE_ID=mini-main
2129+BAA_SHARED_TOKEN=replace-me
2130+```
2131+
2132+mac 上至少需要不同的变量:
2133+
2134+- `BAA_CONDUCTOR_HOST`
2135+- `BAA_CONDUCTOR_ROLE`
2136+- `BAA_NODE_ID`
2137+
2138+## 28. 安全模型
2139+
2140+## 28.1 Control API
2141+
2142+- conductor 和 worker 调用必须鉴权
2143+- pause、resume、drain、promote 等操作需要更强的 admin 鉴权
2144+
2145+## 28.2 直连节点域名
2146+
2147+- `mini-conductor.makefile.so` 和 `mac-conductor.makefile.so` 必须受保护
2148+- 使用 Basic Auth、IP allowlist 或两者同时使用
2149+
2150+## 28.3 日志
2151+
2152+日志中可能包含:
2153+
2154+- prompts
2155+- diffs
2156+- 文件路径
2157+- 测试输出
2158+
2159+规则:
2160+
2161+- 尽可能做 secrets 脱敏
2162+- 不公开暴露原始日志
2163+
2164+## 29. 可观测性
2165+
2166+每个 conductor 应暴露:
2167+
2168+- `/healthz`
2169+- `/readyz`
2170+- `/rolez`
2171+- 如果后续需要,可加 `/metrics`
2172+
2173+有用的状态字段:
2174+
2175+- controller id
2176+- role
2177+- lease holder
2178+- lease expiry
2179+- active runs
2180+- queue depth
2181+- paused mode
2182+
2183+## 30. 故障场景
2184+
2185+## 30.1 Mini 在空闲状态下死亡
2186+
2187+- lease 过期
2188+- mac 抢到 lease
2189+- 队列继续运行
2190+
2191+## 30.2 Mini 在 Codex Step 中死亡
2192+
2193+- run lease 过期
2194+- mac 成为 leader
2195+- mac 读取最新 checkpoint
2196+- mac 回放 checkpoint 或直接重跑该 step
2197+
2198+## 30.3 Mac 在 Standby 状态下死亡
2199+
2200+- 不影响服务
2201+
2202+## 30.4 D1 暂时不可用
2203+
2204+leader 应:
2205+
2206+- 停止 claim 新工作
2207+- 在安全前提下继续监管本地正在运行的 worker
2208+- 持续尝试重连
2209+- 在控制面未确认前,不进行不安全写入
2210+
2211+## 30.5 浏览器 UI 失效
2212+
2213+- 自动化仍然继续
2214+- 只是人类控制面临时不可用
2215+
2216+## 30.6 VPS 死亡
2217+
2218+如果 VPS 掉了:
2219+
2220+- 公网入口失效
2221+- 公网域名无法再直接做运维操作
2222+- 只要 mini 与 mac 还能访问 `control-api.makefile.so`,内部调度仍可继续
2223+
2224+推荐处理:
2225+
2226+- 不要因为公网入口消失就主动停掉 worker 集群
2227+- 单独修复 VPS
2228+
2229+## 30.7 Cloudflare Worker 或 D1 退化
2230+
2231+如果 control API 不可用:
2232+
2233+- leaders 停止 claim 新工作
2234+- 已在本地运行的 steps 可以在安全前提下继续
2235+- 不应盲目提交不可逆的调度状态
2236+
2237+恢复规则:
2238+
2239+- control API 恢复后,conductors 要把本地 run 状态与 D1 重新对账
2240+
2241+## 31. 建议的 API 返回码
2242+
2243+- `200` 正常读取
2244+- `202` 接受异步创建
2245+- `409` 节点健康但不是 leader,不能执行写操作
2246+- `423` task 或 step 被 lease 锁住
2247+- `503` 控制平面退化
2248+
2249+## 32. 建议的初始端口
2250+
2251+以下只是建议值:
2252+
2253+- conductor 本地 HTTP API: `4317`
2254+- status API: `4318`
2255+- 可选 metrics: `4319`
2256+
2257+## 33. 时序流程
2258+
2259+## 33.1 Task 创建流程
2260+
2261+1. human 在可见 Claude `control` 中下指令
2262+2. `control` 通过 control API 创建 task
2263+3. leader conductor 取到 task
2264+4. leader 选择模板或 planner
2265+5. 持久化 plan
2266+6. steps 进入 runnable
2267+
2268+## 33.2 Step 执行流程
2269+
2270+1. leader claim 下一个 runnable step
2271+2. leader 分配 worker 与 worktree
2272+3. worker 启动
2273+4. worker 把日志持续写到本地
2274+5. conductor 写 heartbeat 与 checkpoints
2275+6. worker 退出
2276+7. conductor 把 step 标记为 complete 或 failed
2277+
2278+## 33.3 Failover 流程
2279+
2280+1. mini lease 过期
2281+2. mac 获取 leadership
2282+3. mac 扫描未完成 steps
2283+4. mac 基于 checkpoints 恢复 runnable 工作
2284+
2285+## 33.4 人工 Pause 与接管流程
2286+
2287+1. 人在 Firefox 插件或状态页点击 `Pause`
2288+2. 浏览器调用 `POST /v1/system/pause`
2289+3. control API 更新 D1 中的 `system_state`
2290+4. 当前 leader 观察到 mode 变化
2291+5. leader 停止启动新的 steps
2292+6. 人开始和可见 Claude `control` 对话
2293+7. 人点击 `Resume`
2294+8. leader 恢复调度
2295+
2296+## 34. 建议的 MVP 范围
2297+
2298+第一阶段至少要包含:
2299+
2300+1. 仓库骨架
2301+2. D1 schema 与 migration scripts
2302+3. control API Worker
2303+4. conductor lease loop
2304+5. task 与 step 持久化
2305+6. 一个带本地日志流的 Codex step runner
2306+7. 一个 shell step runner
2307+8. pause、resume、drain
2308+9. mini -> mac 的基础 failover
2309+10. Firefox 插件按钮接到 control API
2310+
2311+## 35. 建议的并行开发拆分
2312+
2313+这一节的目的就是让多个 Codex worker 立刻开始并行干活。
2314+
2315+每个 task 只能有一个分支和一个 owner。
2316+
2317+## 35.0 协作规则
2318+
2319+这些规则在多 Codex 并行时必须遵守。
2320+
2321+### 分支规则
2322+
2323+- 一个 task 等于一个 branch
2324+- 一个 branch 同时只允许一个 owner
2325+- branch 名由 conductor 或 task 创建方分配
2326+
2327+### Worktree 规则
2328+
2329+- 一个 task 等于一个 worktree
2330+- 不允许两个 worker 共用一个活跃 worktree
2331+
2332+### Write Scope 规则
2333+
2334+每个 task 都必须声明 `write_scope`。
2335+
2336+如果需要修改超出 `write_scope` 的文件:
2337+
2338+- 要么新建 task
2339+- 要么显式修订 scope
2340+
2341+### 全局真相规则
2342+
2343+- task 真相在 D1
2344+- 本地真相在 run 目录
2345+- 对话记录不是 task 真相
2346+
2347+### 热点文件规则
2348+
2349+以下内容应视为协调敏感文件:
2350+
2351+- 根级 lockfile
2352+- 全局 CI 配置
2353+- 共享 schema 文件
2354+- 一旦开始实现后,这份 `DESIGN.md` 本身
2355+
2356+对热点文件的修改应串行化,或提前明确指派。
2357+
2358+## 35.0.1 Task Card 模板
2359+
2360+未来仓库中,每个并行 task 都应该有一个任务卡,例如:
2361+
2362+```md
2363+---
2364+task_id: T-004
2365+title: Conductor lease and heartbeat loop
2366+owner: codex-mini-01
2367+branch: feat/T-004-conductor-lease
2368+repo: /Users/george/code/baa-conductor
2369+base_ref: main
2370+write_scope:
2371+ - apps/conductor-daemon/**
2372+ - packages/db/**
2373+depends_on:
2374+ - T-002
2375+ - T-003
2376+acceptance:
2377+ - leader lease acquisition works
2378+ - standby nodes do not schedule
2379+ - lease renewal tests added
2380+status: in_progress
2381+---
2382+```
2383+
2384+## 35.0.2 完成报告模板
2385+
2386+task 完成后,worker 应至少回报:
2387+
2388+```md
2389+task_id: T-004
2390+branch: feat/T-004-conductor-lease
2391+base_ref: <commit>
2392+files_changed:
2393+ - apps/conductor-daemon/src/index.ts
2394+ - packages/db/src/lease.ts
2395+commands_run:
2396+ - pnpm test
2397+result:
2398+ - passed targeted tests
2399+risks:
2400+ - none
2401+```
2402+
2403+## 35.1 Task A: 仓库骨架
2404+
2405+Branch:
2406+
2407+- `feat/T-001-repo-scaffold`
2408+
2409+Scope:
2410+
2411+- 仓库目录结构
2412+- package manager 配置
2413+- 根配置
2414+- 初始 README
2415+
2416+不要改:
2417+
2418+- D1 SQL
2419+- Nginx 配置
2420+- Firefox 插件
2421+
2422+## 35.2 Task B: D1 Schema 与 Migrations
2423+
2424+Branch:
2425+
2426+- `feat/T-002-d1-schema`
2427+
2428+Scope:
2429+
2430+- `ops/sql/**`
2431+- `packages/db/**`
2432+
2433+不要改:
2434+
2435+- conductor runtime
2436+- worker runner
2437+
2438+## 35.3 Task C: Control API Worker
2439+
2440+Branch:
2441+
2442+- `feat/T-003-control-api`
2443+
2444+Scope:
2445+
2446+- `apps/control-api-worker/**`
2447+
2448+依赖:
2449+
2450+- Task B
2451+
2452+## 35.4 Task D: Conductor Lease 与 Heartbeat Loop
2453+
2454+Branch:
2455+
2456+- `feat/T-004-conductor-lease`
2457+
2458+Scope:
2459+
2460+- `apps/conductor-daemon/**`
2461+- `packages/db/**`
2462+
2463+依赖:
2464+
2465+- Task B
2466+- Task C
2467+
2468+## 35.5 Task E: Worker Runner 与本地日志流
2469+
2470+Branch:
2471+
2472+- `feat/T-005-worker-runner`
2473+
2474+Scope:
2475+
2476+- `apps/worker-runner/**`
2477+- `packages/logging/**`
2478+
2479+不要改:
2480+
2481+- Nginx
2482+- Firefox 插件
2483+
2484+## 35.6 Task F: Checkpoint 与 Git Diff Snapshots
2485+
2486+Branch:
2487+
2488+- `feat/T-006-checkpointing`
2489+
2490+Scope:
2491+
2492+- `packages/checkpointing/**`
2493+- worker-runner 集成
2494+
2495+依赖:
2496+
2497+- Task E
2498+
2499+## 35.7 Task G: Planner 抽象与模板
2500+
2501+Branch:
2502+
2503+- `feat/T-007-planner`
2504+
2505+Scope:
2506+
2507+- `packages/planner/**`
2508+- `packages/step-templates/**`
2509+
2510+## 35.8 Task H: Nginx 与 VPS 运维
2511+
2512+Branch:
2513+
2514+- `feat/T-008-ops-nginx`
2515+
2516+Scope:
2517+
2518+- `ops/nginx/**`
2519+- 部署文档
2520+
2521+## 35.9 Task I: Firefox 插件 Pause 与 Resume
2522+
2523+Branch:
2524+
2525+- `feat/T-009-firefox-pause`
2526+
2527+Scope:
2528+
2529+- Firefox 插件集成文档
2530+- 浏览器状态协议
2531+
2532+这部分可以落在独立仓库里,但协议应该在这里先定义清楚。
2533+
2534+## 35.10 Task J: Status API 与基础 UI
2535+
2536+Branch:
2537+
2538+- `feat/T-010-status-api`
2539+
2540+Scope:
2541+
2542+- `apps/status-api/**`
2543+
2544+## 35.11 Task K: launchd 与本地 Runtime 布局
2545+
2546+Branch:
2547+
2548+- `feat/T-011-launchd-runtime`
2549+
2550+Scope:
2551+
2552+- `ops/launchd/**`
2553+- 本地 runtime 路径文档
2554+
2555+## 35.12 Task L: 鉴权与 Token 模型
2556+
2557+Branch:
2558+
2559+- `feat/T-012-auth-model`
2560+
2561+Scope:
2562+
2563+- worker 鉴权设计
2564+- token 校验中间件
2565+- 角色定义
2566+
2567+## 36. Runbook
2568+
2569+这一节故意写成运维操作说明,是真正出问题时要照着用的。
2570+
2571+## 36.1 初始部署 Runbook
2572+
2573+1. 部署 Cloudflare Worker 与 D1
2574+2. 创建 D1 schema
2575+3. 部署 `control-api.makefile.so`
2576+4. 配置 DNS 记录
2577+5. 在 VPS 安装 Nginx 配置
2578+6. 验证 `mini-conductor.makefile.so` 与 `mac-conductor.makefile.so`
2579+7. 在 mini 安装 launchd 服务
2580+8. 在 mac 安装 launchd 服务
2581+9. 启动 mini conductor 并确认拿到 lease
2582+10. 启动 mac conductor 并确认处于 standby
2583+11. 验证浏览器 `Pause` 与 `Resume`
2584+
2585+## 36.2 计划内切换 Runbook
2586+
2587+从 mini 安全切到 mac:
2588+
2589+1. 把全局模式设为 `draining`
2590+2. 等活跃 runs 正常结束或至少完成 checkpoint
2591+3. 确认 mini 没有未收口的可变工作
2592+4. demote mini
2593+5. promote mac,或让 mac 自然获取 lease
2594+6. 确认 mac 的 `GET /rolez` 返回 `leader`
2595+7. 把全局模式改回 `running`
2596+
2597+## 36.3 紧急故障切换 Runbook
2598+
2599+如果 mini 突然挂掉:
2600+
2601+1. 确认 mini heartbeat 已过期
2602+2. 等 lease 超时
2603+3. 确认 mac 已获取 lease
2604+4. 查看未完成 runs
2605+5. 基于 checkpoints 恢复或重跑当前 steps
2606+
2607+## 36.4 切回 Mini 的 Runbook
2608+
2609+mini 恢复后:
2610+
2611+1. 先让 mini 继续保持 standby
2612+2. 修复原始故障
2613+3. 选择一个低峰窗口
2614+4. 把系统设为 `draining`
2615+5. drain mac
2616+6. promote mini
2617+7. 确认 mini 成为 leader
2618+8. 把系统切回 `running`
2619+
2620+## 37. 接下来立刻要做的事
2621+
2622+下一波开发建议按这个顺序推进:
2623+
2624+1. 创建代码骨架
2625+2. 实现 D1 schema
2626+3. 实现 control API
2627+4. 实现 conductor lease
2628+5. 实现一个 worker runner
2629+6. 实现 checkpoints
2630+7. 接上 Firefox 的 pause 与 resume
2631+8. 补上 VPS Nginx 配置
2632+
2633+做到这里,这套系统就能真正支撑多 Codex 并行执行。
2634+
2635+## 38. 总结
2636+
2637+这份设计刻意把以下东西拆开:
2638+
2639+- 人类对话
2640+- 自动化对话
2641+- 规划
2642+- 编排
2643+- 执行
2644+
2645+durable 核心是:
2646+
2647+- D1 存共享真相
2648+- conductor 做确定性编排
2649+- Codex 做短生命周期 step worker
2650+- 本地日志加周期性 checkpoint
2651+- mini 做首选 leader,mac 做 standby
2652+
2653+后续所有实现都应该以这个模型为准。
+84,
-0
1@@ -0,0 +1,84 @@
2+# baa-conductor
3+
4+`baa-conductor` 是新的 AI 执行编排仓库。
5+
6+它的目标不是提供单个 AI 会话,而是提供一套可以让 `mini` 主控、`mac` 备主、多个 Codex worker 并行工作的稳定基础设施。
7+
8+当前仓库状态:
9+
10+- 设计文档已落在 [`DESIGN.md`](./DESIGN.md)
11+- 代码目录骨架已建立
12+- 具体功能大多仍为占位实现
13+- 并行任务文档已放在 [`coordination/`](./coordination/)
14+
15+## 先读什么
16+
17+每个 Codex 实例启动后,按这个顺序读:
18+
19+1. [`DESIGN.md`](./DESIGN.md)
20+2. [`coordination/TASK_OVERVIEW.md`](./coordination/TASK_OVERVIEW.md)
21+3. 自己的任务卡,例如 `coordination/tasks/T-004-conductor-lease.md`
22+
23+## 当前目录结构
24+
25+```text
26+apps/
27+ control-api-worker/
28+ conductor-daemon/
29+ status-api/
30+ worker-runner/
31+packages/
32+ auth/
33+ checkpointing/
34+ db/
35+ git-tools/
36+ logging/
37+ planner/
38+ schemas/
39+ step-templates/
40+ops/
41+ launchd/
42+ nginx/
43+ sql/
44+coordination/
45+ STATUS_SUMMARY.md
46+ TASK_OVERVIEW.md
47+ WORKFLOW.md
48+ tasks/
49+docs/
50+ auth/
51+ decisions/
52+ firefox/
53+ ops/
54+ runtime/
55+```
56+
57+## 当前约定
58+
59+- 一个任务一个分支
60+- 一个任务一个 worktree
61+- 一个任务一个任务卡
62+- 任务真相靠任务卡与 D1,不靠聊天记录
63+- 各 Codex 只更新自己的任务卡
64+- 汇总状态由整合者更新到 `coordination/STATUS_SUMMARY.md`
65+
66+## 现在可以做什么
67+
68+当前最适合的工作方式:
69+
70+1. 由整合者分配 `coordination/tasks/` 里的任务
71+2. 每个 Codex 建自己的分支与 worktree
72+3. 在声明好的 `write_scope` 内开发
73+4. 完成后更新任务卡中的状态、命令、风险和交付物
74+5. 由整合者统一汇总和集成
75+
76+## 非目标
77+
78+当前骨架阶段不追求:
79+
80+- 已完成的生产功能
81+- 完整测试覆盖
82+- 完整 control API
83+- 完整的 D1 接入
84+
85+这些都将在后续并行任务中完成。
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/conductor-daemon",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+36,
-0
1@@ -0,0 +1,36 @@
2+export type ConductorRole = "primary" | "standby";
3+export type LeaseState = "leader" | "standby" | "degraded";
4+
5+export interface ConductorConfig {
6+ nodeId: string;
7+ host: string;
8+ role: ConductorRole;
9+ controlApiBase: string;
10+}
11+
12+export interface StartupChecklistItem {
13+ key: string;
14+ description: string;
15+}
16+
17+export class ConductorDaemon {
18+ constructor(private readonly config: ConductorConfig) {}
19+
20+ getStartupChecklist(): StartupChecklistItem[] {
21+ return [
22+ { key: "register-controller", description: "注册 controller 心跳" },
23+ { key: "acquire-lease", description: "尝试获取或续租 leader lease" },
24+ { key: "load-runs", description: "扫描本地 run 目录并恢复状态" },
25+ { key: "start-scheduler", description: "启动 scheduler loop" }
26+ ];
27+ }
28+
29+ describeIdentity(): string {
30+ return `${this.config.nodeId}@${this.config.host}(${this.config.role})`;
31+ }
32+
33+ async start(): Promise<LeaseState> {
34+ return "standby";
35+ }
36+}
37+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/control-api-worker",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+46,
-0
1@@ -0,0 +1,46 @@
2+export type ControlApiRouteMethod = "GET" | "POST";
3+
4+export interface ControlApiRoute {
5+ method: ControlApiRouteMethod;
6+ path: string;
7+ summary: string;
8+}
9+
10+export interface ControlApiRequestContext {
11+ requestId: string;
12+ actorRole: "controller" | "worker" | "browser_admin" | "ops_admin" | "readonly";
13+}
14+
15+export interface ControlApiResponseShape {
16+ ok: boolean;
17+ status: number;
18+ error?: string;
19+ message: string;
20+}
21+
22+export const CONTROL_API_ROUTES: ControlApiRoute[] = [
23+ { method: "POST", path: "/v1/controllers/heartbeat", summary: "controller 心跳" },
24+ { method: "POST", path: "/v1/leader/acquire", summary: "获取或续租 leader lease" },
25+ { method: "POST", path: "/v1/tasks", summary: "创建 task" },
26+ { method: "POST", path: "/v1/tasks/claim", summary: "claim task 或 step" },
27+ { method: "POST", path: "/v1/system/pause", summary: "暂停自动化" },
28+ { method: "POST", path: "/v1/system/resume", summary: "恢复自动化" },
29+ { method: "POST", path: "/v1/system/drain", summary: "drain 自动化" },
30+ { method: "GET", path: "/v1/system/state", summary: "读取系统状态" }
31+];
32+
33+export function describeControlApiSurface(): string[] {
34+ return CONTROL_API_ROUTES.map((route) => `${route.method} ${route.path} - ${route.summary}`);
35+}
36+
37+export async function handleControlApiRequest(
38+ _context: ControlApiRequestContext
39+): Promise<ControlApiResponseShape> {
40+ return {
41+ ok: false,
42+ status: 501,
43+ error: "not_implemented",
44+ message: "Control API Worker 目前只有骨架,具体逻辑待后续任务实现。"
45+ };
46+}
47+
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/status-api",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+20,
-0
1@@ -0,0 +1,20 @@
2+export type AutomationMode = "running" | "draining" | "paused";
3+
4+export interface StatusSnapshot {
5+ leaderHost: string | null;
6+ mode: AutomationMode;
7+ queueDepth: number;
8+ activeRuns: number;
9+ updatedAt: string;
10+}
11+
12+export function createEmptyStatusSnapshot(): StatusSnapshot {
13+ return {
14+ leaderHost: null,
15+ mode: "paused",
16+ queueDepth: 0,
17+ activeRuns: 0,
18+ updatedAt: "1970-01-01T00:00:00.000Z"
19+ };
20+}
21+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/worker-runner",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+26,
-0
1@@ -0,0 +1,26 @@
2+export type WorkerKind = "codex" | "shell" | "git";
3+
4+export interface StepExecutionRequest {
5+ taskId: string;
6+ stepId: string;
7+ stepName: string;
8+ workerKind: WorkerKind;
9+ logDir: string;
10+}
11+
12+export interface StepExecutionResult {
13+ ok: boolean;
14+ summary: string;
15+ checkpointCount: number;
16+ logsPath: string;
17+}
18+
19+export async function runStep(request: StepExecutionRequest): Promise<StepExecutionResult> {
20+ return {
21+ ok: false,
22+ summary: `Step ${request.stepId} 仅有骨架,尚未接入真实 ${request.workerKind} worker。`,
23+ checkpointCount: 0,
24+ logsPath: request.logDir
25+ };
26+}
27+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+43,
-0
1@@ -0,0 +1,43 @@
2+# 全局状态汇总
3+
4+由整合者维护。
5+
6+## 当前时间
7+
8+- `2026-03-21`
9+
10+## 总览
11+
12+- `done`: 1
13+- `in_progress`: 0
14+- `blocked`: 0
15+- `todo`: 11
16+
17+## 已完成
18+
19+- `T-001` 仓库骨架
20+
21+## 第一波建议并行任务
22+
23+- `T-002`
24+- `T-005`
25+- `T-007`
26+- `T-008`
27+- `T-009`
28+- `T-011`
29+- `T-012`
30+
31+## 需要整合者关注的点
32+
33+- `apps/conductor-daemon/**` 和 `packages/db/**` 后续会形成热点区域
34+- `README.md`、`DESIGN.md`、根配置文件应避免多人同时修改
35+- `ops/nginx/**`、`ops/launchd/**`、`docs/firefox/**`、`docs/auth/**` 可以相对独立推进
36+
37+## 后续汇总规则
38+
39+每次整合时,建议至少更新:
40+
41+- 已完成任务
42+- 当前进行中任务
43+- 阻塞原因
44+- 下一波建议分发
+70,
-0
1@@ -0,0 +1,70 @@
2+# 任务总览
3+
4+这是当前 `baa-conductor` 仓库的并行任务总览。
5+
6+## 1. 当前仓库基线
7+
8+当前已经完成的基础工作:
9+
10+- 设计文档 [`../DESIGN.md`](../DESIGN.md) 已落地
11+- 仓库目录骨架已建立
12+- `apps/`、`packages/`、`ops/`、`coordination/` 基础占位代码与文件已创建
13+
14+这意味着:
15+
16+- `T-001` 视为已完成
17+- 其他任务可以在当前骨架上并行开展
18+
19+## 2. 启动前必读
20+
21+每个 Codex 实例必须先读:
22+
23+1. [`../DESIGN.md`](../DESIGN.md)
24+2. [`WORKFLOW.md`](./WORKFLOW.md)
25+3. 自己的任务卡
26+
27+## 3. 第一波可并行启动任务
28+
29+下面这些任务现在就可以同时开工:
30+
31+- `T-002` D1 Schema 与 Migrations
32+- `T-005` Worker Runner 与本地日志流
33+- `T-007` Planner 抽象与模板
34+- `T-008` Nginx 与 VPS 运维
35+- `T-009` Firefox 插件 Pause/Resume 协议
36+- `T-011` launchd 与本地 Runtime 布局
37+- `T-012` 鉴权与 Token 模型
38+
39+这些任务之间的写入范围基本独立,适合第一波并发。
40+
41+## 4. 第二波任务
42+
43+这些任务需要第一波部分结果后再接:
44+
45+- `T-003` Control API Worker
46+- `T-004` Conductor Lease 与 Heartbeat
47+- `T-006` Checkpoint 与 Git Diff Snapshots
48+- `T-010` Status API 与基础 UI
49+
50+## 5. 任务矩阵
51+
52+| Task | 标题 | 状态 | 分支 | 依赖 | 主要写入范围 |
53+| --- | --- | --- | --- | --- | --- |
54+| `T-001` | 仓库骨架 | `done` | `feat/T-001-repo-scaffold` | 无 | 根配置、`apps/`、`packages/`、`ops/` |
55+| `T-002` | D1 Schema 与 Migrations | `todo` | `feat/T-002-d1-schema` | `T-001` | `ops/sql/**`, `packages/db/**` |
56+| `T-003` | Control API Worker | `todo` | `feat/T-003-control-api` | `T-002` | `apps/control-api-worker/**` |
57+| `T-004` | Conductor Lease 与 Heartbeat | `todo` | `feat/T-004-conductor-lease` | `T-002`, `T-003` | `apps/conductor-daemon/**`, `packages/db/**` |
58+| `T-005` | Worker Runner 与本地日志流 | `todo` | `feat/T-005-worker-runner` | `T-001` | `apps/worker-runner/**`, `packages/logging/**` |
59+| `T-006` | Checkpoint 与 Git Diff Snapshots | `todo` | `feat/T-006-checkpointing` | `T-005` | `packages/checkpointing/**`, `apps/worker-runner/**` |
60+| `T-007` | Planner 抽象与模板 | `todo` | `feat/T-007-planner` | `T-001` | `packages/planner/**`, `packages/step-templates/**` |
61+| `T-008` | Nginx 与 VPS 运维 | `todo` | `feat/T-008-ops-nginx` | `T-001` | `ops/nginx/**`, `docs/ops/**` |
62+| `T-009` | Firefox 插件 Pause/Resume 协议 | `todo` | `feat/T-009-firefox-pause` | `T-001` | `docs/firefox/**` |
63+| `T-010` | Status API 与基础 UI | `todo` | `feat/T-010-status-api` | `T-003`, `T-004` | `apps/status-api/**` |
64+| `T-011` | launchd 与 Runtime 布局 | `todo` | `feat/T-011-launchd-runtime` | `T-001` | `ops/launchd/**`, `docs/runtime/**` |
65+| `T-012` | 鉴权与 Token 模型 | `todo` | `feat/T-012-auth-model` | `T-001` | `packages/auth/**`, `docs/auth/**` |
66+
67+## 6. 汇总方式
68+
69+- 每个任务的详细状态在对应任务卡中
70+- 全局汇总在 [`STATUS_SUMMARY.md`](./STATUS_SUMMARY.md)
71+- 如需新任务,按现有模板新增到 `coordination/tasks/`
+88,
-0
1@@ -0,0 +1,88 @@
2+# 协作工作流
3+
4+这份文档定义多个 Codex 实例同时开发时的最小协作规则。
5+
6+## 1. 每个实例启动后要读什么
7+
8+按顺序阅读:
9+
10+1. [`../DESIGN.md`](../DESIGN.md)
11+2. [`TASK_OVERVIEW.md`](./TASK_OVERVIEW.md)
12+3. 自己的任务卡,例如 `tasks/T-004-conductor-lease.md`
13+
14+## 2. 每个实例开始前要做什么
15+
16+1. 在自己的任务卡里填写或更新:
17+ - `owner`
18+ - `status`
19+ - `base_ref`
20+ - `updated_at`
21+2. 建自己的 branch
22+3. 建自己的 worktree
23+4. 只在 `write_scope` 内开发
24+
25+## 3. 每个实例开发时的规则
26+
27+- 只改自己的任务卡
28+- 不改别人的任务卡
29+- 不把聊天内容当作任务真相
30+- 不和其他任务共用 worktree
31+- 不随手改超出 `write_scope` 的文件
32+
33+如果确实需要越界:
34+
35+- 先在任务卡里记录原因
36+- 再由整合者决定是扩 scope 还是拆新任务
37+
38+## 4. 每个实例完成后要做什么
39+
40+任务完成后,必须在自己的任务卡中更新:
41+
42+- `status`
43+- `updated_at`
44+- `files_changed`
45+- `commands_run`
46+- `result`
47+- `risks`
48+- `next_handoff`
49+
50+## 5. 汇总规则
51+
52+- 各个 worker 只更新自己的任务卡
53+- 全局状态板由整合者更新
54+- 如果出现冲突,以任务卡和代码为准
55+
56+## 6. 推荐的分支与 worktree 模式
57+
58+建议:
59+
60+```bash
61+git worktree add ../baa-conductor-T004 -b feat/T-004-conductor-lease main
62+```
63+
64+对应关系:
65+
66+- 一个 task
67+- 一个 branch
68+- 一个 worktree
69+- 一个 owner
70+
71+## 7. 当前状态字段约定
72+
73+任务卡 frontmatter 的 `status` 统一使用:
74+
75+- `todo`
76+- `in_progress`
77+- `blocked`
78+- `review`
79+- `done`
80+
81+## 8. 当前 owner 字段约定
82+
83+`owner` 建议使用稳定字符串,例如:
84+
85+- `codex-mini-01`
86+- `codex-mac-02`
87+- `claude-dispatch`
88+- `main-rollout`
89+
1@@ -0,0 +1,89 @@
2+---
3+task_id: T-001
4+title: 仓库骨架
5+status: done
6+owner: main-rollout
7+branch: feat/T-001-repo-scaffold
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on: []
11+write_scope:
12+ - .gitignore
13+ - README.md
14+ - package.json
15+ - pnpm-workspace.yaml
16+ - tsconfig.base.json
17+ - apps/**
18+ - packages/**
19+ - ops/**
20+ - coordination/**
21+ - docs/**
22+updated_at: 2026-03-21
23+---
24+
25+# T-001 仓库骨架
26+
27+## 目标
28+
29+建立 `baa-conductor` 的基础目录、根配置、占位代码与协作文档结构,让后续多个任务可以并行展开。
30+
31+## 本任务包含
32+
33+- 创建根级工作区配置
34+- 创建 `apps/`、`packages/`、`ops/`、`docs/`、`coordination/` 基础目录
35+- 创建最小占位代码文件
36+- 创建任务总览与协作规则
37+
38+## 本任务不包含
39+
40+- 真实 D1 逻辑
41+- 真实 conductor 调度逻辑
42+- 真实 worker 执行逻辑
43+- 真实 Nginx 部署配置
44+
45+## 建议起始文件
46+
47+- 已完成
48+
49+## 交付物
50+
51+- 初始仓库骨架
52+- 协作任务体系
53+
54+## 验收
55+
56+- 目录结构完整
57+- 协作文档可读
58+- 后续任务可以直接接着写
59+
60+## files_changed
61+
62+- 根配置文件
63+- `apps/**`
64+- `packages/**`
65+- `ops/**`
66+- `coordination/**`
67+- `docs/decisions/README.md`
68+
69+## commands_run
70+
71+- 未执行功能构建
72+
73+## result
74+
75+- 骨架已建立,可供并行任务继续开发
76+
77+## risks
78+
79+- 目前大部分代码是占位实现
80+
81+## next_handoff
82+
83+- 开始分发第一波并行任务
84+
85+## progress_log
86+
87+- `2026-03-21`: 初始化新仓库
88+- `2026-03-21`: 写入设计文档
89+- `2026-03-21`: 创建代码骨架与协作文档结构
90+
+85,
-0
1@@ -0,0 +1,85 @@
2+---
3+task_id: T-002
4+title: D1 Schema 与 Migrations
5+status: todo
6+owner: unassigned
7+branch: feat/T-002-d1-schema
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-001
12+write_scope:
13+ - ops/sql/**
14+ - packages/db/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-002 D1 Schema 与 Migrations
19+
20+## 目标
21+
22+把 `DESIGN.md` 第 10 节中的 D1 schema 真正落成可执行 SQL,并建立 migration 约定与基础数据库访问层。
23+
24+## 本任务包含
25+
26+- 完成 `ops/sql/schema.sql`
27+- 完成 `ops/sql/migrations/0001_init.sql`
28+- 在 `packages/db/` 建立最小数据库访问抽象
29+- 明确表、索引、初始化状态写法
30+
31+## 本任务不包含
32+
33+- Cloudflare Worker 路由逻辑
34+- conductor lease loop
35+- worker runtime
36+
37+## 建议起始文件
38+
39+- `ops/sql/schema.sql`
40+- `ops/sql/migrations/0001_init.sql`
41+- `packages/db/src/index.ts`
42+- `DESIGN.md`
43+
44+## 交付物
45+
46+- 完整 schema SQL
47+- 初始 migration
48+- `packages/db` 的最小接口和模型定义
49+
50+## 验收
51+
52+- 包含 `leader_lease`
53+- 包含 `controllers`
54+- 包含 `workers`
55+- 包含 `tasks`
56+- 包含 `task_steps`
57+- 包含 `task_runs`
58+- 包含 `task_checkpoints`
59+- 包含 `task_logs`
60+- 包含 `system_state`
61+- 包含 `task_artifacts`
62+
63+## files_changed
64+
65+- 待填写
66+
67+## commands_run
68+
69+- 待填写
70+
71+## result
72+
73+- 待填写
74+
75+## risks
76+
77+- 待填写
78+
79+## next_handoff
80+
81+- 给 `T-003` 和 `T-004` 提供稳定的数据模型基线
82+
83+## progress_log
84+
85+- `2026-03-21`: 创建任务卡
86+
+73,
-0
1@@ -0,0 +1,73 @@
2+---
3+task_id: T-003
4+title: Control API Worker
5+status: todo
6+owner: unassigned
7+branch: feat/T-003-control-api
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-002
12+write_scope:
13+ - apps/control-api-worker/**
14+updated_at: 2026-03-21
15+---
16+
17+# T-003 Control API Worker
18+
19+## 目标
20+
21+实现 Cloudflare Worker 风格的 control API 骨架,把设计中的关键接口落成代码结构。
22+
23+## 本任务包含
24+
25+- 明确路由注册方式
26+- 为核心接口建立 handler 骨架
27+- 整理 request/response schema
28+- 预留 D1 绑定与鉴权挂载点
29+
30+## 本任务不包含
31+
32+- 完整 lease 算法
33+- 真实 worker 调度
34+- 前端页面
35+
36+## 建议起始文件
37+
38+- `apps/control-api-worker/src/index.ts`
39+- `DESIGN.md` 第 11 节
40+
41+## 交付物
42+
43+- 可扩展的 Worker 路由骨架
44+- 核心接口清单与占位 handler
45+
46+## 验收
47+
48+- 至少包含 heartbeat、acquire、tasks、steps、system state 路由骨架
49+- 结构上便于后续接 D1
50+
51+## files_changed
52+
53+- 待填写
54+
55+## commands_run
56+
57+- 待填写
58+
59+## result
60+
61+- 待填写
62+
63+## risks
64+
65+- 待填写
66+
67+## next_handoff
68+
69+- 供 `T-004` 和 `T-010` 接入
70+
71+## progress_log
72+
73+- `2026-03-21`: 创建任务卡
74+
1@@ -0,0 +1,77 @@
2+---
3+task_id: T-004
4+title: Conductor Lease 与 Heartbeat
5+status: todo
6+owner: unassigned
7+branch: feat/T-004-conductor-lease
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-002
12+ - T-003
13+write_scope:
14+ - apps/conductor-daemon/**
15+ - packages/db/**
16+updated_at: 2026-03-21
17+---
18+
19+# T-004 Conductor Lease 与 Heartbeat
20+
21+## 目标
22+
23+实现 conductor 的主备基础行为:注册、heartbeat、lease 获取与续租、standby 拒绝调度。
24+
25+## 本任务包含
26+
27+- conductor startup checklist 落代码
28+- leader/standby 状态骨架
29+- heartbeat loop 骨架
30+- acquire/renew lease 调用路径
31+
32+## 本任务不包含
33+
34+- 完整 step 调度
35+- worker 实际执行
36+- checkpoint 恢复
37+
38+## 建议起始文件
39+
40+- `apps/conductor-daemon/src/index.ts`
41+- `packages/db/src/index.ts`
42+- `DESIGN.md` 第 8、16 节
43+
44+## 交付物
45+
46+- conductor 基础状态机骨架
47+- leader 与 standby 的最小分支逻辑
48+
49+## 验收
50+
51+- 能表达 leader/standby/degraded
52+- 能区分 acquire 与 renew
53+- 结构上支持后续 scheduler 接入
54+
55+## files_changed
56+
57+- 待填写
58+
59+## commands_run
60+
61+- 待填写
62+
63+## result
64+
65+- 待填写
66+
67+## risks
68+
69+- 待填写
70+
71+## next_handoff
72+
73+- 给后续 scheduler 和 status API 提供主备状态基线
74+
75+## progress_log
76+
77+- `2026-03-21`: 创建任务卡
78+
1@@ -0,0 +1,74 @@
2+---
3+task_id: T-005
4+title: Worker Runner 与本地日志流
5+status: todo
6+owner: unassigned
7+branch: feat/T-005-worker-runner
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-001
12+write_scope:
13+ - apps/worker-runner/**
14+ - packages/logging/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-005 Worker Runner 与本地日志流
19+
20+## 目标
21+
22+建立 worker-runner 的最小执行框架,使其能围绕一个 step 管理本地日志、结果结构和执行骨架。
23+
24+## 本任务包含
25+
26+- `worker-runner` 的 step request/result 结构
27+- 本地 stdout/stderr/worker.log 组织方式
28+- logging 包中的基础数据结构
29+
30+## 本任务不包含
31+
32+- checkpoint diff 逻辑
33+- 真实 Codex 集成细节
34+- control API 上报
35+
36+## 建议起始文件
37+
38+- `apps/worker-runner/src/index.ts`
39+- `packages/logging/src/index.ts`
40+- `DESIGN.md` 第 17、19 节
41+
42+## 交付物
43+
44+- worker-runner 框架骨架
45+- logging 包基础结构
46+
47+## 验收
48+
49+- 能表达 step request/result
50+- 能表达本地日志目录与事件模型
51+
52+## files_changed
53+
54+- 待填写
55+
56+## commands_run
57+
58+- 待填写
59+
60+## result
61+
62+- 待填写
63+
64+## risks
65+
66+- 待填写
67+
68+## next_handoff
69+
70+- 给 `T-006` 提供 checkpoint 接入点
71+
72+## progress_log
73+
74+- `2026-03-21`: 创建任务卡
75+
1@@ -0,0 +1,74 @@
2+---
3+task_id: T-006
4+title: Checkpoint 与 Git Diff Snapshots
5+status: todo
6+owner: unassigned
7+branch: feat/T-006-checkpointing
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-005
12+write_scope:
13+ - packages/checkpointing/**
14+ - apps/worker-runner/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-006 Checkpoint 与 Git Diff Snapshots
19+
20+## 目标
21+
22+把设计中的 checkpoint 概念落到代码结构里,尤其是 `summary`、`git_diff`、`log_tail` 这些中途恢复能力。
23+
24+## 本任务包含
25+
26+- checkpoint 类型定义
27+- checkpoint 文件命名
28+- worker-runner 的 checkpoint 接口预留
29+- git diff checkpoint 的骨架接口
30+
31+## 本任务不包含
32+
33+- 完整 Git 命令执行器
34+- 完整 D1 上报细节
35+
36+## 建议起始文件
37+
38+- `packages/checkpointing/src/index.ts`
39+- `apps/worker-runner/src/index.ts`
40+- `DESIGN.md` 第 20 节
41+
42+## 交付物
43+
44+- checkpoint 数据结构与最小接口
45+- worker-runner 中的 checkpoint 接口位置
46+
47+## 验收
48+
49+- 至少支持 `summary`、`git_diff`、`log_tail`
50+- 结构上支持未来的 patch 回放
51+
52+## files_changed
53+
54+- 待填写
55+
56+## commands_run
57+
58+- 待填写
59+
60+## result
61+
62+- 待填写
63+
64+## risks
65+
66+- 待填写
67+
68+## next_handoff
69+
70+- 给 failover 恢复实现提供基础接口
71+
72+## progress_log
73+
74+- `2026-03-21`: 创建任务卡
75+
+74,
-0
1@@ -0,0 +1,74 @@
2+---
3+task_id: T-007
4+title: Planner 抽象与模板
5+status: todo
6+owner: unassigned
7+branch: feat/T-007-planner
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-001
12+write_scope:
13+ - packages/planner/**
14+ - packages/step-templates/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-007 Planner 抽象与模板
19+
20+## 目标
21+
22+把 `planner` 作为抽象角色落到代码骨架里,并把设计中的模板任务拆分成可复用的 step template。
23+
24+## 本任务包含
25+
26+- `Planner` 接口整理
27+- `ProposedPlan` 与 `ProposedStep` 数据结构
28+- `feature_impl`、`bugfix` 等模板的完善
29+
30+## 本任务不包含
31+
32+- 调真实 Claude 或 Codex
33+- conductor 的最终 plan 验收逻辑
34+
35+## 建议起始文件
36+
37+- `packages/planner/src/index.ts`
38+- `packages/step-templates/src/index.ts`
39+- `DESIGN.md` 第 14、15 节
40+
41+## 交付物
42+
43+- planner 抽象层
44+- step template 定义
45+
46+## 验收
47+
48+- 模板命名清晰
49+- step kind 与设计文档一致
50+- 后续可直接被 conductor 调用
51+
52+## files_changed
53+
54+- 待填写
55+
56+## commands_run
57+
58+- 待填写
59+
60+## result
61+
62+- 待填写
63+
64+## risks
65+
66+- 待填写
67+
68+## next_handoff
69+
70+- 给 conductor 调用 planner 提供稳定接口
71+
72+## progress_log
73+
74+- `2026-03-21`: 创建任务卡
75+
+77,
-0
1@@ -0,0 +1,77 @@
2+---
3+task_id: T-008
4+title: Nginx 与 VPS 运维
5+status: todo
6+owner: unassigned
7+branch: feat/T-008-ops-nginx
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-001
12+write_scope:
13+ - ops/nginx/**
14+ - docs/ops/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-008 Nginx 与 VPS 运维
19+
20+## 目标
21+
22+把设计里的二级域名、Nginx 转发、直连节点域名、Basic Auth 与运维说明落到可执行配置和文档中。
23+
24+## 本任务包含
25+
26+- 完成 `ops/nginx/baa-conductor.conf`
27+- 补充 `includes/` 公共片段
28+- 编写 VPS 部署说明
29+- 明确 `conductor.makefile.so`、`mini-conductor.makefile.so`、`mac-conductor.makefile.so` 的配置关系
30+
31+## 本任务不包含
32+
33+- Cloudflare Worker 代码
34+- launchd
35+
36+## 建议起始文件
37+
38+- `ops/nginx/baa-conductor.conf`
39+- `ops/nginx/includes/common-proxy.conf`
40+- `ops/nginx/includes/direct-node-auth.conf`
41+- `docs/ops/README.md`
42+- `DESIGN.md` 第 6、7 节
43+
44+## 交付物
45+
46+- 可执行或接近可执行的 Nginx 配置
47+- VPS 运维说明
48+
49+## 验收
50+
51+- 包含统一入口
52+- 包含直达 mini/mac
53+- 包含 80 -> 443
54+- 包含鉴权或保护建议
55+
56+## files_changed
57+
58+- 待填写
59+
60+## commands_run
61+
62+- 待填写
63+
64+## result
65+
66+- 待填写
67+
68+## risks
69+
70+- 待填写
71+
72+## next_handoff
73+
74+- 提供给后续真实部署
75+
76+## progress_log
77+
78+- `2026-03-21`: 创建任务卡
1@@ -0,0 +1,70 @@
2+---
3+task_id: T-009
4+title: Firefox 插件 Pause 与 Resume 协议
5+status: todo
6+owner: unassigned
7+branch: feat/T-009-firefox-pause
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-001
12+write_scope:
13+ - docs/firefox/**
14+updated_at: 2026-03-21
15+---
16+
17+# T-009 Firefox 插件 Pause 与 Resume 协议
18+
19+## 目标
20+
21+先在本仓库中明确 Firefox 插件与 conductor control API 之间的协议,方便后续在插件仓库同步实现。
22+
23+## 本任务包含
24+
25+- 定义浏览器按钮行为
26+- 定义状态读取字段
27+- 定义 `pause`、`resume`、`drain` 的请求格式
28+- 定义可见 `control` 与隐藏 `dispatch` 的职责边界
29+
30+## 本任务不包含
31+
32+- 直接修改 Firefox 插件仓库代码
33+- 完整 UI 实现
34+
35+## 建议起始文件
36+
37+- `DESIGN.md` 第 22、23 节
38+- `docs/firefox/README.md`
39+
40+## 交付物
41+
42+- 一份清晰的浏览器协议说明文档
43+
44+## 验收
45+
46+- 能明确告诉插件仓库该怎么接 control API
47+- 能明确区分 control 与 dispatch
48+
49+## files_changed
50+
51+- 待填写
52+
53+## commands_run
54+
55+- 待填写
56+
57+## result
58+
59+- 待填写
60+
61+## risks
62+
63+- 待填写
64+
65+## next_handoff
66+
67+- 给 `baa-firefox` 仓库实现方使用
68+
69+## progress_log
70+
71+- `2026-03-21`: 创建任务卡
+71,
-0
1@@ -0,0 +1,71 @@
2+---
3+task_id: T-010
4+title: Status API 与基础 UI
5+status: todo
6+owner: unassigned
7+branch: feat/T-010-status-api
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-003
12+ - T-004
13+write_scope:
14+ - apps/status-api/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-010 Status API 与基础 UI
19+
20+## 目标
21+
22+实现最小状态读取层,让人和浏览器能读取 leader、mode、queue depth、active runs 等关键信息。
23+
24+## 本任务包含
25+
26+- status snapshot 结构完善
27+- 状态读取接口骨架
28+- 基础 UI 或最小渲染接口预留
29+
30+## 本任务不包含
31+
32+- 完整管理后台
33+- 真实图表
34+
35+## 建议起始文件
36+
37+- `apps/status-api/src/index.ts`
38+- `DESIGN.md` 第 23、29 节
39+
40+## 交付物
41+
42+- status API 骨架
43+- 最小状态结构
44+
45+## 验收
46+
47+- 至少能表达 mode、leader、queue depth、active runs
48+
49+## files_changed
50+
51+- 待填写
52+
53+## commands_run
54+
55+- 待填写
56+
57+## result
58+
59+- 待填写
60+
61+## risks
62+
63+- 待填写
64+
65+## next_handoff
66+
67+- 提供给浏览器控制面或 CLI 面板使用
68+
69+## progress_log
70+
71+- `2026-03-21`: 创建任务卡
72+
1@@ -0,0 +1,72 @@
2+---
3+task_id: T-011
4+title: launchd 与本地 Runtime 布局
5+status: todo
6+owner: unassigned
7+branch: feat/T-011-launchd-runtime
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-001
12+write_scope:
13+ - ops/launchd/**
14+ - docs/runtime/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-011 launchd 与本地 Runtime 布局
19+
20+## 目标
21+
22+把 mini 与 mac 上的 launchd 配置、运行目录创建方式、环境变量与安装步骤整理清楚。
23+
24+## 本任务包含
25+
26+- 完善 `ops/launchd/*.plist`
27+- 说明 `~/Library/LaunchAgents` 与 `/Library/LaunchDaemons` 的差异
28+- 说明 `runs/`、`worktrees/`、`logs/`、`tmp/` 的初始化方式
29+
30+## 本任务不包含
31+
32+- 真实业务逻辑
33+- D1 schema
34+
35+## 建议起始文件
36+
37+- `ops/launchd/*.plist`
38+- `docs/runtime/README.md`
39+- `DESIGN.md` 第 25、27 节
40+
41+## 交付物
42+
43+- launchd 配置模板
44+- 本地 runtime 初始化说明
45+
46+## 验收
47+
48+- 能明确 mini 与 mac 分别怎么安装
49+- 能明确需要哪些环境变量
50+
51+## files_changed
52+
53+- 待填写
54+
55+## commands_run
56+
57+- 待填写
58+
59+## result
60+
61+- 待填写
62+
63+## risks
64+
65+- 待填写
66+
67+## next_handoff
68+
69+- 给真实部署阶段使用
70+
71+## progress_log
72+
73+- `2026-03-21`: 创建任务卡
+74,
-0
1@@ -0,0 +1,74 @@
2+---
3+task_id: T-012
4+title: 鉴权与 Token 模型
5+status: todo
6+owner: unassigned
7+branch: feat/T-012-auth-model
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on:
11+ - T-001
12+write_scope:
13+ - packages/auth/**
14+ - docs/auth/**
15+updated_at: 2026-03-21
16+---
17+
18+# T-012 鉴权与 Token 模型
19+
20+## 目标
21+
22+把设计中的角色、token、权限边界落实成清晰的鉴权模型和代码挂载点。
23+
24+## 本任务包含
25+
26+- 角色定义
27+- token 形式说明
28+- middleware 或校验器骨架
29+- `controller`、`worker`、`browser_admin`、`ops_admin`、`readonly` 的权限矩阵
30+
31+## 本任务不包含
32+
33+- 完整生产级 secrets 管理
34+- 外部身份供应商接入
35+
36+## 建议起始文件
37+
38+- `packages/auth/src/index.ts`
39+- `docs/auth/README.md`
40+- `DESIGN.md` 第 11.3、11.4、28 节
41+
42+## 交付物
43+
44+- 鉴权设计文档
45+- 代码侧鉴权挂载点
46+
47+## 验收
48+
49+- 角色边界清晰
50+- 写接口与读接口权限可区分
51+- 浏览器控制与 controller 调度权限可区分
52+
53+## files_changed
54+
55+- 待填写
56+
57+## commands_run
58+
59+- 待填写
60+
61+## result
62+
63+- 待填写
64+
65+## risks
66+
67+- 待填写
68+
69+## next_handoff
70+
71+- 给 control API 实现与部署配置使用
72+
73+## progress_log
74+
75+- `2026-03-21`: 创建任务卡
+74,
-0
1@@ -0,0 +1,74 @@
2+---
3+task_id: T-XXX
4+title: 任务标题
5+status: todo
6+owner: unassigned
7+branch: feat/T-XXX-short-name
8+repo: /Users/george/code/baa-conductor
9+base_ref: main
10+depends_on: []
11+write_scope: []
12+updated_at: 2026-03-21
13+---
14+
15+# 任务标题
16+
17+## 目标
18+
19+TODO
20+
21+## 本任务包含
22+
23+- TODO
24+
25+## 本任务不包含
26+
27+- TODO
28+
29+## 建议起始文件
30+
31+- TODO
32+
33+## 交付物
34+
35+- TODO
36+
37+## 验收
38+
39+- TODO
40+
41+## 更新要求
42+
43+完成时更新 frontmatter 的:
44+
45+- `status`
46+- `owner`
47+- `base_ref`
48+- `updated_at`
49+
50+并补充下面这些内容:
51+
52+## files_changed
53+
54+- TODO
55+
56+## commands_run
57+
58+- TODO
59+
60+## result
61+
62+- TODO
63+
64+## risks
65+
66+- TODO
67+
68+## next_handoff
69+
70+- TODO
71+
72+## progress_log
73+
74+- `2026-03-21`: 创建任务卡
75+
+6,
-0
1@@ -0,0 +1,6 @@
2+# auth
3+
4+这个目录预留给鉴权与 token 模型文档。
5+
6+当前只建立目录边界,具体内容由 `T-012` 完成。
7+
+12,
-0
1@@ -0,0 +1,12 @@
2+# decisions
3+
4+这个目录用于后续放 ADR 或设计决策记录。
5+
6+建议命名:
7+
8+- `0001-control-api-auth.md`
9+- `0002-failover-policy.md`
10+- `0003-checkpoint-storage.md`
11+
12+当前阶段先保留目录与约定,不写具体决策内容。
13+
+6,
-0
1@@ -0,0 +1,6 @@
2+# firefox
3+
4+这个目录预留给 Firefox 插件与 conductor 之间的协议说明。
5+
6+当前只建立目录边界,具体内容由 `T-009` 完成。
7+
+6,
-0
1@@ -0,0 +1,6 @@
2+# ops
3+
4+这个目录预留给 VPS、Nginx、部署和运维说明。
5+
6+当前只建立目录边界,具体内容由 `T-008` 完成。
7+
+6,
-0
1@@ -0,0 +1,6 @@
2+# runtime
3+
4+这个目录预留给 launchd、运行目录、环境变量和本地节点初始化说明。
5+
6+当前只建立目录边界,具体内容由 `T-011` 完成。
7+
1@@ -0,0 +1,23 @@
2+<?xml version="1.0" encoding="UTF-8"?>
3+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4+<plist version="1.0">
5+ <dict>
6+ <key>Label</key>
7+ <string>so.makefile.baa-conductor</string>
8+ <key>ProgramArguments</key>
9+ <array>
10+ <string>/usr/bin/env</string>
11+ <string>node</string>
12+ <string>/Users/george/code/baa-conductor/apps/conductor-daemon/dist/index.js</string>
13+ <string>--host</string>
14+ <string>mini</string>
15+ <string>--role</string>
16+ <string>primary</string>
17+ </array>
18+ <key>RunAtLoad</key>
19+ <true/>
20+ <key>KeepAlive</key>
21+ <true/>
22+ </dict>
23+</plist>
24+
1@@ -0,0 +1,19 @@
2+<?xml version="1.0" encoding="UTF-8"?>
3+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4+<plist version="1.0">
5+ <dict>
6+ <key>Label</key>
7+ <string>so.makefile.baa-status-api</string>
8+ <key>ProgramArguments</key>
9+ <array>
10+ <string>/usr/bin/env</string>
11+ <string>node</string>
12+ <string>/Users/george/code/baa-conductor/apps/status-api/dist/index.js</string>
13+ </array>
14+ <key>RunAtLoad</key>
15+ <true/>
16+ <key>KeepAlive</key>
17+ <true/>
18+ </dict>
19+</plist>
20+
1@@ -0,0 +1,19 @@
2+<?xml version="1.0" encoding="UTF-8"?>
3+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4+<plist version="1.0">
5+ <dict>
6+ <key>Label</key>
7+ <string>so.makefile.baa-worker-runner</string>
8+ <key>ProgramArguments</key>
9+ <array>
10+ <string>/usr/bin/env</string>
11+ <string>node</string>
12+ <string>/Users/george/code/baa-conductor/apps/worker-runner/dist/index.js</string>
13+ </array>
14+ <key>RunAtLoad</key>
15+ <true/>
16+ <key>KeepAlive</key>
17+ <true/>
18+ </dict>
19+</plist>
20+
+20,
-0
1@@ -0,0 +1,20 @@
2+# baa-conductor Nginx 配置骨架。
3+# 具体 upstream、server_name、证书路径与鉴权细节由 T-008 补全。
4+
5+upstream conductor_primary {
6+ # TODO: mini 主、mac 备
7+}
8+
9+server {
10+ listen 80;
11+ server_name conductor.makefile.so mini-conductor.makefile.so mac-conductor.makefile.so;
12+ return 301 https://$host$request_uri;
13+}
14+
15+server {
16+ listen 443 ssl http2;
17+ server_name conductor.makefile.so;
18+
19+ # TODO: 补全 TLS 证书与反向代理配置
20+}
21+
+6,
-0
1@@ -0,0 +1,6 @@
2+proxy_http_version 1.1;
3+proxy_set_header Host $host;
4+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
5+proxy_set_header X-Forwarded-Proto $scheme;
6+proxy_set_header X-Request-Id $request_id;
7+
1@@ -0,0 +1,6 @@
2+# 直连节点域名推荐启用的 Basic Auth 配置占位。
3+# 具体账户文件路径由 T-008 确认。
4+
5+auth_basic "Restricted";
6+auth_basic_user_file /etc/nginx/.htpasswd-baa-conductor;
7+
+9,
-0
1@@ -0,0 +1,9 @@
2+-- 初始 migration 占位文件。
3+-- 该文件应在 T-002 中根据 schema.sql 完成。
4+
5+BEGIN TRANSACTION;
6+
7+-- TODO: 初始化 D1 schema
8+
9+COMMIT;
10+
+18,
-0
1@@ -0,0 +1,18 @@
2+-- D1 schema 占位文件。
3+-- 具体实现请按照 DESIGN.md 第 10 节补全。
4+
5+BEGIN TRANSACTION;
6+
7+-- TODO: 创建 leader_lease
8+-- TODO: 创建 controllers
9+-- TODO: 创建 workers
10+-- TODO: 创建 tasks
11+-- TODO: 创建 task_steps
12+-- TODO: 创建 task_runs
13+-- TODO: 创建 task_checkpoints
14+-- TODO: 创建 task_logs
15+-- TODO: 创建 system_state
16+-- TODO: 创建 task_artifacts
17+
18+COMMIT;
19+
+16,
-0
1@@ -0,0 +1,16 @@
2+{
3+ "name": "baa-conductor",
4+ "private": true,
5+ "packageManager": "pnpm@10.6.0",
6+ "scripts": {
7+ "build": "pnpm -r build",
8+ "typecheck": "pnpm -r typecheck",
9+ "lint": "echo 'TODO: add linting'",
10+ "test": "echo 'TODO: add tests'",
11+ "tasks": "ls coordination/tasks"
12+ },
13+ "devDependencies": {
14+ "typescript": "^5.8.2"
15+ }
16+}
17+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/auth",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+24,
-0
1@@ -0,0 +1,24 @@
2+export type AuthRole =
3+ | "controller"
4+ | "worker"
5+ | "browser_admin"
6+ | "ops_admin"
7+ | "readonly";
8+
9+export interface AuthContext {
10+ subject: string;
11+ role: AuthRole;
12+}
13+
14+export interface AuthDecision {
15+ ok: boolean;
16+ reason: string;
17+}
18+
19+export function deny(reason: string): AuthDecision {
20+ return {
21+ ok: false,
22+ reason
23+ };
24+}
25+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/checkpointing",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+20,
-0
1@@ -0,0 +1,20 @@
2+export type CheckpointType = "summary" | "git_diff" | "log_tail" | "test_output";
3+
4+export interface CheckpointRecord {
5+ checkpointId: string;
6+ taskId: string;
7+ stepId: string;
8+ runId: string;
9+ seq: number;
10+ type: CheckpointType;
11+ summary: string;
12+}
13+
14+export interface CheckpointManager {
15+ createSummaryCheckpoint(summary: string): CheckpointRecord;
16+}
17+
18+export function createCheckpointFilename(seq: number, suffix: string): string {
19+ return `${String(seq).padStart(4, "0")}-${suffix}`;
20+}
21+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/db",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+32,
-0
1@@ -0,0 +1,32 @@
2+export interface LeaderLeaseRecord {
3+ leaseName: string;
4+ holderId: string;
5+ holderHost: string;
6+ term: number;
7+ leaseExpiresAt: number;
8+}
9+
10+export interface TaskClaimRequest {
11+ controllerId: string;
12+ host: string;
13+}
14+
15+export interface ControlPlaneRepository {
16+ getCurrentLease(): Promise<LeaderLeaseRecord | null>;
17+ acquireOrRenewLease(controllerId: string, host: string, ttlSec: number): Promise<LeaderLeaseRecord>;
18+ claimNextRunnableStep(request: TaskClaimRequest): Promise<string | null>;
19+}
20+
21+export const D1_TABLES = [
22+ "leader_lease",
23+ "controllers",
24+ "workers",
25+ "tasks",
26+ "task_steps",
27+ "task_runs",
28+ "task_checkpoints",
29+ "task_logs",
30+ "system_state",
31+ "task_artifacts"
32+] as const;
33+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/git-tools",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+21,
-0
1@@ -0,0 +1,21 @@
2+export interface WorktreeSpec {
3+ repoPath: string;
4+ taskId: string;
5+ branchName: string;
6+ baseRef: string;
7+}
8+
9+export interface GitCommandPlan {
10+ description: string;
11+ commands: string[];
12+}
13+
14+export function createWorktreeCommandPlan(spec: WorktreeSpec): GitCommandPlan {
15+ return {
16+ description: `为 ${spec.taskId} 创建 worktree`,
17+ commands: [
18+ `git -C ${spec.repoPath} worktree add ${spec.repoPath}/worktrees/${spec.taskId} -b ${spec.branchName} ${spec.baseRef}`
19+ ]
20+ };
21+}
22+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/logging",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+21,
-0
1@@ -0,0 +1,21 @@
2+export type LogLevel = "debug" | "info" | "warn" | "error";
3+
4+export interface RunEvent {
5+ seq: number;
6+ level: LogLevel;
7+ message: string;
8+ createdAt: string;
9+}
10+
11+export interface BufferedLogState {
12+ lastSeq: number;
13+ buffer: RunEvent[];
14+}
15+
16+export function createBufferedLogState(): BufferedLogState {
17+ return {
18+ lastSeq: 0,
19+ buffer: []
20+ };
21+}
22+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/planner",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+20,
-0
1@@ -0,0 +1,20 @@
2+export type PlanningStrategy = "template_first" | "planner_assisted" | "manual";
3+
4+export interface ProposedStep {
5+ stepName: string;
6+ stepKind: "planner" | "codex" | "shell" | "git" | "review" | "finalize";
7+ timeoutSec: number;
8+ retryLimit: number;
9+}
10+
11+export interface ProposedPlan {
12+ taskType: string;
13+ strategy: PlanningStrategy;
14+ reasoning: string;
15+ steps: ProposedStep[];
16+}
17+
18+export interface Planner {
19+ plan(taskId: string, goal: string): Promise<ProposedPlan>;
20+}
21+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/schemas",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+26,
-0
1@@ -0,0 +1,26 @@
2+export type AutomationMode = "running" | "draining" | "paused";
3+export type TaskStatus = "queued" | "planning" | "running" | "paused" | "done" | "failed" | "canceled";
4+export type StepStatus = "pending" | "running" | "done" | "failed" | "timeout";
5+export type StepKind = "planner" | "codex" | "shell" | "git" | "review" | "finalize";
6+
7+export interface TaskRecord {
8+ taskId: string;
9+ repo: string;
10+ taskType: string;
11+ title: string;
12+ goal: string;
13+ status: TaskStatus;
14+ branchName: string | null;
15+ baseRef: string | null;
16+}
17+
18+export interface StepRecord {
19+ stepId: string;
20+ taskId: string;
21+ stepIndex: number;
22+ stepName: string;
23+ stepKind: StepKind;
24+ status: StepStatus;
25+ timeoutSec: number;
26+}
27+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+9,
-0
1@@ -0,0 +1,9 @@
2+{
3+ "name": "@baa-conductor/step-templates",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "build": "pnpm exec tsc --noEmit -p tsconfig.json",
8+ "typecheck": "pnpm exec tsc --noEmit -p tsconfig.json"
9+ }
10+}
+25,
-0
1@@ -0,0 +1,25 @@
2+export interface TemplateStep {
3+ stepName: string;
4+ stepKind: "planner" | "codex" | "shell" | "git" | "review" | "finalize";
5+}
6+
7+export const STEP_TEMPLATES = {
8+ feature_impl: [
9+ { stepName: "prepare_branch", stepKind: "git" },
10+ { stepName: "inspect_context", stepKind: "codex" },
11+ { stepName: "implement", stepKind: "codex" },
12+ { stepName: "run_tests", stepKind: "shell" },
13+ { stepName: "review_fix", stepKind: "review" },
14+ { stepName: "commit_push", stepKind: "git" },
15+ { stepName: "finalize", stepKind: "finalize" }
16+ ],
17+ bugfix: [
18+ { stepName: "prepare_branch", stepKind: "git" },
19+ { stepName: "reproduce", stepKind: "shell" },
20+ { stepName: "implement_fix", stepKind: "codex" },
21+ { stepName: "run_targeted_tests", stepKind: "shell" },
22+ { stepName: "commit_push", stepKind: "git" },
23+ { stepName: "finalize", stepKind: "finalize" }
24+ ]
25+} as const satisfies Record<string, TemplateStep[]>;
26+
1@@ -0,0 +1,9 @@
2+{
3+ "extends": "../../tsconfig.base.json",
4+ "compilerOptions": {
5+ "rootDir": "src",
6+ "outDir": "dist"
7+ },
8+ "include": ["src/**/*.ts"]
9+}
10+
+48,
-0
1@@ -0,0 +1,48 @@
2+lockfileVersion: '9.0'
3+
4+settings:
5+ autoInstallPeers: true
6+ excludeLinksFromLockfile: false
7+
8+importers:
9+
10+ .:
11+ devDependencies:
12+ typescript:
13+ specifier: ^5.8.2
14+ version: 5.9.3
15+
16+ apps/conductor-daemon: {}
17+
18+ apps/control-api-worker: {}
19+
20+ apps/status-api: {}
21+
22+ apps/worker-runner: {}
23+
24+ packages/auth: {}
25+
26+ packages/checkpointing: {}
27+
28+ packages/db: {}
29+
30+ packages/git-tools: {}
31+
32+ packages/logging: {}
33+
34+ packages/planner: {}
35+
36+ packages/schemas: {}
37+
38+ packages/step-templates: {}
39+
40+packages:
41+
42+ typescript@5.9.3:
43+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
44+ engines: {node: '>=14.17'}
45+ hasBin: true
46+
47+snapshots:
48+
49+ typescript@5.9.3: {}
+4,
-0
1@@ -0,0 +1,4 @@
2+packages:
3+ - apps/*
4+ - packages/*
5+
+14,
-0
1@@ -0,0 +1,14 @@
2+{
3+ "compilerOptions": {
4+ "target": "ES2022",
5+ "module": "ES2022",
6+ "moduleResolution": "Node",
7+ "strict": true,
8+ "noImplicitOverride": true,
9+ "noUncheckedIndexedAccess": true,
10+ "noFallthroughCasesInSwitch": true,
11+ "forceConsistentCasingInFileNames": true,
12+ "skipLibCheck": true
13+ }
14+}
15+