fix(orchestrator): 拆解未批准前冻结子任务——父任务 plan_review/analyzing 期间子任务不可领取
修复 bug:auto-approved 项目里 planner 拆解后子任务立即被领取执行,而父任务还在 plan_review 等人审批,导致拆解若被驳回则子任务已白跑。claimable 加闸:子任务的父任务 处于 plan_review(待审拆解)或 analyzing(拆解中)时冻结,批准(decomposed)后放行。 仅冻结这两态,不影响 todo-sync 等其它带 parentId 的流程。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -133,6 +133,11 @@ export function createOrchestrator(store: Store, log: OrchestratorLogger, deps:
|
||||
if (!CLAIMABLE.has(t.status)) return false;
|
||||
if (inflight.has(t.id)) return false; // 已有在途 run(executor/planner)
|
||||
if (parents.has(t.id)) return false; // 非叶子(容器)跳过
|
||||
// 父任务拆解待审批 / 拆解中 → 冻结子任务,避免拆解被驳回时子任务已白跑(仅冻结这两态,不影响 sync 等流程)
|
||||
if (t.parentId) {
|
||||
const ps = byId.get(t.parentId)?.status;
|
||||
if (ps === 'plan_review' || ps === 'analyzing') return false;
|
||||
}
|
||||
if (easyOnly && t.complexity !== 'easy') return false;
|
||||
if (t.nextEligibleAt && nowMs < Date.parse(t.nextEligibleAt)) return false; // 退避冷却中
|
||||
return t.deps.every((dep) => byId.get(dep)?.status === 'done');
|
||||
|
||||
@@ -415,6 +415,23 @@ test('claimable 纳入 analyzing:hard 任务派 planner-decompose', () => {
|
||||
store.close();
|
||||
});
|
||||
|
||||
test('子任务在父任务 plan_review 期间不可领取,拆解批准(decomposed)后放行', () => {
|
||||
const { store, projectId } = setup('auto-approved');
|
||||
const parent = store.createTask({ projectId, title: 'epic', complexity: 'hard' }); // → analyzing
|
||||
const child = store.createTask({ projectId, parentId: parent.id, title: 'sub', complexity: 'easy' }); // → ready
|
||||
store.transition(parent.id, 'plan_review', { by: 'test' }); // 模拟拆解完成待审
|
||||
const state = freshState();
|
||||
const { deps } = mockDeps(state);
|
||||
const orch = createOrchestrator(store, noopLog, deps);
|
||||
orch.claimTick();
|
||||
assert.equal(state.spawned.length, 0, '父任务 plan_review(拆解待审)期间,子任务不应被领取');
|
||||
store.transition(parent.id, 'decomposed', { by: 'test' }); // 批准拆解
|
||||
orch.claimTick();
|
||||
assert.equal(state.spawned.length, 1, '拆解批准后子任务放行可领取');
|
||||
assert.equal(store.listRuns(child.id).length, 1, '被领取的是该子任务');
|
||||
store.close();
|
||||
});
|
||||
|
||||
test('auto-easy 不做规划:medium(speccing) 不被领取', () => {
|
||||
const { store, projectId } = setup('auto-easy');
|
||||
const t = store.createTask({ projectId, title: 'feat', complexity: 'medium' }); // → speccing
|
||||
|
||||
Reference in New Issue
Block a user