feat(merge): 自动合并失败时建最高优先级补救任务来完成合并
decide 端 exec_review accept→merge 冲突失败时,不再只是报错留在闸: 自动建一个 P0 easy 补救任务(operations 写明分支/目标/冲突详情与解冲突步骤), 自治项目会自动领取去解决。幂等:补救任务 id 记到原任务 result.mergeTaskId, 已有未结束补救任务则复用不重复建;结束(done/cancelled)后允许新建。 - model/types: TaskResult 加 mergeTaskId 字段 - mappers/orchestrator: 同步透传/补 null - store.ensureMergeRemediationTask + store.test 覆盖(P0/easy/ready/operations/幂等/可重建) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -184,6 +184,7 @@ test('成功路径:状态流转 + setResult(四字段) + executor/reviewer/sec
|
||||
verdict: 'approve',
|
||||
securitySummary: '## 安全审计\nmock 审计通过',
|
||||
securityVerdict: 'approve',
|
||||
mergeTaskId: null,
|
||||
});
|
||||
assert.equal(codeReportSeen, '执行自述:改了 README'); // runner finalText 传给两个复审作执行者自述
|
||||
assert.equal(secReportSeen, '执行自述:改了 README');
|
||||
|
||||
@@ -208,3 +208,46 @@ test('容器收口:已拆解 Hard 的子任务全 done → 容器自动 done
|
||||
assert.equal(s.getTask(root.id).status, 'done'); // 子全 done → 容器自动 done
|
||||
s.close();
|
||||
});
|
||||
|
||||
test('合并失败补救:建最高优先级 easy 任务,且幂等不重复建', () => {
|
||||
const s = freshStore();
|
||||
const p = s.createProject({ name: 'm', repoPath: '/tmp/m-' + Math.random() });
|
||||
// 造一个进入 exec_review、带分支结果的原任务
|
||||
const t = s.createTask({ projectId: p.id, title: '配置面板补 model', complexity: 'easy', priority: 1 });
|
||||
s.setOperations(t.id, 'op');
|
||||
s.transition(t.id, 'queued');
|
||||
s.transition(t.id, 'executing');
|
||||
s.setResult(t.id, {
|
||||
branch: 'maestro/' + t.id, worktree: '/tmp/wt', diffSummary: '', commits: [], prUrl: null,
|
||||
summary: null, verdict: null, securitySummary: null, securityVerdict: null, mergeTaskId: null,
|
||||
});
|
||||
s.transition(t.id, 'exec_review');
|
||||
|
||||
const rem = s.ensureMergeRemediationTask(t.id, {
|
||||
branch: 'maestro/' + t.id, targetBranch: 'main', conflictError: '冲突文件:web/app.js',
|
||||
});
|
||||
assert.equal(rem.complexity, 'easy');
|
||||
assert.equal(rem.priority, 0, '补救任务应为最高优先级 P0');
|
||||
assert.equal(rem.status, 'ready', 'easy 无依赖 → 直达 ready,自治项目可领');
|
||||
assert.match(rem.title, /合并冲突待解决/);
|
||||
assert.match(rem.operations ?? '', /git merge maestro\//, 'operations 应含合并步骤');
|
||||
assert.match(rem.operations ?? '', /web\/app\.js/, 'operations 应含冲突详情');
|
||||
// 幂等标记写回原任务
|
||||
assert.equal(s.getTask(t.id).result?.mergeTaskId, rem.id);
|
||||
|
||||
// 再次调用 → 复用同一补救任务,不新建
|
||||
const again = s.ensureMergeRemediationTask(t.id, {
|
||||
branch: 'maestro/' + t.id, targetBranch: 'main', conflictError: '再次冲突',
|
||||
});
|
||||
assert.equal(again.id, rem.id, '已有未结束补救任务 → 复用');
|
||||
const remCount = s.listTasks(p.id).filter((x) => x.title.includes('合并冲突待解决')).length;
|
||||
assert.equal(remCount, 1, '不应重复建补救任务');
|
||||
|
||||
// 补救任务结束(cancelled)后再调用 → 允许新建
|
||||
s.transition(rem.id, 'cancelled');
|
||||
const fresh = s.ensureMergeRemediationTask(t.id, {
|
||||
branch: 'maestro/' + t.id, targetBranch: 'main', conflictError: '又冲突',
|
||||
});
|
||||
assert.notEqual(fresh.id, rem.id, '旧补救任务已结束 → 建新的');
|
||||
s.close();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user