feat(agent): 接通 plan 自动放行(autoApprovePlan)+ 项目配置可写
之前 auto_approve_plan/auto_approve_exec/checks 列与 Project 类型已存在,但 PatchProjectInput 不含它们 → 无法启用(dead config)。本次接通: - PatchProjectInput + patchProject 支持 autoApprovePlan/autoApproveExec/checks - API PATCH /projects 透传三字段 - ingest decompose-result:auto-approved + autoApprovePlan + 全 easy + 子任务≤5 → store.decide(accept) 跳过 plan_review 直达 decomposed(默认关) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -255,6 +255,55 @@ test('ingest planner-decompose:setPlan + 建子任务 + 转 plan_review', () =
|
||||
});
|
||||
});
|
||||
|
||||
test('autoApprovePlan:auto-approved + 开关 + 全 easy + 改动面小 → 跳过 plan_review 直接 decomposed', () => {
|
||||
withTmpDataDir(() => {
|
||||
const { store, taskId, runId } = setupPlanning('hard');
|
||||
const pid = store.getTask(taskId)!.projectId;
|
||||
store.patchProject(pid, { autoApprovePlan: true });
|
||||
appendOutbox(runId, {
|
||||
type: 'decompose-result', plan: '拆成两步',
|
||||
subtasks: [{ title: 'a', complexity: 'easy' }, { title: 'b', complexity: 'easy' }],
|
||||
transcriptRef: null, sessionId: null,
|
||||
});
|
||||
appendOutbox(runId, { type: 'done' });
|
||||
ingestRun(store, noopLog, runId);
|
||||
assert.equal(store.getTask(taskId)!.status, 'decomposed', '全 easy 自动放行');
|
||||
store.close();
|
||||
});
|
||||
});
|
||||
|
||||
test('autoApprovePlan:开关关 → 仍走 plan_review 人审', () => {
|
||||
withTmpDataDir(() => {
|
||||
const { store, taskId, runId } = setupPlanning('hard'); // autoApprovePlan 默认 false
|
||||
appendOutbox(runId, {
|
||||
type: 'decompose-result', plan: 'x',
|
||||
subtasks: [{ title: 'a', complexity: 'easy' }],
|
||||
transcriptRef: null, sessionId: null,
|
||||
});
|
||||
appendOutbox(runId, { type: 'done' });
|
||||
ingestRun(store, noopLog, runId);
|
||||
assert.equal(store.getTask(taskId)!.status, 'plan_review', '开关关 → 人审');
|
||||
store.close();
|
||||
});
|
||||
});
|
||||
|
||||
test('autoApprovePlan:含 medium 子任务 → 不放行(改动面/复杂度不满足)', () => {
|
||||
withTmpDataDir(() => {
|
||||
const { store, taskId, runId } = setupPlanning('hard');
|
||||
const pid = store.getTask(taskId)!.projectId;
|
||||
store.patchProject(pid, { autoApprovePlan: true });
|
||||
appendOutbox(runId, {
|
||||
type: 'decompose-result', plan: 'x',
|
||||
subtasks: [{ title: 'a', complexity: 'easy' }, { title: 'b', complexity: 'medium' }],
|
||||
transcriptRef: null, sessionId: null,
|
||||
});
|
||||
appendOutbox(runId, { type: 'done' });
|
||||
ingestRun(store, noopLog, runId);
|
||||
assert.equal(store.getTask(taskId)!.status, 'plan_review', '有 medium → 人审');
|
||||
store.close();
|
||||
});
|
||||
});
|
||||
|
||||
test('ingest planner failed:走 failPlanAttempt(退避,任务留 speccing,不进 failed)', () => {
|
||||
withTmpDataDir(() => {
|
||||
const { store, taskId, runId } = setupPlanning('medium');
|
||||
|
||||
Reference in New Issue
Block a user