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:
+26
-1
@@ -25,12 +25,14 @@ const EVENT_LABEL = {
|
||||
'task.created': '任务创建', 'task.updated': '任务更新', 'status.changed': '状态变更',
|
||||
'approval.requested': '请求审批', 'approval.granted': '审批通过', 'approval.rejected': '审批驳回',
|
||||
'run.started': '运行开始', 'run.finished': '运行结束', 'project.synced': 'todo 同步',
|
||||
'merge.failed': '合并失败', 'merge.remediated': '补救完成 → 完成',
|
||||
};
|
||||
const EVENT_CLASS = {
|
||||
'task.created': 'ev-created', 'task.updated': 'ev-updated', 'status.changed': 'ev-status',
|
||||
'approval.requested': 'ev-gatewait', 'approval.granted': 'ev-approve',
|
||||
'approval.rejected': 'ev-rejected', 'run.started': 'ev-run', 'run.finished': 'ev-run',
|
||||
'project.synced': 'ev-created',
|
||||
'merge.failed': 'ev-rejected', 'merge.remediated': 'ev-approve',
|
||||
};
|
||||
const AUTONOMY_LABEL = {
|
||||
manual: '手动', 'auto-easy': '自动执行 Easy', 'auto-approved': '自动执行已批准',
|
||||
@@ -458,6 +460,14 @@ function gateDocs(t) {
|
||||
if (gate === 'exec') {
|
||||
if (t.result) {
|
||||
const r = t.result;
|
||||
if (r.mergeTaskId) { // 合并失败:已建补救任务在解冲突,原任务停在审核闸
|
||||
const rem = S.tasks.find((x) => x.id === r.mergeTaskId);
|
||||
const remDone = rem && (rem.status === 'done' || rem.status === 'cancelled');
|
||||
blocks.push(`<div class="gate-doc-label">合并状态</div>
|
||||
<div class="doc merge-failed-banner">⚠️ 自动合并失败(冲突)→ 已建最高优先级补救任务
|
||||
${rem ? `「${esc(rem.title)}」(${STATUS_LABEL[rem.status] || esc(rem.status)})` : `${esc(r.mergeTaskId)}`}
|
||||
${remDone ? '' : '<br>补救任务解决冲突并合并后,本任务将自动标记完成,无需手动「仅通过」。'}</div>`);
|
||||
}
|
||||
blocks.push(reviewReportBlocks(r)); // CODE REVIEW + 安全审计 两节(各带 verdict 徽章),置于 DIFF 摘要上方
|
||||
blocks.push(`<div class="gate-doc-label">RESULT · 执行结果</div>
|
||||
<dl class="result-kv">
|
||||
@@ -643,7 +653,8 @@ function tlActor(p) {
|
||||
if (p.by) who.push(p.by === 'orchestrator' ? '编排器' : p.by);
|
||||
if (p.actor) who.push(p.actor === 'importer' ? '导入器' : p.actor);
|
||||
if (p.auto) who.push({ 'deps-met': '依赖满足自动放行', 'deps-reconcile': '启动对账', 'deps-unmet': '依赖未满足落位',
|
||||
'children-done': '子任务全完成自动收口', 'import-done': '导入历史完成', 'daemon-restart': '重启恢复' }[p.auto] || p.auto);
|
||||
'children-done': '子任务全完成自动收口', 'import-done': '导入历史完成', 'daemon-restart': '重启恢复',
|
||||
'merge-remediated': '补救完成自动收口' }[p.auto] || p.auto);
|
||||
return who.join(' · ');
|
||||
}
|
||||
|
||||
@@ -721,6 +732,15 @@ function renderArchiveModal(t, runs, events) {
|
||||
if (e.type === 'approval.rejected') text = `审批驳回(${GATE_LABEL[p.gate] || p.gate})${p.reason ? ':' + p.reason : ''}`;
|
||||
if (e.type === 'run.started') text = `run 开始(${RUN_KIND[p.kind] || p.kind || ''})`;
|
||||
if (e.type === 'run.finished') text = `run 结束(${RUN_ST[p.status] || p.status || ''})`;
|
||||
if (e.type === 'merge.failed') {
|
||||
const files = (p.conflictFiles && p.conflictFiles.length) ? `(冲突文件:${p.conflictFiles.join('、')})` : '';
|
||||
const remT = p.remediationTaskId ? (byId.get(p.remediationTaskId)?.title || p.remediationTaskId) : '';
|
||||
text = `合并失败${files}${remT ? ` → 补救任务「${remT}」` : ''}`;
|
||||
}
|
||||
if (e.type === 'merge.remediated') {
|
||||
const remT = p.remediationTaskId ? (byId.get(p.remediationTaskId)?.title || p.remediationTaskId) : '';
|
||||
text = `补救完成 → 完成${remT ? `(补救任务「${remT}」已合并)` : ''}`;
|
||||
}
|
||||
const who = tlActor(p);
|
||||
return `<div class="tl-item">
|
||||
<span class="tl-time">${fmtFull(e.at)}</span>
|
||||
@@ -1195,6 +1215,11 @@ function eventDetail(e) {
|
||||
if (p.kind === 'project') return `项目「${p.name || ''}」已登记`;
|
||||
return `${p.title || name}(${CPLX_LABEL[p.complexity] || ''})`;
|
||||
}
|
||||
if (e.type === 'merge.failed') {
|
||||
const files = (p.conflictFiles && p.conflictFiles.length) ? `(冲突文件:${p.conflictFiles.join('、')})` : '';
|
||||
return `${name}:合并失败${files} → 补救任务`;
|
||||
}
|
||||
if (e.type === 'merge.remediated') return `${name}:补救完成 → 完成`;
|
||||
if (e.type === 'task.updated') return `${name} · 字段 ${p.field || ''}`;
|
||||
if (e.type === 'project.synced') {
|
||||
return `新建 ${p.created ?? 0} · 推进 done ${p.doneAdvanced ?? 0} · 跳过 ${p.skipped ?? 0}`;
|
||||
|
||||
@@ -912,6 +912,14 @@ li.ev-updated { --ev: var(--muted); }
|
||||
.tl-who { color: var(--muted); font-size: 11px; }
|
||||
.archive-row { cursor: pointer; }
|
||||
|
||||
/* 合并失败横幅(结果评审闸) */
|
||||
.merge-failed-banner {
|
||||
border-left: 3px solid var(--red, #d9534f);
|
||||
background: rgba(217, 83, 79, 0.08);
|
||||
padding: 8px 12px; border-radius: 4px;
|
||||
font-size: 12px; line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 顶栏动作不换行(同步 todo / 配置 按钮文字曾被挤成两行) */
|
||||
.top-actions { flex-wrap: nowrap; }
|
||||
.top-actions .btn, .sync-meta { white-space: nowrap; }
|
||||
|
||||
Reference in New Issue
Block a user