feat: Phase2 完整管线——score 调度 + 自动复审 + 模型分级 + 归档与详情
调度: - model/scoring.ts: score = 自身分(P0=3/P1=2/P2=1) + 已完成依赖分(链条惯性) + 等待解锁的 blocked 任务分(解锁加权),编排器与 nextExecutable 同一打分 - createTask 校验 deps 存在且同项目(依赖图天然无环) - daemon 重启中断自愈: executing 任务标 failed run 后重新入队(reconcileInterrupted) 执行管线: - executor/cc.ts: 公共 headless CC 执行器(转录/超时/模型回退重试) - executor/reviewer.ts: 执行后自动复审(只读 CC 审 diff),固定模板 summary (做了什么/怎么做/测试/CodeReview/安全Review/结论) + VERDICT 解析 - executor/models.ts: 按复杂度选模型(easy→sonnet/medium→opus/hard→fable5), env 可覆盖、project.model 最优先、不可用自动回退链 - runner: 测试/构建命令白名单(npm/go/shellcheck/make/pytest),prompt 要求实跑测试 - TaskResult 加 summary/verdict; RunKind 加 reviewer - 容器收口: 已拆解 Hard 子任务全 done → 容器自动 done(afterDone 逐级向上) 看板: - 五徽章组(待审批/待执行/执行中/被阻塞/总量,hover 展开,均不含已完成) - 归档区: 深度1整树完成沉底,时间倒序分页(10/20/50/100 chip 选择) - 归档详情对话框: 全属性/执行历史与时长/审批记录/状态流转时间线(含相关人或事) - Agent 面板显示调度模式 + 各复杂度实际模型 - 结果闸展示复审 summary + 建议通过/拒绝徽章 - 筛选修复(组选与单选分离、已拆解移出进行中)、同步按钮收进配置面板、 保存配置自动收起、预览全宽、被依赖阻塞→被阻塞 - API: GET /api/tasks/:id/events(任务级事件时间线)、/api/agents 带 scheduling/models 测试: 49/49(新增 scoring/复审/模型/容器收口/deps 校验/中断恢复) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import type { Task } from './types.js';
|
||||
|
||||
/**
|
||||
* 调度打分(在领取那一刻现算,不落库):
|
||||
* score = 自身分 + Σ(已完成依赖的自身分) —— 链条惯性:前置投入越重越优先出活
|
||||
* + Σ(直接等我解锁的 blocked 任务的自身分) —— 解锁效应:等我的人越多越优先
|
||||
* 自身分按优先级:P0=3 / P1=2 / P2=1。
|
||||
* 依赖与解锁均只算一层(不递归):deps 在创建时即固定且只能引用已存在任务,
|
||||
* 因此依赖图天然无环,但递归会让深链分数膨胀失控,故不递归。
|
||||
*/
|
||||
export function baseScore(priority: number): number {
|
||||
return Math.max(1, 3 - priority);
|
||||
}
|
||||
|
||||
/** 反向索引:taskId → 直接依赖它的任务列表 */
|
||||
export function buildDependentsIndex(tasks: Task[]): Map<string, Task[]> {
|
||||
const idx = new Map<string, Task[]>();
|
||||
for (const t of tasks) {
|
||||
for (const d of t.deps) {
|
||||
const list = idx.get(d) ?? [];
|
||||
list.push(t);
|
||||
idx.set(d, list);
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
export function scoreTask(t: Task, byId: Map<string, Task>, dependents: Map<string, Task[]>): number {
|
||||
let s = baseScore(t.priority);
|
||||
for (const d of t.deps) {
|
||||
const dt = byId.get(d);
|
||||
if (dt?.status === 'done') s += baseScore(dt.priority); // 链条惯性
|
||||
}
|
||||
for (const w of dependents.get(t.id) ?? []) {
|
||||
if (w.status === 'blocked') s += baseScore(w.priority); // 解锁效应
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** 把候选任务按 score 降序排(同分创建早的在前)。返回新数组,附带分数。 */
|
||||
export function rankByScore(candidates: Task[], all: Task[]): Array<{ task: Task; score: number }> {
|
||||
const byId = new Map(all.map((t) => [t.id, t]));
|
||||
const dependents = buildDependentsIndex(all);
|
||||
return candidates
|
||||
.map((task) => ({ task, score: scoreTask(task, byId, dependents) }))
|
||||
.sort((a, b) => b.score - a.score || a.task.createdAt.localeCompare(b.task.createdAt));
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ export const STATUS_LABEL: Record<TaskStatus, string> = {
|
||||
speccing: '写方案中',
|
||||
spec_review: '待确认方案',
|
||||
ready: '可执行',
|
||||
blocked: '被依赖阻塞',
|
||||
blocked: '被阻塞',
|
||||
queued: '排队中',
|
||||
executing: '执行中',
|
||||
exec_review: '待审/合',
|
||||
|
||||
+7
-2
@@ -32,12 +32,17 @@ export interface ApprovalRecord {
|
||||
at: string;
|
||||
}
|
||||
|
||||
/** 复审结论:approve=建议通过 / reject=建议拒绝 / null=未复审(复审失败或旧数据) */
|
||||
export type ReviewVerdict = 'approve' | 'reject';
|
||||
|
||||
export interface TaskResult {
|
||||
branch: string | null;
|
||||
worktree: string | null;
|
||||
diffSummary: string | null;
|
||||
commits: string[];
|
||||
prUrl: string | null;
|
||||
summary: string | null; // 复审 summary(markdown),旧数据缺省 null
|
||||
verdict: ReviewVerdict | null; // 复审结论,解析不到 / 复审失败 = null
|
||||
}
|
||||
|
||||
export interface Task {
|
||||
@@ -61,13 +66,13 @@ export interface Task {
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export type RunKind = 'planner' | 'executor';
|
||||
export type RunKind = 'planner' | 'executor' | 'reviewer';
|
||||
export type RunStatus = 'started' | 'succeeded' | 'failed' | 'cancelled';
|
||||
|
||||
export interface Run {
|
||||
id: Id;
|
||||
taskId: Id;
|
||||
kind: RunKind; // planner(产出拆解/方案)| executor(执行改动)
|
||||
kind: RunKind; // planner(产出拆解/方案)| executor(执行改动)| reviewer(执行后自动复审)
|
||||
worktree: string | null;
|
||||
branch: string | null;
|
||||
status: RunStatus;
|
||||
|
||||
Reference in New Issue
Block a user