feat(retry+decompose): 范围型驳回不重试 + 拆解 prompt 补 scope 铁律

- ingest:复审 reject 时若任务有 scopeFiles 且理由命中越界/范围外/声明范围等标记 → 判为范围型驳回,调 store.failScopeReject 直接转 needs_attention(清退避、不进重试链),因 executor 碰不了范围外文件、重试必然同样被驳回;- store.failScopeReject:补记 failed run 承载原因 + 直转 needs_attention(带单测);- runner 拆解 prompt:加「files 范围铁律」——覆盖全跨层足迹 / 后端能力别塞前端范围 / 宁空勿窄 / 全或空。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-29 23:32:10 +08:00
parent 1c62059424
commit 5a609a8675
4 changed files with 57 additions and 2 deletions
+19
View File
@@ -163,6 +163,25 @@ test('markReeval(规则13):executing → needs_attention,收尾 run 为
s.close();
});
test('failScopeReject(范围型驳回):直接 needs_attention、不进重试链、清退避,记一条 failed run 承载原因', () => {
const s = freshStore();
const p = s.createProject({ name: 'scoperej', repoPath: '/tmp/scoperej-' + Math.random() });
const t = s.createTask({ projectId: p.id, title: 'x', complexity: 'easy', scopeFiles: ['design/**'] });
s.setOperations(t.id, 'op');
s.transition(t.id, 'queued');
s.transition(t.id, 'executing');
const after = s.failScopeReject(t.id, '复审因范围不足驳回(停止重试):Scope A entirely undone');
assert.equal(after.status, 'needs_attention', '范围型驳回应直接转 needs_attention(不重试)');
assert.equal(after.nextEligibleAt, null, '应清退避、不进重试链');
// 补记一条 failed executor run 承载原因(供 UI lastRunError 兜底)
const failed = s.listRuns(t.id).filter((r) => r.kind === 'executor' && r.status === 'failed');
assert.equal(failed.length, 1, '应补记一条 failed executor run');
assert.match(failed[0].error ?? '', /范围不足/);
s.close();
});
test('reconcileInterrupted:执行中被 cancel 的任务仍挂 started run → 仅收尾不崩(绝不非法 cancelled→failed', () => {
const s = freshStore();
const p = s.createProject({ name: 'recon', repoPath: '/tmp/recon-' + Math.random() });