| name | claude-dispatch |
| description | Use when working on, arming, debugging, or reasoning about the @claude / /claude impl containerized implementer that lands on main from a Forgejo issue comment - the gate script, the claude-issue.yml workflow, the INERT-by-design enablement + author gates, the goose+qwen (not Anthropic-key) backing, per-issue cap and concurrency, or why a summon did or did not fire. Aliases - @claude, /claude impl, claude dispatch, claude-issue.yml, claude_dispatch.py gate, CLAUDE_DISPATCH_ENABLED. |
claude-dispatch
Status: 🛠 Runbook | Last updated: 2026-07-03
@claude (or /claude impl) in a Forgejo issue comment summons a containerized
implementer that works the issue and lands it on main (pre-commit + tests green,
then push, else files an autonomous-block: issue and exits nonzero). Canonical
reusable workflow. Full detail lives in references/claude-dispatch.md - read it
before touching the wiring.
What this is vs the advisory summon
Unlike the advisory coilyco-ops summon, this one implements and pushes. It
fires only from .forgejo/workflows/claude-issue.yml on issue_comment
(created only) - the CI surface. /claude consult is recognized but declined
(consult is a workstation surface). Tokens match only at line start.
INERT by design - do not "fix" it into firing
This path does not fire today, on purpose (infra#429). Kai's standing directive
rejects Anthropic-key Claude automation. So:
- Enablement gate -> the job
if: needs vars.CLAUDE_DISPATCH_ENABLED == 'true',
which is unset. Job is skipped before any step runs.
- No
ANTHROPIC_API_KEY, no pinned model in the dispatch step - landing the
runner cannot arm the wrong backing. #337 (the key wiring) is closed superseded.
- When it arms, it is backed by goose + qwen on the local model box (#336
reworked), not the Anthropic API. The gate logic is backing-agnostic and kept.
The author gate is load-first and non-negotiable
The comment body is the prompt on a job that will hold a push token, so
scripts/claude_dispatch.py gate runs first, default-deny:
- Acts only for a repo
write/admin/owner author or the CLAUDE_DISPATCH_ALLOW
allowlist. Everyone else is logged and ignored, never a public reply.
- Skips PR comments, the bot's own comments (loop guard), non-
created actions.
- The gate step holds only a repo-read + comment-write token. Push token and any
model credential live only in later steps, gated on the gate's
run output.
- Pure logic (token, authorization, cap, decision) is unit-tested in
scripts/claude_dispatch_test.py.
Runaway control
Enablement gate; per-issue concurrency claude-dispatch-<issue> (queued not stomped);
per-issue 24h cap CLAUDE_DISPATCH_ISSUE_CAP (default 5); timeout-minutes: 45.
Arm / operate
- Build the goose + qwen backing and rework the runner image (#336) to carry the
toolchain, the dispatch CLI, and
claude_dispatch.py as claude-dispatch on PATH.
- Wire the secrets/vars per the reference (push token, gate token, allowlist, bin).
- Only then set
vars.CLAUDE_DISPATCH_ENABLED='true'. Never before the
goose+qwen backing exists.
Debugging a non-fire (expected today)
Check in order -> is CLAUDE_DISPATCH_ENABLED set? is the author authorized? is it a
created non-PR non-bot comment? under the 24h cap? token at line start? See
references/claude-dispatch.md and .forgejo/workflows/claude-issue.yml.