| name | run |
| description | Autonomously pick up and implement Epic tasks one at a time from inside a git worktree: claim a ready task, write a spec, get it auto-reviewed, implement it, open a PR, block until it is merged, then immediately take the next task — never stopping until every reachable task is done. Use whenever the user wants to run an epic, start or continue an epic runner, work epic tasks in parallel, join an epic as a worker, or says things like 'epic:run', 'run the epic', 'start a runner', 'take a task', 'work the epic'. This is the orchestrator's execution half and pairs with epic:plan. It MUST run inside a worktree and coordinates with other runners through the shared .epic/ state. |
Epic Runner
You are one of several independent runners working an Epic. Other runners are
separate processes you never talk to directly; you all coordinate only through the
shared .epic/ state via epic.py. Your contract: keep claiming and finishing
tasks, fully autonomously, until there is nothing left you can do.
Read references/state-model.md for the state machine and the full CLI. The
scripts are executable — call them directly by path, with no python3/bash
prefix (e.g. "${CLAUDE_PLUGIN_ROOT}/scripts/epic.py" <command> …). The
shebangs pick the interpreter; a python3/bash prefix is unnecessary and may
be blocked by interpreter-level permission policies. The epic name comes from the
command argument (e.g. /epic:run my-epic); if omitted and exactly one epic
exists, the CLI resolves it.
Step 0 — Hard guard: you MUST be in a worktree
A runner changes code on its own branch in its own worktree, nested under the
epic root. Running in the root checkout itself would let parallel runners
clobber each other, so refuse to proceed if you are not in a linked worktree.
git rev-parse --is-inside-work-tree
test "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)"
If the second test fails, you are in a top-level checkout, not a nested worktree.
Stop and tell the user to create one under the epic root and start you from
inside it:
git worktree add .epic-worktrees/runner-N -b epic/<epic>/runner-N <base>
cd .epic-worktrees/runner-N && claude
Do not create code here. Being nested under the root is also how the CLI finds
the shared state: it walks up from your cwd to the nearest .epic/.
The one and only path outside your worktree you may write is that .epic/, and
only ever through epic.py. Never edit the root's or a sibling worktree's source
files.
Step 1 — Identify yourself and register
Derive a stable runner id from your worktree directory name (e.g. the
basename runner-2) so your identity survives restarts and your heartbeats stay
consistent:
EPIC="${CLAUDE_PLUGIN_ROOT}/scripts/epic.py"
RUNNER="$(basename "$(git rev-parse --show-toplevel)")"
"$EPIC" register --name <epic> --runner "$RUNNER" --worktree "$(pwd)"
Step 2 — The autonomous loop (do not stop)
Repeat the following until the loop terminates on all_done or blocked. Treat
this as a real loop: after each merge, go straight back to the top. Do not
hand control back to the user between tasks.
2a. Acquire a task (blocks)
"$EPIC" wait-ready --name <epic> --runner "$RUNNER" --worktree "$(pwd)"
This blocks until a task is claimable and then claims it for you. Read the JSON:
{"result":"claimed","task":{…}} → proceed with task.
{"result":"all_done"} → everything reachable is done. Report and exit the loop.
{"result":"blocked"} → only failed / needs_replan tasks remain. Report which
ones (via status) and exit; the planner needs to intervene.
2b. Branch off the base
Bring in everything that merged before you (dependencies are already in the base
branch, since a task only becomes ready once its deps are done):
BASE="$("$EPIC" base-branch --name <epic>)"
git fetch origin "$BASE"
git checkout -b "epic/<epic>/<task-id>" "origin/$BASE"
"$EPIC" set-status --name <epic> --id <task-id> --branch "epic/<epic>/<task-id>" --runner "$RUNNER"
2c. Write the spec
Read .epic/<epic>/design.md — the binding overall design (to-be structure, data
models, interface contracts, processing flow) plus your task's brief in §3 実装
計画. Write a full implementation spec for this task to
.epic/<epic>/specs/<task-id>.md, in Japanese (same convention as
design.md; code/identifiers/commits/PR stay English), at the granularity of a
real design doc — three parts:
- 要件定義 — what this task must achieve; acceptance criteria.
- 設計 — your approach; the concrete to-be structure you'll build (files/
modules/types with fields); the interfaces you own and consume at
signature level (conform to
design.md exactly — never reinvent a shared
interface); the processing/control flow; error handling.
- 実装計画 — affected files, ordered steps, the test plan (unit/integration
Then:
"$EPIC" spec-submit --name <epic> --id <task-id> --runner "$RUNNER"
2d. Auto-review the spec (subagent), revise until approved
Spawn a reviewer subagent (a fresh agent, so the critique is independent of
your own reasoning) and ask it to review the spec against design.md, the task's
contracts, and the existing codebase. It should check: does the approach satisfy
the acceptance criteria? does it conform to the owned/consumed contracts? right
size for one PR? test plan adequate? any conflict with the overall design?
- If it requests changes:
spec-reject --reason "…", revise the spec, and review
again. Loop until it approves.
- When it approves:
spec-approve --id <task-id> --by reviewer (this moves the
task to implementing).
Keep the human out of this gate — it is meant to run unattended. The spec lives in
.epic/ so a human can inspect or intervene asynchronously, but you don't wait
on them here.
2e. Implement
Implement strictly per the approved spec, inside this worktree. Write tests for
everything you add (this is non-negotiable). Run the project's checks/tests and
make them pass. Commit with a clear message. Follow the repo's own conventions
and any CLAUDE.md rules.
2f. Open the PR
git push -u origin "epic/<epic>/<task-id>"
gh pr create --base "$BASE" --head "epic/<epic>/<task-id>" \
--title "<type>: <concise title>" --body "<summary + 'Epic: <epic> / task: <task-id>'>"
PR=$(gh pr view --json number -q .number)
"$EPIC" set-status --name <epic> --id <task-id> --status pr_open --pr "$PR" --runner "$RUNNER"
2g. Block until merged
"$EPIC" wait-merge --name <epic> --id <task-id> --runner "$RUNNER"
This blocks (polling gh) until a human merges the PR — then it marks the task
done and unblocks its dependents for the whole fleet — or until the PR is
closed unmerged (→ the task is marked failed). On merged, loop back to 2a
immediately. On closed, note it and loop back to 2a (other tasks may remain).
Failure handling
- Mis-scoped task (too big, wrong dependencies, contract gap that needs the
planner):
flag-replan --id <task-id> --runner "$RUNNER" --reason "…", then
loop back to 2a. The planner picks it up.
- Recoverable local failure you can't finish right now:
release --id <task-id> --runner "$RUNNER" --reason "…" to return it to ready for another
runner (or --failed if it's genuinely dead). Then loop back to 2a.
- Never silently abandon a claimed task — always release, fail, or flag it so it
doesn't sit pinned to a runner that has moved on.
Coordination notes
- All
.epic/ writes go through epic.py; never hand-edit state files —
concurrency safety depends on the CLI's locking.
- The
wait-* commands heartbeat for you while they block, so the fleet sees you
alive. For unusually long local work between CLI calls, you may
heartbeat --runner "$RUNNER" to be safe.
- You and the other runners may pick up dependent tasks the moment a dependency
merges — that's the point. Trust the state; don't assume you're alone.