Milestone workflow
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.From its SKILL.md
npx -y skills add richkuo/rk-skills --skill milestone-workflowAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
27.8 KB, as published. Nobody here has run it
milestone-workflow
Turn a reviewed milestone into a running multi-agent pipeline. Static plan, dependency-aware dispatch: the dependency graph is decided here with the user; execution waits for stable predecessor results and builds hard-dependent work from their reviewed code. With merging on (the default when review loops run), no subagent ever merges: at each LGTM the run pauses as awaiting_merge, the orchestrating session merges that PR in-session (step 5 owns the procedure), then resumes the run with the merge recorded in args.merged — successors build from the updated base branch. When every issue merges, the orchestrator syncs docs and publishes the release (phase 7).
For a read-only look at the milestone before committing to a run — a single table of what routing is stamped on each issue (complexity, dependencies, validate/build model and effort, fableplan, plan effort, first review) — use milestoneplan first. It reads without editing anything, then hands off here.
Steps
1. Build the dependency tracks
Fetch the milestone's issues and build a typed dependency graph before grouping them into tracks.
- Filter for idempotency first: list the milestone's issues with
--state alland search for open PRs that close each one. Drop closed issues from the plan. An open issue with an existing open PR is planned as a resume — runfix-pr-review-loopon that PR instead of dispatching it through the pipeline — never a fresh build, which would open a duplicate PR. Show three buckets in the run plan: build, resume, skip (closed). - Resolve cross-bucket dependency edges before building tracks —
after/runsAftercan only reference tracks in this run, so an edge from a build-bucket issue to a resume- or skip-bucket issue must be settled here, never silently dropped (the implement agent would otherwise build from the default branch and miss the predecessor's unmerged code). A predecessor whose PR merged: the edge is satisfied — drop it, the base branch has the code. A predecessor in the resume bucket: run itsfix-pr-review-loopto completion before invoking the pipeline — that satisfies ordering-only edges; when the run will merge (the default), also merge that PR once its loop reaches LGTM and CI is green (the approved run plan covers this merge), which satisfies hard edges through the base branch. With merging off, a hard edge still needs the predecessor's code, so exclude the dependent issue and its hard descendants from this run'stracks, report them as blocked pending merge of PR #X in the run plan, and offer to re-run the milestone once it merges. A predecessor closed without a merged PR: the dependency is unsatisfiable as filed — exclude the dependent and its hard descendants, report them as blocked pending decision, and ask the user whether to reopen the predecessor or re-scope the dependent. - Read
**Depends on:**first for hard code/product prerequisites and**Runs after:**for ordering-only constraints. An explicitnoneis authoritative: never infer an edge for a field that is present. For an older issue missing either field, infer only that missing edge kind from Approach/Problem prose and label every inferred edge in the plan. - Classify an inferred code/product prerequisite as a hard edge and an inferred same-package or no-overlap constraint as an ordering edge. If the prose does not establish the edge kind, flag it for the mandatory plan review instead of guessing.
- Reject references to issues outside the milestone, and cycles across the union of both edge kinds (edges to resume/skip-bucket issues inside the milestone are resolved by the cross-bucket rules above, not rejected). A hard edge means the successor needs predecessor code; an ordering edge only prevents overlapping work.
- Express each track as
{ issues: [...], after: [<track index>...], runsAfter: [<track index>...] }. Dependency-free islands have neither predecessor list. Combine issues into oneissuesarray only when every serial edge is truly hard; untyped serial edges are treated as hard by the workflow, so ordering-only chains must remain separate tracks joined byrunsAfter. - Preserve concurrency: unrelated tracks start together. Multiple hard predecessors remain separate
afterentries so the runtime can create and verify one integration base containing every head. - With merging on (the default), prerequisites merge to the default branch before successors start inside one invocation — the policy that used to require separate invocations. Use separate workflow invocations for that policy only when merging is off.
2. Present the run plan — review before beginning (mandatory)
Show: numbered tracks with issue titles; hard after edges separately from ordering-only runsAfter edges; which edges were inferred; each issue's model/effort/fableplan, plus the Plan effort on every fableplan: Yes issue; the stable readiness boundary; and merge-order expectations. Do not invoke the Workflow tool until the user approves this plan — the approval is both the safety checkpoint and the explicit multi-agent opt-in the Workflow tool requires.
State the GitHub writes the run performs, so the approval covers them explicitly: agents apply validation corrections to issue bodies, post each fableplan: Yes issue's implementation plan as a comment on that issue, open PRs, (with review loops on) post review-trigger and disposition comments, and — with merging on, the default — the orchestrating session itself squash-merges each PR at LGTM plus green CI, deletes its branch, closes its issue, and, when every issue merges, lands a docs-sync change and publishes a GitHub release. Every merge is an in-session gh pr merge by the orchestrator, run under the session's own permission mode — no background subagent ever holds merge authority. Approving the plan authorizes those merges and the release; run with merge: false to keep merging fully manual.
Add a Run size line before asking for approval:
- Compute the planned direct-agent baseline, assuming every issue reaches each enabled phase, as
1 prep + sum over issues of (1 validate + (fableplan ? 1 plan : 0) + 1 implement + (reviewLoop && reviewMode === 'subagent' ? 1 review-loop : 0)). Merging and the release dispatch no agents — the orchestrator does both in-session. In subagent mode, the review-loop term is the happy-path first reviewer. Each additional cycle dispatches a fixer and re-reviewer pair, except the unresolved final cycle dispatches no fixer, so the worst case is2×maxReviewCycles − 1review-phase agents per issue. In default github mode, the implement agent handles cycle 1. Each later fix agent handles up to two cycles, so the worst case addsceil((maxReviewCycles − 1) / 2)review-phase agents per issue. All loop state lives on the PR, so agent rotation loses no history. - Also compute the retry-aware direct ceiling as
planned direct-agent count + 3 × number of issues, because each issue's validation can dispatch one retry, plus one band-escalation re-validation with its own retry. Show both numbers. - Label them as planning bounds, not a total-agent guarantee: invalid issues or failures can reduce the count, while review loops can dispatch nested fix agents beyond the retry-aware ceiling. The warning counts all scheduled agents, so never label a plan safe merely because either direct count is below the threshold.
maxReviewCycleschanges the stopping rule after an LGTM; it is not a guaranteed cap while reviews keep returningNeeds Updates. - Compare both direct counts with the effective Dynamic workflow size guideline when one is present in session context; otherwise use Claude Code's documented default threshold of more than 25 scheduled agents. Name the threshold source in the plan so the comparison is inspectable. If the baseline crosses it, mark the warning expected; if only the retry-aware ceiling crosses it, mark the run retry-sensitive; if both stay under and review loops are enabled, state that nested fixes can still trigger the warning. The Claude Code workflow cost documentation is authoritative.
- State that Claude Code can also trigger
Large workflowwhen its projected token total exceeds 1.5 million. In an ultracode session, label both comparisons informational because the warning is suppressed. - When either risk is apparent, call it out before approval and recommend splitting the milestone into separate tracked
Workflowinvocations. DisablingreviewLoopreduces the direct count but forfeits automatic review readiness. LoweringmaxReviewCyclesmay reduce repeat work after non-blocking LGTM reviews, but never present it as a guaranteed cap. - When the user set a token target (a "+500k"-style directive), state that the workflow enforces a floor: before each issue starts, if fewer than 80k tokens remain (override with
budgetFloor), that issue and the rest of its track are deferred asbudget_deferredand the run returns partial results cleanly instead of an agent dying at the hard ceiling. Without a target, no floor applies. The floor is best-effort, not a guarantee: it is checked only when an issue starts, so an in-flight issue's agents can still hit the hard ceiling, and concurrent tracks can each pass the check before spending. SizebudgetFloorto roughly one issue's worst-case cost — implement plus a full review loop — which is typically well above the 80k default when review loops are on.
3. Preflight the repo
gh auth statussucceeds andgh api repos/<owner>/<repo> --jq .permissions.pushreturnstrue. A bad token or read-only access must stop the run here — otherwise it surfaces as confusing per-agent failures mid-run.- Review-mode dependencies: the default
reviewMode: 'github'requires.github/workflows/claude.yml(the@claudereview bot — copy from rk-skillstemplates/claude-review.ymland confirm the API-key secret if missing) plus working GitHub Actions. WithreviewBot: 'codex'the same dependency reads.github/workflows/codex.yml(copy from rk-skillstemplates/codex-review.yml, or install the fulltemplates/codex-workflow/bundle) plus the Codex secrets and repository variable that thefix-pr-review-loopreview-bot preflight lists (the fix cycles push, so the write-route items are required here too). Without that bot, passreviewMode: 'subagent'— reviews then run in-session (a reviewer agent posts apr-reviewcomment, a fixer agent resolves it) with no Actions dependency — or setreviewLoop: falseto skip reviews entirely (phase 5 states the readiness effect). Note CI checks that run on Actions are an independent dependency either way — subagent mode removes the review's Actions dependency, not CI's. - Base branch protection / merge expectations understood: with merging on (the default), the authenticated account must be allowed to squash-merge and delete branches on the base branch — protection rules that require human approvals or block squash merges must be relaxed first, or the run switched to
merge: false(merging then stays with the user and successors stack on unmerged heads). - CLAUDE.md in the target repo covers conventions the agents must follow (package manager, test commands).
4. Run
Invoke the Workflow tool with {name: 'milestone-pipeline', args: {tracks: [{issues:[2,3]}, {issues:[9], after:[0]}, {issues:[12], runsAfter:[0]}], reviewLoop: true, maxReviewCycles: 5}}. Legacy issue-array tracks remain accepted, but use typed objects for new plans. budgetFloor (tokens, default 80000) is accepted when a token target is set. reviewMode defaults to 'github' — reviews route through the repo's @claude Action; pass 'subagent' to review with in-session agents instead. reviewBot defaults to 'claude' and only applies in github review mode; pass 'codex' to route every trigger and re-trigger to @codex — never infer it from a codex.yml existing in the repo, only from the user naming Codex. merge defaults to reviewLoop — on unless review loops are off; merge: true with reviewLoop: false is rejected because LGTM readiness is the merge criterion. merged carries the PRs the orchestrator has already merged in-session, as [{issue, pr, merge_sha, issue_state}] records — empty on the first invocation, appended to on every merge-resume; step 5 sub-step 7 owns the record format and its validation. release defaults to merge; release: true with merge: false is rejected; phase 7 owns the deferred-release behavior.
Immediately after the invocation returns, post its runId and persisted script path as a comment on the milestone's first issue (footer: Created with LLM: <current model> | high | Harness: milestone-workflow), so run state survives losing the conversation — resumeFromRunId resumes same-session; cross-session the record enables a hand-authored continuation from the persisted script.
The workflow validates all assignments, predecessor indices, duplicates, and cycles before prep, then:
- Prep — one agent reads every issue's
[C..]score and Execution block → per-issue model/effort/fableplan/plan effort. - Validate — immediately before each issue starts, a validation agent runs the
validate-issueprocedure against the current dependency base, with deduplicated predecessor PRs/skips and hard base refs. Model and effort are both derived from the[C..]score band, never from a stamp (thevalidate-issuestep 6 band table owns the mapping; a missing prefix routes as band 5). The validator also returns its own score; when it lands in a higher band than the prefix, the pipeline re-validates once on the stronger route (upward only) and re-routes the whole issue from the higher score: build model, build effort and fableplan always move to the escalated band's defaults, replacing the stale stamps. A stampedPR review:reviewer is treated separately — it moves to the escalated review band's default only when that default outranks the stamp, so an operator who named a stronger reviewer keeps it, and a rescore never lowers review routing. Each rescored issue carries arescorerecord in the run results; the post-run report below acts on it.INVALIDissues are skipped and reported, never built. - Plan — issues flagged
fableplan: Yesget a Fable 5 planning agent (validation-aware) whose plan is posted to the issue, at the issue'sPlan effort(default high; every tier is legal because the planner is always Fable). The builder implements against it. - Implement — per-issue agent on its assigned model/effort applies validation corrections, invokes
work-on-issuewith the verified hardbaseRefs, creates a deterministic integration base for multiple heads, and opens the PR. In default github review mode, the same agent also handles review cycle 1: it posts the band-derived trigger (@claude sonnet reviewat 0–10,@claude reviewat 11–40,@claude opus reviewat 41–80,@claude fable review effort:highat 81+, or the issue's stamped trigger), waits, fixes or refutes, re-triggers when needed (a fable first review never repeats: the blocking re-triggers after it step down one rung each,@claude opus reviewthen@claude review), and waits for the standing verdict. WithreviewBot: 'codex'the heavier bands collapse onto Codex's single flagship — C11 and above posts a bare@codex review, while the cheap tier keeps a shorthand (@codex luna review) for both the C0–C10 first review and the non-blocking re-review. - Review readiness — the review loop runs until LGTM before any hard or ordering successor starts, preventing review fixes from racing same-package work. In subagent mode the script alternates an independent reviewer agent (first review on the issue's stamped
PR review:model/effort when one is named, else the review-band default — sonnet/high at 0–10, the session default model at 11–40 (the bare-@claudeequivalent), opus/high at 41–80, fable/high at 81+; posts apr-reviewcomment) with afix-pr-reviewfixer agent on the build model/effort — a re-review after only non-blocking fixes drops to sonnet/high, and a fable first review never repeats — its blocking re-reviews step down one rung each, opus/high for the first and the session-default reviewer for every one after that. In default github mode, each fresh fix agent after the implement agent handles up to two review-bot Action cycles before it exits, on whichever botreviewBotselected. Unrelated tracks and their review loops stay concurrent. WithreviewLoop: false, implementation completion is the readiness boundary. - Merge — with merging on, no merge subagent runs. A PR whose merge the orchestrator already recorded in
args.mergedcounts asmergedand its successors build from the updated base branch with nobaseRefs. The run accepts a record only when it passes step 5 sub-step 7's validation; a rejected record returnsmerge_record_mismatchand blocks the track instead of counting as a merge. A PR at LGTM without a record pauses its track: the issue returnsawaiting_merge(also listed in the run's top-levelawaiting_mergearray), in-track successors returnmerge_pending, and descendants in other tracks returndependency_blocked; the run then ends so the orchestrator can merge in-session and resume (step 5). - Release — when every issue in the run reached
merged, the run returnsrelease.deferred: trueand the orchestrator runs thesync-docs-releaseskill in-session: sync the docs to the merged PRs, land that change (merging its doc PR after checks pass), thencreate-release. Any non-merged issue skips the release; a partial milestone never publishes. - Failure propagation — failed, blocked, non-LGTM, or unmerged (with merging on) hard predecessors block descendants. Ordering-only skips without a PR do not; an unresolved predecessor PR does. Integration conflicts block before product changes.
5. Merge-resume loop, monitor, close out
With merging on, the run pauses every time a PR reaches LGTM without a recorded merge. On each awaiting_merge return, the orchestrator merges that PR in-session, then resumes:
- Verify the PR:
gh pr view <num> --json state,headRefName,headRefOid,mergeStateStatus— OPEN, andheadRefOidexactly the reviewed readiness SHA the run returned for it. A different head here means somebody pushed commits after the review: send the PR back through the review loop instead of merging. This check is against the head before any catch-up this procedure performs; the catch-up in sub-step 2 is expected to move the head, and sub-step 3 states which commit that leaves as the merge target. - CI gate:
gh pr checks <num> --watch; any failed check blocks the merge. No checks configured counts as passing. If the branch is behind the base, rungh pr update-branch <num>only when it merges cleanly, re-capture the head (gh pr view <num> --json headRefOid), and repeat the CI gate on that new head — the reviewed code must prove itself against the base it actually lands on. Ifgh pr update-branchreports conflicts, stop; never resolve conflicts yourself. - Fix
<verified-sha>— the exact commit sub-step 4 pins. It depends on the review mode, because the two modes bind the LGTM to the code differently:- Subagent review mode:
<verified-sha>is the reviewed readiness SHA when sub-step 2 did not update the branch, or the head you re-captured aftergh pr update-branchwhen it did. A catch-up that only adds base commits keeps the reviewed product diff, so it does not restart the review loop. - Github review mode:
<verified-sha>is the reviewed readiness SHA only. If sub-step 2 changed the head, the standing review-bot LGTM now predates the commit you would merge: stop and send the PR back through the review loop until a fresh LGTM lands on the new head. Never merge a caught-up head on an older verdict. - Either way, if a concurrent track merges first and forces a second
gh pr update-branch, re-apply this same rule to the newest head — subagent mode re-pins and re-gates CI, github mode requires another fresh review.
- Subagent review mode:
- Github review mode only — the LGTM recency gate. Run it as the final read before the merge, and re-run it after every CI wait and every branch update. Its job is to prove the standing verdict reviewed the exact code you are about to land.
- Re-fetch the live PR head and the PR's full issue-comment history.
- Find the newest exact one-line
@<review bot> [model] review [effort]trigger comment, and the newestgithub-actions[bot]output whose body links/actions/runs/<run-id>. - Resolve that linked run and require
status == completedandconclusion == success. A newer trigger, or a run-linked bot comment without a completed matching output, blocks the merge. Never fall back to an older LGTM. - Require the output's
created_atto be later than the trigger'screated_at, and require its body to carry exactly one standalone verdict line readingLGTM(neverNeeds Updates). - Do not compare the Actions run's
head_shato the PR head. Anissue_commentrun reports the last commit on the default branch, so that field never equals the PR head and the comparison rejects every valid verdict. Bind the verdict to the code by time instead. - Resolve when the head became visible on GitHub — the earliest
created_atamonggh api repos/{owner}/{repo}/commits/<verified-sha>/check-suites, or that commit's.commit.committer.datewhen it has no check suite — and require the LGTM output'screated_atto be strictly later. An LGTM that predates its head reviewed older code: stop. - Once this gate passes, run sub-step 5 immediately. No other command may run between the gate and the pinned merge.
- Merge pinned:
gh pr merge <num> --squash --delete-branch --match-head-commit <verified-sha>— never unpinned. If GitHub rejects the merge because the head no longer matches, a commit landed after your CI gate: do not retry with a fresh SHA, and restart this procedure from sub-step 1. - Confirm the linked issue closed (
gh issue view <n> --json state; close it explicitly if thecloseslink did not fire), and capture the merge SHA (gh pr view <num> --json mergeCommit). - Resume: re-invoke Workflow with the same
scriptPathandresumeFromRunId, passing the complete originalargs— every field the first invocation carried, includingtracks,reviewMode,maxReviewCycles,budgetFloor,merge, andrelease— with onlyargs.mergedextended by{issue, pr, merge_sha, issue_state}for every PR merged so far.args.tracksis required, so a resume that carriesmergedalone throws immediately; re-running fresh instead would re-validate and re-implement merged issues and open duplicate PRs. Each record names both the issue and the PR that closes it: the run rejects an issue outside the run, a repeated issue or PR, and a record whose PR does not match the PR the run opened for that issue (merge_record_mismatch), and it reports any record it never used inunmatched_merged_records. Cached agents replay instantly; the run proceeds past the recorded merge. Repeat until noawaiting_mergeremains.
Concurrent tracks pause together at each return, so serial milestones lose little and wide ones trade some concurrency for in-session merge control.
When the run returns release.deferred: true, run the sync-docs-release skill in-session per phase 7 and report the tag and URL.
Relay meaningful progress (PRs opened, review loops finishing, merges, blockers) — not raw logs. On completion, report a results table: issue → PR → review status → merge status, plus flags the agents raised and the release outcome (tag and URL, or why the release was skipped). For every result that carries a rescore record, do two things: (1) restamp the issue — load the github-issue-format skill first, update the [C..] title prefix to the rescored score, and update the Execution block's Build model / Effort / fableplan lines to the values the run actually used (footer verb Updated); (2) tell the user which issues were rescored, from what to what, and how the routing changed — never let a rescore pass silently. With merging off, recommend dependency merge order, with every hard prerequisite before its descendants, and if a later phase was deferred pending merges, offer to chain the next invocation.
Context discipline
The orchestrating session holds no implementation detail — issues and PRs are the memory. Between phases (or after compaction), everything needed to resume lives in: the milestone's issues, their Execution blocks, the open PRs, and the runId comment posted in step 4. Losing the conversation must never lose state.
Failure modes
| Situation | Do this |
|---|---|
| An issue lacks an Execution block at run time | Stop before running; send it through execution-plan-review |
| The user wants the milestone checked before committing to a run | Run milestoneplan — the read-only single-table plan, without touching an issue |
| No review-bot workflow in the repo | Install the template for the selected bot (templates/claude-review.yml, or templates/codex-review.yml with reviewBot: 'codex') for the default github mode, or pass reviewMode: 'subagent' (no Actions dependency); reviewLoop: false remains the no-review fallback |
| GitHub Actions billing or a self-hosted runner outage stalls reviews | Switch the next invocation to reviewMode: 'subagent' (github is the default); already-running github-mode loops stay blocked until Actions recovers |
| A successor hard-depends on unmerged predecessor PRs (merging off) | Use after; the workflow waits for stable heads and work-on-issue verifies/integrates every base before implementation. With merging on, the predecessor merges first and the successor builds from the base branch |
| A PR reaches LGTM but CI fails, the head moved, or the base conflicts before merging | The orchestrator refuses the in-session merge (step 5 gates); the track pauses per phase 6 — fix the cause, merge, then resume with the record |
A resume's args.merged record names the wrong PR for an issue | The run rejects it per step 5 sub-step 7 and blocks that issue's descendants. Correct the record against gh pr view <num> --json number,state,mergeCommit, then resume |
| Branch protection requires human approvals or blocks squash merges | Run with merge: false; merging returns fully to the user and successors stack on unmerged heads |
Some issues did not reach merged at run end | The release is skipped; merge the stragglers in-session, then resume (or run sync-docs-release yourself) |
| A same-package predecessor is ordering-only | Use runsAfter; the successor waits but does not inherit code |
| A build-bucket issue hard-depends on a resume- or skip-bucket issue | Apply the step 1 cross-bucket rules; step 1 owns the full resolution for merged, resume-bucket, and closed predecessors |
| A dependency integration conflicts | The affected track and hard descendants stop blocked before product changes; report the conflicting heads |
| Workflow returns empty/odd results | Read the run's journal.jsonl before re-running; resume with resumeFromRunId rather than restarting |
| User asks to start without reviewing the plan | Present the plan anyway — step 2 is not skippable |