feat: 合并失败→补救→完成 全程事件轨迹与自动收口 [tsk_BagzbKZNAHXV]

不新增顶层状态,用 events + result 字段记录完整轨迹:
- 新增 merge.failed / merge.remediated 两类事件
- merge 失败时在原任务时间线记 merge.failed 节点(结构化冲突文件 + 补救任务链接)
- 补救任务成功合并后,findMergeOriginTask 反查原任务,remediateOrigin 自动把原任务标 done
  并记 merge.remediated 节点,无需人工 merge:false
- 看板:归档时间线渲染失败/补救节点;结果评审闸显示「合并失败 → 补救任务」横幅

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-13 15:04:39 +08:00
parent f020e15137
commit b1d881a4b4
7 changed files with 149 additions and 8 deletions
+11 -1
View File
@@ -324,7 +324,8 @@ export function buildServer(opts: ApiOptions): FastifyInstance {
if (!mr.ok) {
// 自动合并失败(多为冲突)→ 建/复用最高优先级补救任务来完成合并,原任务留在审核闸
const rem = store.ensureMergeRemediationTask(task.id, {
branch, targetBranch: project.defaultBranch, conflictError: mr.error ?? '未知冲突',
branch, targetBranch: project.defaultBranch,
conflictError: mr.error ?? '未知冲突', conflictFiles: mr.conflictFiles,
});
throw new StoreError(`合并失败:${mr.error}(原任务保留在审核闸;已建最高优先级补救任务 ${rem.id}${rem.title}」来完成合并)`);
}
@@ -332,6 +333,15 @@ export function buildServer(opts: ApiOptions): FastifyInstance {
store.decide(id, 'accept', b.actor ?? 'user', b.reason ?? null);
// 合并产物记录:复用 prUrl 字段写 merged:<mergeCommit>
store.setResult(id, { ...task.result, prUrl: `merged:${mr.mergeCommit}` });
// 补救完成自动收口:若本任务是某原任务的「合并冲突补救」任务,合并成功后把原任务也标 done
const origin = store.findMergeOriginTask(id);
if (origin) {
try {
store.remediateOrigin(origin.id, id, mr.mergeCommit);
} catch (e) {
app.log.error(`补救任务 ${id} 合并后收口原任务 ${origin.id} 失败(不影响本任务结果):${(e as Error).message}`);
}
}
// 异步回收:执行 worktree + 已合并的任务分支(失败只记日志,不影响响应)
void (async () => {
try {