Files
maestro/docs/task-rules.html

230 lines
19 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Maestro · 任务执行规则参考</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
<style>
:root{--bg:#0d0d0d;--bg2:#141414;--bg3:#1a1a1a;--border:#252525;--border2:#333;--text:#e8e8e8;--text2:#a0a0a0;--text3:#555;--cyan:#00d4d4;--cyan2:#009999;--green:#4ec94e;--green2:#2d6e2d;--amber:#f0a500;--amber2:#9c6a00;--violet:#9b7bff;--violet2:#6644cc;--red:#ff5555;--red2:#aa2222;--blue:#5599ff;--blue2:#2255cc;--code-bg:#0a0a0a;--shadow:0 4px 32px rgba(0,0,0,.7)}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'IBM Plex Mono',monospace;background:var(--bg);color:var(--text);font-size:13px;line-height:1.7}
.topbar{position:sticky;top:0;z-index:100;background:var(--bg2);border-bottom:1px solid var(--border);height:48px;display:flex;align-items:center;padding:0 24px;gap:12px}
.topbar-brand{font-size:13px;font-weight:600;color:var(--cyan);letter-spacing:.06em}
.topbar-sub{font-size:11px;color:var(--text3)}
.topbar a{margin-left:auto;font-size:11px;color:var(--text3);text-decoration:none}
.topbar a:hover{color:var(--cyan)}
.wrap{max-width:1000px;margin:0 auto;padding:32px 24px 80px}
.lead{font-size:13px;color:var(--text2);margin:0 0 10px;line-height:1.8}
.lead strong{color:var(--text)}
.meta{font-size:11px;color:var(--text3);margin:0 0 26px}
h2{font-size:17px;color:var(--text);margin:36px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px}
h2 .n{font-size:12px;color:var(--cyan);border:1px solid var(--cyan2);border-radius:4px;padding:1px 8px;font-weight:400}
h3{font-size:14px;color:var(--cyan);margin:22px 0 8px}
p{color:var(--text2);margin:8px 0}
strong{color:var(--text)}
em{color:var(--amber);font-style:normal}
code{font-family:inherit;background:var(--code-bg);border:1px solid var(--border);border-radius:3px;padding:1px 5px;color:var(--cyan);font-size:12px}
ul{margin:8px 0 8px 4px;padding-left:20px;color:var(--text2)}
ol{margin:8px 0 8px 4px;padding-left:22px;color:var(--text2)}
li{margin:4px 0}
li::marker{color:var(--green)}
table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px}
th,td{border:1px solid var(--border);padding:7px 10px;text-align:left;vertical-align:top}
th{background:var(--bg3);color:var(--text);font-weight:600}
td{color:var(--text2)}
td code,th code{font-size:11.5px}
.card{background:var(--bg2);border:1px solid var(--border);border-left:3px solid var(--cyan2);border-radius:6px;padding:14px 16px;margin:12px 0}
.card.violet{border-left-color:var(--violet2)}
.card.green{border-left-color:var(--green2)}
.card.amber{border-left-color:var(--amber2)}
.card.red{border-left-color:var(--red2)}
.card-ttl{font-size:13px;font-weight:600;color:var(--text);margin-bottom:6px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.tag{font-size:10px;padding:1px 7px;border-radius:3px;border:1px solid}
.tag.hard{color:var(--red);border-color:var(--red2);background:rgba(255,85,85,.08)}
.tag.med{color:var(--amber);border-color:var(--amber2);background:rgba(240,165,0,.08)}
.tag.easy{color:var(--green);border-color:var(--green2);background:rgba(78,201,78,.08)}
.tag.ro{color:var(--blue);border-color:var(--blue2);background:rgba(85,153,255,.08)}
.tag.wr{color:var(--violet);border-color:var(--violet2);background:rgba(155,123,255,.08)}
.note{background:rgba(0,212,212,.06);border:1px solid var(--cyan2);border-radius:5px;padding:10px 13px;margin:12px 0;font-size:12px;color:var(--text2)}
.note.warn{background:rgba(240,165,0,.06);border-color:var(--amber2)}
.note strong{color:var(--text)}
.foot{margin-top:40px;font-size:11px;color:var(--text3);border-top:1px solid var(--border);padding-top:14px}
.src{color:var(--text3);font-size:11px}
</style>
</head>
<body>
<div class="topbar">
<span class="topbar-brand">MAESTRO</span>
<span class="topbar-sub">· 任务执行规则参考</span>
<a href="index.html">← 文档索引</a>
</div>
<div class="wrap">
<p class="lead">Maestro 后台 agent 处理任务的<strong>当前实现规则</strong>(非设计提案,是线上真实行为)。按<strong>复杂度路由</strong>分四类执行路径:① 任务拆解(HARD)· ② 方案撰写(MEDIUM)· ③ 任务执行(executor)· ④ 解冲突(conflict),外加跨类通用规则与模型档位。</p>
<p class="meta">真相源:<code>src/executor/runner.ts</code> · <code>src/daemon/orchestrator.ts</code> · <code>src/daemon/ingest.ts</code> · <code>src/executor/pipeline.ts</code> · <code>src/executor/models.ts</code> · <code>src/model/status.ts</code>。如与代码不符,以代码为准。</p>
<!-- ───────── 概览 ───────── -->
<h2><span class="n">0</span> 复杂度 → 生命周期路由</h2>
<p>任务按 <code>complexity</code>(可由 <code>auto</code> 让模型判定)走不同路径;只有产生代码改动的任务才过 <code>exec_review</code>(双复审 + 合并)。</p>
<table>
<thead><tr><th>复杂度</th><th>路径</th><th>人审闸</th><th>谁来做</th></tr></thead>
<tbody>
<tr><td><span class="tag easy">EASY</span></td><td><code>init → ready → queued → executing → exec_review → done</code></td><td>exec_review</td><td>executor(直接执行)</td></tr>
<tr><td><span class="tag med">MEDIUM</span></td><td><code>init → speccing → spec_review → ready → … → exec_review → done</code></td><td>spec_review + exec_review</td><td>planner 写方案 → executor 执行</td></tr>
<tr><td><span class="tag hard">HARD</span></td><td><code>init → analyzing → plan_review → decomposed →(子任务各自跑)→ done</code></td><td>plan_review +(各子任务的闸)</td><td>planner 拆解 → 子任务递归</td></tr>
</tbody>
</table>
<div class="note">三种 run 类型(<code>runs.kind</code>):<strong>planner</strong>(拆解 HARD / 写 MEDIUM 方案,只读)· <strong>executor</strong>worktree 内改代码)· <strong>reviewer / security</strong>(执行后双复审,只读)· <strong>conflict</strong>(解合并冲突,本会话新增)。</div>
<!-- ───────── ① 拆解 ───────── -->
<h2><span class="n"></span> 任务拆解(decompose<span class="tag hard">HARD only</span> <span class="tag ro">只读</span></h2>
<div class="card">
<div class="card-ttl">触发 + 执行环境</div>
<ul>
<li><strong>HARD</strong> 任务拆解;编排器把它领进 <code>analyzing</code> 态,起一个 <strong>planner run</strong></li>
<li><strong>只读</strong>cwd = 主仓(<em>不建 worktree、不改任何文件</em>);工具白名单仅 <code>Read / Glob / Grep</code> + <code>Bash(git log/diff/show)</code></li>
<li>模型:planner 档,<strong>默认 <code>claude-opus-4-8</code></strong>(项目可配);<code>maxTurns=90</code></li>
<li>触发前提:叶子任务 · 不在退避冷却 · 依赖全 <code>done</code> · 项目 <code>autonomy ≠ manual</code></li>
</ul>
</div>
<div class="card">
<div class="card-ttl">拆解约束(prompt 硬性要求)</div>
<ul>
<li>拆成 <strong>26 个</strong>更小、可独立交付的子任务。</li>
<li><strong>层级闸</strong><code>depth ≤ 4</code>(根=1),prompt 注入「剩余可拆层数」;子任务只有在<em>剩余层数 &gt; 0</em> 时才允许再标 <code>hard</code>(防无限拆)。</li>
<li>每个子任务 5 个字段:<code>title</code> · <code>complexity</code>(easy/medium/hard) · <code>priority</code>(0/1/2) · <code>deps</code>(依赖<em>本列表的 0-based 序号</em>) · <code>files</code>(文件范围 glob)。</li>
<li><code>files</code> = 该子任务预计改动的文件范围;执行时改<strong>越界文件会被硬闸拦截</strong>,故须尽量精确,不确定留空数组(不限)。</li>
</ul>
</div>
<div class="card">
<div class="card-ttl">输出格式(三段)</div>
<ol>
<li><strong>Markdown 表格总览</strong>(序号 / 标题 / 复杂度 / 优先级 / 依赖 / 文件范围)——给人审看;</li>
<li>一段<strong>分析与拆解理由</strong>正文;</li>
<li>末尾<strong>唯一一个 fenced JSON</strong> <code>{plan, subtasks:[…]}</code>——给机器解析。解析失败 = planner 失败、退避重试。</li>
</ol>
</div>
<div class="card amber">
<div class="card-ttl">落库(ingest 处理结果)</div>
<ul>
<li>JSON 的 <code>plan</code>(一句话理由)→ 存父任务 <code>plan</code> 字段(即评审界面的「说明」);</li>
<li>每个 subtask 建成子任务:<code>priority</code><code>files→scopeFiles</code> 直接落库;<code>deps</code> 先全建好子任务再做<strong>「序号→taskId」二次映射</strong>写依赖;</li>
<li><strong>⚠ 表格 + 分析正文那两段被丢弃</strong>,只留在该 run 的 <em>transcript</em> 里(评审界面「拆解轨迹」可展开查看)。</li>
</ul>
</div>
<div class="card violet">
<div class="card-ttl">审批 + 之后调度</div>
<ul>
<li>默认 → 转 <code>plan_review</code> <strong>人审</strong></li>
<li><strong>可选自动放行</strong>(默认关):项目 <code>auto-approved</code> + <code>autoApprovePlan</code> 开 + 拆解结果<strong>全 easy</strong> + <strong>子任务数 ≤ 5</strong>,四条全满足才跳过人审直接 <code>decomposed</code></li>
<li>父任务在 <code>plan_review</code> / <code>analyzing</code> 期间,<strong>子任务被冻结</strong>(不可领取,UI 显示「待拆解确认」)。</li>
<li>批准后(plan_review→decomposed),子任务解冻,按 <strong>deps + priority</strong> 调度,<strong>各自独立 worktree</strong>(都从 main 拉出)执行、独立合并回 main。</li>
</ul>
</div>
<div class="card red">
<div class="card-ttl">失败 + 收口</div>
<ul>
<li>解析失败 / run 失败 → <code>failPlanAttempt</code> 退避重试,超 <code>maxRetries</code>(默认 2)→ 转 <code>needs_attention</code> 待人工;</li>
<li>父任务 <code>decomposed</code> 后,<strong>子任务全 done → 父任务自动 done</strong></li>
</ul>
</div>
<!-- ───────── ② 方案 ───────── -->
<h2><span class="n"></span> 方案撰写(spec<span class="tag med">MEDIUM</span> <span class="tag ro">只读</span></h2>
<ul>
<li>MEDIUM 任务进 <code>speccing</code> 态,起 <strong>planner run</strong>(与拆解同为 planner kind,但只读写「方案」不拆子任务)。</li>
<li>只读跑在主仓;工具同拆解(Read/Glob/Grep + git 只读);<code>maxTurns=60</code></li>
<li>输出 = 一份 Markdown 方案正文(改动什么 / 为什么 / 验收),存任务 <code>spec</code> 字段。</li>
<li>→ 转 <code>spec_review</code> <strong>人审</strong>;批准后 → <code>ready</code> 进执行队列。reject 须带意见,回流 <code>speccing</code> 重写。</li>
</ul>
<!-- ───────── ③ 执行 ───────── -->
<h2><span class="n"></span> 任务执行(executor<span class="tag wr">改代码</span></h2>
<div class="card">
<div class="card-ttl">执行环境</div>
<ul>
<li>每个任务独立 <strong>worktree</strong>(分支 <code>maestro/&lt;taskId&gt;</code>,从 <code>defaultBranch</code>(main) 拉出);<code>permissionMode=acceptEdits</code></li>
<li>工具:<code>Read/Edit/Write/Glob/Grep</code> + <code>git</code>(status/diff/log/add/commit<em>无 push</em>) + 测试/构建(npm/go/pytest/make…,仍关在 worktree、不发包);<code>maxTurns=100</code></li>
<li>模型:executor 档(默认 opus-4-8,项目可配;旧 <code>project.model</code> 也覆盖 executor)。</li>
<li><strong>必须 commit</strong>message 带任务 ID<code>ensureCommitted</code> 兜底)。</li>
</ul>
</div>
<div class="card amber">
<div class="card-ttl">approve 前的硬闸(任一不过 = 不进 exec_review</div>
<ol>
<li><strong>执行前同步 main + 分歧重评估(规则13)</strong>:起跑前把最新 <code>origin/main</code> 并进 worktree;若撞上<em>本任务声明范围内</em>的文件改动 → 转 <code>needs_attention</code> 待重评估(不盲目硬解)。</li>
<li><strong>verify 闸</strong>:项目 <code>verifyCmd</code> 在 worktree 内跑(10minexit 0 才过;未配=视为通过)。</li>
<li><strong>分项检查闸 checks</strong>:独立的 lint / typecheck / build(项目可配,分项报告哪项挂)。</li>
<li><strong>diff 闸</strong>:体量超阈值、或改了<em>声明范围外文件</em>task.scopeFiles 非空时)→ 拦截。</li>
<li><strong>双复审硬闸</strong>code review + security audit <em>并行</em>跑(各只读、最强档不被 project.model 降档);<strong>任一 verdict=reject → 退回重执行</strong>(带复审意见),不进 exec_review。</li>
</ol>
</div>
<div class="card violet">
<div class="card-ttl">审批 + 合并</div>
<ul>
<li>全绿 → 转 <code>exec_review</code> <strong>人审</strong>accept 触发 <code>--no-ff</code> 自动合并回 main<code>merge:false</code> 可跳过合并)。</li>
<li><strong>可选自动放行</strong>(默认关):<code>auto-approved</code> + <code>autoApproveExec</code> 开 + 双复审均 approve → 跳过人审自动合并。</li>
<li>合并冲突 → 自动建 <strong>P0 解冲突补救任务</strong>(见 ④),原任务留 exec_review,补救成功后自动收口。</li>
</ul>
</div>
<!-- ───────── ④ 解冲突 ───────── -->
<h2><span class="n"></span> 解冲突(conflict<span class="tag wr">改代码</span></h2>
<ul>
<li>exec_review 合并撞冲突时,自动建一个 <strong>P0 最高优先级</strong>补救任务,以专用 <code>runKind=conflict</code> 跑。</li>
<li>专用管线:worker 在补救 worktree 内<strong>真正发起 <code>git merge &lt;原分支&gt;</code></strong> 触发冲突态,再交 CC 逐个解(白名单额外含 <code>Bash(git merge:*)</code>)。</li>
<li>专用 prompt:保留两边意图、解完跑测试验证、禁丢任一方改动、commit 沿用合并信息。</li>
<li>模型:conflict 角色,项目可配,<strong>默认 opus-4-8</strong></li>
<li>走 exec_review + 双复审;合并成功后<strong>自动收口</strong>原任务。P0 插队但<em>不中断在途任务</em>No-preemption)。</li>
</ul>
<!-- ───────── 通用规则 ───────── -->
<h2><span class="n"></span> 跨类通用规则</h2>
<table>
<thead><tr><th>规则</th><th>说明</th></tr></thead>
<tbody>
<tr><td><strong>执行期锁</strong></td><td>任务一旦有在途 runplanner/executor/reviewer/conflict)→ 锁定只读:禁改 title/spec/complexity/deps、禁再调度;run 结束(finish/fail/reap)解锁。</td></tr>
<tr><td><strong>worktree 隔离</strong></td><td>写类 run 只在自己 worktree 内改动,禁碰 worktree 外文件;planner/reviewer 只读、不建 worktree。</td></tr>
<tr><td><strong>不 push / 不部署</strong></td><td>任何 run 都不得 <code>git push</code> / 对外发布;解冲突的 merge 是受控本地合并。</td></tr>
<tr><td><strong>commit 含 task ID</strong></td><td>所有产出 commit message 必带任务 ID<code>ensureCommitted</code> 兜底。</td></tr>
<tr><td><strong>重试退避 + 升级</strong></td><td>失败按退避重试,超 <code>maxRetries</code>(默认 2)→ <code>needs_attention</code> 转人工(planner 留原态、executor 走 failed)。</td></tr>
<tr><td><strong>自动放行默认关</strong></td><td>所有「跳过人审」(plan/exec)默认关闭,须项目级开关 + <code>auto-approved</code> 才生效。</td></tr>
<tr><td><strong>reject 必带意见</strong></td><td>plan/spec/exec 的驳回必须带 reason,按状态机回流重做。</td></tr>
<tr><td><strong>复审独立性</strong></td><td>复审只读、与执行分离、用最强模型、不被 <code>project.model</code> 降档;verdict=reject 是硬闸。</td></tr>
<tr><td><strong>daemon 唯一 DB 写者</strong></td><td>worker 不碰 DB,只读 job.json、写 outbox.ndjson;状态变更只经 <code>transition()</code> 守卫。</td></tr>
<tr><td><strong>超时分层</strong></td><td>planner/executor 30min(可配)· verify 10min · 复审 15min(并行)· conflict 给足强档时间。</td></tr>
</tbody>
</table>
<!-- ───────── 模型 ───────── -->
<h2><span class="n"></span> 模型档位</h2>
<p>内置默认:<strong>每个角色 × 每个复杂度都是 <code>claude-opus-4-8</code></strong><code>DEFAULT_MODEL</code>)。可<strong>按项目</strong><code>projects.models</code>(JSON) 配置。</p>
<p><strong>解析优先级</strong>(高→低):① <code>project.models[role]</code>per-role,含 reviewer/conflict)→ ② 旧 <code>project.model</code>(仅 executor/planner)→ ③ env<code>MAESTRO_MODEL_*</code> / <code>_REVIEW_*</code> / <code>_PLAN_*</code>)→ ④ <code>DEFAULT_MODEL</code></p>
<p>四角色:<code>executor</code> / <code>planner</code> / <code>reviewer</code> / <code>conflict</code>。不可用回退链:<code>opus-4-8 → sonnet-4-6 → fable-5</code>(单 run 内只重试一次)。</p>
<div class="note warn"><strong></strong><code>claude-fable-5</code> 在部分环境不可用,已移出内置默认;原「按复杂度分档(hard=fable / medium=opus / easy=sonnet)」的设计因此改为<strong>统一 opus-4-8 + 项目可配</strong></div>
<!-- ───────── 状态机 ───────── -->
<h2><span class="n"></span> 关键状态转移</h2>
<table>
<thead><tr><th></th><th></th><th>触发</th></tr></thead>
<tbody>
<tr><td><code>init</code></td><td><code>analyzing</code> / <code>speccing</code> / <code>ready</code></td><td>按 hard / medium / easy 路由</td></tr>
<tr><td><code>analyzing</code></td><td><code>plan_review</code></td><td>拆解完成</td></tr>
<tr><td><code>plan_review</code></td><td><code>decomposed</code> / <code>analyzing</code></td><td>accept / reject(+意见)</td></tr>
<tr><td><code>speccing</code></td><td><code>spec_review</code></td><td>方案完成</td></tr>
<tr><td><code>spec_review</code></td><td><code>ready</code> / <code>speccing</code></td><td>accept / reject(+意见)</td></tr>
<tr><td><code>ready</code></td><td><code>queued</code> / <code>blocked</code></td><td>领取 / 依赖未满足</td></tr>
<tr><td><code>queued</code></td><td><code>executing</code></td><td>worker 起跑</td></tr>
<tr><td><code>executing</code></td><td><code>exec_review</code> / <code>failed</code></td><td>执行完成 / 失败</td></tr>
<tr><td><code>exec_review</code></td><td><code>done</code> / <code>ready</code></td><td>accept(合并) / reject(返工)</td></tr>
<tr><td><code>failed</code></td><td><code>queued</code> / <code>needs_attention</code></td><td>重试 / 超阈值</td></tr>
<tr><td><code>decomposed</code></td><td><code>done</code></td><td>子任务全 done</td></tr>
</tbody>
</table>
<p class="src">完整合法转移表见 <code>src/model/status.ts</code><code>TRANSITIONS</code>;状态机/ER 全图见 <a href="optimization-plan.html" style="color:var(--cyan)">优化方案文档</a> ⑤⑥ 节。</p>
<div class="foot">单一入口见 <a href="index.html" style="color:var(--cyan)">docs/index.html</a> · 本文档描述当前线上规则,随实现演进更新 · 与代码不符以代码为准。</div>
</div>
</body>
</html>