docs: maestro 后端重构 Phase1 设计文档(schedule_status × work_type)

8 章设计文档 HTML(架构决策/状态设计/DB Schema/记忆注入/Agent 规格/
运维护栏/安全沙箱/前端 API 契约),全图深色内联 SVG,附 docs/index.html 索引。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-24 13:52:53 +08:00
parent 0a896439d1
commit 1fcd811c12
5 changed files with 4841 additions and 0 deletions
+440
View File
@@ -0,0 +1,440 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MAESTRO · 架构设计</title>
<style>
:root {
--bg: #0a0d0b;
--bg-deep: #070908;
--panel: #111610;
--panel-2: #161c13;
--line: #1e2a1c;
--ink: #c8d4c0;
--muted: #7a9070;
--faint: #445040;
--green: #4ade80;
--green-dim: rgba(74,222,128,.12);
--violet: #a78bfa;
--violet-dim: rgba(167,139,250,.1);
--amber: #fbbf24;
--amber-dim: rgba(251,191,36,.1);
--red: #f87171;
--red-dim: rgba(248,113,113,.1);
--cyan: #22d3ee;
--cyan-dim: rgba(34,211,238,.1);
--mono: 'IBM Plex Mono', 'Noto Sans SC', monospace;
--radius-sm: 4px;
--radius-md: 6px;
}
[data-theme="light"] {
--bg: #f4f6f2; --bg-deep: #eaede7; --panel: #ffffff; --panel-2: #f0f2ee;
--line: #d0d8cc; --ink: #1a2418; --muted: #4a6045; --faint: #8a9e86;
--green: #16a34a; --green-dim: rgba(22,163,74,.1);
--violet: #7c3aed; --violet-dim: rgba(124,58,237,.1);
--amber: #d97706; --amber-dim: rgba(217,119,6,.1);
--red: #dc2626; --red-dim: rgba(220,38,38,.1);
--cyan: #0891b2; --cyan-dim: rgba(8,145,178,.1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
background: var(--bg); color: var(--ink);
font-family: var(--mono); font-size: 13px; line-height: 1.6;
padding: 32px;
}
body::before {
content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
background:
repeating-linear-gradient(0deg, rgba(0,0,0,.14) 0 1px, transparent 1px 3px),
radial-gradient(ellipse at 50% 40%, transparent 55%, rgba(0,0,0,.45));
opacity: .45;
}
[data-theme="light"] body::before { opacity: .07; }
.page { position: relative; z-index: 1; max-width: 1200px; margin: 0 auto; }
.header {
display: flex; align-items: baseline; gap: 16px;
border-bottom: 1px solid var(--line); padding-bottom: 20px; margin-bottom: 32px;
}
.logo { font-size: 18px; font-weight: 700; letter-spacing: .25em; color: var(--green); text-shadow: 0 0 16px var(--green); }
.logo span { animation: blink 1.1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.subtitle { color: var(--muted); font-size: 11px; letter-spacing: .2em; }
.theme-btn {
margin-left: auto; background: var(--panel); border: 1px solid var(--line);
color: var(--muted); font-family: var(--mono); font-size: 11px;
padding: 4px 10px; border-radius: var(--radius-sm); cursor: pointer; letter-spacing: .1em;
}
.theme-btn:hover { color: var(--ink); border-color: var(--muted); }
.sec-head {
display: flex; align-items: center; gap: 8px;
font-size: 10px; font-weight: 600; letter-spacing: .28em;
color: var(--muted); text-transform: uppercase;
margin-bottom: 16px; margin-top: 40px;
}
.sec-mark { width: 3px; height: 14px; border-radius: 2px; }
/* Architecture grid */
.arch { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; margin-bottom: 12px; }
.layer {
border: 1px solid var(--line); border-radius: var(--radius-md);
background: var(--panel); padding: 16px;
}
.layer-title {
font-size: 10px; font-weight: 600; letter-spacing: .18em;
text-transform: uppercase; margin-bottom: 12px;
display: flex; align-items: center; gap: 8px;
}
.dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.span-2 { grid-column: span 2; }
.span-3 { grid-column: span 3; }
.layer-frontend { border-color: rgba(34,211,238,.4); background: linear-gradient(135deg, var(--panel), rgba(34,211,238,.03)); }
.layer-frontend .layer-title { color: var(--cyan); }
.layer-api { border-color: rgba(167,139,250,.4); background: linear-gradient(135deg, var(--panel), rgba(167,139,250,.03)); }
.layer-api .layer-title { color: var(--violet); }
.layer-db { border-color: rgba(251,191,36,.4); background: linear-gradient(135deg, var(--panel), rgba(251,191,36,.03)); }
.layer-db .layer-title { color: var(--amber); }
.layer-daemon { border-color: rgba(74,222,128,.4); background: linear-gradient(135deg, var(--panel), rgba(74,222,128,.03)); }
.layer-daemon .layer-title { color: var(--green); }
.layer-worker { border-color: rgba(248,113,113,.4); background: linear-gradient(135deg, var(--panel), rgba(248,113,113,.03)); }
.layer-worker .layer-title { color: var(--red); }
.modules { display: flex; flex-wrap: wrap; gap: 6px; }
.mod {
font-size: 11px; padding: 4px 8px; border-radius: var(--radius-sm);
border: 1px solid var(--line); background: var(--panel-2); color: var(--muted); line-height: 1.5;
}
.mod b { color: var(--ink); font-weight: 600; display: block; }
.mod small { font-size: 10px; color: var(--faint); }
.mod-c { border-color: rgba(34,211,238,.35); background: var(--cyan-dim); color: var(--cyan); }
.mod-v { border-color: rgba(167,139,250,.35); background: var(--violet-dim); color: var(--violet); }
.mod-a { border-color: rgba(251,191,36,.35); background: var(--amber-dim); color: var(--amber); }
.mod-g { border-color: rgba(74,222,128,.35); background: var(--green-dim); color: var(--green); }
.mod-r { border-color: rgba(248,113,113,.35); background: var(--red-dim); color: var(--red); }
.connector {
grid-column: span 3; display: flex; justify-content: space-around;
padding: 6px 0; color: var(--faint); font-size: 10px; letter-spacing: .12em;
align-items: center;
}
.conn-item { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.conn-line { width: 1px; height: 16px; background: var(--line); }
/* Flow */
.flow {
display: grid; grid-template-columns: repeat(5, 1fr);
border: 1px solid var(--line); border-radius: var(--radius-md); overflow: hidden;
margin-bottom: 40px;
}
.flow-step {
padding: 14px 14px; border-right: 1px solid var(--line); position: relative;
}
.flow-step:last-child { border-right: none; }
.flow-num { font-size: 10px; color: var(--faint); letter-spacing: .15em; margin-bottom: 4px; }
.flow-title { font-size: 12px; font-weight: 600; color: var(--ink); margin-bottom: 4px; }
.flow-desc { font-size: 11px; color: var(--muted); line-height: 1.5; }
.flow-arr {
position: absolute; right: -9px; top: 50%; transform: translateY(-50%);
color: var(--faint); font-size: 16px; z-index: 2; background: var(--panel); padding: 2px 1px;
}
/* Detail grid */
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 40px; }
.detail-card {
border: 1px solid var(--line); border-radius: var(--radius-md);
background: var(--panel); padding: 16px;
}
.detail-card h3 { font-size: 11px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; margin-bottom: 10px; }
.detail-card ul { list-style: none; display: flex; flex-direction: column; gap: 6px; }
.detail-card li { font-size: 11px; color: var(--muted); display: flex; gap: 8px; align-items: flex-start; }
.detail-card li::before { content: '→'; color: var(--faint); flex-shrink: 0; margin-top: 1px; }
.detail-card li b { color: var(--ink); }
.note { font-size: 10px; color: var(--faint); display: block; margin-top: 1px; }
/* File tree */
.file-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 40px; }
.file-tree {
background: var(--bg-deep); border: 1px solid var(--line);
border-radius: var(--radius-md); padding: 20px;
font-size: 12px; line-height: 2; margin-bottom: 40px;
}
.ft-dir { color: var(--cyan); font-weight: 600; }
.ft-file { color: var(--muted); }
.ft-new { color: var(--green); }
.ft-del { color: var(--red); text-decoration: line-through; opacity: .55; }
.ft-c { color: var(--faint); font-size: 10px; }
.i1 { padding-left: 16px; }
.i2 { padding-left: 32px; }
.i3 { padding-left: 48px; }
</style>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="page">
<div class="header">
<div class="logo">MAESTRO<span></span></div>
<div class="subtitle">架构设计 · ARCHITECTURE DESIGN v2.0 DRAFT</div>
<button class="theme-btn" onclick="document.documentElement.dataset.theme=document.documentElement.dataset.theme==='dark'?'light':'dark'">⊙ 切换主题</button>
</div>
<!-- 全景架构 -->
<div class="sec-head"><div class="sec-mark" style="background:var(--green)"></div>全景架构 · SYSTEM OVERVIEW</div>
<div class="arch">
<!-- Frontend -->
<div class="layer layer-frontend span-3">
<div class="layer-title"><div class="dot" style="background:var(--cyan);box-shadow:0 0 8px var(--cyan)"></div>前端层 · FRONTEND — React 18 + TypeScript + Vite</div>
<div class="modules">
<div class="mod mod-c"><b>Design System 组件</b><small>从 claude.ai/design 迁移 · TSX</small></div>
<div class="mod mod-c"><b>三栏看板</b><small>Sidebar · Main · EventPanel · 拖拽宽度</small></div>
<div class="mod mod-c"><b>Mobile Tab</b><small>390px · 底部四 Tab 导航</small></div>
<div class="mod mod-c"><b>Zustand</b><small>客户端状态管理</small></div>
<div class="mod mod-c"><b>i18n · 5 语言</b><small>zh / en / es / ja / fr</small></div>
<div class="mod mod-c"><b>WebSocket Client</b><small>实时看板刷新</small></div>
<div class="mod"><b>Vite Dev Server</b><small>开发时 API 代理 · HMR</small></div>
<div class="mod"><b>dist/web/</b><small>构建产物 → Fastify 托管</small></div>
</div>
</div>
<div class="connector">
<div class="conn-item"><div class="conn-line"></div><span>REST · /api/*</span><div class="conn-line"></div></div>
<div class="conn-item"><div class="conn-line"></div><span>WebSocket · /ws</span><div class="conn-line"></div></div>
<div class="conn-item"><div class="conn-line"></div><span style="opacity:.4">Auth Token(预留)</span><div class="conn-line"></div></div>
</div>
<!-- API -->
<div class="layer layer-api span-2">
<div class="layer-title"><div class="dot" style="background:var(--violet);box-shadow:0 0 8px var(--violet)"></div>API 层 · Fastify(路由拆分 + Schema 校验)</div>
<div class="modules">
<div class="mod mod-v"><b>routes/projects</b><small>CRUD · logo · sync · reorder</small></div>
<div class="mod mod-v"><b>routes/tasks</b><small>CRUD · decide · requeue · transition</small></div>
<div class="mod mod-v"><b>routes/runs</b><small>transcript · active runs</small></div>
<div class="mod mod-v"><b>routes/approvals</b><small>pendingApprovals</small></div>
<div class="mod mod-v"><b>routes/metrics</b><small>健康度 · 成本聚合</small></div>
<div class="mod mod-v"><b>ws.ts</b><small>Store 事件 → 广播所有客户端</small></div>
<div class="mod"><b>middleware/auth.ts</b><small>no-op · 预留 JWT 插槽</small></div>
<div class="mod"><b>schemas/</b><small>Fastify JSON Schema 请求校验</small></div>
</div>
</div>
<!-- DB -->
<div class="layer layer-db">
<div class="layer-title"><div class="dot" style="background:var(--amber);box-shadow:0 0 8px var(--amber)"></div>数据层 · DB ABSTRACTION</div>
<div class="modules">
<div class="mod mod-a"><b>DBAdapter 接口</b><small>统一抽象</small></div>
<div class="mod mod-a"><b>SqliteAdapter</b><small>当前实现</small></div>
<div class="mod" style="border-style:dashed;opacity:.55"><b>PostgresAdapter</b><small>未来多租户</small></div>
<div class="mod mod-a"><b>ProjectRepo</b></div>
<div class="mod mod-a"><b>TaskRepo</b></div>
<div class="mod mod-a"><b>RunRepo</b></div>
<div class="mod mod-a"><b>ApprovalRepo</b></div>
<div class="mod mod-a"><b>EventRepo</b></div>
<div class="mod mod-a"><b>MetricsRepo</b></div>
</div>
</div>
<div class="connector">
<div class="conn-item"><div class="conn-line"></div><span>读写 DB</span><div class="conn-line"></div></div>
<div class="conn-item"><div class="conn-line"></div><span>事件订阅 → WS 推送</span><div class="conn-line"></div></div>
<div class="conn-item"><div class="conn-line"></div><span>调度查询</span><div class="conn-line"></div></div>
</div>
<!-- Daemon -->
<div class="layer layer-daemon span-2">
<div class="layer-title"><div class="dot" style="background:var(--green);box-shadow:0 0 8px var(--green)"></div>Daemon 层(拆分 4 职责)</div>
<div class="modules">
<div class="mod mod-g"><b>Orchestrator</b><small>协调者 · tick 主循环</small></div>
<div class="mod mod-g"><b>Scheduler</b><small>纯调度逻辑 · score → 选任务</small></div>
<div class="mod mod-g"><b>WorkerManager</b><small>spawn / reap / pid 管理</small></div>
<div class="mod mod-g"><b>Ingestor</b><small>outbox.ndjson → DB · 幂等摄取</small></div>
<div class="mod mod-g"><b>MergeCoordinator</b><small>merge-resolve 池 + settle</small></div>
<div class="mod"><b>config.ts</b></div>
<div class="mod"><b>notify.ts</b></div>
</div>
</div>
<!-- Worker -->
<div class="layer layer-worker">
<div class="layer-title"><div class="dot" style="background:var(--red);box-shadow:0 0 8px var(--red)"></div>Worker 进程(按类型拆分)</div>
<div class="modules">
<div class="mod mod-r"><b>pipelines/executor</b></div>
<div class="mod mod-r"><b>pipelines/planner</b></div>
<div class="mod mod-r"><b>pipelines/conflict</b></div>
<div class="mod mod-r"><b>pipelines/merge-resolve</b></div>
<div class="mod mod-r"><b>runners/executor</b><small>prompt + runTask</small></div>
<div class="mod mod-r"><b>runners/planner</b><small>prompt + runPlanner</small></div>
<div class="mod mod-r"><b>runners/conflict</b></div>
<div class="mod mod-r"><b>runners/merge-resolve</b></div>
<div class="mod" style="border-style:dashed"><b>文件协议(保留不变)</b><small>job.json → outbox.ndjson → heartbeat</small></div>
</div>
</div>
</div><!-- /arch -->
<!-- 执行数据流 -->
<div class="sec-head"><div class="sec-mark" style="background:var(--cyan)"></div>任务执行数据流 · EXECUTION FLOW</div>
<div class="flow">
<div class="flow-step" style="background:var(--violet-dim)">
<div class="flow-num">01 · 触发</div>
<div class="flow-title">用户 / API</div>
<div class="flow-desc">POST /api/.../tasks<br>创建任务 → init 态</div>
<div class="flow-arr"></div>
</div>
<div class="flow-step" style="background:var(--green-dim)">
<div class="flow-num">02 · 调度</div>
<div class="flow-title">Scheduler</div>
<div class="flow-desc">score 排序 → claimable<br>ready → queued → executing</div>
<div class="flow-arr"></div>
</div>
<div class="flow-step" style="background:var(--green-dim)">
<div class="flow-num">03 · 起进程</div>
<div class="flow-title">WorkerManager</div>
<div class="flow-desc">spawn worker 进程<br>写 job.json · 记 pid</div>
<div class="flow-arr"></div>
</div>
<div class="flow-step" style="background:var(--red-dim)">
<div class="flow-num">04 · 执行</div>
<div class="flow-title">Worker Pipeline</div>
<div class="flow-desc">CC Agent 执行任务<br>追加 outbox.ndjson</div>
<div class="flow-arr"></div>
</div>
<div class="flow-step" style="background:var(--amber-dim)">
<div class="flow-num">05 · 摄取</div>
<div class="flow-title">Ingestor</div>
<div class="flow-desc">outbox → DB<br>→ WS 推送 → 审核闸</div>
</div>
</div>
<!-- 重构变更 -->
<div class="sec-head"><div class="sec-mark" style="background:var(--amber)"></div>重构变更对照 · WHAT CHANGES</div>
<div class="detail-grid">
<div class="detail-card">
<h3 style="color:var(--red)">✕ 删除</h3>
<ul>
<li><b>web/app.js</b> (2473 行)<span class="note">替换为 React + TypeScript 前端</span></li>
<li><b>web/style.css</b> (1333 行)<span class="note">设计 token 迁移到 frontend/src/tokens/</span></li>
<li><b>web/index.html</b><span class="note">Vite 入口替代</span></li>
</ul>
</div>
<div class="detail-card">
<h3 style="color:var(--green)">✓ 保留不动</h3>
<ul>
<li><b>文件协议</b> — job.json / outbox.ndjson / heartbeat</li>
<li><b>状态机</b> — src/model/status.ts · TRANSITIONS</li>
<li><b>Score 调度算法</b> — src/model/scoring.ts</li>
<li><b>CC 封装</b> — src/executor/cc.ts</li>
<li><b>Schema SQL</b> — src/store/schema.sql</li>
<li><b>MCP server</b> — src/mcp/</li>
<li><b>CLI</b> — src/cli/</li>
</ul>
</div>
<div class="detail-card">
<h3 style="color:var(--amber)">⟳ 拆分重构</h3>
<ul>
<li><b>store.ts</b> (1227行) → <span style="color:var(--amber)">6 Repo + DBAdapter 接口</span></li>
<li><b>orchestrator.ts</b> (426行) → <span style="color:var(--amber)">Scheduler + WorkerManager + Ingestor + MergeCoordinator</span></li>
<li><b>runner.ts</b> (353行) → <span style="color:var(--amber)">runners/ 4 个独立文件</span></li>
<li><b>pipeline.ts</b> (358行) → <span style="color:var(--amber)">pipelines/ 4 个独立文件</span></li>
<li><b>server.ts</b> (401行) → <span style="color:var(--amber)">routes/ 5 个路由模块 + Schema</span></li>
</ul>
</div>
<div class="detail-card">
<h3 style="color:var(--cyan)">✦ 新增</h3>
<ul>
<li><b>frontend/</b> — React + TypeScript + Vite 工程</li>
<li><b>frontend/components/</b> — claude design → TSX 组件库</li>
<li><b>src/store/db.ts</b> — DBAdapter 抽象接口</li>
<li><b>src/store/repos/</b> — 6 个职责单一的 Repository</li>
<li><b>src/api/middleware/auth.ts</b> — Auth 插槽(no-op</li>
<li><b>src/api/schemas/</b> — Fastify JSON Schema 校验</li>
</ul>
</div>
</div>
<!-- 目录结构 -->
<div class="sec-head"><div class="sec-mark" style="background:var(--violet)"></div>目录结构 · FILE STRUCTURE</div>
<div class="file-tree">
<div class="file-grid">
<div>
<div class="ft-dir">frontend/ <span class="ft-new">← 全新</span></div>
<div class="i1 ft-new">src/</div>
<div class="i2 ft-new">main.tsx</div>
<div class="i2 ft-new">App.tsx <span class="ft-c">三栏布局 · 状态根</span></div>
<div class="i2 ft-new">components/ <span class="ft-c">← claude design 迁移</span></div>
<div class="i3 ft-new">core/ <span class="ft-c">Button · StatusChip · ...</span></div>
<div class="i3 ft-new">forms/ <span class="ft-c">Input · Select · ...</span></div>
<div class="i3 ft-new">surfaces/ <span class="ft-c">Panel · GateCard · ...</span></div>
<div class="i2 ft-new">ui/ <span class="ft-c">页面级组件</span></div>
<div class="i3 ft-new">Sidebar.tsx</div>
<div class="i3 ft-new">Topbar.tsx</div>
<div class="i3 ft-new">GateSection.tsx</div>
<div class="i3 ft-new">TaskTree.tsx</div>
<div class="i3 ft-new">EventPanel.tsx</div>
<div class="i3 ft-new">ArchiveSection.tsx</div>
<div class="i2 ft-new">api/ <span class="ft-c">HTTP + WS 客户端</span></div>
<div class="i2 ft-new">store/ <span class="ft-c">Zustand 状态管理</span></div>
<div class="i2 ft-new">i18n/ <span class="ft-c">5 语言字符串</span></div>
<div class="i2 ft-new">tokens/ <span class="ft-c">CSS 自定义属性</span></div>
<div class="i1 ft-new">index.html</div>
<div class="i1 ft-new">vite.config.ts</div>
<div class="i1 ft-new">tsconfig.json</div>
<br>
<div class="ft-del">web/ <span class="ft-c">整体删除</span></div>
<div class="i1 ft-del">app.js <span class="ft-c">2473 行</span></div>
<div class="i1 ft-del">style.css <span class="ft-c">1333 行</span></div>
<div class="i1 ft-del">index.html</div>
</div>
<div>
<div class="ft-dir">src/</div>
<div class="i1 ft-dir">api/</div>
<div class="i2 ft-file">server.ts <span class="ft-c">入口(精简为注册插件)</span></div>
<div class="i2 ft-new">routes/ <span class="ft-c">← 拆分</span></div>
<div class="i3 ft-new">projects.ts · tasks.ts · runs.ts</div>
<div class="i3 ft-new">approvals.ts · metrics.ts</div>
<div class="i2 ft-new">schemas/ <span class="ft-c">JSON Schema 请求校验</span></div>
<div class="i2 ft-new">middleware/auth.ts <span class="ft-c">no-op 插槽</span></div>
<br>
<div class="i1 ft-dir">store/</div>
<div class="i2 ft-new">db.ts <span class="ft-c">DBAdapter 接口</span></div>
<div class="i2 ft-new">sqlite.ts <span class="ft-c">当前实现</span></div>
<div class="i2 ft-new">repos/ <span class="ft-c">← 拆分自 store.ts</span></div>
<div class="i3 ft-new">ProjectRepo · TaskRepo · RunRepo</div>
<div class="i3 ft-new">ApprovalRepo · EventRepo · MetricsRepo</div>
<div class="i2 ft-file">index.ts <span class="ft-c">Store 聚合(外部接口不变)</span></div>
<div class="i2 ft-file">mappers.ts <span class="ft-c">保留</span></div>
<br>
<div class="i1 ft-dir">daemon/</div>
<div class="i2 ft-new">scheduler.ts <span class="ft-c">← 拆分 · 纯调度逻辑</span></div>
<div class="i2 ft-new">worker-manager.ts <span class="ft-c">← 拆分 · 进程管理</span></div>
<div class="i2 ft-file">ingest.ts <span class="ft-c">保留接口不变</span></div>
<div class="i2 ft-new">merge-coordinator.ts <span class="ft-c">← 拆分</span></div>
<div class="i2 ft-new">orchestrator.ts <span class="ft-c">精简为协调者</span></div>
<br>
<div class="i1 ft-dir">executor/</div>
<div class="i2 ft-new">runners/ <span class="ft-c">← 拆分自 runner.ts</span></div>
<div class="i3 ft-new">executor · planner · conflict · merge-resolve</div>
<div class="i2 ft-new">pipelines/ <span class="ft-c">← 拆分自 pipeline.ts</span></div>
<div class="i3 ft-new">executor · planner · conflict · merge-resolve</div>
<div class="i2 ft-file">cc.ts · models.ts · worktree.ts <span class="ft-c">保留</span></div>
</div>
</div>
</div>
<div style="padding:14px 16px; border:1px solid var(--line); border-radius:var(--radius-md); background:var(--panel); color:var(--faint); font-size:11px; letter-spacing:.08em;">
MAESTRO ARCHITECTURE · v2.0 DRAFT · 仅设计文档,尚未实现 · <span style="color:var(--green)">保留</span> · <span style="color:var(--amber)">拆分重构</span> · <span style="color:var(--cyan)">新增</span> · <span style="color:var(--red)">删除</span>
</div>
</div>
</body>
</html>
+96
View File
@@ -0,0 +1,96 @@
<!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)}
.wrap{max-width:1000px;margin:0 auto;padding:32px 24px 80px}
.lead{font-size:13px;color:var(--text2);margin:0 0 28px;line-height:1.8}
.lead strong{color:var(--text)}
.cat{margin:0 0 30px}
.cat-hdr{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:600;color:var(--text);margin:0 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.cat-hdr .ico{font-size:11px;color:var(--cyan);border:1px solid var(--cyan2);border-radius:4px;padding:1px 7px}
.cat-hdr .cnt{font-size:11px;color:var(--text3);font-weight:400}
.doc{display:block;background:var(--bg2);border:1px solid var(--border);border-left:3px solid var(--border2);border-radius:6px;padding:14px 16px;margin:0 0 10px;text-decoration:none;color:inherit;transition:border-color .15s,background .15s}
.doc:hover{border-color:var(--cyan2);border-left-color:var(--cyan);background:var(--bg3)}
.doc.plan{border-left-color:var(--violet2)}
.doc.plan:hover{border-left-color:var(--violet)}
.doc.src{border-left-color:var(--amber2)}
.doc.src:hover{border-left-color:var(--amber)}
.doc-ttl{font-size:14px;font-weight:600;color:var(--text)}
.doc:hover .doc-ttl{color:var(--cyan)}
.doc.plan:hover .doc-ttl{color:var(--violet)}
.doc.src:hover .doc-ttl{color:var(--amber)}
.doc-path{font-size:11px;color:var(--text3);margin:3px 0 6px}
.doc-desc{font-size:12px;color:var(--text2);line-height:1.7}
.tags{margin-top:8px;display:flex;gap:6px;flex-wrap:wrap}
.tag{font-size:10px;padding:1px 7px;border-radius:3px;border:1px solid}
.tag.design{color:var(--blue);border-color:var(--blue2);background:rgba(85,153,255,.08)}
.tag.plan{color:var(--violet);border-color:var(--violet2);background:rgba(155,123,255,.08)}
.tag.src{color:var(--amber);border-color:var(--amber2);background:rgba(240,165,0,.08)}
.tag.html{color:var(--cyan);border-color:var(--cyan2);background:rgba(0,212,212,.08)}
.tag.md{color:var(--text2);border-color:var(--border2);background:rgba(255,255,255,.03)}
.empty{font-size:12px;color:var(--text3);font-style:italic;padding:6px 2px}
.foot{margin-top:36px;font-size:11px;color:var(--text3);border-top:1px solid var(--border);padding-top:14px}
</style>
</head>
<body>
<div class="topbar">
<span class="topbar-brand">MAESTRO</span>
<span class="topbar-sub">· 文档索引 / docs index</span>
</div>
<div class="wrap">
<p class="lead">Maestro 项目全部文档的单一入口。按 <strong>设计方案 / 实现计划 / 知识库调研 / 排障 Runbook</strong> 分类汇总(HTML 与历史 MD 都列)。<strong>新增文档须同时登记此处。</strong></p>
<!-- ── 设计方案 ── -->
<div class="cat">
<div class="cat-hdr"><span class="ico">设计</span> 设计方案 / 架构设计 <span class="cnt">· 1</span></div>
<a class="doc" href="architecture.html">
<div class="doc-ttl">MAESTRO · 架构设计</div>
<div class="doc-path">docs/architecture.html</div>
<div class="doc-desc">系统整体架构设计:进程模型(daemon 唯一 DB 写者 / worker 文件协议)、前后端、数据流、调度与执行管道总览。</div>
<div class="tags"><span class="tag design">架构</span><span class="tag html">HTML</span></div>
</a>
</div>
<!-- ── 实现计划 ── -->
<div class="cat">
<div class="cat-hdr"><span class="ico">计划</span> 实现计划 <span class="cnt">· 2</span></div>
<a class="doc plan" href="superpowers/plans/2026-06-22-maestro-refactor-phase1.html">
<div class="doc-ttl">Maestro 大重构全景 · 现状 → 目标 → 计划</div>
<div class="doc-path">docs/superpowers/plans/2026-06-22-maestro-refactor-phase1.html</div>
<div class="doc-desc">扁平 FSM → <strong>schedule_status × work_type</strong> 正交模型的完整技术蓝图(HTML 阅读版)。四标签:现状架构 / 重构目标 / 设计方案 / 具体实现。专题含:状态设计、<strong>任务提交与入口(网页/MCP/todo 同步三入口·图片文件附件·AI 提任务)</strong>、并发调度(CAS·couplingPenalty)、<strong>交互平面·stuck 人工接管</strong>、数据库 SchemaER 图)、Agent 记忆注入 L1L4、<strong>Agent 规格与注册表(每 agent 流程/模型/skill/prompt/上下文 + 动态扩展)</strong><strong>运维与护栏(成本预算治理·限流背压·资源 GC·可观测·依赖环检测)</strong><strong>安全与沙箱</strong><strong>前端 API 契约(REST 全量 + WS 事件)</strong>、Phase 1 逐步实施计划。全部图表为深色内联 SVG(自包含、无 CDN)。</div>
<div class="tags"><span class="tag plan">实现计划</span><span class="tag design">设计</span><span class="tag html">HTML 阅读版</span></div>
</a>
<a class="doc src" href="superpowers/plans/2026-06-22-maestro-refactor-phase1.md">
<div class="doc-ttl">同上 · Markdown 执行真相源</div>
<div class="doc-path">docs/superpowers/plans/2026-06-22-maestro-refactor-phase1.md</div>
<div class="doc-desc">上面 HTML 的同源 <code>.md</code>,保留 <code>- [ ]</code> checkbox 供 executing-plans / subagent-driven-development 驱动执行跟踪。<strong>.md 是执行真相源,.html 仅供阅读。</strong></div>
<div class="tags"><span class="tag src">执行真相源</span><span class="tag md">MD</span></div>
</a>
</div>
<!-- ── 知识库调研 ── -->
<div class="cat">
<div class="cat-hdr"><span class="ico">调研</span> 知识库调研 <span class="cnt">· 0</span></div>
<div class="empty">(暂无)</div>
</div>
<!-- ── 排障 Runbook ── -->
<div class="cat">
<div class="cat-hdr"><span class="ico">排障</span> 排障 Runbook <span class="cnt">· 0</span></div>
<div class="empty">(暂无)</div>
</div>
<div class="foot">单一入口 · 直接 file:// 打开 · 新增 / 迁移文档请同步登记本页。</div>
</div>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,517 @@
# Maestro 重构设计文档
> 状态:草稿 · 2026-06-22
> 范围:Task 模型 · 调度算法 · 流式通信 · Hook 可观测性 · 前端重构 · 后端拆分
---
## 1. 背景与目标
Maestro 是一个本地优先的 Git 任务编排守护进程,驱动多项目的 headless Claude Code agent 自动执行代码任务。当前版本(约 1.0)已具备基础的"创建 → 方案 → 执行 → 审批 → 合并"完整闭环,但存在以下痛点需要通过重构解决:
| 类别 | 问题 |
|---|---|
| Task 模型 | 缺少 `task_type`/`scope`/`ownedFiles`/`expected_output`,拆解产物元数据不足 |
| 调度算法 | 简单优先级排序,缺少依赖链 unlock 价值、饥饿保护、文件耦合感知 |
| 执行管道 | 并发 Code+Security 审查是串行的;verify 过于单一;审查 verdict=reject 不挡 |
| 流式通信 | agent 执行中实时 token 无法到达前端;无 human-in-the-loop 动态暂停 |
| 可观测性 | outbox phase 不广播 WS;无 Trace ID 跨进程;transcript 无 Web 检索 |
| 代码组织 | `store.ts`(1227 行) God Object、`orchestrator.ts`(426 行)、`web/app.js`(2473 行) 亟需拆分 |
| 前端 | 纯原生 JS SPA,无组件化,无类型安全,与 Claude Design System 脱节 |
**重构目标**
1. Task 模型增强(分类/文件所有权/期望产出)
2. 调度算法升级为 CPM-based rankU + 老化加成 + 耦合感知
3. 执行管道三类优化(已在 plan 中详细定稿)
4. SSE 流式通信 + Hook 拦截 + Trace ID 传播
5. 后端按职责拆分(store Repos / daemon 四组件 / API routes
6. 前端迁移至 React 18 + TypeScript + Vite + Zustand,对接 Claude Design System
---
## 2. Task 模型增强
### 2.1 新增字段
```typescript
// src/model/types.ts — Task 接口扩展
export type TaskType = 'feature' | 'bugfix' | 'refactor' | 'chore' | 'docs';
export type TaskScope = 'file' | 'module' | 'service' | 'cross-service';
export interface Task {
// === 现有字段(保留)===
id, projectId, parentId, depth, title, complexity, status,
priority, deps, plan, spec, operations, approvals, result,
assignee, retryBaseline, nextEligibleAt, lastRunError, createdAt, updatedAt
// === 新增字段 ===
taskType: TaskType | null; // 任务分类(feature/bugfix/refactor/chore/docs
scope: TaskScope | null; // 改动范围维度
ownedFiles: string[]; // 声明的文件所有权(冲突检测用)
expectedOutput: string | null; // "done" 的可验证描述,供 exec_review 对照
parentVersionId: string | null; // reject 后新建版本指向前一版,构成版本链
version: number; // 任务版本号(每次 reject 递增)
}
```
### 2.2 ownedFiles 冲突检测
`orchestrator.ts``claimable()` 中增加文件交集检查:
```typescript
// 已声明 ownedFiles 的在途任务集合
function hasFileConflict(candidate: Task, inFlight: Task[]): boolean {
if (!candidate.ownedFiles?.length) return false;
const candidateSet = new Set(candidate.ownedFiles);
return inFlight.some(t =>
t.ownedFiles?.some(f => candidateSet.has(f))
);
}
```
- 文件交集 → 降低 score(-0.5/个重叠文件),不硬 block(防饥饿)
- `agingBonus` 兜底:等待超 48h 的任务最多加 1 点,确保不被永久回避
### 2.3 自动元数据填充(MCP 工具)
新增 MCP 工具 `suggest_task_metadata`:人工输入 `title` 后,调用 sonnet 分析仓库上下文自动推断 `taskType`/`scope`/`ownedFiles`/`expectedOutput`,人工确认后写入。触发:MCP 工具调用 or UI "智能填充"按钮。
### 2.4 planner 输出扩展
Hard/Medium 任务 planner 的 decompose JSON 格式扩展:
```json
{
"plan": "...(分析正文)...",
"subtasks": [
{
"title": "类型定义与接口",
"complexity": "easy",
"priority": 0,
"deps": [],
"ownedFiles": ["src/model/types.ts"],
"expectedOutput": "类型文件通过 typecheck"
},
{
"title": "TaskRepo 实现",
"complexity": "medium",
"priority": 1,
"deps": [0],
"ownedFiles": ["src/store/taskRepo.ts"],
"expectedOutput": "TaskRepo CRUD 方法通过单测"
}
]
}
```
- 正文先输出 Markdown 子任务表格(供 plan_review 人审)
- JSON 块作为机器解析源,`deps` 用子任务数组序号引用
- daemon `ingest.ts` 在子任务全建好后做"序号→taskId"二次映射
---
## 3. 调度算法升级(CPM-based rankU
### 3.1 算法设计
**CPMCritical Path Method)后向传播** 替代简单优先级排序:
```typescript
// src/model/scoring.ts
/** 递归计算任务向后传播的 unlock 价值 */
function rankU(taskId: string, cache: Map<string, number>): number {
if (cache.has(taskId)) return cache.get(taskId)!;
const task = getTask(taskId);
const base = baseScore(task); // P0=3, P1=2, P2=1
const unlockValue = dependents(taskId)
.reduce((sum, dep) => sum + rankU(dep.id, cache), 0);
const result = base + unlockValue;
cache.set(taskId, result);
return result;
}
/** 老化加成:等待越久加分越多,防饥饿 */
function agingBonus(task: Task): number {
const base = baseScore(task);
const waitHours = (Date.now() - new Date(task.createdAt).getTime()) / 3600000;
return Math.min(base, waitHours / 48); // 48h 达到 base 上限
}
/** 耦合惩罚:文件重叠 */
function couplingPenalty(task: Task, inFlight: Task[]): number {
const overlaps = inFlight.reduce((sum, t) => {
const shared = (task.ownedFiles ?? []).filter(f => t.ownedFiles?.includes(f));
return sum + shared.length;
}, 0);
return overlaps * 0.5;
}
/** 最终调度得分 */
function scheduleScore(task: Task, completedDeps: Task[], inFlight: Task[]): number {
return rankU(task.id, new Map())
+ completedDeps.reduce((s, d) => s + baseScore(d), 0) // 链惯性
+ agingBonus(task)
- couplingPenalty(task, inFlight);
}
```
### 3.2 CAS 防双重 claim
```sql
-- orchestrator claim 阶段
UPDATE tasks
SET status = 'queued', claimed_at = datetime('now')
WHERE id = ? AND status = 'ready' AND claimed_at IS NULL
```
**重要**:所有使 task 回到 `ready` 的转移(reject/requeue/retry)必须同时清空 `claimed_at`
```sql
UPDATE tasks SET status = 'ready', claimed_at = NULL WHERE id = ?
```
否则 retry 任务永远命中 `claimed_at IS NULL` = false,无法再被 claim。
### 3.3 调度决策日志
每次 claim 记录结构化日志:
```json
{
"taskId": "t-001",
"score": 4.5,
"breakdown": {
"rankU": 3.0,
"chainInertia": 1.0,
"agingBonus": 0.5,
"couplingPenalty": 0.0
},
"competitors": [...]
}
```
---
## 4. 三类执行管道优化(定稿,已在 plan 中逐维确认)
`.claude/plans/tidy-jumping-shell.md` 的完整定稿小结,此处仅列关键决策:
### 4.1 Planner(任务拆解)
| 维度 | 决策 |
|---|---|
| 触发/执行期锁 | 在途即锁只读:禁改、禁再调度,run 结束解锁 |
| 模型/档位 | hard=fable-5 / medium=opus-4-8 / easy=sonnet-4-6env 三档可覆盖) |
| 输出格式 | 子任务表格(人审) + 扩展 JSON{title,complexity,priority,deps,ownedFiles,expectedOutput} |
| 审批 | 默认人审;可选 auto-approved+全 easy+改动小 自动放行(默认关) |
### 4.2 Executor(代码执行)
| 维度 | 决策 |
|---|---|
| 双复审 | code + security 改 `Promise.all` **并行**(≤15min 替代 ≤30min |
| 复审模型 | 统一 fable-5,不被 project.model 降档 |
| 新增硬闸 | 分项 checkslint/typecheck/build+ diff 越界/体量闸 + verdict=reject 变硬闸 |
| 执行前同步 | createWorktree 后先 merge main;分歧超阈值 → needs_attention 重评估 |
### 4.3 ConflictResolver(解冲突)
| 维度 | 决策 |
|---|---|
| 架构 | 新增 `runKind=conflict`;专用 pipeline:真正 git merge → CC 解 → commit → 复审 |
| 模型 | 固定 fable-5,不随原任务复杂度降档 |
| 调度 | 插队/预留名额,优先于普通 executor |
| 白名单 | 仅 conflict pipeline 开放 `Bash(git merge:*)` |
### 4.4 通用规则(摘录)
- **执行期锁**:在途 run → task 只读,run 结束解锁
- **复审独立性**:复审只读、用最强模型;verdict=reject 变硬闸
- **模型回退链**`fable-5 → opus-4-8 → sonnet-4-6`(单 run 只重试一次)
- **Reflect 阶段**:连续失败 2 次,agent 先分析失败原因再 retry(非盲目重试)
- **stages.json 检查点**pipeline 各阶段写入完成状态,重启后跳过已完成阶段
---
## 5. 流式通信(Q7
### 5.1 SSE 实时 token 推送
**架构**`cc.ts for-await``onToken 回调``daemon EventEmitter (per runId)``SSE /api/tasks/:id/stream`
```typescript
// cc.ts - 已有 for await,增加 onToken 钩子
for await (const message of q) {
out.write(JSON.stringify(message) + '\n'); // 保留:持久化到 transcript
options.onToken?.(message); // 新增:实时推送回调
}
// server.ts - 新增 SSE 端点(以 taskId 为索引,内部映射到当前活跃 runId)
fastify.get('/api/tasks/:taskId/stream', (req, reply) => {
reply.raw.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
// 查找该 task 当前 in-progress 的 run,按 runId 注册 emitter
const activeRunId = store.getActiveRunId(req.params.taskId);
const emitter = activeRunId ? tokenEmitters.get(activeRunId) : null;
emitter?.on('token', (msg) => reply.raw.write(`data: ${JSON.stringify(msg)}\n\n`));
});
```
**端点设计**:URL 用 taskId(对前端友好),内部 `tokenEmitters` 以 runId 为键。一个 task 可有多次 run,端点始终映射到最新 in-progress runrun 结束时清理对应 emitter。
**选型理由**SSE 天然支持重连+Last-Event-ID(断网续读),无需引入 gRPC/额外 WebSocket。
### 5.2 类 Chat 任务(执行中注入指令)
```
POST /api/tasks/:id/inject { message: string }
→ daemon 写入 runs/<runId>/inbox.json
→ worker 在 pipeline 断点轮询 inbox.json
→ cc.ts resume() 带入新内容
```
不做真正交互式会话——worker 是 subprocess,双向实时通道复杂度过高。实际方案:中途补充指令 → 追加到 next prompt turn。
### 5.3 动态暂停(Human-in-the-loop 增强)
新增 OutboxRecord 类型:
```typescript
// src/executor/protocol.ts
type OutboxRecord =
| { type: 'phase'; phase: string }
| { type: 'result'; ... }
| { type: 'clarify'; question: string; runId: string } // 新增
| { type: 'error'; ... }
```
状态机新增 `awaiting_input`(在 `executing``exec_review` 之间):
```
executing → awaiting_input (agent 输出 <ask>...</ask>)
awaiting_input → executing (POST /api/tasks/:id/reply 提供答复)
```
---
## 6. Hook 拦截与可观测性(Q8
### 6.1 Hook 契约
```typescript
// .maestro/hooks.ts(项目级)或 ~/.maestro/hooks.ts(全局级)
export interface MaestroHooks {
'before:task:claim'?: (task: Task) => Promise<void | { cancel: string }>;
'after:planner:output'?: (task: Task, plan: string) => Promise<string>; // 可改 plan
'before:execute'?: (task: Task, job: JobSpec) => Promise<void | { cancel: string }>;
'before:merge'?: (task: Task, branch: string) => Promise<void | { cancel: string }>;
'on:conflict'?: (task: Task, files: string[]) => Promise<'auto' | 'manual'>;
'before:exec-review'?: (task: Task, result: TaskResult) => Promise<void | { cancel: string }>;
}
```
- 超时 5s → 等同于 cancel
- Shell script 方式:`.maestro/hooks/before-execute.sh`exit != 0 = cancelstdout = reason
- 项目级优先,全局兜底
### 6.2 Trace ID 传播
```typescript
// src/executor/protocol.ts - JobSpec 新增
interface JobSpec {
runId: string;
taskId: string;
traceId: string; // 新增:daemon 写 job.json 时 crypto.randomUUID()
// ...
}
```
- Worker 所有 `appendOutbox` 记录携带 `traceId`
- Dashboard 可按 traceId 聚合 plan → execute → review → merge 的完整链路
### 6.3 Phase 事件广播
`ingest.ts` 处理 `phase` 记录时,增加 WebSocket 广播:
```typescript
case 'phase':
log.info({ taskId, runId, phase: record.phase }, 'pipeline phase');
store.broadcast({ type: 'run.phase', taskId, runId, phase: record.phase }); // 新增
break;
```
前端看板实时显示"分析中 / 执行中 / 验证中 / 复审中"进度条。
### 6.4 Transcript 回放
```
GET /api/tasks/:id/transcript → 流式返回 runs/<runId>/transcript.jsonl
GET /api/tasks/:id/transcript?q=keyword → 服务端 grep 返回匹配行
```
无需 ElasticSearch,本地 JSONL grep 即可。
---
## 7. 后端代码结构重构
### 7.1 store.ts 拆分
```
src/store/
├── db.ts # DBAdapter 接口(SqliteAdapter / future PostgresAdapter
├── store.ts # 入口(组合所有 Repos,提供 subscribe/broadcast
├── projectRepo.ts # Project CRUD
├── taskRepo.ts # Task CRUD + 状态机守卫
├── runRepo.ts # Run CRUD
├── approvalRepo.ts # ApprovalRecord
├── eventRepo.ts # Event 追加 + 查询
└── metricsRepo.ts # 聚合指标查询
```
### 7.2 daemon 拆分
```
src/daemon/
├── orchestrator.ts # 入口:tick = Scheduler.claim → WorkerManager.spawn/reap → Ingestor.ingest
├── scheduler.ts # 纯调度逻辑(scheduleScore/claimable/claim CAS
├── workerManager.ts # spawn/reap/heartbeat 检测
├── ingestor.ts # outbox.ndjson → DB 事件(原 ingest.ts
└── mergeCoordinator.ts # merge-resolve 任务池 + 收口原任务
```
### 7.3 API 拆分
```
src/api/
├── server.ts # Fastify 初始化 + 路由注册 + WS 挂载
├── middleware/
│ └── auth.ts # No-op 占位(future JWT
├── routes/
│ ├── projects.ts
│ ├── tasks.ts
│ ├── runs.ts
│ ├── approvals.ts
│ └── metrics.ts
└── schemas/ # Fastify JSON Schema 校验
```
### 7.4 executor 拆分
```
src/executor/
├── protocol.ts # 文件协议(含 clarify 类型、traceId
├── cc.ts # Claude Agent SDK wrapper(含 onToken 钩子)
├── pipelines/
│ ├── executor.ts # 代码执行 pipeline
│ ├── planner.ts # 任务拆解 pipeline
│ ├── conflict.ts # 解冲突 pipeline(新增)
│ └── reviewer.ts # 复审 pipeline(并行 code+security
└── worker.ts # 入口(读 job.json → 分发到对应 pipeline
```
---
## 8. 前端重构
### 8.1 技术栈
| 层 | 选型 |
|---|---|
| 框架 | React 18 + TypeScript |
| 构建 | Vite |
| 状态管理 | Zustand(全局 storeprojects/tasks/ws 连接) |
| 样式 | CSS Modules + IBM Plex MonoClaude Design System 字体) |
| 国际化 | i18next5 语言:zh/en/es/ja/fr |
### 8.2 目录结构
```
web/
├── index.html
├── vite.config.ts
├── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── store/ # Zustand stores
│ ├── api/ # REST + SSE + WS client
│ ├── components/ # 通用组件(Button/Badge/Modal...
│ ├── screens/ # 页面(Dashboard/TaskDetail/Settings
│ └── i18n/
└── public/
```
### 8.3 布局
3 列布局(对齐 Claude Design System):
- 左侧:项目列表(侧栏)
- 中间:任务看板(按状态分组)
- 右侧:任务详情(审批/流式输出/transcript
### 8.4 实时特性
- WebSocket:任务状态变更 + Phase 事件 → 看板实时刷新
- SSETaskDetail 右侧面板显示 agent 实时 token 输出
- 审批闸:plan_review / spec_review / exec_review → 内联 approve/reject
---
## 9. Schema 变更(src/store/schema.sql
```sql
-- tasks 表新增列
ALTER TABLE tasks ADD COLUMN task_type TEXT; -- feature/bugfix/refactor/chore/docs
ALTER TABLE tasks ADD COLUMN scope TEXT; -- file/module/service/cross-service
ALTER TABLE tasks ADD COLUMN owned_files TEXT; -- JSON string[]
ALTER TABLE tasks ADD COLUMN expected_output TEXT; -- 验收描述
ALTER TABLE tasks ADD COLUMN parent_version_id TEXT; -- 版本链前驱
ALTER TABLE tasks ADD COLUMN version INTEGER DEFAULT 1;
ALTER TABLE tasks ADD COLUMN claimed_at TEXT; -- CAS claim 时间戳
-- runs 表 kind 新增 'conflict'TEXT 无 CHECK,兼容)
-- runs 表新增 trace_id
ALTER TABLE runs ADD COLUMN trace_id TEXT;
-- 新状态 'awaiting_input' 已在 status.ts TRANSITIONS 中处理,无需 schema 改动
```
---
## 10. 实现优先级
| 阶段 | 内容 | 依赖 |
|---|---|---|
| P0(核心正确性) | CAS claim / 执行期锁 / 分项 checks 闸 / verdict→硬闸 | — |
| P0Task 模型) | 新增 owned_files/expected_output/task_type 字段 + Schema | — |
| P1(调度升级) | rankU + agingBonus + couplingPenalty | Task 模型 |
| P1(管道优化) | 复审并行 / Planner 分档 / stages.json 检查点 | — |
| P1(解冲突) | conflict runKind + 专用 pipeline | — |
| P2(流式通信) | cc.ts onToken + SSE endpoint + Phase WS | — |
| P2Hook | MaestroHooks 契约 + shell/TS 两种实现 | — |
| P2Trace ID | JobSpec.traceId 传播 + outbox 携带 | — |
| P3(后端拆分) | store Repos / daemon 四组件 / API routes | P0-P1 稳定后 |
| P3(前端迁移) | React+Vite + Zustand + 实时流 | P2 SSE/WS |
---
## 附录 A:状态机新增状态
```
awaiting_input (新增)
← executing (clarify 记录触发)
→ executing (POST /reply 恢复)
→ cancelled
```
完整状态机见 `src/model/status.ts`
## 附录 B:调研来源
- Agent 1Task 分类与自动拆解(Plan-and-Execute / DAG vs Tree / MCP auto-fill
- Agent 2:文件冲突最小化(ownedFiles / interface-first / madge 分析)
- Agent 3:调度算法(CPM rankU / agingBonus / CAS
- Agent 4:流式通信与 Human-in-the-loopSSE / clarify / Hook 契约 / Trace ID
- Agent 5AI Agent 编排最佳实践(expected_output / stages.json / Reflect 阶段)