merge: maestro/tsk_-HYVwxzHIbrS [tsk_-HYVwxzHIbrS]
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
本地优先的 daemon:管理多个本地 git 项目的任务,按复杂度分级驱动审批闸,并用后台 agent(Claude Code)自动执行可执行任务。
|
||||
|
||||
> **当前实现状态(2025-06)**:Phase 1(核心 + 看板 + MCP)与 Phase 2(编排器 + 执行器 + 双复审 + 自动合并)已全面落地;Phase 3 通知、额度透传、归档详情已一并完成。本文档对应已实现代码,不再是纯设计预稿。
|
||||
> **当前实现状态(2026-06)**:Phase 1(核心 + 看板 + MCP)与 Phase 2(编排器 + 执行器 + 双复审 + 自动合并)已全面落地;Phase 3 通知、额度透传、归档详情已一并完成,并把执行从 **in-process** 重构为 **多进程解耦**(daemon 监工 + 独立 worker 进程 + 文件通讯 + 重启 re-adopt,见 §7)。本文档对应已实现代码,不再是纯设计预稿。
|
||||
|
||||
## Context(为什么做)
|
||||
前身是 `~/.claude/skills/todo`(JSON + HTML 单机原型):单项目、无真正后端、确认靠临时 http hack、无自动执行。它验证了「复杂度分级 + 确认闸 + 看板」的形态,但不可扩展。Maestro 把它升级为正式系统:**多项目、任务层级化、复杂度驱动的审批闸、完整状态机、本地常驻 daemon、每项目一个后台执行 agent、与 Claude Code 深度集成(MCP + Agent SDK)**。
|
||||
@@ -17,15 +17,19 @@
|
||||
|
||||
## 1. 系统总览
|
||||
```
|
||||
maestrod(核心 daemon,常驻)
|
||||
maestrod(核心 daemon,常驻 · 唯一 DB 写者 · 监工)
|
||||
├─ 任务存储 SQLite ├─ 项目注册表
|
||||
├─ REST + WebSocket API ├─ MCP server(给任意 Claude Code 会话)
|
||||
├─ 编排器 Orchestrator └─ 每项目 worker 池(执行器)
|
||||
│ │
|
||||
├─ 编排器 Orchestrator └─ 监工:spawn worker / ingest outbox / 回收死 worker
|
||||
│ (领取 + 文件通讯 + 判活,自身不跑 agent)
|
||||
│
|
||||
└── worker 进程(每任务一个,独立 OS 进程,哑执行器·完全不碰 DB)
|
||||
执行 agent(executor run):Agent SDK 在 worktree 跑 headless CC
|
||||
code review agent(reviewer run):只读,审 diff 产出报告 + verdict
|
||||
安全审计 agent(security run):只读,仅审安全问题
|
||||
↕ 文件通讯:job.json(daemon→worker)/ outbox.ndjson + heartbeat(worker→daemon)/ SIGTERM(取消)
|
||||
|
||||
Web 看板(多项目·实时·页面内 accept/reject)
|
||||
执行 agent(executor run):Agent SDK 在 worktree 跑 headless CC
|
||||
code review agent(reviewer run):只读,审 diff 产出报告 + verdict
|
||||
安全审计 agent(security run):只读,仅审安全问题
|
||||
```
|
||||
闭环:建任务 →(Hard 分析拆解 / Medium 写方案 / Easy 写操作)→ 前置审批闸 → ready → 编排器 score 调度领取 → worktree 起 headless CC 执行 → verify → 双复审(code review + 安全审计)→ exec_review(带复审报告)→ accept 自动合并 → done。
|
||||
|
||||
@@ -62,30 +66,57 @@ Hard / Medium / Easy ↔ 旧 tier 1/2/3。每个任务(含子任务)都带 `
|
||||
|
||||
## 6. 数据模型(见 `src/model/types.ts` 与 `src/store/schema.sql`)
|
||||
- **Project**:repo_path、default_branch、verify_cmd、autonomy、model、concurrency、status、logo、sort_order、last_sync_at
|
||||
- **Task**:project_id、parent_id、depth、complexity、status、priority(P0/P1/P2)、deps[]、plan/spec/operations、result(含 summary/verdict/securitySummary/securityVerdict/branch/worktree/commits/prUrl)、assignee、source_ref(旧 todo 映射用)
|
||||
- **Task**:project_id、parent_id、depth、complexity、status、priority(P0/P1/P2)、deps[]、plan/spec/operations、result(含 summary/verdict/securitySummary/securityVerdict/branch/worktree/commits/prUrl)、assignee、source_ref(旧 todo 映射用)、next_eligible_at(持久化退避)、retry_baseline(手动重投基线)
|
||||
- **ApprovalRecord**:gate(plan/spec/exec)、action、actor、reason(reject 必填)、at
|
||||
- **Run**:kind(planner/executor/reviewer/security)、worktree、branch、status、transcript_ref、claude_session_id、error
|
||||
- **Run**:kind(planner/executor/reviewer/security)、worktree、branch、status、transcript_ref、claude_session_id、error、worker_pid(多进程执行的 worker pid,判活用)、last_seq(outbox ingest 幂等游标)
|
||||
- **Event**(append-only):审计 + 实时看板推送源
|
||||
|
||||
`TaskResult.prUrl` 在合并后写 `merged:<mergeCommit>`,复用字段记录合并产物。
|
||||
|
||||
## 7. 编排器与执行 agent(见 `src/daemon/orchestrator.ts`)
|
||||
## 7. 多进程执行架构:监工 + worker(见 `src/daemon/orchestrator.ts`、`src/daemon/ingest.ts`、`src/executor/{protocol,worker,pipeline}.ts`)
|
||||
|
||||
编排循环由 `MAESTRO_ORCH_INTERVAL`(秒,默认 15)驱动,`0` 关闭。每轮对 `status=active` 且 `autonomy≠manual` 的项目:在途数 < `concurrency` 时领任务。
|
||||
执行已从早期的 **in-process**(编排器自己 `await` 跑 agent)重构为 **多进程解耦**。核心铁律两条:
|
||||
|
||||
**领取规则**:
|
||||
- **daemon = 唯一 DB 写者 + 监工**:只负责领任务、spawn worker、把 worker 的产出摄取入库、回收死 worker。**所有** DB 写(含执行进度/结果落库)都在 daemon 进程内,故 WS 推送照常经 `store.subscribe` 触发。
|
||||
- **worker = 哑执行器**:每个执行任务 spawn **一个独立 OS 进程**(`node dist/executor/worker.js <runId>`),它跑「worktree → 执行 → verify → 双复审 → 产出」全流程,但**完全不碰 DB(连读都不碰)**,也不做任何重试 / `needs_attention` 决策。重试、退避、转终态全归 daemon。
|
||||
|
||||
二者只经 **文件 + 进程信号** 通讯,故天然跨 daemon 重启持久、无内存在途态:
|
||||
|
||||
| 方向 | 载体 | 说明 |
|
||||
|---|---|---|
|
||||
| daemon → worker | `runs/<runId>/job.json` | worker 的**唯一输入**:完整 `Task` + `Project` + 确定性 worktree 路径/分支。worker 据它自行 `pickModel`/`buildPrompt`,无需查 DB。 |
|
||||
| daemon → worker | `SIGTERM` | 取消 / 超时。worker 兜底报 `failed` + `done` 后干净退出。 |
|
||||
| worker → daemon | `runs/<runId>/outbox.ndjson` | 追加式、带单调递增 `seq` 的进度/结果流(`started`/`phase`/`failed`/`result`/`done`)。daemon **幂等 ingest**(按 `run.lastSeq` 游标,只消费 `seq>lastSeq`)。 |
|
||||
| worker → daemon | `runs/<runId>/heartbeat` | 心跳文件,worker 每 10s `touch`(刷 mtime)。daemon 据其 mtime「年龄」判活。 |
|
||||
|
||||
**领取规则**(编排循环由 `MAESTRO_ORCH_INTERVAL` 秒驱动,默认 15,`0` 关闭;每轮 `ingest → reap → claim`):
|
||||
- 候选 = 叶子(非容器)、status=`ready` 或 `queued`(重试/重启遗留)、deps 全 `done`
|
||||
- `auto-easy` 模式只领 `easy` 任务;`auto-approved` 领全部
|
||||
- 并发闸读 `store.countExecuting`(每个 `executing` 对应一个活 worker),退避读持久化的 `task.nextEligibleAt`(早于它不领)
|
||||
- 按 §8 Score 降序排列,同分创建早的优先
|
||||
|
||||
**执行流程**(单任务):
|
||||
1. `executing` → 创 worktree → 起 executor run(`runTask`)
|
||||
2. `verify_cmd` → 跑项目校验命令
|
||||
3. 双复审顺序执行:code review run(kind=reviewer)→ 安全审计 run(kind=security),任一失败不挡任务(折叠为 `verdict=null`,summary 写失败原因)
|
||||
4. `setResult`(branch/diffSummary/commits/summary/verdict/securitySummary/securityVerdict)→ `exec_review`
|
||||
5. 失败 → `failed` → 最多 2 次重试(重试 2 次 = 最多 3 次执行)→ `needs_attention`
|
||||
**领取一个任务**(`claimOne`):算分支/worktree 路径 → `executing` → `startRun(executor)` → 写 `job.json` → `spawnWorker`(记 `worker_pid`)。spawn 用 `detached:false`(保持与 daemon 同会话,worker 内起的 `claude` 才能沿用 macOS keychain 鉴权)+ `unref`(daemon 退出后 worker 作为孤儿被 launchd 收养、继续跑)。`env` 用**黑名单**收敛:剥离 `*_SECRET/*_TOKEN/*_KEY/AWS_/CF_/GH_` 等真密钥,保留会话变量(keychain 需要)与 `ANTHROPIC_/CLAUDE_` 鉴权变量。
|
||||
|
||||
中断现场恢复:daemon 启动时 `reconcileInterrupted()` 把上次退出时仍 `started` 的 run 标 `failed`,`executing` 的任务 → `failed` → `queued` 等重新领取。
|
||||
**worker 执行管线**(`runPipeline`,纯执行、全程经 `emit` 写 outbox):
|
||||
1. `createWorktree` → `emit phase:executing` → `runTask`;失败 → `emit failed` + `done`,返回(不抛)
|
||||
2. `runVerify`(项目 `verify_cmd`);失败 → `emit failed` + `done`
|
||||
3. `worktreeDiff` → `emit phase:reviewing` → 双复审顺序执行(code review → 安全审计,任一失败折叠为 `verdict=null`、不挡任务)
|
||||
4. 成功 → `emit result`(branch/worktree/diffSummary/commits/executor/code/security)+ `done`
|
||||
|
||||
**daemon ingest**(`ingest.ts`,把 outbox 映射为 DB 写):
|
||||
- `result` → 建 reviewer/security 两条复审 run + `setResult`(四字段)+ 转 `exec_review` + 收尾 executor run
|
||||
- `failed` → `finishRun(failed)` + `failTaskAttempt`(失败/重试策略,见下)
|
||||
- 幂等:每条处理后 `setLastSeq(seq)`;崩溃重启续读时已落库的 `seq` 不重做,终态记录只消费一次
|
||||
|
||||
**失败/重试/退避**(`store.failTaskAttempt`,是 ingest 的 `failed`、reaper 判死、reconcile 判死的**唯一落点**):净失败次数(相对 `retry_baseline`)未到 `project.maxRetries`(默认 2)→ `queued` + 持久化退避 `next_eligible_at = now + min(30s·2^(n-1), 10min)`;到上限 → `needs_attention`(清退避)。
|
||||
|
||||
**回收死 worker**(reaper,每轮 tick):对每个 `executing` 任务按 `worker_pid + 心跳` 判活,死 → `failTaskAttempt` 回收重试。
|
||||
|
||||
**重启 re-adopt / 回收**(`reconcileInterrupted`,daemon 启动时):逐个 `executing` 任务判活——
|
||||
- **活**(心跳新鲜,或 boot 窗口内 pid 仍在)→ **re-adopt**:保留 `executing`,daemon 续 ingest 它的 outbox(worker 全程没停);
|
||||
- **死** → `failTaskAttempt` 回收重试。
|
||||
|
||||
**判活逻辑**(`isWorkerAlive`,见 `protocol.ts`):主信号=心跳新鲜(年龄 < `HEARTBEAT_GRACE_MS=60s`);`started` 后 `BOOT_GRACE_MS=30s` 启动空窗内额外接受 `pid` 存活兜底;窗口后仅认心跳——规避 pid 复用误判(被复用的 pid 不会更新本 run 的心跳)。
|
||||
|
||||
## 8. Score 调度(见 `src/model/scoring.ts`)
|
||||
|
||||
@@ -184,10 +215,11 @@ daemon 启动时订阅 Store 事件流,符合条件时用 `osascript display n
|
||||
- 60s 内存缓存 + 并发去重(in-flight 共享);任何失败(无凭证/网络/非200)→ `null`,前端降级显示。
|
||||
|
||||
## 15. 后端架构
|
||||
- 存储 SQLite(better-sqlite3,单文件零运维);run 转录日志存 JSONL 文件,Run 表引用路径。
|
||||
- 存储 SQLite(better-sqlite3,单文件零运维);run 转录日志存 JSONL 文件,Run 表引用路径。**daemon 是唯一 DB 读写者**(worker 进程完全不碰 DB,见 §7)。
|
||||
- API:REST(CRUD)+ WebSocket(`/ws`,Store 事件广播,驱动看板实时刷新)。
|
||||
- 项目绑定:`maestro project add <repo>` 记录 repo 路径/默认分支/verify/autonomy/model/并发;daemon 中央 DB 行。
|
||||
- Worktree:执行在 repo 的 git worktree(隔离分支),合并由 §11 逻辑控制,不自动 push。
|
||||
- 进程间通讯目录:`<dataDir>/runs/<runId>/`(`job.json` / `outbox.ndjson` / `heartbeat`),是 daemon 与 worker 的唯一共享面,跨重启持久(见 §7)。`worker_pid`、`last_seq`、`next_eligible_at` 三列落 DB,使在途态/退避无需内存即可恢复。
|
||||
|
||||
## 16. Web 看板(`web/`)
|
||||
多项目切换(侧栏拖动排序)/ 全局总览;任务树(层级折叠)、复杂度徽章、状态、内联审批闸(accept/reject,reject 必填意见)、实时执行状态、`exec_review` 的 code review + 安全审计报告 + diff/分支信息。实时 WS 驱动。
|
||||
@@ -196,7 +228,7 @@ daemon 启动时订阅 Store 事件流,符合条件时用 `osascript display n
|
||||
|
||||
## 17. Claude Code 集成
|
||||
- **MCP server(交互面)**:项目里的 Claude Code 配 `.mcp.json` 接 `maestro-mcp`。工具:`list_projects` / `create_project` / `list_tasks` / `get_task` / `create_task` / `decompose_task` / `write_plan` / `write_spec` / `write_operations` / `update_status` / `get_next_executable` / `get_pending_approvals`(accept/reject 在 Web 看板操作)。
|
||||
- **Agent SDK(自动面)**:编排器用 Claude Agent SDK 起 headless CC 跑 executor / reviewer / security run。
|
||||
- **Agent SDK(自动面)**:编排器 spawn 的独立 worker 进程用 Claude Agent SDK 起 headless CC 跑 executor / reviewer / security run(见 §7 多进程架构)。
|
||||
- approve/reject 是用户动作,走看板按钮(或直接 `POST /api/tasks/:id/decide`)。
|
||||
|
||||
## 18. 从现有 skill 迁移
|
||||
@@ -205,12 +237,12 @@ daemon 启动时订阅 Store 事件流,符合条件时用 `osascript display n
|
||||
## 19. 分期实施状态
|
||||
- ✅ **Phase 1(核心,已完成)**:daemon + SQLite + 项目注册 + 任务模型 + 状态机 + Web 看板 + MCP(读写 + 审批)+ CLI + todo 同步引擎。
|
||||
- ✅ **Phase 2(自动化,已完成)**:编排器 + score 调度 + Agent SDK executor(Easy/Medium/Hard worktree 自动执行)+ verify + 双复审(code review + 安全审计)+ 自动合并(--no-ff + 逃生口)。
|
||||
- ✅ **Phase 3(打磨,核心已完成)**:macOS 原生通知 + Claude 订阅额度透传 + 归档详情时间线 + 模型分级(复杂度→模型映射 + env 覆盖 + 回退链)+ 项目 logo + 侧栏拖动排序。
|
||||
- ✅ **Phase 3(打磨,核心已完成)**:多进程执行解耦(daemon 监工 + 独立 worker 进程 + job.json/outbox/heartbeat 文件通讯 + 重启 re-adopt,见 §7)+ macOS 原生通知 + Claude 订阅额度透传 + 归档详情时间线 + 模型分级(复杂度→模型映射 + env 覆盖 + 回退链)+ 项目 logo + 侧栏拖动排序。
|
||||
- 🔲 **后续**:并发资源精细监控 / 指标面板 / 可选远程看板 / PR 平台集成(GitHub PR 链接)。
|
||||
|
||||
## 20. 风险与注意事项
|
||||
- **Agent SDK headless 执行可靠性**:verify_cmd 把关 + 双复审 + needs_attention 人工兜底。executor run 最多 100 轮、30min 超时兜底;reviewer run 最多 40 轮、15min 超时兜底。
|
||||
- **自动执行安全**:worktree 隔离(改动不进主检出)+ 不自动 push + merge 需 exec_review accept + 安全审计 run 专门检查凭证/注入/权限问题。
|
||||
- **中断恢复**:daemon 重启后 `reconcileInterrupted()` 自动恢复执行中断的任务(标 failed → queued)。
|
||||
- **自动执行安全**:worktree 隔离(改动不进主检出)+ 不自动 push + merge 需 exec_review accept + 安全审计 run 专门检查凭证/注入/权限问题;worker 进程不碰 DB(无法越权改任务状态),spawn 时黑名单剥离真密钥类 env。
|
||||
- **中断恢复(多进程 re-adopt)**:daemon 重启后 `reconcileInterrupted()` 按 `worker_pid + 心跳` 真判活——worker 仍活则 **re-adopt**(保留 executing,续 ingest 其 outbox,不打断正在跑的 agent);已死则 `failTaskAttempt` 回收重试。daemon 与 worker 解耦后,daemon 崩溃/重启不再丢失在途执行。
|
||||
- **长任务上下文超限**:100 turns / 30min 兜底,超时后转 failed → 重试。
|
||||
- **worktree 泄漏**:合并后异步清理,daemon 启动时 `git worktree prune` 清残留。
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
按任务复杂度(Hard / Medium / Easy)分级驱动审批闸,用后台 agent(Claude Code)在隔离 git worktree 自动执行可执行任务,执行后自动跑 code review + 安全审计,accept 时 `--no-ff` 自动合并,改动不自动 push。
|
||||
|
||||
执行是**多进程解耦**的:daemon 是唯一 DB 写者兼监工,每个执行任务 spawn 一个**独立 worker 进程**(哑执行器,完全不碰 DB),二者只经文件 + 进程信号通讯(`job.json` / `outbox.ndjson` / `heartbeat` / `SIGTERM`),daemon 重启可按心跳判活 re-adopt 仍在跑的 worker。详见 [DESIGN.md](./DESIGN.md) §7。
|
||||
|
||||
- 完整设计见 **[DESIGN.md](./DESIGN.md)**。
|
||||
- 当前状态:**Phase 1 + Phase 2 + Phase 3 核心均已落地**(编排器 + score 调度 + 双复审 + 自动合并 + macOS 通知 + 额度透传 + 归档)。
|
||||
- 当前状态:**Phase 1 + Phase 2 + Phase 3 核心均已落地**(编排器 + score 调度 + 双复审 + 自动合并 + 多进程执行解耦 + macOS 通知 + 额度透传 + 归档)。
|
||||
|
||||
## 结构
|
||||
```
|
||||
@@ -14,9 +16,11 @@ src/
|
||||
store/ SQLite schema + 访问层(状态机守卫 + 审批闸)
|
||||
api/ REST + WebSocket API + logo 解析
|
||||
mcp/ MCP server(给 Claude Code)
|
||||
executor/ Agent SDK 封装(cc.ts)、worktree 管理、verify、
|
||||
runner(executor run)、reviewer(双复审)、merge、模型分级
|
||||
daemon/ maestrod 入口、编排器、macOS 通知、额度透传、配置
|
||||
executor/ Agent SDK 封装(cc.ts)、worktree 管理、verify、runner、reviewer(双复审)、merge、模型分级
|
||||
protocol.ts(daemon↔worker 文件协议:job/outbox/heartbeat/判活)
|
||||
worker.ts(独立 worker 进程入口)、pipeline.ts(纯执行管线,不碰 DB)
|
||||
daemon/ maestrod 入口、编排器(监工:spawn/ingest/回收)、ingest(outbox→DB)、
|
||||
macOS 通知、额度透传、配置
|
||||
sync/ 旧 todo.json → maestro 单向同步引擎
|
||||
web/ 看板(多项目·实时 WS·内联审批·归档详情)
|
||||
scripts/ poc-exec.mjs(执行闭环验证脚本)
|
||||
@@ -52,6 +56,12 @@ MCP 工具:`list_projects` / `create_project` / `list_tasks` / `get_task` / `c
|
||||
|
||||
## 核心功能速览
|
||||
|
||||
### 多进程执行(监工 + worker)
|
||||
- **daemon = 唯一 DB 写者 + 监工**:领任务 → 写 `job.json` → spawn worker → ingest 其 outbox → 回收死 worker。每轮 tick 顺序 `ingest → reap → claim`。
|
||||
- **worker = 哑执行器**:`node dist/executor/worker.js <runId>`,每任务一个独立进程,跑 worktree→执行→verify→双复审→产出,**完全不碰 DB**,进度/结果只追加进 `outbox.ndjson`。
|
||||
- **通讯**:`runs/<runId>/job.json`(daemon→worker 唯一输入)/ `outbox.ndjson`(worker→daemon,带 seq,daemon 按 `last_seq` 幂等 ingest)/ `heartbeat`(worker 每 10s 刷 mtime,daemon 判活)/ `SIGTERM`(取消/超时)。
|
||||
- **重启 re-adopt**:daemon 启动 `reconcileInterrupted()` 按 `worker_pid + 心跳`判活——活则 re-adopt 续 ingest(不打断 agent),死则回收重试(持久化退避 `next_eligible_at = min(30s·2^(n-1), 10min)`,超 `maxRetries` → `needs_attention`)。
|
||||
|
||||
### Score 调度
|
||||
编排器按 score 降序领任务(不抢占,领取时现算):
|
||||
```
|
||||
@@ -95,6 +105,7 @@ exec_review accept 时自动 `--no-ff` 合并:若 defaultBranch 在主检出
|
||||
| `MAESTRO_PORT` | daemon 监听端口 | `4517` |
|
||||
| `MAESTRO_DATA_DIR` | SQLite + worktree 数据目录 | `~/.maestro` |
|
||||
| `MAESTRO_ORCH_INTERVAL` | 编排器轮询间隔(秒),`0`=关闭 | `15` |
|
||||
| `MAESTRO_WORKER_CMD` | 覆盖 worker 进程启动命令(空格分隔),供 tsx 跑 .ts 入口/测试用 | `node dist/executor/worker.js` |
|
||||
| `MAESTRO_NOTIFY` | `0` 关闭 macOS 通知 | 开启 |
|
||||
| `MAESTRO_MODEL_EASY` | executor Easy 模型覆盖 | `claude-sonnet-4-6` |
|
||||
| `MAESTRO_MODEL_MEDIUM` | executor Medium 模型覆盖 | `claude-opus-4-8` |
|
||||
|
||||
Reference in New Issue
Block a user