merge: maestro/tsk_-HYVwxzHIbrS [tsk_-HYVwxzHIbrS]

This commit is contained in:
maestro
2026-06-13 15:50:42 +08:00
2 changed files with 71 additions and 28 deletions
+56 -24
View File
@@ -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)
执行 agentexecutor run):Agent SDK 在 worktree 跑 headless CC
code review agentreviewer run):只读,审 diff 产出报告 + verdict
安全审计 agentsecurity run):只读,仅审安全问题
↕ 文件通讯:job.jsondaemon→worker/ outbox.ndjson + heartbeatworker→daemon/ SIGTERM(取消)
Web 看板(多项目·实时·页面内 accept/reject
执行 agentexecutor run):Agent SDK 在 worktree 跑 headless CC
code review agentreviewer run):只读,审 diff 产出报告 + verdict
安全审计 agentsecurity 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_seqoutbox 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 runkind=reviewer)→ 安全审计 runkind=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 它的 outboxworker 全程没停);
- **死** → `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. 后端架构
- 存储 SQLitebetter-sqlite3,单文件零运维);run 转录日志存 JSONL 文件,Run 表引用路径。
- 存储 SQLitebetter-sqlite3,单文件零运维);run 转录日志存 JSONL 文件,Run 表引用路径。**daemon 是唯一 DB 读写者**worker 进程完全不碰 DB,见 §7)。
- APIRESTCRUD+ 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 executorEasy/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` 清残留。
+15 -4
View File
@@ -4,8 +4,10 @@
按任务复杂度(Hard / Medium / Easy)分级驱动审批闸,用后台 agentClaude 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、
runnerexecutor run)、reviewer(双复审)、merge、模型分级
daemon/ maestrod 入口、编排器、macOS 通知、额度透传、配置
executor/ Agent SDK 封装(cc.ts)、worktree 管理、verify、runner、reviewer(双复审)、merge、模型分级
protocol.tsdaemon↔worker 文件协议:job/outbox/heartbeat/判活)
worker.ts(独立 worker 进程入口)、pipeline.ts(纯执行管线,不碰 DB
daemon/ maestrod 入口、编排器(监工:spawn/ingest/回收)、ingestoutbox→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,带 seqdaemon 按 `last_seq` 幂等 ingest/ `heartbeat`worker 每 10s 刷 mtimedaemon 判活)/ `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` |