Skip to content

TODO:借鉴其他项目的优点,改进ccloop #6

Description

@yuzebin

TODO: 这是另一个优秀的项目,借鉴他的设计优点,改进ccloop:

AI Dispatcher
一个轻量的 GitHub Issue 驱动 worker。
它负责拿任务、启动 AI、把结果回写到 GitHub。

它解决什么问题
把这段流程自动化:

Issue -> AI 执行 -> PR 或报告 / paused / stuck

适合单仓库或少量仓库、仍然保留人工 review 的场景。

当前模型
ai-todo:待处理
ai-running:已有 worker 正在处理
ai-blocked:依赖未满足,暂不启动 worker
ai-paused:中断但有进展,仍由原 worker 续做
ai-review:PR 已创建,等待人工 review
ai-stuck:没形成稳定结果,需要人工处理
任务归属和抢占历史写到 issue comments 里,业务状态只放在 labels 上。

任务类型用额外 label 表示:

ai-task:code:代码任务,默认类型,完成后必须创建 PR
ai-task:test:测试 / E2E 验证任务,不改代码,不创建 PR,完成后回写测试报告
ai-task:investigate:调查分析任务,不改代码,不创建 PR,完成后回写调查报告
ai-task:maintain:维护任务,不启动 AI;由 dispatcher 同步已有 PR 分支
如果不加任务类型 label,按 ai-task:code 处理。

ai-paused:已经形成了可续做的分支进度,默认由原 owner 继续
ai-blocked:dispatcher 只做只读检查,不 claim、不切分支、不启动 AI
ai-stuck:这一轮没有形成可靠续做点,需要人工判断是否重开
ai-review:如果有人在 issue 里继续追问,原 owner 会自动续做并更新原 PR
claim 方式
当前用的是“comment 事件流 + 状态标签”:

worker 看到 ai-todo、属于自己的 ai-paused,或属于自己的 ai-review 追问
先 append 一条 machine-readable claim comment
回读 issue comments
按 comment 顺序确定胜者
胜者把 issue 切到 ai-running
loser 写 abandon comment 并退出
这不是原子锁,但比节点优先级和节点标签都更干净。

如果 issue 声明了依赖,claim 前会先做 dependency preflight:

部署需要什么
最小运行集是 3 个文件:

ai-dispatcher.sh
/.env
/.ai-dispatcher-policy.env
如果想省事初始化 worker,再加:

init-worker.sh
旧的 install.sh 仍可用,但新项目优先用 init-worker.sh,它会一次性处理 state dir、.env、项目 policy、GitHub labels、.claude-account 和 cron。

./init-worker.sh
--repo owner/repo
--workdir /path/to/project
--worker-id worker-a
--claude-account account-a
--base-branch main
常用参数:

--instance name:多实例并存时指定 state dir 后缀,默认用 repo 名
--state-dir path:显式指定 state dir
--worker-cmd cmd:默认优先使用 ~/.local/bin/claude-account,否则用 claude
--clone-url url:workdir 不存在时自动 clone
--no-cron:只生成配置,不安装定时任务
--dry-run:只打印计划,不写文件、不访问 GitHub
如果是接管已有 worker,一定要传原来的 --state-dir 或匹配的 --instance,否则会创建一个新的并行实例。

运行配置
/.env

WORKER_ID=worker-a
CLAUDE_CMD=claude
REPO=owner/repo
WORKDIR=/path/to/project
MAX_TURNS=200

可选:私有仓库附件下载优先使用本实例 token;未设置时回退到 gh auth token

GH_TOKEN=

可选:引用 issue 上下文默认先用 LLM 生成摘要,失败才降级机械摘要

REFERENCE_SUMMARY_MODE=llm
如果本机跑 Codex,可以把 CLAUDE_CMD 指到 codex-worker.sh。

WORKER_ID=worker-b
CLAUDE_CMD=/path/to/dispatcher/codex-worker.sh
REPO=owner/repo
WORKDIR=/path/to/project
MAX_TURNS=200
默认 state-dir 是 ~/.ai-dispatcher。如果一台机器要并行监控多个仓库,就给每个实例单独目录,例如:

~/.ai-dispatcher-project-a
~/.ai-dispatcher-project-b
对应 cron 里用 AI_DISPATCHER_STATE_DIR=/path/to/state-dir 区分实例。

Claude 账号并发
dispatcher 会按 Claude 账号做全局并发控制:同一个 .claude-account 同时只允许一个 worker 领取并执行 Claude 任务;拿不到账号锁时,本轮直接跳过,不改 issue label。不同 Claude 账号可以并行。

默认锁目录是 ~/.ai-dispatcher-account-locks。如果项目使用 Codex worker,或任务类型是 ai-task:maintain,不会占用 Claude 账号锁。必要时可在实例 .env 中设置:

CLAUDE_ACCOUNT_LOCK_ENABLED=true
CLAUDE_ACCOUNT_LOCK_DIR="$HOME/.ai-dispatcher-account-locks"
项目策略
项目根目录放 .ai-dispatcher-policy.env,常用字段:

BASE_BRANCH=main
TEST_COMMAND="npm test"

可选:维护任务同步 PR 后运行;留空则只同步并 push,不做本地验证

MAINTENANCE_TEST_COMMAND="npm test"
COMMIT_GUIDANCE="commit message 简洁说明改了什么"
PR_TITLE_GUIDANCE="PR title 格式:fix/feat/refactor: 简短描述 (#ISSUE)"
PR_BODY_GUIDANCE="PR body 包含改动摘要和测试结果"
可以从 policy.example.env 复制。

规则边界
dispatcher 代码只处理通用调度能力:claim、续做、状态流转、报告回写、附件提取、多实例运行。
仓库或业务特有规则不要写死进 dispatcher,优先放到项目根目录的 .ai-dispatcher-policy.env,必要时再通过 PROMPT_APPEND_FILE 补充长规则。
生产调查、只读约束、脱敏要求、某类 issue 的优先调查路径,默认都属于项目规则,不属于通用调度层。
如果一条规则只对某个仓库成立,就应放在该仓库的 policy / prompt 文件里;只有跨项目稳定复用后,才考虑上升到 dispatcher 本身。
报告渲染约束
报告型任务可以使用 Mermaid 辅助说明流程、依赖、状态流转或系统边界。dispatcher 会在回写 issue 前做轻量预检,目标是提前拦截 GitHub 常见渲染失败,而不是替 Mermaid 做完整语法解析。

建议写法:

flowchart TD
  A["输入"]
  B["处理「引用」"]
  C["输出"]
  A --> B --> C
Loading

注意:

节点 ID 只用英文、数字、下划线。
节点文字优先写成 A["文字"]。
节点文字和边标签里不要使用 ASCII 双引号;需要引用时用中文引号「」。
不要把整份报告包在 ```markdown 里;只有真正的 Mermaid、代码或日志片段才用 fenced block。
需求收敛
需求明确、验收标准清楚、一次 PR 能收完的任务,直接用 ai-task:code。
目标不清楚、范围偏大、实现路径不止一种、需要先调查现状的任务,先用 ai-task:investigate。
最稳的默认流程是:先收敛,再执行。不要让 AI 一边猜需求、一边改代码。
AI 可以给出拆分建议,但默认不自动创建多个子 issue;先由人工确认,再决定是否拆票。
如果调查报告包含可解析的后续 issue 候选,可以在原 issue 评论回复确认指令,由 dispatcher 创建。
如果一条规则只是在“需求不清楚时先调查、先收敛”,这仍然属于项目规则或工作流约定,不需要改 dispatcher 代码。
任务类型
Issue 写法规则
写 issue 时,只写“任务意图”和“验收标准”。不要重复写 worker 已经默认知道的执行命令。

默认不用写:

不用写“先 checkout / 拉 main / 新建分支 / 提交 / 开 PR”。
不用写“不要提交 token / 不要提交临时产物 / 不要提交附件输出”。
不用写“跑测试 / 写 PR 描述 / 回写结果”,除非这个仓库有特殊命令或特殊格式。
不用写账号、token、内部路径、登录态、机器名;这些放在执行机本地配置、项目 policy 或 skill 里。
不用把同一套业务规则复制到每个 issue;稳定规则放到 .ai-dispatcher-policy.env 的 PROMPT_APPEND_TEXT 或 PROMPT_APPEND_FILE。
建议固定结构:

目标

一句话说明要达成什么。

背景

只写这次任务必须知道的上下文;可以引用 #123。

范围

  • 必须做什么
  • 明确不做什么

验收

  • 可检查的完成条件
  • 需要跑的特殊验证,如果没有特殊要求可省略
    代码型 issue 尽量短:

目标

修复外部回调重复处理导致的重复写入。

背景

参考 #123 的调查结论。

范围

  • 修复后端幂等判断
  • 补最小测试
  • 不改前端展示

验收

  • 同一个 webhook event 重放不会重复写入
  • 相关测试通过
    测试 / E2E issue:

目标

在 staging 环境验证新用户注册到关键业务动作的完整链路。

重点

  • 记录实际步骤、结果、失败点
  • 不修改代码

验收

  • issue 评论中给出测试报告
  • 如果发现 bug,列出建议创建的后续 issue
    调查型 issue:

目标

评估某方案是否适合当前阶段。

需要回答

  • 解决什么问题
  • 有哪些方案
  • 推荐哪个,为什么
  • 风险和下一步
    维护型 issue 只用于机械同步已有 PR。遇到内容冲突不会自动解决,应改开普通代码型 issue。

代码任务:

labels: ai-todo, ai-task:code
测试或 E2E 任务:

labels: ai-todo, ai-task:test
调查任务:

labels: ai-todo, ai-task:investigate
维护任务:

labels: ai-todo, ai-task:maintain
ai-task:test 和 ai-task:investigate 只产出 issue 报告。
如果发现需要改代码,报告里说明问题,另开代码型 issue。

dispatcher 会对报告做最低质量校验。输出太短、缺少结构化章节、没有明确结论、没有证据 / 执行结果 / 风险说明,或者只有“Report complete / No further action required”这类空泛完成声明,会被标记为 ai-stuck,不会进入 ai-review。结论章节不要求固定标题,## 一句话结论、## 三句话结论、## 核心结论、## 结论摘要、## 推荐方案、## Findings 等都可以。

ai-task:maintain 不会创建 fresh 分支,也不会调用大模型。它只执行明确声明的机械维护动作,例如同步已有 PR 分支。

ai-task:investigate 最好输出结构化收敛结果,至少包含:

目标理解
已知事实
缺失信息
风险与边界
可选方案
推荐方案
建议拆分
下一步建议
GitHub Issue / PR 会直接渲染 Mermaid。报告遇到流程、状态流转、依赖关系、时序链路、系统边界时,建议补一张小图辅助理解,例如 flowchart、sequence diagram 或 state diagram。图只用于降低理解成本,不要替代结论、证据和文字说明。

报告型任务不能把核心调查、E2E 或长耗时验证放到后台后提前结束。必须等待核心命令完成,并把结果写入最终报告。

其中“建议拆分”只回答:是否值得拆、如果拆建议拆成哪几个任务、每个任务的完成定义是什么。默认不要自动开一串新 issue。

涉及支付、法律、政策、价格、平台条款、第三方服务能力或时效性信息时,调查报告必须访问官方来源核验,并列出官方 URL、访问日期和核验结论。无法访问或无法确认时,必须明确标注“未核验”或“无法确认”,不能仅凭模型记忆下结论。

如果报告末尾有“建议创建的后续 Issue”,候选项应使用固定标题 ## 建议创建的后续 Issue,并写成可见 checkbox 编号列表。编号对应隐藏结构化候选的数组顺序。

建议创建的后续 Issue

  • 1. 代码型:补服务端 Dockerfile + 优雅停机
  • 2. 代码型:补最小 CI workflow
  • 3. 调查型:评估部署环境的网络与 I/O 性能
    首选确认方式:勾选要创建的项,然后给这条报告评论添加 🚀 reaction。dispatcher 会读取被勾选的编号并创建对应 issue。

隐藏结构化块必须放在整份报告最后;引用清单、附录、备注等内容都放在隐藏块之前。JSON 必须是严格合法 JSON:body 里的换行写成 \n,双引号写成 ",不要放未转义的控制字符。

如果隐藏 JSON 损坏,dispatcher 会尝试从可见 checkbox 标题兜底创建 issue。兜底创建只保留标题、任务类型和来源链接,不包含完整正文,所以仍应优先保证隐藏 JSON 合法。

文字回复仍保留为兜底:

确认创建后续 issue:全部
确认创建后续 issue:1,3
确认创建后续 issue:只创建代码修复
issue 1 创建吧
后续 issue 全部创建吧
dispatcher 会根据报告中的隐藏结构化候选创建新 issue,并自动加上 ai-todo 和对应任务类型 label。创建结果会回写到原 issue。

报告会按 Markdown 直接渲染到 issue。dispatcher 负责统一回写;如果 worker 已经直接贴了报告,dispatcher 只记录执行状态,避免重复。

报告会默认脱敏,不应输出账号、token、验证码、数据库连接、内部 IP、内部文件路径或可直接复用的登录/攻击命令。

维护任务
如果要把已有 PR 分支同步到最新 main,不要发普通代码任务。使用 ai-task:maintain 和隐藏块:

默认推荐 strategy: "merge":

不需要 force push
保留 PR 分支历史
GitHub PR 会自动更新
对 dispatcher 自动执行最安全
也支持显式 rebase:

rebase 会使用 git push --force-with-lease,只在你明确需要线性历史时使用。

维护任务行为:

claim 成功后读取 PR head branch,不创建 ai/issue-N 新分支。
merge 成功后 push PR 分支;如果配置了 MAINTENANCE_TEST_COMMAND,会运行本地验证。
同步成功且验证通过:issue -> ai-review。
同步冲突:中止 merge/rebase,issue -> ai-stuck,评论列出冲突文件。
push 失败或验证失败:issue -> ai-stuck,评论写明原因和输出。
maintenance 是机械操作,失败后不会自动重试;需要人工处理,或新建普通代码任务解决冲突。
PR 不存在、PR 已关闭、隐藏块格式错误:issue -> ai-stuck。
依赖门禁
如果一个 issue 必须等其他 issue 或 PR 完成后才能做,不要只写自然语言说明。正文里加一个隐藏结构化块:

写 issue 时,正文可见部分仍然建议给人看:

依赖

  • PR #95 合并后再做
  • #91 完成决策后再做
    dispatcher 只读取隐藏块;普通 #95、#91 引用只会作为上下文,不会阻塞任务。

支持的依赖:

{"type":"pr","number":95,"state":"merged"}:PR 必须已合并
{"type":"pr","number":95,"state":"closed"}:PR 已关闭或已合并
{"type":"issue","number":91,"state":"closed"}:issue 必须关闭
{"type":"issue","number":91,"state":"review"}:issue 必须带 ai-review
{"type":"issue","number":91,"state":"done"}:issue 关闭,或带完成标签
行为:

依赖未满足:从 ai-todo 切到 ai-blocked,评论列出阻塞项,不启动 worker。
依赖满足:后续 cron 自动从 ai-blocked 切回 ai-todo。
隐藏块格式错误:也会进入 ai-blocked,评论提示 JSON 格式问题。
跨仓库依赖暂不自动判断;需要先人工确认,或拆到同仓库 issue 里表达。
项目策略可配置完成标签:

DEPENDENCY_DONE_LABELS="ai-review,ai-done,done"
DEPENDENCY_CLOSED_IS_DONE=true
DEPENDENCY_PR_MERGED_IS_DONE=true
附加材料
issue 里如果有图片、PDF 或附件链接,dispatcher 会下载并提取可读内容,附加到 prompt。
图片会尽量做 OCR;PDF 会尽量抽文本。

引用 Issue
如果 issue 正文或评论里明确写了同仓库引用,例如 #728 或 GitHub issue 链接,dispatcher 会把被引用 issue 的摘要加入上下文。

规则很轻:

只抓同仓库 issue
最多抓 5 个
只抓一层,不递归展开
包含标题、状态、labels、body 摘要、最近评论和附件链接
摘要默认由 LLM 基于被引用 issue 的完整正文和评论生成,并按 issue 更新时间缓存;LLM 摘要失败时才降级为明确标注的机械摘要
附件链接从原始全文提取,不依赖摘要文本,避免摘要遗漏图片或 PDF
Skill 使用
可以在 issue 里写明要使用某个 AI skill 或工作模式,例如:

请按 xxx-skill 的模式做一次 E2E 验证。
issue 里只放任务目标、非敏感上下文和附件。
账号、token、内部路径、登录态等敏感信息应放在执行机本地配置或 skill 本地文件里,不要写进 issue。

结果回写
代码任务成功产出 PR:ai-review
代码任务续做已有 PR 分支并能定位到对应 PR:ai-review
维护任务成功同步 PR:ai-review
代码任务在 ai-review 中续做但没有新增 commit:仍保持 ai-review,并把 worker 的诊断/说明回写到 issue
测试 / 调查任务成功产出报告:ai-review
中断但有 commit:ai-paused
维护任务同步冲突、push 失败或验证失败:ai-stuck
没有形成稳定产物:ai-stuck
同时会写两类记录:

issue comments:claim / abandon / state / result
~/.ai-dispatcher/runs/*.json:本地结构化 run metadata
claim / abandon / state 这类机器事件在 GitHub 上显示为一行简短说明,完整协议数据放在隐藏 HTML comment 里。旧版纯 JSON comment 仍然兼容。

工作区残留
dispatcher 领取新任务前会检查工作区:

只有未跟踪文件:先打包到 ~/.ai-dispatcher/artifacts/dirty-worktree-backups/,再清理并继续领取。
有 tracked 文件修改:不自动处理,暂停领取,避免丢失有效代码。
任务结束后如果只残留未跟踪文件,也会同样备份并清理,避免卡住后续任务。
worker 异常退出时如果留下 tracked 修改,会保存 patch/stash 到 dirty-worktree-backups/,清空工作区,并在 issue 里回写备份位置。
手工重开
如果某个 issue 需要重新开始,直接把它改回 ai-todo。

这适用于:

ai-stuck 想重开
ai-review 想重新来一轮
中间状态异常后需要重新排队
如果当前是 ai-paused,而你想保留原 owner 续做,就不要改回 ai-todo。

手动测试
AI_DISPATCHER_STATE_DIR=~/.ai-dispatcher-my-project /path/to/dispatcher/ai-dispatcher.sh
tail -f ~/.ai-dispatcher-my-project/dispatcher.log
ls ~/.ai-dispatcher-my-project/runs/
当前边界
不是中心调度
不是原子锁
ai-paused 不自动切换 owner
PR 创建后仍然需要人工 review 和最终关闭 issue

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions