autonomous-dispatcher

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Autonomous 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
gh
CLI calls MUST use a GitHub App token, NOT the default user token.
Before running any
gh
command
, generate and export the App token using the shared script at
scripts/gh-app-token.sh
:
bash
undefined
重要提示: 所有
gh
CLI调用必须使用GitHub App令牌,而非默认用户令牌。
在运行任何
gh
命令之前
,使用位于
scripts/gh-app-token.sh
的共享脚本生成并导出App令牌:
bash
undefined

Source 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

环境变量

  • REPO
    : GitHub repo in
    owner/repo
    format (e.g.,
    myorg/myproject
    )
  • PROJECT_DIR
    : Absolute path to the project root on the local machine
  • MAX_CONCURRENT
    : Max parallel tasks (default:
    5
    )
  • MAX_RETRIES
    : Max dev retry attempts before marking issue as
    stalled
    (default:
    3
    )
  • PROJECT_ID
    : Project identifier for log/PID files (default:
    project
    )
  • DISPATCHER_APP_ID
    : GitHub App ID for the dispatcher bot
  • DISPATCHER_APP_PEM
    : Path to the GitHub App private key PEM file
  • REPO
    : GitHub仓库,格式为
    owner/repo
    (例如:
    myorg/myproject
  • PROJECT_DIR
    : 本地机器上项目根目录的绝对路径
  • MAX_CONCURRENT
    : 最大并行任务数(默认值:
    5
  • MAX_RETRIES
    : 标记Issue为
    stalled
    之前的最大开发重试次数(默认值:
    3
  • PROJECT_ID
    : 日志/PID文件的项目标识符(默认值:
    project
  • DISPATCHER_APP_ID
    : 调度器机器人的GitHub App ID
  • DISPATCHER_APP_PEM
    : GitHub App私钥PEM文件的路径

Local Dispatch Helper Script

本地调度辅助脚本

CRITICAL: All task dispatches (dev-new, dev-resume, review) MUST use the helper script
scripts/dispatch-local.sh
in the project root's
scripts/
directory. The script handles:
  • 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
undefined

PROJECT_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
in-progress
OR
reviewing
:
bash
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.
统计带有
in-progress
reviewing
标签的Issue数量:
bash
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
autonomous
label but NO state labels:
bash
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
## Dependencies
section. Parse issue references (
#N
) from that section. For each referenced issue, check if it is closed:
bash
undefined
查找带有
autonomous
标签但无状态标签的Issue:
bash
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正文并查找
## Dependencies
部分。解析该部分中的Issue引用(
#N
)。对于每个依赖Issue,检查其是否已关闭:
bash
undefined

Extract 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]+')
DEPS=$(gh issue view ISSUE_NUM --repo "$REPO" --json body -q '.body'
| 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_NUM
5. 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_NUM
5. 每次调度后重新检查并发数

Step 3: Scan for Review Tasks

步骤3:扫描评审任务

Find issues with
autonomous
+
pending-review
(no
reviewing
):
bash
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):
  1. Remove
    pending-review
    , add
    reviewing
  2. Comment:
    Dispatching autonomous review...
  3. Dispatch via helper script:
bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" review ISSUE_NUM
查找带有
autonomous
+
pending-review
标签(无
reviewing
标签)的Issue:
bash
gh issue list --repo "$REPO" --state open --limit 100 \
  --label "autonomous,pending-review" --json number,labels \
  -q '[.[] | select([.labels[].name] | contains(["reviewing"]) | not)]'
对于每个找到的Issue(需遵守并发限制):
  1. 移除
    pending-review
    标签,添加
    reviewing
    标签
  2. 评论:
    Dispatching autonomous review...
  3. 通过辅助脚本调度:
bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" review ISSUE_NUM

Step 4: Scan for Pending-Dev (Resume)

步骤4:扫描待恢复开发任务(Resume)

Find issues with
autonomous
+
pending-dev
:
bash
gh issue list --repo "$REPO" --state open --limit 100 \
  --label "autonomous,pending-dev" --json number,labels,comments
For each found issue (respecting concurrency limit):
1. Check retry count — before dispatching, count the number of failed
Agent Session Report (Dev)
comments (exit code ≠ 0) on the issue. Successful dev completions (exit code 0) that were sent back by review do NOT count as retries:
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
fi
If failed retry count exceeds
MAX_RETRIES
(default 3), add
stalled
label, remove
pending-dev
, post a comment, and skip this issue.
2. Extract latest dev session ID from issue comments (search for
Dev Session ID:
— do NOT match
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
pending-dev
, add
in-progress
4. Comment:
Resuming development (session: SESSION_ID)...
5. Dispatch via helper script:
bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-resume ISSUE_NUM SESSION_ID
查找带有
autonomous
+
pending-dev
标签的Issue:
bash
gh issue list --repo "$REPO" --state open --limit 100 \
  --label "autonomous,pending-dev" --json number,labels,comments
对于每个找到的Issue(需遵守并发限制):
1. 检查重试次数 — 调度前,统计Issue上失败
Agent Session Report (Dev)
评论数量(退出码≠0)。评审退回的成功开发完成(退出码0)不计为重试:
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
如果失败重试次数超过
MAX_RETRIES
(默认值为3),则添加
stalled
标签、移除
pending-dev
标签、发布评论并跳过此Issue
2. 从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. 移除
pending-dev
标签,添加
in-progress
标签 4. 评论:
Resuming development (session: SESSION_ID)...
5. 通过辅助脚本调度:
bash
bash "$PROJECT_DIR/scripts/dispatch-local.sh" dev-resume ISSUE_NUM SESSION_ID

Step 5: Stale Detection

步骤5:停滞检测

Find issues with
in-progress
or
reviewing
that may be stuck.
For 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:
  • in-progress
    issues use PID file:
    /tmp/agent-${PROJECT_ID}-issue-ISSUE_NUM.pid
  • reviewing
    issues use PID file:
    /tmp/agent-${PROJECT_ID}-review-ISSUE_NUM.pid
bash
undefined
查找带有
in-progress
reviewing
标签且可能已停滞的Issue。
对于每个此类Issue,检查本地Agent进程是否仍在运行。根据Issue当前标签使用正确的PID文件前缀:
  • in-progress
    Issue使用PID文件:
    /tmp/agent-${PROJECT_ID}-issue-ISSUE_NUM.pid
  • reviewing
    Issue使用PID文件:
    /tmp/agent-${PROJECT_ID}-review-ISSUE_NUM.pid
bash
undefined

For 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." \
  --announce
bash
openclaw cron add \
  --name "Autonomous Dispatcher" \
  --cron "*/5 * * * *" \
  --session isolated \
  --message "Run the autonomous-dispatcher skill. Check GitHub issues and dispatch tasks." \
  --announce

Label Definitions

标签定义

LabelColorDescription
autonomous
#0E8A16
Issue should be processed by autonomous pipeline
in-progress
#FBCA04
Agent is actively developing
pending-review
#1D76DB
Development complete, awaiting review
reviewing
#5319E7
Agent is actively reviewing
pending-dev
#E99695
Review failed, needs more development
approved
#0E8A16
Review passed. PR merged (or awaiting manual merge if
no-auto-close
present)
no-auto-close
#d4c5f9
Used with
autonomous
— skip auto-merge after review passes, requires manual approval
stalled
#B60205
Issue exceeded max retry attempts; requires manual investigation
标签颜色描述
autonomous
#0E8A16
Issue应由自主流水线处理
in-progress
#FBCA04
Agent正在积极开发中
pending-review
#1D76DB
开发完成,等待评审
reviewing
#5319E7
Agent正在积极评审中
pending-dev
#E99695
评审未通过,需要进一步开发
approved
#0E8A16
评审通过。PR已合并(如果带有
no-auto-close
标签则等待手动合并)
no-auto-close
#d4c5f9
autonomous
配合使用 — 评审通过后跳过自动合并,需要手动批准
stalled
#B60205
Issue已超过最大重试次数;需要手动调查

Model Strategy

模型策略

TaskModelRationale
Development (
autonomous-dev.sh
)
Opus (default)Complex coding, architecture decisions
Review (
autonomous-review.sh
)
Sonnet (
--model sonnet
)
Checklist verification, avoids Opus quota contention
任务模型理由
开发(
autonomous-dev.sh
Opus(默认)复杂编码、架构决策
评审(
autonomous-review.sh
Sonnet(
--model sonnet
检查表验证,避免Opus配额竞争