---
name: milestone-workflow
description: Use when the user wants a milestone of Execution-block-stamped GitHub issues implemented via a multi-agent dynamic workflow — "create the workflow for v0", "run v0 continuously", "/milestone-workflow v0". Builds dependency tracks, presents the run plan for approval, then runs the milestone-pipeline workflow: per-issue model/effort from the Execution blocks, optional fableplan stage, PRs, optional review loops until LGTM — the repo's @claude Action by default, or in-session subagent reviewers in subagent mode — then pauses at each LGTM so the orchestrating session merges the PR in-session (no merge subagents) and resumes, and, when every issue merges, the orchestrator syncs docs and publishes a release. Stage 7 of the new-app-pipeline.
---

# milestone-workflow

Turn a reviewed milestone into a running multi-agent pipeline. The dependency graph is decided here with the user; the workflow builds hard-dependent work from reviewed predecessor code. With merging on (the default when review loops run), no subagent ever holds merge authority: at each LGTM the run pauses as `awaiting_merge`, the orchestrating session merges in-session (step 5), then resumes with the merge recorded in `args.merged`. When every issue merges, the orchestrator syncs docs and publishes the release. For a read-only table of the milestone first, use `milestoneplan`.

## Steps

### 1. Build the dependency tracks

- List the milestone's issues with `--state all` and search for open PRs that close each one. Three buckets: **build**, **resume** (open issue with an open PR — run `fix-pr-review-loop` on it, never a fresh build), **skip** (closed).
- Cross-bucket edges must be settled here, never dropped. Predecessor merged: drop the edge. Predecessor in resume: finish its loop first; with merging on, also merge it at LGTM plus green CI (the approved plan covers this); with merging off, a hard edge excludes the dependent and its hard descendants as **blocked pending merge of PR #X**. Predecessor closed without a merged PR: exclude the dependent and its hard descendants as **blocked pending decision** and ask whether to reopen or re-scope.
- Read `**Depends on:**` (hard) and `**Runs after:**` (ordering-only). An explicit `none` is authoritative. For an issue missing a field, infer only that edge kind from the prose and label every inferred edge; flag an unclear kind for plan review instead of guessing.
- Reject references outside the milestone and cycles across both edge kinds.
- Tracks are `{ issues: [...], after: [<track>...], runsAfter: [<track>...] }`. Combine issues into one `issues` array only when every serial edge is hard; ordering-only chains stay separate tracks joined by `runsAfter`. Unrelated tracks start together; multiple hard predecessors stay separate `after` entries.

### 2. Present the run plan — approval is mandatory

Show the target branch (the repo default, or the `targetBranch` the user named; a non-default target also states that the run publishes no release), then numbered tracks with titles, hard `after` edges apart from `runsAfter` edges, inferred edges, each issue's model/effort/fableplan (fableplan issues plan at the stamped Plan effort, else high) with the harness named on every issue whose Build model stamps the Codex CLI or the Cursor CLI (those build through the `cli-dispatch` shim under an Opus 5 driver agent), the readiness boundary, and merge order. **Do not invoke the Workflow tool until the user approves this plan**; the approval is the multi-agent opt-in.

State the GitHub writes the run performs: validation corrections to issue bodies, plan comments on `fableplan: Yes` issues, PRs, review-trigger and disposition comments, and, with merging on, in-session squash-merges at LGTM plus green CI, branch deletion, issue closure, a docs-sync change, and a GitHub release. Approving the plan authorizes those merges and the release; `merge: false` keeps merging manual.

Add a **Run size** line: the baseline `1 prep + per issue (1 validate + (fableplan ? 1 plan : 0) + 1 implement + (subagent review ? 1 reviewer : 0))`, and the retry-aware ceiling `baseline + 3 × issues`. Compare both with the session's Dynamic workflow size guideline (else Claude Code's default of more than 25 scheduled agents) and name the source. Both are planning bounds: review loops dispatch fix and re-review agents beyond them, and a projected token total above 1.5 million also triggers the warning. When either count crosses the threshold, recommend splitting the milestone into separate invocations. With a token target, the run defers an issue as `budget_deferred` when fewer than `budgetFloor` tokens (default 80k) remain at its start; size the floor to one issue's worst-case cost. **External CLI spend is outside every number above.** An issue whose Build model stamps the Codex CLI or the Cursor CLI spends on that CLI's own account for the build and every fix pass (and again for a retry); the agent count and the token projection describe only its Opus 5 driver, and neither the token target nor `budgetFloor` counts or bounds that spend. State this on the Run size line whenever the plan carries a CLI stamp.

### 3. Preflight the repo

- `gh auth status` succeeds and `gh api repos/<owner>/<repo> --jq .permissions.push` is `true`.
- Review mode: the default `reviewMode: 'github'` needs `.github/workflows/claude.yml` (from `templates/claude-workflow/workflows/claude.yml`, plus `CLAUDE_CODE_OAUTH_TOKEN` and the Claude GitHub App) and working Actions; with `reviewBot: 'codex'` it needs `codex.yml` plus the secrets the `fix-pr-review-loop` preflight lists. Set `runs-on` to your runner label when no self-hosted runner exists. Without a bot, pass `reviewMode: 'subagent'` or `reviewLoop: false`. Actions-hosted CI stays a separate dependency.
- With a `targetBranch`, `git ls-remote --heads origin "refs/heads/<branch>"` lists exactly one line; validate the name per `work-on-issue` step 1 ("Target") first. A missing branch stops the run here; never create it silently.
- With merging on, the account must be allowed to squash-merge and delete branches on the base (the target branch); otherwise relax protection or run `merge: false`.
- For every issue whose Build model stamps an external CLI: `command -v codex` plus `codex login status` for a Codex CLI stamp, `command -v agent` plus `agent status` for a Cursor CLI stamp. A missing binary or a signed-out status blocks that issue at run time while the rest of the milestone runs; fix it here or restamp the issue through `execution-plan-review`.
- The target repo's CLAUDE.md covers package manager and test commands.

### 4. Run

Invoke `{name: 'milestone-pipeline', args: {tracks: [...], reviewLoop: true, maxReviewCycles: 5}}`. Options: `budgetFloor`; `reviewMode` (`'github'` default, `'subagent'`); `reviewBot` (`'claude'` default, `'codex'` only when the user names Codex, never inferred from a `codex.yml`); `merge` (defaults to `reviewLoop`; `merge: true` with `reviewLoop: false` is rejected); `merged` (records from step 5, empty at first); `release` (defaults to `merge`, or to `false` when `targetBranch` is set; rejected without merge); `targetBranch` (the branch every PR opens against and every worktree starts from; default the repo default branch; passed to `validate-issue` and `work-on-issue` on every issue, and returned as `target_branch`).

Right after the invocation returns, post its runId and script path as a comment on the milestone's first issue (footer: `Created with LLM: <current model> | high | Harness: milestone-workflow`).

Phases: **Prep** reads each issue's `[C..]` score and Execution block. **Validate** runs `validate-issue` per issue on the band model from `validate-issue` step 6 at the stamped Validate effort, else the band effort; a higher validator score re-validates once upward and re-routes build model, effort, and fableplan, with a stamped `PR review:` reviewer kept unless the escalated review default outranks it; `INVALID` issues are skipped. **Plan** posts a Fable 5.1 plan for `fableplan: Yes` issues at the stamped Plan effort, else high. **Implement** runs `work-on-issue` with verified `baseRefs` — on a Claude model directly, or through the `cli-dispatch` shim under an Opus 5 driver when the Build model stamps the Codex CLI or the Cursor CLI, and the driver never substitutes a Claude build; in github mode it also runs review cycle 1 with the first-review trigger from the `validate-issue` step 6 table (or the stamped trigger), and `fix-pr-review-loop` owns the step-down ladder and the Codex mapping. **Review readiness** loops until LGTM before any successor starts; in subagent mode a reviewer agent posts a `pr-review` comment and a `fix-pr-review` fixer answers it. **Merge** counts a validated `args.merged` record as merged; a PR at LGTM without a record returns `awaiting_merge` (successors `merge_pending` or `dependency_blocked`) and the run ends for step 5. **Release** returns `release.deferred: true` when every issue merged; a partial milestone never publishes, and with `targetBranch` set the release stays off unless `release: true` is passed. Failed, blocked, or unmerged hard predecessors block descendants.

### 5. Merge-resume loop, monitor, close out

On each `awaiting_merge` return, merge that PR in-session, then resume. Merge one PR at a time and finish sub-steps 1 to 6 for it before the next PR, so GitHub recomputes every remaining PR against the new base. A PR merges only on a bare LGTM: one standalone `LGTM` line and no recommended or optional items. An LGTM that still lists items returns to the review loop, which runs `fix-pr-review` and a re-review under `fix-pr-review-loop`, whose stop rule (bare LGTM, or the first LGTM past 5 cycles) still applies.

1. `gh pr view <num> --json state,headRefName,headRefOid,mergeStateStatus`: OPEN, and `headRefOid` equals the reviewed readiness SHA the run returned. A different head means commits landed after the review: send the PR back through the review loop.
2. CI gate: `gh pr checks <num> --watch`; any failed check blocks. No checks counts as passing. The base is the PR's `baseRefName`, which must equal the target branch; a PR against another branch blocks. If behind the base, `gh pr update-branch <num>` only when it merges cleanly, re-capture the head, and repeat the CI gate. On conflicts (`mergeStateStatus` `DIRTY`, or a failed update), resolve them in-session: check out the PR branch in a worktree, merge the base, keep the intent of both sides, run the repo tests and stop on any failure, push, record the conflicted file list, re-capture the head, and repeat the CI gate on that head. Then apply the conflict re-review rule in sub-step 3.
3. Fix `<verified-sha>`. Subagent mode: the reviewed SHA, or the re-captured head after a clean update (a base-only catch-up keeps the reviewed diff). Github mode: the reviewed SHA only; if the head changed, the standing LGTM predates it, so return to the review loop for a fresh LGTM. **Conflict re-review decision** (both modes): when the only commits after the reviewed SHA are conflict resolutions this session pushed, read the diff from the reviewed SHA to the head and decide whether it changes behavior. Prose only (wording, docs, comments, formatting that no program, test, workflow, or agent executes): the standing LGTM holds and `<verified-sha>` becomes the new head. A behavior change, or any doubt (source, tests, config, workflows, scripts, and agent-executed Markdown such as a `SKILL.md`, `CLAUDE.md`, or `AGENTS.md` file): post `@claude sonnet review` (github mode) or run one subagent `pr-review` cycle (subagent mode) and require a fresh bare LGTM on the new head before merging. The file extension is evidence and never the answer; record the decision and reason in the progress relay. A second update forced by a concurrent merge re-applies the same decision. `fix-pr-review` step 7 and `fix-pr-review-loop` step 4 make the same decision when a review loop resolves a conflict.
4. **Github mode LGTM recency gate**, run last and again after every CI wait or branch update: re-fetch the live head and full comment history; find the newest one-line `@<bot> [model] review [effort]` trigger and the newest `github-actions[bot]` output linking `/actions/runs/<run-id>`; that run must be `completed` with `success`; the output must be later than the trigger and carry exactly one standalone `LGTM` line. A newer trigger or a missing matching output blocks; never fall back to an older LGTM. Do not compare the run's `head_sha` to the PR head (an `issue_comment` run reports the default branch). Instead require the output's `created_at` to be later than when the head became visible: the earliest check-suite `created_at` on `<verified-sha>`, else its committer date. One exemption, the prose-only conflict case from sub-step 3: when every commit after the reviewed SHA is a conflict resolution this session pushed and sub-step 3 decided the diff from `<reviewed-sha>` to `<verified-sha>` is prose only, the visibility time is the reviewed SHA's, so the standing LGTM passes the gate on `<verified-sha>`. Any other commit after the LGTM, including an author push, blocks as before. Run sub-step 5 immediately after the gate; nothing in between.
5. `gh pr merge <num> --squash --delete-branch --match-head-commit <verified-sha>`, never unpinned. If GitHub rejects the head, restart from sub-step 1; never retry with a fresh SHA.
6. Confirm the issue closed (`gh issue view <n> --json state`; close it if needed) and capture the merge SHA (`gh pr view <num> --json mergeCommit`).
7. Resume with the same `scriptPath` and `resumeFromRunId`, passing the **complete original `args`** (`tracks` is required) with `args.merged` extended by `{issue, pr, merge_sha, issue_state}` for every merge so far. The run rejects an issue outside the run, a repeated issue or PR, or a PR that differs from the one it opened (`merge_record_mismatch`, blocking that issue's descendants), and reports unused records in `unmatched_merged_records`. Repeat until no `awaiting_merge` remains.

When the run returns `release.deferred: true`, run `sync-docs-release` in-session and report the tag and URL. When `target_branch` is set and differs from the repo default branch, skip `sync-docs-release` (`create-release` cuts only from the default branch) and report that the release stays manual until the target branch merges into the default.

Relay progress (PRs opened, loops finishing, merges, blockers). On completion, report issue → PR → review status → merge status, agent flags, and the release outcome. For every `rescore` record: load `github-issue-format`, restamp the `[C..]` title and the Execution block's build model, effort, and fableplan to the values the run used (footer verb `Updated`), and tell the user what changed. With merging off, recommend merge order with every hard prerequisite first.

## Context discipline

The session holds no implementation detail: the milestone's issues, their Execution blocks, the open PRs, and the runId comment are the memory. Losing the conversation must never lose state.

## Failure modes

| Situation | Do this |
|---|---|
| An issue lacks an Execution block | Stop; send it through `execution-plan-review` |
| A stamped Codex CLI or Cursor CLI is absent or signed out | That issue blocks with the binary or login named and the rest of the run continues; install or sign in, or restamp through `execution-plan-review`, then resume |
| Actions billing or runner outage stalls reviews | Next invocation uses `reviewMode: 'subagent'`; running github-mode loops wait for Actions |
| Branch protection blocks squash merges or needs human approval | Run `merge: false`; successors stack on unmerged heads |
| Some issues did not reach `merged` | Release skipped; merge stragglers in-session and resume, or run `sync-docs-release` yourself |
| `targetBranch` is missing on origin or fails name validation | Stop in preflight; create or correct the branch, then rerun; never fall back to the default branch |
| `targetBranch` is not the default branch and the run asks for a release | Merges land on the target; `sync-docs-release` is skipped and the release stays manual |
| A dependency integration conflicts | The track and hard descendants stop before product changes; report the conflicting heads |
| A remaining PR conflicts after a merge | Resolve it in-session under step 5 sub-step 2; a prose-only fix keeps the LGTM, a behavior change or any doubt needs a sonnet re-review |
| Workflow returns empty or odd results | Read the run's `journal.jsonl`; resume with `resumeFromRunId` rather than restarting |
| User asks to skip the plan review | Step 2 is not skippable |
