im_wower
·
2026-03-29
T-S045.md
1# Task T-S045:串联 artifact-db 写入与 D1 同步队列
2
3## 状态
4
5- 当前状态:`已完成`
6- 规模预估:`S`
7- 依赖任务:`T-S040`、`T-S042`
8- 建议执行者:`Claude`(需要理解 artifact-db store 和 d1-client sync-queue 的交互,在写入路径中精确插入 enqueue 调用)
9
10## 直接给对话的提示词
11
12读 `/Users/george/code/baa-conductor/tasks/T-S045.md` 任务文档,完成开发任务。
13
14如需补背景,再读:
15
16- `/Users/george/code/baa-conductor/plans/ARTIFACT_STATIC_SERVICE.md`
17- `/Users/george/code/baa-conductor/packages/artifact-db/src/store.ts`
18- `/Users/george/code/baa-conductor/packages/d1-client/src/sync-queue.ts`
19- `/Users/george/code/baa-conductor/packages/d1-client/src/sync-worker.ts`
20
21## 当前基线
22
23- 仓库:`/Users/george/code/baa-conductor`
24- 分支基线:`main`
25- 提交:`80ecf7b`
26
27## 分支与 worktree(强制)
28
29每个任务必须使用独立的分支和 worktree,禁止直接在 main 上修改,禁止多个任务共用同一个 worktree。
30
31- 分支名:`feat/artifact-d1-sync`
32- worktree 路径:`/Users/george/code/baa-conductor-artifact-d1-sync`
33
34开工步骤:
35
361. `cd /Users/george/code/baa-conductor`
372. `git worktree add ../baa-conductor-artifact-d1-sync -b feat/artifact-d1-sync main`
383. `cd ../baa-conductor-artifact-d1-sync`
394. 在这个 worktree 目录里开发,不要回到主仓库目录
40
41完成后提交与推送(由执行者完成,不要合并):
42
431. 在 worktree 里提交所有变更(包括更新后的任务文档)
442. `git push -u origin feat/artifact-d1-sync`
45
46## 目标
47
48将 artifact-db 的 insertMessage / insertExecution / upsertSession 写入操作与 D1 同步队列串联,每次本地写入后自动 enqueue 一条同步记录。同时让 sync worker 定期清理已同步的记录。
49
50## 背景
51
52T-S042 实现了 D1 客户端和同步队列,但还没与 artifact-db 的写入路径串联。当前本地写入不会触发 D1 同步。`purgeSynced()` 方法已实现但未被自动调用,长期运行会积累已同步记录。
53
54## 涉及仓库
55
56- `/Users/george/code/baa-conductor`
57
58## 范围
59
60- artifact-db 写入时自动 enqueue 同步记录
61- sync worker 定期清理已同步记录
62- conductor 启动时注入 sync queue 到 artifact store
63
64## 允许修改的目录
65
66- `/Users/george/code/baa-conductor/packages/artifact-db/src/` (store.ts、types.ts)
67- `/Users/george/code/baa-conductor/packages/d1-client/src/` (sync-worker.ts,如需加 purge 调度)
68- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/index.ts` (注入 sync queue)
69
70## 尽量不要修改
71
72- `/Users/george/code/baa-conductor/packages/d1-client/src/client.ts` (D1 客户端已完成)
73- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/local-api.ts`
74- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/instructions/`
75- `/Users/george/code/baa-conductor/plugins/`
76
77## 必须完成
78
79### 1. artifact-db 写入时自动 enqueue
80
81- ArtifactStore 接受可选的 SyncQueue 实例(通过构造函数或 setter 注入)
82- `insertMessage()` 成功后,调用 `syncQueue.enqueue("messages", id, "insert", payload)`
83- `insertExecution()` 成功后,调用 `syncQueue.enqueue("executions", instructionId, "insert", payload)`
84- `upsertSession()` 成功后,调用 `syncQueue.enqueue("sessions", id, "insert", payload)`
85- enqueue 失败只 log,不影响本地写入(与 artifact 写入的 best-effort 原则一致)
86- SyncQueue 未注入时(D1 未配置),跳过 enqueue
87
88### 2. sync worker 定期清理已同步记录
89
90- sync worker 每次扫描完 pending 记录后,调用 `purgeSynced()` 清理已同步记录
91- 保留最近 1000 条 synced 记录(或可配置),删除更早的
92- 清理失败只 log,不影响 sync worker 运行
93
94### 3. conductor 启动时注入
95
96- ConductorRuntime 创建 D1SyncWorker 时,同时获取其 SyncQueue 实例
97- 将 SyncQueue 注入到 ArtifactStore
98- D1 未配置时,ArtifactStore 的 syncQueue 为 null,写入时跳过 enqueue
99
100## 需要特别注意
101
102- enqueue 操作必须在本地 SQLite 事务提交成功之后执行,不能在事务内
103- enqueue 失败不能回滚已成功的本地写入
104- SyncQueue 的 `enqueue()` 写的是 sync_queue 表(d1-client 管理),不是 artifact.db 里的表——注意两个 SQLite 库的区分
105- 所有开发必须在 worktree 中进行,不要在主仓库目录修改代码
106
107## 验收标准
108
109- D1 环境变量设置后,insertMessage 触发 sync_queue 中出现 pending 记录
110- D1 环境变量未设置时,insertMessage 正常工作,无 enqueue 调用
111- sync worker 扫描后,synced 记录被自动清理(保留最近 N 条)
112- enqueue 失败时本地写入不受影响
113- 现有测试全部通过
114
115## 推荐验证命令
116
117- `cd /Users/george/code/baa-conductor-artifact-d1-sync && pnpm build`
118- `cd /Users/george/code/baa-conductor-artifact-d1-sync && pnpm test`
119
120## 执行记录
121
122> 以下内容由执行任务的 AI 填写,创建任务时留空。
123
124### 开始执行
125
126- 执行者:Claude
127- 开始时间:2026-03-29
128- 状态变更:`待开始` → `进行中`
129
130### 完成摘要
131
132- 完成时间:2026-03-29
133- 状态变更:`进行中` → `已完成`
134- 修改了哪些文件:
135 - `packages/artifact-db/src/types.ts` — 新增 `SyncEnqueuer` 接口
136 - `packages/artifact-db/src/index.ts` — 导出 `SyncEnqueuer` 类型
137 - `packages/artifact-db/src/store.ts` — ArtifactStore 增加 `syncQueue` 字段、`setSyncQueue()` setter,三个写入方法(insertMessage、insertExecution、upsertSession)在事务提交后 enqueue 同步记录
138 - `packages/d1-client/src/types.ts` — SyncWorkerConfig 增加 `purgeKeepCount` 选项
139 - `packages/d1-client/src/sync-queue.ts` — 新增 `purgeSyncedKeepRecent()` 按数量保留的清理方法
140 - `packages/d1-client/src/sync-worker.ts` — 新增 `getQueue()` 访问器,tick 后自动调用 `purgeCompleted()`
141 - `apps/conductor-daemon/src/index.ts` — ConductorRuntime 构造函数中将 D1SyncWorker 的 SyncQueue 注入 ArtifactStore
142- 核心实现思路:
143 - 在 artifact-db 中定义最小化 `SyncEnqueuer` 接口,避免对 d1-client 的直接依赖
144 - 通过 setter 注入(而非构造函数),因为 ArtifactStore 在 D1SyncWorker 之前创建
145 - enqueue 在 `executeWrite`(SQLite 事务)提交成功之后执行,失败时静默 catch,不影响本地写入
146 - payload 使用 snake_case 列名,与 D1 表结构一致,供 sync-worker 的 buildUpsertSql 直接使用
147 - sync worker 每次 tick 后调用 `purgeSyncedKeepRecent(1000)` 清理已同步记录
148- 跑了哪些测试:
149 - `pnpm build` — 全量构建通过
150 - `pnpm --filter @baa-conductor/artifact-db test` — 1 test passed
151 - `pnpm --filter @baa-conductor/d1-client test` — 11 tests passed
152 - `pnpm test` — conductor-daemon 有若干预存失败(main 分支同样失败,与本次改动无关)
153
154### 执行过程中遇到的问题
155
156- conductor-daemon 测试中有多个 `handleConductorHttpRequest` 相关的 `TypeError: Cannot read properties of undefined (reading 'localApiBase')` 失败,经确认 main 分支同样存在,与本次改动无关。
157
158### 剩余风险
159
160- `insertMessage` / `insertExecution` 内部调用 `buildDerivedSessionRecord` 会隐式 upsert session,但本次只在顶层三个方法末尾 enqueue 对应表(messages/executions/sessions),内部 session upsert 没有单独 enqueue。如果需要 D1 侧也同步 session 的每次更新,可以在 `writeSessionArtifacts` 之后也加 enqueue,但这会增加同步量。当前策略是:session 数据通过后续的 `upsertSession` 调用或下一次 message/execution 写入时自然同步。
161