| name | do-epic |
| description | Work on a multi-story GitHub epic end-to-end. Builds a DAG from the child stories, groups them into waves, runs waves in parallel via per-story worktrees, ejects PRs that fail agent-code-review for human handling, and propagates blocks to downstream stories. Requires git worktrees. |
| argument-hint | epic issue number (e.g., 123) |
| user-invocable | true |
| disable-model-invocation | false |
Depends on: mav-scope-boundaries, mav-multi-instance-coordination, mav-durability-on-gh, mav-block-propagation, mav-git-workflow, mav-stacked-prs, mav-github-issue-workflow, mav-create-solution-design, mav-create-tasks, mav-plan-execution, mav-local-verification, mav-bp-cicd, mav-bp-remote-code-review, mav-claude-code-recovery, do-issue-solo
Work on GitHub Epic (Autonomous, DAG-scheduled)
Work on epic issue `` end-to-end. Builds a dependency DAG
from the epic's child stories, schedules them into waves, runs each wave
in parallel via per-story worktrees, and drives every story through agent
code review with binary PASS/FAIL verdicts.
Preflight (mandatory)
Run this first. If it exits non-zero, halt and report the stderr output to the user verbatim. Do not claim, do not branch, do not start any worktree.
uv run maverick preflight do-epic
The check verifies: the project is initialised, the Maverick GitHub
App is configured (maverick gh-app status reports configured: true),
git worktrees are usable, and required tools (gh, git, uv) are on
PATH. PR code review runs locally inside each story's worktree as the
agent-code-reviewer subagent (delegated from
do-issue-solo Phase 9). For epics that fan out across
many parallel workers, consider opting into the CI-side re-run
described in mav-bp-remote-code-review so the merge is
gated by an independent check even if a worker's local review fails to
fire — that workflow is optional and not enforced by preflight.
Before You Begin
`` must be the GitHub issue number of a Maverick epic
— an issue whose body contains a task table of child stories. If it is
a single story, dispatch do-issue-solo instead.
Phase 0: Coordination
- Resolve repo:
gh repo view --json nameWithOwner.
- Cold-start hydrate per
mav-durability-on-gh:
- Read the
maverick-dag marker on the epic.
- Read the
maverick-state marker.
- Read the
maverick-bprop marker — if present, resume block
propagation first before continuing.
- Read open PRs for the epic's child-story branches.
- Decide claim scope:
- Whole epic if no live claim exists.
- A specific wave if another instance already holds other stories.
- Single story if another instance holds the epic but not the story
you were asked about.
- Claim:
uv run maverick coord claim <repo> --scope <csv-of-issues>.
On ClaimRejected, abort and report per
mav-multi-instance-coordination.
- Start heartbeat for every issue in the claim scope. Run the
self-terminating foreground loop in the background — it exits cleanly
when the matching
coord release runs at session end, so there is no
manual kill step:
for issue in <epic> <story1> <story2> …; do
uv run maverick coord heartbeat-loop <repo> $issue >/dev/null 2>&1 &
done
- Register release handler for every exit path. Run
coord release for every claimed issue; the matching heartbeat-loop
detects the cleared label on its next iteration and exits.
Phase 1: Design + task decomposition (if needed)
If the epic does not yet have a solution design:
- Dispatch
agent-issue-analyst with the epic issue.
- Dispatch
agent-github-issue-planner — the planner will
create child stories as sub-issues (per the ≥ 5-tasks threshold in
mav-create-tasks).
- Extend the claim to cover every newly-created child story:
uv run maverick coord claim <repo> <child-issue> for each.
If the epic already has stories, skip to Phase 2.
Phase 2: DAG analysis
Build a machine-readable DAG of child stories.
- For each child story, read its issue body. Look for:
- Explicit "depends on #N" references
- Shared files in the implementation plan
- Shared APIs or data models
- Construct the DAG payload per the schema in
docs/conventions/github-markers.md:
{
"epic": ,
"stories": {
"140": {"deps": [], "files": ["app/src/boot.ts"]},
"142": {"deps": ["140"], "files": ["app/src/admin/guard.ts"]}
}
}
- Persist the DAG to GitHub — this is durable for any instance to
resume from:
uv run maverick gh-app gh -- issue comment --body-file /tmp/dag-marker.md
Alternatively, the CLI helper composes the marker for you — preferred:
construct the payload JSON and post via the App's gh with a fenced
maverick-dag block.
Phase 3: Wave grouping
uv run maverick dag waves <repo>
The output is a list of lists — each inner list is a set of stories with
no un-satisfied deps on later waves. Record the waves in the epic's
human-readable task-table comment as well, so a human skimming the epic
can see execution order.
Phase 4: Initialise epic state
- For each child story, seed
maverick-state with initial status:
pending if not yet started
merged if already merged (detected by reading PR state)
in_flight only if another instance's heartbeat is live for it
- Persist:
uv run maverick state set <repo> <story> <status>
Each call mirrors the local cache to the maverick-state marker on
the epic.
Phase 5: Wave loop
Repeat until every story is terminal (merged or ejected) or every
remaining wave is fully blocked.
Phase 5a: Select the next wave
- Read waves:
uv run maverick dag waves <repo> .
- Read current state:
uv run maverick state show <repo> .
- Pick the first wave whose every member is not yet merged.
- Exclude stories carrying
blocked-by:#N — re-read the labels at
selection time so human-resolved ejections automatically unblock.
- If every remaining story in every remaining wave is blocked, halt
the epic cleanly:
- Update
maverick-state.
- Post a summary comment on the epic listing the blockers.
- Release the epic-level claim.
- Report to the user.
Phase 5b: Create worktrees
Resolve the story base branch from config:
STORY_BASE=$(uv run maverick git-workflow story-base)
For each unblocked story in the wave, decide its base branch:
- If the story depends on a sibling in the same wave that is still
unmerged, stack per
mav-stacked-prs.
- Otherwise, branch from
$STORY_BASE.
Create the worktree:
uv run maverick worktree create <branch> [--base <sibling-branch>]
Phase 5c: Parallel-or-serial decision
Decide based on:
- User policy: does the user want parallel subagents? (Check the
session; if unsure, default to serial.)
- Capacity: no more than
MAX_CONCURRENT_WORKTREES=4 active at once.
If parallel: dispatch one subagent per worktree, running
do-issue-solo for that story inside that worktree. Track
the subagents and aggregate results.
If serial: iterate worktrees one at a time, running
do-issue-solo in each.
Phase 5d: Per-story flow
For each story being worked on (whether as subagent or serially):
- Re-verify the claim:
uv run maverick coord heartbeat <repo> <story>
— if the story now carries blocked-by:#N, abort without pushing.
- Run the story through
do-issue-solo's per-story phases
(Phase 4 onward — branch is already created).
- On FAIL from agent-code-reviewer (eject):
- Mark story
ejected in epic state:
uv run maverick state set <repo> <story> ejected.
- Run block propagation per
mav-block-propagation:
- Write
maverick-bprop marker on the epic.
- Walk descendants, apply
blocked-by:#<story> to each.
- Cancel any in-flight subagent work for a now-blocked story.
- Clear the
maverick-bprop marker when the walk completes.
- On PASS + auto-merge:
- Mark story
merged in epic state.
- Release the story-level claim.
- Destroy the worktree.
Phase 5e: Wave completion
When every story in the wave is terminal (merged or ejected):
- If every remaining wave is fully blocked by ejections, halt
(Phase 5a's block check handles this on the next iteration).
- Otherwise, return to Phase 5a for the next wave.
Phase 6: Epic completion
- Every child story is either merged or ejected.
- Post a summary comment on the epic:
- Count merged vs ejected.
- Link to every PR.
- Call out the ejected stories as the human's queue.
- Update
maverick-state one last time.
- Release the epic-level claim:
uv run maverick coord release <repo> --reason complete.
- The
heartbeat-loop background processes self-terminate within one
--interval window once their claims are released — wait for them
if you backgrounded them with PIDs, or just let the parent shell exit.
- Close the epic issue only if no stories were ejected AND the
per-project close policy allows it (#52). Per-PR closes for child
stories already ran via
do-issue-solo Phase 10, but the epic
itself spans multiple branches/PRs, so the simple close-on-merge
CLI doesn't fit — query the policy and decide:
policy=$(uv run maverick issue policy)
if [ "$policy" != "manual" ] && [ "$ejected_count" -eq 0 ]; then
gh issue close \
--comment "Epic complete: $merged_count stories merged."
fi
If any stories were ejected, leave the epic open for the human to
resolve them and close manually. If the policy is manual, leave
the epic open regardless — the team's promotion workflow handles
final close.
Resumption
At any entry, if another instance was mid-flight:
- A
maverick-bprop marker means a block walk was in progress — resume it
per mav-block-propagation.
- A
maverick-claim on some stories with a stale lease means an instance
died — decide takeover per mav-multi-instance-coordination.
- A
maverick-state with stories marked in_flight but no live claim
means those stories are orphaned — reclaim and resume, or mark back to
pending if the worktree was destroyed.
The workflow is idempotent on every entry — you can re-run do-epic on
the same epic any number of times and it will converge to the same final
state.
Rules
- Worktrees are mandatory. Abort on entry if unavailable.
- The Maverick GitHub App is mandatory. Abort on entry if
maverick gh-app status does not report configured: true.
- Binary review everywhere. No story merges without PASS from
agent-code-reviewer. No fix loop.
- Block propagation is idempotent. Re-running it after a crash is
safe; the marker-walk-clear protocol handles partial completion.
- Release every claim on every exit. Success, eject, abort, crash.
- Never auto-close an epic with ejected stories. Those belong to the
human.