codex@macbookpro
·
2026-03-30
T-S059.md
1# Task T-S059:续命执行任务与运维接口
2
3## 状态
4
5- 当前状态:`已完成`
6- 规模预估:`M`
7- 依赖任务:`T-S055`、`T-S056`、`T-S057`、`T-S058`
8- 建议执行者:`Codex`
9
10## 直接给对话的提示词
11
12读 `/Users/george/code/baa-conductor/tasks/T-S059.md` 任务文档,完成开发任务。
13
14如需补背景,再读:
15
16- `/Users/george/code/baa-conductor/plans/discuss/DISCUSS-TIMED-JOBS-RENEWAL-REQUIREMENTS.md`
17- `/Users/george/code/baa-conductor/tasks/T-S058.md`
18
19## 当前基线
20
21- 仓库:`/Users/george/code/baa-conductor`
22- 分支基线:`main`
23- 提交:`071abbf`
24
25## 分支与 worktree(强制)
26
27- 分支名:`feat/renewal-dispatcher-ops`
28- worktree 路径:`/Users/george/code/baa-conductor-renewal-dispatcher-ops`
29
30开工步骤:
31
321. `cd /Users/george/code/baa-conductor`
332. `git worktree add ../baa-conductor-renewal-dispatcher-ops -b feat/renewal-dispatcher-ops main`
343. `cd ../baa-conductor-renewal-dispatcher-ops`
354. 在这个 worktree 目录里开发,不要回到主仓库目录
36
37完成后提交与推送:
38
391. 在 worktree 里提交所有变更(包括更新后的任务文档)
402. `git push -u origin feat/renewal-dispatcher-ops`
41
42合并步骤(由合并者执行):
43
441. `cd /Users/george/code/baa-conductor`
452. `git fetch origin`
463. `git merge origin/feat/renewal-dispatcher-ops`
474. `git push`
485. `git worktree remove ../baa-conductor-renewal-dispatcher-ops`(如果 worktree 还在)
49
50合并冲突处理:
51
521. 如果 `git merge` 报冲突,先 `git diff` 查看冲突文件
532. 手动解决冲突后 `git add` 冲突文件
543. `git merge --continue` 完成合并
554. 不要用 `git merge --abort` 然后 force 覆盖
56
57命名规则:
58
59- 功能任务分支名以 `feat/` 开头
60- 缺陷任务分支名以 `bug/` 开头
61
62## 目标
63
64实现“续命执行任务”:扫描待执行续命任务,通过浏览器 API 代理发送,并补齐最小运维读接口。
65
66## 背景
67
68在消息已经投影为 `renewal_jobs` 之后,还需要独立 dispatcher 完成:
69
70- 扫描到期任务
71- 执行发送
72- 更新状态
73- 写外部日志
74- 提供最小可排障的读接口
75
76## 涉及仓库
77
78- `/Users/george/code/baa-conductor`
79
80## 范围
81
82- 实现 dispatcher runner
83- 调用现有浏览器 API 代理发送
84- 实现超时、重试、退避和最终失败
85- 提供最小查询接口查看对话和续命任务状态
86
87## 路径约束
88
89- 首版只走 API 代理,不做 GUI / a11y
90- 不直接复用现有 `tasks/runs`
91- 仍然通过 timed-jobs 框架运行
92
93## 推荐实现边界
94
95建议优先做:
96
97- `apps/conductor-daemon/src/timed-jobs/`
98- `apps/conductor-daemon/src/renewal/`
99- `apps/conductor-daemon/src/local-api.ts`
100
101## 允许修改的目录
102
103- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/timed-jobs/`
104- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/renewal/`
105- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/local-api.ts`
106- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/index.ts`
107- `/Users/george/code/baa-conductor/packages/artifact-db/`
108
109## 尽量不要修改
110
111- `/Users/george/code/baa-conductor/plugins/`
112- `/Users/george/code/baa-conductor/packages/d1-client/`
113
114## 必须完成
115
116### 1. dispatcher
117
118- 扫描 `next_attempt_at <= now` 的到期任务
119- 每轮最多处理约 `10` 个任务
120- 调现有浏览器 API 代理发送
121- 成功推进到 `done`
122
123### 2. 超时与重试
124
125- 单次执行有内部超时
126- 失败后更新 `attempt_count`
127- 失败后写 `next_attempt_at`
128- 超过限制后进入 `failed`
129
130### 3. 运维接口与日志
131
132- 提供最小续命任务查询接口
133- 提供最小对话自动化状态查询接口
134- dispatcher 全流程写外部日志
135
136## 需要特别注意
137
138- `paused` 对话不应继续推进待执行任务
139- 执行中的任务允许自然结束,不要求强制中断
140- 发送接口应尽量复用现有内部 service,不要通过 HTTP 自己调自己
141- 所有开发必须在 worktree 中进行,不要在主仓库目录修改代码
142
143## 验收标准
144
145- `pending` 任务可以被 dispatcher 扫到并发送
146- 成功任务进入 `done`
147- 失败任务能重试并最终进入 `failed`
148- dispatcher 执行过程会写外部日志
149- 可以通过本地 API 读取任务和对话状态
150
151## 推荐验证命令
152
153- `cd /Users/george/code/baa-conductor-renewal-dispatcher-ops && pnpm build`
154- `cd /Users/george/code/baa-conductor-renewal-dispatcher-ops && pnpm -C apps/conductor-daemon test`
155
156## 执行记录
157
158> 以下内容由执行任务的 AI 填写,创建任务时留空。
159
160### 开始执行
161
162- 执行者:`Codex`
163- 开始时间:`2026-03-30 16:34:10 CST`
164- 状态变更:`待开始` → `进行中`
165
166### 完成摘要
167
168- 完成时间:`2026-03-30 16:57:56 CST`
169- 状态变更:`进行中` → `已完成`
170- 修改了哪些文件:
171 - `apps/conductor-daemon/src/renewal/dispatcher.ts`
172 - `apps/conductor-daemon/src/renewal/projector.ts`
173 - `apps/conductor-daemon/src/local-api.ts`
174 - `apps/conductor-daemon/src/index.ts`
175 - `apps/conductor-daemon/src/index.test.js`
176 - `docs/api/business-interfaces.md`
177 - `docs/api/control-interfaces.md`
178 - `tasks/T-S059.md`
179- 核心实现思路:
180 - 新增 `renewal.dispatcher` timed-job runner,扫描 `next_attempt_at <= now` 的 `pending` job,执行前重新读取对话自动化状态和 active link,只通过 `browser.proxy_delivery` 发送续命消息
181 - 为 dispatcher 补齐单次执行超时、失败重试、指数退避、最终失败和 `paused/manual` 对话延后重查逻辑,并把完整阶段写入 timed-jobs JSONL 外部日志
182 - 在 `/v1/renewal/` 下补充 `jobs` 列表/详情读接口,保留现有 `conversations` / `links` 作为最小排障读面,同时把 renewal 读写接口写入 API 文档
183 - 顺手收紧 projector 的 route 可用性判断,只为具备真实 `browser.proxy_delivery` tab 目标的对话生成续命任务
184- 跑了哪些测试:
185 - `cd /Users/george/code/baa-conductor-renewal-dispatcher-ops && pnpm install`
186 - `cd /Users/george/code/baa-conductor-renewal-dispatcher-ops && pnpm -C apps/conductor-daemon test`
187 - `cd /Users/george/code/baa-conductor-renewal-dispatcher-ops && pnpm build`
188
189### 执行过程中遇到的问题
190
191- 新 worktree 初始没有安装依赖,第一次 `pnpm -C apps/conductor-daemon test` 失败于 `pnpm exec tsc` 找不到;补跑一次 `pnpm install` 后恢复正常构建和测试流程
192
193### 剩余风险
194
195- 当前 dispatcher 只处理 `pending -> running -> done/failed` 的首版闭环,还没有做“进程意外退出后回收陈旧 running 任务”的恢复逻辑;如果后续需要跨重启补偿,需要再补 stale-running reconcile