autonomous-dispatcher
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAutonomous Dev Team Dispatcher
自主开发团队调度器
Scan GitHub issues and dispatch dev/review tasks locally.
扫描GitHub Issues并在本地调度开发/评审任务。
GitHub Authentication — USE APP TOKEN, NOT USER TOKEN
GitHub身份验证 — 使用APP令牌,而非用户令牌
CRITICAL: All CLI calls MUST use a GitHub App token, NOT the default user token.
ghBefore running any command, generate and export the App token using the shared script at :
ghscripts/gh-app-token.shbash
undefined重要提示: 所有 CLI调用必须使用GitHub App令牌,而非默认用户令牌。
gh在运行任何命令之前,使用位于的共享脚本生成并导出App令牌:
ghscripts/gh-app-token.shbash
undefinedSource the shared token generator
引入共享令牌生成脚本
source "${PROJECT_DIR}/scripts/gh-app-token.sh"
source "${PROJECT_DIR}/scripts/gh-app-token.sh"
Generate token for the dispatcher's GitHub App
为调度器的GitHub App生成令牌
GH_TOKEN=$(get_gh_app_token "$DISPATCHER_APP_ID" "$DISPATCHER_APP_PEM" "$REPO_OWNER" "$REPO_NAME") || {
echo "FATAL: Failed to generate GitHub App token" >&2
exit 1
}
if [[ -z "$GH_TOKEN" ]]; then
echo "FATAL: GitHub App token is empty" >&2
exit 1
fi
export GH_TOKEN
The `DISPATCHER_APP_PEM` env var must point to the App's private key PEM file.
If not set, provide the path explicitly.
This ensures all issue comments, label changes, and API calls appear as the configured GitHub App bot instead of a personal user account. The token is valid for 1 hour and scoped to the target repo only.
**DO NOT skip this step.** If `GH_TOKEN` is not set, `gh` will fall back to the user's personal token, which is incorrect.GH_TOKEN=$(get_gh_app_token "$DISPATCHER_APP_ID" "$DISPATCHER_APP_PEM" "$REPO_OWNER" "$REPO_NAME") || {
echo "FATAL: Failed to generate GitHub App token" >&2
exit 1
}
if [[ -z "$GH_TOKEN" ]]; then
echo "FATAL: GitHub App token is empty" >&2
exit 1
fi
export GH_TOKEN
`DISPATCHER_APP_PEM`环境变量必须指向App的私钥PEM文件。如果未设置,请显式提供路径。
这确保所有Issue评论、标签变更和API调用都显示为已配置的GitHub App机器人,而非个人用户账户。令牌有效期为1小时,且仅作用于目标仓库。
**请勿跳过此步骤。** 如果未设置`GH_TOKEN`,`gh`将回退到用户的个人令牌,这是不符合要求的。Environment Variables
环境变量
- : GitHub repo in
REPOformat (e.g.,owner/repo)myorg/myproject - : Absolute path to the project root on the local machine
PROJECT_DIR - : Max parallel tasks (default:
MAX_CONCURRENT)5 - : Max dev retry attempts before marking issue as
MAX_RETRIES(default:stalled)3 - : Project identifier for log/PID files (default:
PROJECT_ID)project - : GitHub App ID for the dispatcher bot
DISPATCHER_APP_ID - : Path to the GitHub App private key PEM file
DISPATCHER_APP_PEM
- : GitHub仓库,格式为
REPO(例如:owner/repo)myorg/myproject - : 本地机器上项目根目录的绝对路径
PROJECT_DIR - : 最大并行任务数(默认值:
MAX_CONCURRENT)5 - : 标记Issue为
MAX_RETRIES之前的最大开发重试次数(默认值:stalled)3 - : 日志/PID文件的项目标识符(默认值:
PROJECT_ID)project - : 调度器机器人的GitHub App ID
DISPATCHER_APP_ID - : GitHub App私钥PEM文件的路径
DISPATCHER_APP_PEM
Local Dispatch Helper Script
本地调度辅助脚本
CRITICAL: All task dispatches (dev-new, dev-resume, review) MUST use the helper script in the project root's directory. The script handles:
scripts/dispatch-local.shscripts/- Background process spawning via
nohup - Input validation (numeric issue numbers, safe session IDs)
- Config loading from
scripts/autonomous.conf
Usage:
bash
undefined重要提示: 所有任务调度(dev-new、dev-resume、评审)必须使用项目根目录下的辅助脚本。该脚本负责:
scripts/scripts/dispatch-local.sh- 通过启动后台进程
nohup - 输入验证(数字Issue编号、安全会话ID)
- 从加载配置
scripts/autonomous.conf
使用方法:
bash
undefinedPROJECT_DIR is the absolute path to the project root
PROJECT_DIR是项目根目录的绝对路径
For new dev task:
新开发任务:
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-new ISSUE_NUM
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-new ISSUE_NUM
For review task:
评审任务:
bash "$PROJECT_DIR/scripts/dispatch-local.sh" review ISSUE_NUM
bash "$PROJECT_DIR/scripts/dispatch-local.sh" review ISSUE_NUM
For resume dev task:
恢复开发任务:
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-resume ISSUE_NUM SESSION_ID
**DO NOT construct dispatch commands manually.** Always use the dispatch-local.sh script.
**DO NOT commit or push code to the target repository.** The dispatcher's role is strictly:
1. Read issue labels and comments via GitHub API
2. Update labels and post comments via GitHub API
3. Dispatch local processes using the helper script
All code changes happen via the autonomous-dev/review scripts. The dispatcher MUST NOT modify source files or push to any branch (especially main).bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-resume ISSUE_NUM SESSION_ID
**请勿手动构造调度命令。** 请始终使用dispatch-local.sh脚本。
**请勿向目标仓库提交或推送代码。** 调度器的角色严格限定为:
1. 通过GitHub API读取Issue标签和评论
2. 通过GitHub API更新标签并发布评论
3. 使用辅助脚本调度本地进程
所有代码变更通过autonomous-dev/review脚本执行。调度器不得修改源文件或推送到任何分支(尤其是main分支)。Dispatch Logic
调度逻辑
When triggered (cron every 5 minutes), execute the following steps IN ORDER:
当触发时(每5分钟执行一次定时任务),按以下顺序执行步骤:
Step 1: Check Concurrency
步骤1:检查并发数
Count issues with labels OR :
in-progressreviewingbash
ACTIVE=$(gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous" --json labels \
-q '[.[] | select(.labels[].name | IN("in-progress","reviewing"))] | length')If ACTIVE >= MAX_CONCURRENT (default 5), STOP. Log "Concurrency limit reached (ACTIVE/MAX_CONCURRENT)" and exit.
统计带有或标签的Issue数量:
in-progressreviewingbash
ACTIVE=$(gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous" --json labels \
-q '[.[] | select(.labels[].name | IN("in-progress","reviewing"))] | length')如果ACTIVE >= MAX_CONCURRENT(默认值为5),则停止执行。记录日志“Concurrency limit reached (ACTIVE/MAX_CONCURRENT)”并退出。
Step 2: Scan for New Tasks
步骤2:扫描新任务
Find issues with label but NO state labels:
autonomousbash
gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous" --json number,labels,title \
-q '[.[] | select(
[.labels[].name] | (
contains(["in-progress"]) or
contains(["pending-review"]) or
contains(["reviewing"]) or
contains(["pending-dev"]) or
contains(["stalled"]) or
contains(["approved"])
) | not
)]'For each found issue (respecting concurrency limit):
1. Check Dependencies — before dispatching, read the issue body and look for a section. Parse issue references () from that section. For each referenced issue, check if it is closed:
## Dependencies#Nbash
undefined查找带有标签但无状态标签的Issue:
autonomousbash
gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous" --json number,labels,title \
-q '[.[] | select(
[.labels[].name] | (
contains(["in-progress"]) or
contains(["pending-review"]) or
contains(["reviewing"]) or
contains(["pending-dev"]) or
contains(["stalled"]) or
contains(["approved"])
) | not
)]'对于每个找到的Issue(需遵守并发限制):
1. 检查依赖 — 调度前,读取Issue正文并查找部分。解析该部分中的Issue引用()。对于每个依赖Issue,检查其是否已关闭:
## Dependencies#Nbash
undefinedExtract dependency issue numbers from the issue body
从Issue正文中提取依赖Issue编号
DEPS=$(gh issue view ISSUE_NUM --repo "$REPO" --json body -q '.body'
| sed -n '/^## Dependencies/,/^## /p'
| grep -oP '#\K[0-9]+')
| sed -n '/^## Dependencies/,/^## /p'
| grep -oP '#\K[0-9]+')
DEPS=$(gh issue view ISSUE_NUM --repo "$REPO" --json body -q '.body'
| sed -n '/^## Dependencies/,/^## /p'
| grep -oP '#\K[0-9]+')
| sed -n '/^## Dependencies/,/^## /p'
| grep -oP '#\K[0-9]+')
Check if all dependencies are closed
检查所有依赖是否已关闭
BLOCKED=false
for DEP in $DEPS; do
STATE=$(gh issue view "$DEP" --repo "$REPO" --json state -q '.state')
if [ "$STATE" != "CLOSED" ]; then
BLOCKED=true
break
fi
done
if [ "$BLOCKED" = true ]; then
Skip this issue — dependency not yet resolved
continue
fi
If any dependency issue is still open, **skip this issue silently** (do not add labels or comment). It will be picked up in the next dispatch cycle after its dependencies are resolved.
**2.** Add `in-progress` label
**3.** Comment: `Dispatching autonomous development...`
**4.** Dispatch via helper script:
```bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-new ISSUE_NUM5. Re-check concurrency after each dispatch
BLOCKED=false
for DEP in $DEPS; do
STATE=$(gh issue view "$DEP" --repo "$REPO" --json state -q '.state')
if [ "$STATE" != "CLOSED" ]; then
BLOCKED=true
break
fi
done
if [ "$BLOCKED" = true ]; then
跳过此Issue — 依赖尚未解决
continue
fi
如果任何依赖Issue仍处于打开状态,则**静默跳过此Issue**(不添加标签或评论)。待依赖解决后,下一次调度周期会重新处理它。
**2.** 添加`in-progress`标签
**3.** 评论:`Dispatching autonomous development...`
**4.** 通过辅助脚本调度:
```bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-new ISSUE_NUM5. 每次调度后重新检查并发数
Step 3: Scan for Review Tasks
步骤3:扫描评审任务
Find issues with + (no ):
autonomouspending-reviewreviewingbash
gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous,pending-review" --json number,labels \
-q '[.[] | select([.labels[].name] | contains(["reviewing"]) | not)]'For each found issue (respecting concurrency limit):
- Remove , add
pending-reviewreviewing - Comment:
Dispatching autonomous review... - Dispatch via helper script:
bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" review ISSUE_NUM查找带有 + 标签(无标签)的Issue:
autonomouspending-reviewreviewingbash
gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous,pending-review" --json number,labels \
-q '[.[] | select([.labels[].name] | contains(["reviewing"]) | not)]'对于每个找到的Issue(需遵守并发限制):
- 移除标签,添加
pending-review标签reviewing - 评论:
Dispatching autonomous review... - 通过辅助脚本调度:
bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" review ISSUE_NUMStep 4: Scan for Pending-Dev (Resume)
步骤4:扫描待恢复开发任务(Resume)
Find issues with + :
autonomouspending-devbash
gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous,pending-dev" --json number,labels,commentsFor each found issue (respecting concurrency limit):
1. Check retry count — before dispatching, count the number of failed comments (exit code ≠ 0) on the issue. Successful dev completions (exit code 0) that were sent back by review do NOT count as retries:
Agent Session Report (Dev)bash
RETRY_COUNT=$(gh issue view ISSUE_NUM --repo "$REPO" --json comments \
-q '[.comments[] | select((.body | test("Agent Session Report \\(Dev\\)")) and (.body | test("Exit code: 0") | not))] | length')
MAX_RETRIES="${MAX_RETRIES:-3}"
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
# Issue has exceeded retry limit — mark as stalled
gh issue edit ISSUE_NUM --repo "$REPO" \
--remove-label "pending-dev" \
--add-label "stalled"
gh issue comment ISSUE_NUM --repo "$REPO" \
--body "Issue has exceeded the maximum retry limit ($MAX_RETRIES failed attempts). Marking as stalled. @${REPO_OWNER} please investigate manually."
continue
fiIf failed retry count exceeds (default 3), add label, remove , post a comment, and skip this issue.
MAX_RETRIESstalledpending-dev2. Extract latest dev session ID from issue comments (search for — do NOT match ):
Dev Session ID:Review Session ID:bash
SESSION_ID=$(gh issue view ISSUE_NUM --repo "$REPO" --json comments \
-q '[.comments[].body | capture("Dev Session ID: `(?P<id>[a-zA-Z0-9_-]+)`"; "g") | .id] | last // empty')3. Remove , add
4. Comment:
5. Dispatch via helper script:
pending-devin-progressResuming development (session: SESSION_ID)...bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-resume ISSUE_NUM SESSION_ID查找带有 + 标签的Issue:
autonomouspending-devbash
gh issue list --repo "$REPO" --state open --limit 100 \
--label "autonomous,pending-dev" --json number,labels,comments对于每个找到的Issue(需遵守并发限制):
1. 检查重试次数 — 调度前,统计Issue上失败的评论数量(退出码≠0)。评审退回的成功开发完成(退出码0)不计为重试:
Agent Session Report (Dev)bash
RETRY_COUNT=$(gh issue view ISSUE_NUM --repo "$REPO" --json comments \
-q '[.comments[] | select((.body | test("Agent Session Report \\(Dev\\)")) and (.body | test("Exit code: 0") | not))] | length')
MAX_RETRIES="${MAX_RETRIES:-3}"
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
# Issue已超过重试限制 — 标记为停滞
gh issue edit ISSUE_NUM --repo "$REPO" \
--remove-label "pending-dev" \
--add-label "stalled"
gh issue comment ISSUE_NUM --repo "$REPO" \
--body "Issue has exceeded the maximum retry limit ($MAX_RETRIES failed attempts). Marking as stalled. @${REPO_OWNER} please investigate manually."
continue
fi如果失败重试次数超过(默认值为3),则添加标签、移除标签、发布评论并跳过此Issue。
MAX_RETRIESstalledpending-dev2. 从Issue评论中提取最新的开发会话ID(搜索 — 请勿匹配):
Dev Session ID:Review Session ID:bash
SESSION_ID=$(gh issue view ISSUE_NUM --repo "$REPO" --json comments \
-q '[.comments[].body | capture("Dev Session ID: `(?P<id>[a-zA-Z0-9_-]+)`"; "g") | .id] | last // empty')3. 移除标签,添加标签
4. 评论:
5. 通过辅助脚本调度:
pending-devin-progressResuming development (session: SESSION_ID)...bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-resume ISSUE_NUM SESSION_IDStep 5: Stale Detection
步骤5:停滞检测
Find issues with or that may be stuck.
in-progressreviewingFor each such issue, check if the agent process is still alive locally. Use the correct PID file prefix based on the issue's current label:
- issues use PID file:
in-progress/tmp/agent-${PROJECT_ID}-issue-ISSUE_NUM.pid - issues use PID file:
reviewing/tmp/agent-${PROJECT_ID}-review-ISSUE_NUM.pid
bash
undefined查找带有或标签且可能已停滞的Issue。
in-progressreviewing对于每个此类Issue,检查本地Agent进程是否仍在运行。根据Issue当前标签使用正确的PID文件前缀:
- Issue使用PID文件:
in-progress/tmp/agent-${PROJECT_ID}-issue-ISSUE_NUM.pid - Issue使用PID文件:
reviewing/tmp/agent-${PROJECT_ID}-review-ISSUE_NUM.pid
bash
undefinedFor in-progress issues:
针对in-progress Issue:
kill -0 $(cat /tmp/agent-${PROJECT_ID}-issue-ISSUE_NUM.pid 2>/dev/null) 2>/dev/null && echo ALIVE || echo DEAD
kill -0 $(cat /tmp/agent-${PROJECT_ID}-issue-ISSUE_NUM.pid 2>/dev/null) 2>/dev/null && echo ALIVE || echo DEAD
For reviewing issues:
针对reviewing Issue:
kill -0 $(cat /tmp/agent-${PROJECT_ID}-review-ISSUE_NUM.pid 2>/dev/null) 2>/dev/null && echo ALIVE || echo DEAD
If DEAD and issue still has `in-progress`:
1. Comment: `Task appears to have crashed. Moving to pending-review for assessment.`
2. Remove `in-progress`, add `pending-review`
If DEAD and issue still has `reviewing`:
1. Comment: `Review process appears to have crashed. Moving to pending-dev for retry.`
2. Remove `reviewing`, add `pending-dev`kill -0 $(cat /tmp/agent-${PROJECT_ID}-review-ISSUE_NUM.pid 2>/dev/null) 2>/dev/null && echo ALIVE || echo DEAD
如果进程已停止且Issue仍带有`in-progress`标签:
1. 评论:`Task appears to have crashed. Moving to pending-review for assessment.`
2. 移除`in-progress`标签,添加`pending-review`标签
如果进程已停止且Issue仍带有`reviewing`标签:
1. 评论:`Review process appears to have crashed. Moving to pending-dev for retry.`
2. 移除`reviewing`标签,添加`pending-dev`标签Cron Configuration (OpenClaw)
定时任务配置(OpenClaw)
bash
openclaw cron add \
--name "Autonomous Dispatcher" \
--cron "*/5 * * * *" \
--session isolated \
--message "Run the autonomous-dispatcher skill. Check GitHub issues and dispatch tasks." \
--announcebash
openclaw cron add \
--name "Autonomous Dispatcher" \
--cron "*/5 * * * *" \
--session isolated \
--message "Run the autonomous-dispatcher skill. Check GitHub issues and dispatch tasks." \
--announceLabel Definitions
标签定义
| Label | Color | Description |
|---|---|---|
| | Issue should be processed by autonomous pipeline |
| | Agent is actively developing |
| | Development complete, awaiting review |
| | Agent is actively reviewing |
| | Review failed, needs more development |
| | Review passed. PR merged (or awaiting manual merge if |
| | Used with |
| | Issue exceeded max retry attempts; requires manual investigation |
| 标签 | 颜色 | 描述 |
|---|---|---|
| | Issue应由自主流水线处理 |
| | Agent正在积极开发中 |
| | 开发完成,等待评审 |
| | Agent正在积极评审中 |
| | 评审未通过,需要进一步开发 |
| | 评审通过。PR已合并(如果带有 |
| | 与 |
| | Issue已超过最大重试次数;需要手动调查 |
Model Strategy
模型策略
| Task | Model | Rationale |
|---|---|---|
Development ( | Opus (default) | Complex coding, architecture decisions |
Review ( | Sonnet ( | Checklist verification, avoids Opus quota contention |
| 任务 | 模型 | 理由 |
|---|---|---|
开发( | Opus(默认) | 复杂编码、架构决策 |
评审( | Sonnet( | 检查表验证,避免Opus配额竞争 |