Solve Milestone
ASecurityThis skill should be used when the user invokes "/milestone-driver:solve-milestone <name>", or asks to "solve a milestone", "drive a milestone", or "work the milestone autonomously". Iterates every issue in a GitHub milestone in dependency order via /milestone-driver:solve-issue, integrating per the resolved integrationGranularity. Runs unattended: parks blocked or gapped issues and continues; only a systemic failure ends a run early. Builds mutually-independent issues within a Wave concurren...
- 2 stars
- 0 votes
- 0 copies
- 3 views
- Added September 3, 2026
Works with
Security analysis
100/100Pro scans all 15 files and shows the line behind each finding
npx -y skills add kenmulford/milestone-driver --skill solve-milestone --agent claude-codeAre you the author of Solve Milestone?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/kenmulford-solve-milestone)---
name: solve-milestone
argument-hint: <milestone-name | milestone-number>
description: >-
This skill should be used when the user invokes "/milestone-driver:solve-milestone <name>", or asks to "solve a milestone", "drive a milestone", or "work the milestone autonomously". Iterates every issue in a GitHub milestone in dependency order via /milestone-driver:solve-issue, integrating per the resolved integrationGranularity. Runs unattended: parks blocked or gapped issues and continues; only a systemic failure ends a run early. Builds mutually-independent issues within a Wave concurrently in git worktrees by default; a run-start barrier check drops to sequential only when a barrier is present.
---
# solve-milestone - autonomous driver
Order a milestone's issues, run `/milestone-driver:solve-issue` on each, integrate per the resolved `integrationGranularity`. Owns **ordering, the loop, branch re-sync, parking, the final summary**; `solve-issue` owns the per-issue pipeline.
**Coherence pass** (`coherenceReviewAgent`, read-only, never gates). Runs per-issue in `solve-issue` section 6, before that issue's `/code-review`. Under wave granularity it runs instead at the Phase-2 merge-tail re-verify point (`skills/solve-milestone/parallel-waves.md § Parallel mode - Phase 2: serial verified merge tail`), against the integrated wave. Under milestone granularity it runs once at `skills/solve-milestone/milestone-end-gates.md § The ordered sequence` step 3, against the whole branch. Absent → skip silently.
**Blast radius.** Merge only to `integrationBranch`, never `protectedBranch`. Release, closing the milestone object, and deploy are human-only. This skill closes the milestone's issues and authors the CHANGELOG; never the milestone.
**Execution mode: parallel by default.** No flag, no opt-in. Resolved once at run start by the barrier cascade in the last Before-starting step (Resolve execution mode), then held all run. A typed `--parallel`, or "in parallel", in `$ARGUMENTS` is stripped and ignored by step 3's `--<token>` strip.
## Contents
Before starting · The procedure - 1. List the milestone's open issues · 2. Determine the order · 3. Determine the target version · Phase 0 - Triage · 4. Loop over issues in dependency-graph order · Permission pre-flight gate · Simplify pass · 5. Finish · Autonomy · Output spec - Template 1 - Run start / plan board · Template 2 - Status update at each wave boundary · Template 3 - Final results · Output style - Main-thread context · Final summary - 6. Author the CHANGELOG entry · Run-complete notification
## Before starting
1. **Auth preflight.** Run `gh auth status`. Non-zero exit, or output containing "not logged in" / "authentication failed" → print `Error: gh auth status failed - authenticate with 'gh auth login' before running solve-milestone.` and halt.
2. Read the profile (`docs/profile-schema.md`).
| Decision point | Behavior |
|---|---|
| Resolution order (READ only; no migration here) | `<repo>/.milestone-config/driver.json`, else legacy root `<repo>/milestone-driver.json`. Both present → `.milestone-config/driver.json` wins; no move, no overwrite, no deletion of the leftover. |
| Migration (`git mv`) of a legacy layout | Never on this orchestrator's own working tree. The **first dispatched `solve-issue`** performs it on its feature branch at step 3.5. An all-parked milestone defers it to the next building run; the transitional READ covers the gap. |
| Neither file exists, or `integrationBranch` / `protectedBranch` / `sourceGlobs` missing | Invoke `milestone-driver:setup`, then continue. Do **not** fail. |
| `implementerAgent` | Defaults to `milestone-driver:implementer`. |
| Optional keys - `unitTestCmd`, `e2eTestCmd`, `e2eEnv`, `domainSkills`, `nonNegotiables` | Their steps skip cleanly when absent. |
| `integrationGranularity` (resolve here, once, hold all run) | Absent → `"milestone"`. **Fail-open, never a hard error:** an out-of-enum value degrades to `"milestone"`, logging `integrationGranularity "<value>" is not one of "issue", "wave", "milestone", degraded to "milestone"`. A valid value logs nothing; every later read uses it. |
2.0.5. **Self-heal the scratch-ignore**, always, before any `.milestone-config/` scratch write. That directory also holds tracked config (`driver.json`, `feeder.json`): never add a bare `*` or `/` rule, which swallows both. The suffix-scoped `*-notice` does not. Ensure a committed `.milestone-config/.gitignore` carrying the block below: absent → `mkdir -p .milestone-config` and write it; present → do nothing. The first dispatched `solve-issue` commits it alongside the migration.
<!-- KEEP THIS BLOCK IN SYNC with the committed .milestone-config/.gitignore in this repo and with solve-issue / scripts/triage-cache.{sh,ps1} / hooks/tests-green.{sh,ps1}, feeder setup / plan. -->
```gitignore
# milestone-driver / milestone-feeder per-clone scratch - git-invisible by default.
# Committed so per-run scratch stays out of `git status` with zero user setup.
# Patterns are relative to this .milestone-config/ directory. Tracked config
# (driver.json, feeder.json) is intentionally NOT listed, so it stays tracked.
*-notice
triage-cache.json
tests-stamp
.runtime/
worktrees/
```
2.0.6. **Assemble common.md** once; hold its path all run: `mkdir -p <repo-root>/.milestone-config/.runtime/plans`, then `${CLAUDE_PLUGIN_ROOT}/scripts/assemble-common.{sh,ps1} <repo-root> <repo-root>/.milestone-config/.runtime/plans/common.md [<anchor> ...]`, one `<anchor>` per `standingDocs` entry, prefixed with the resolved `projectDocs`. A nonzero exit logs its stderr and re-runs with no anchors (fail-open); a second one holds no path. The main thread never reads `common.md`.
2.1. **One-time notices.** Immediately after the profile read: read `${CLAUDE_PLUGIN_ROOT}/skills/notices.md` and, in file order (= print order), evaluate each section whose `Skills` field includes `solve-milestone`, applying its `Trigger` → `Text` → `Marker` → `Legacy fallback` mechanics exactly as stated there.
3. **Resolve the milestone argument.** Strip flags from `$ARGUMENTS`: a flag is a token starting with `--`. Remove each `--<token>`, plus the following token when it does not start with `--` and the flag is value-bearing (`--parallel` and `--driven` are boolean - strip the flag alone; any other `--<token>` followed by a non-flag token counts as value-bearing, strip both). Then:
- **Purely numeric** (digits only): `gh api repos/{owner}/{repo}/milestones/<milestone-number> --jq '{number, title}'`. Found → record the canonical `{number, title}`, state `"Resolved milestone #<milestone-number> → '<title>'"`. Not found → print the available-milestones table and stop.
- **Otherwise (title):** `gh api "repos/{owner}/{repo}/milestones?state=all&per_page=100" --paginate --jq '.[] | select(.title=="<name>") | {number, title}'`. Found → record it, state `"Resolved milestone '<title>'"`. Not found → stop the same way.
- **Numeric-title halt.** Resolved title purely numeric → halt and prompt the human; do not proceed to Phase 0. Triage reads a bare number as single-issue mode, so a purely-numeric milestone *title* must be renamed to a non-numeric one before this skill can drive it unattended.
- **Available-milestones table** (error path): `gh api "repos/{owner}/{repo}/milestones?state=all&per_page=100" --paginate --jq '.[] | [.number, .title] | @tsv'` as a Markdown table with columns `#` and `Title`.
All downstream steps use the resolved `{number, title}`; do not re-read `$ARGUMENTS`.
**3.5** `integrations.trello` present → read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/trello-sync.md` and run its run-start card resolution (best-effort; a Trello failure never blocks the run).
**3.6 Cherry-pick check for a milestone under a parent group.** Fires only when `--driven` (step 5) is absent; when present, none of it runs - not the first-issue query, not the parent lookup. When absent:
a. **Find the milestone's first issue** - lowest issue number, `--state all` so a fully-built milestone stays inspectable: `gh issue list --milestone "<resolved-title>" --state all --json number --jq 'sort_by(.number) | .[0].number'`. Nothing returned → fall through to step 4.
b. **Read that issue's parent, checking `md-epic` in the same call:** `gh api repos/{owner}/{repo}/issues/<first-issue>/parent`. 404 → no parent. Any other successful response already includes `.labels` - check those for an exact `md-epic` match, no second call. A non-404 failure (auth, 5xx, network) is systemic, not "no parent" - surface it and halt per `## Autonomy`.
c. **No parent, or a parent without `md-epic`** → fall through to step 4, no prompt.
d. **A parent carrying `md-epic`** → read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/md-epic-parent-check.md` and run it: the three-option prompt (build just this milestone · hand off to `/milestone-driver:solve-issue <parent-number>` · pause), each option's branch, and the reactive-only out-of-order note. Reached on no other path.
4. Confirm the working tree is clean and the local `integrationBranch` is current (`git fetch`, fast-forward). Record its tip (`git rev-parse <integrationBranch>`) as `milestoneBaseCommit`, held all run for `### Simplify pass`.
5. **Resolve execution mode (the LAST Before-starting step).** Resolve once, here; hold all run. Evaluate the cascade top-down; first match wins:
**The `--driven` token.** An interpreted token, not a parsed CLI flag - recognized by string presence in the invocation text, never argument parsing. Only an internal caller dispatching this skill supplies it. It gates only row 4 (the DB-hazard interview): a driven run takes row 4′ instead of prompting. Every other Before-starting prompt is unaffected - step 3's numeric-title halt still halts and prompts on a driven run.
| # | Condition | Resolved mode | Dispatch | Surfacing |
|---|---|---|---|---|
| 1 | profile `parallel: false` | **sequential** | background leaf dispatch ok if the gate passes, else synchronous | quiet - standing opt-out |
| 2 | permission-allowlist gap (`### Permission pre-flight gate`) | **sequential** | synchronous | 🔴 gap table + recommend `/fewer-permission-prompts` |
| 3 | profile `parallel: true` | **parallel** | background | quiet - asserted safe |
| 4 | `unitTestCmd` set and `parallel` absent and **interactive** | interview → user's choice, persisted to `parallel` | per choice | 🔴 up-front prompt (below) |
| 4′ | same as row 4 but (`MILESTONE_DRIVER_NONINTERACTIVE=1` OR `--driven` present) | **sequential** | background leaf dispatch ok if the gate passes, else synchronous | loud `⚠` note + how to set `parallel: true`; no persist |
| 5 | otherwise | **parallel** | background | quiet - default |
**The permission pre-flight gate runs here, once** (`### Permission pre-flight gate`): a gap → synchronous dispatch + sequential mode (row 2); no gap → background dispatch is available and the cascade continues. The in-loop references (`sequential-loop.md` step 2, `parallel-waves.md` Phase 1 step 3) read this decision; the gate never re-fires mid-loop.
**DB-hazard interview (row 4).** Trigger: `unitTestCmd` set and `parallel` absent - the only trigger. Fire once, before Phase 0: read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/db-hazard-interview.md` and run it - the prompt, the Yes/No branches writing `parallel: true`/`false` to `.milestone-config/driver.json`, the persistence rule, and the row-4′ path (`MILESTONE_DRIVER_NONINTERACTIVE=1` OR `--driven`), which skips the prompt, falls to sequential with a loud note, and persists nothing. On any other row it is never read.
**Nothing to decide:** `parallel` absent and `unitTestCmd` absent → row 5 → parallel, quiet; no interview, no persisted value.
**Surface the resolved mode** and its reason; it drives Template 1's mode line.
**5.1 Remediate handoff - the run-start question.** Is `/milestone-feeder:remediate` resolvable in this session? Not resolvable → no question, one log line, silent degrade; file never read. Resolvable → read `${CLAUDE_PLUGIN_ROOT}/skills/remediate-handoff.md` and run `## The run-start question` verbatim - asked once, held all run, never re-asked per issue. `MILESTONE_DRIVER_NONINTERACTIVE=1` → do not ask: take that file's non-interactive default with a loud `⚠` note (`--driven` doesn't gate this). Never copy its procedure here.
## The procedure
### 1. List the milestone's open issues
`gh issue list --milestone "<resolved-title>" --state open`, using the title resolved in Before-starting step 3.
### 2. Determine the order
The **milestone description is the ordering source of truth**. Read it (`gh api "repos/{owner}/{repo}/milestones/<resolved-number>" --jq '.description'`, else `gh api "repos/{owner}/{repo}/milestones?state=all" --jq '.[] | select(.title=="<resolved-title>") | .description'`) and follow the Wave / dependency sequence it records. No explicit order → fall back to ascending issue number and state that in the run output.
### 3. Determine the target version
Read `versioning`. **`versioning: false`** → skip this step; record "version-free run - no version determined or bumped" and proceed to Phase 0.
**Otherwise** (`true` or absent) → read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/version-target.md` and run it: the deterministic extractor `${CLAUDE_PLUGIN_ROOT}/scripts/extract-version.{sh,ps1}` (never parse by judgment), its branch table, fail-open behavior, and non-interactive degradation. Hold that target for the loop. Under `versioning: false` it is never read.
### Phase 0 - Triage
Invoke triage across the milestone before the build loop:
```
/milestone-driver:triage <resolved-title>
```
(Pass the resolved title - triage's bare-number path means single-issue mode.)
1. **Present triage output.** Surface the all-clear or gap table; triage's output carries the Wave-ordered dependency graph either way.
2. **Apply triage-recommended park labels.** Triage posts the `🔴 Triage` comment but applies no labels; that is this skill's job. For every issue where `issueStates[n].blockers == true`, apply its `issueStates[n].label` (`"needs design"` or `"needs decision"`) with the apply-time label helper from `${CLAUDE_PLUGIN_ROOT}/skills/setup/SKILL.md` Phase 4, using that taxonomy table's hex color and description. Where `issueStates[n].clearLabel == true` instead, remove the one park label the issue live-carries and add none (`skills/triage/blocker-resolver-dispatch.md (Unparking)`):
```
gh label create "<name>" --color <hex> --description "<desc>" --force
gh issue edit <n> --add-label "<name>"
gh issue edit <n> --remove-label "<name>" # clearLabel == true
```
Under a held **Auto** answer, every issue parked here enters the `## The Auto loop` in `${CLAUDE_PLUGIN_ROOT}/skills/remediate-handoff.md` before step 3 - same gated read-direct, on this main line, cap 1 per issue per run; a `NEEDS_HUMAN` return or a still-dirty re-triage parks for good (`skills/remediate-handoff.md (Park for good)`).
2.5. `integrations.trello` configured → run trello-sync.md `## Phase 0 hooks` (best-effort).
3. **Seed the build queue.** Carry triage's full `dependencyGraph` and `issueStates` into the loop; the loop drives from the validated graph, not the raw declared order.
### 4. Loop over issues in dependency-graph order
**Mode branch point.** In parallel mode: read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/parallel-waves.md` and run its Wave loop - `parallel-waves.md § Parallelizable-set selection (parallel mode)`, then `§ Parallel mode - Phase 1: concurrent stage dispatch`, `§ Parallel mode - Phase 2: serial verified merge tail`, `§ Integration granularity (issue vs wave)` - instead of the sequential build steps below. Never silently degrade to sequential. In sequential mode it is never read.
- **Granularity branch point** (orthogonal to the mode branch). Under `integrationGranularity: "milestone"`, read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/milestone-granularity.md`: `§ Branch model` and `§ Folding an issue into the milestone branch` own condition (a) and every re-sync below; `§ Creating the milestone branch` cuts `milestone-<number>-<slug>` from `integrationBranch` here, before any issue branch. Every issue branch this run is cut from it; nothing reaches origin until milestone end. Under `"issue"` never read; under `"wave"` only where cited.
Create one TodoWrite item per issue. Process issues Wave by Wave; within a Wave, mutually independent issues may be taken in any order. For each issue, determine whether it is **buildable this pass** - iff all three hold:
- **(a)** every issue in `dependencyGraph.edges["<n>"]` (those this issue directly DEPENDS_ON) is already merged to `integrationBranch`, or, under `integrationGranularity: "milestone"`, carries its `Issue: #<n>` trailer on the milestone branch (`milestone-granularity.md § Resume and buildability from the trailer`); and
- Before (b): where (a) holds and `edges["<n>"]` is non-empty, read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/blocked-label-clear.md` and run it.
- **(b)** the issue currently carries no blocker label - check live: `gh issue view <n> --json labels --jq '[.labels[].name]'`, confirming none of `needs design`, `needs decision`, `blocked` is present. This live check is the authoritative park-state. A labeled issue must not be rebuilt until a human (or Phase 0's Auto remediate loop) clears the label; and
- **(c)** `issueStates[n].blockers == false`.
**If buildable:** read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/sequential-loop.md` and run its per-issue build steps 1–4 (re-sync the build target → run `solve-issue <n>` in-thread with the held values restated → park-and-continue on STOP/PAUSE → on-success terminal states, trello tick, milestone-granularity fold). In parallel mode it is never read.
**If not buildable** (triage-parked, live-label park, or dependency not yet merged): read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/not-buildable.md` and run it: the park-label back-fill for a triage/prior-run park, and the `blocked` label, `🔴 Blocked` comment, and transitive holds for a dependency hold. Both modes read it when an issue is not buildable.
The loop **never waits on a human**. Every issue ends merged or parked (label, open branch if applicable, comment), UI included. Comment provenance: triage parks carry Phase 0's `🔴 Triage` comment; build-time STOP/PAUSE parks the reason posted at the park step (`sequential-loop.md` step 3c); dependency holds the `🔴 Blocked` comment (`not-buildable.md`).
In **versioned mode** the first issue's commit sets `plugin.json` to the target version; every later PR is idempotent. In version-free mode no PR carries a version change.
### Permission pre-flight gate
**Runs once per run, at run-start mode resolution (row 2 above), before any dispatch - whenever background dispatch is about to be used.** The mode cascade and the loop read its result.
**Allowlist source.** Union `permissions.allow` from these three settings layers, which the `autocompact` notice (`${CLAUDE_PLUGIN_ROOT}/skills/notices.md`) also reads `autoCompactWindow` from; absent or unreadable layers are skipped in the union, not counted as gaps:
| Priority | File |
|---|---|
| 1 | `~/.claude/settings.json` (user global) |
| 2 | `.claude/settings.json` (project) |
| 3 | `.claude/settings.local.json` (project local) |
<!-- KEEP THIS BLOCK IN SYNC with skills/solve-issue/permission-preflight.md § Tool surface and response, its second copy. -->
**Pipeline tool surface.** The union must cover at minimum:
| Tool category | Required grants |
|---|---|
| Read-only gh ops | `gh pr list`, `gh issue view`, `gh issue list` |
| Git | `git commit`, `git push` |
| PR / issue writes | `gh pr create`, `gh pr merge`, `gh pr edit`, `gh pr comment` |
| Issue management | `gh issue edit`, `gh issue comment`, `gh issue close` |
| Label management | `gh label create` |
| Planner leaf | Write to `.milestone-config/.runtime/plans/issue-<n>.md`, `git rev-parse`, Skill |
| Profile-defined commands | Each command in `unitTestCmd`, `preflightCmd`, `e2eTestCmd` (skip if absent) |
**Gap detection and response.** No gaps → proceed with background dispatch. Gap detected (missing surface coverage, or no readable layer) → do not dispatch in the background: (1) surface a 🔴 gap table naming each missing grant and which layer(s) could supply it; (2) fall back to synchronous dispatch for this run; (3) recommend the consumer run `/fewer-permission-prompts` (see `docs/consumer-setup.md`). That result holds for the run; do not re-read settings per issue.
**Auto-deny handling.** A background leaf reporting an auto-deny it could not work around is a park: post a `blocked` comment naming the denied tool, apply `blocked` (+ `in progress` if the branch has commits), preserve the branch, continue.
### Simplify pass
Runs once, after step 4's loop exhausts, on the clean-completion path only; a systemic halt skips it. Read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/simplify-pass.md` and run it.
### 5. Finish
The run ends when no buildable issues remain.
`integrations.trello` present → apply `## Finish hooks` from `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/trello-sync.md` (best-effort; skipped updates surface in the final summary).
## Autonomy
- **Unattended between systemic failures.** Operate autonomously within an explicit `/milestone-driver:solve-milestone` run. A `solve-issue` STOP or PAUSE parks that issue (label + open branch + comment) and the loop continues.
- **Systemic failures that halt the run** (examples): `gh auth` failure, a broken or inaccessible `integrationBranch`, missing required tooling (`gh`, `git`), and any reference file this skill read-directs being missing or unreadable once its condition has fired - `parallel-waves.md`, `milestone-granularity.md`, `blocked-label-clear.md`, `sequential-loop.md`, `not-buildable.md`, `md-epic-parent-check.md`, `version-target.md`, `db-hazard-interview.md`, `changelog-authoring.md`, `milestone-end-gates.md`. Best-effort integrations (`trello-sync.md`, `simplify-pass.md`, `coherenceReviewAgent`) degrade silently instead. These are conditions where no further issue can make progress: surface the failure, leave the tree clean, all in-flight issues parked, present the summary, and stop - `## Run-complete notification` emits `🚨 Run halted - <reason>`.
- **Architecture is locked** per issue at its plan-approval time. A plan proven wrong is a park (STOP → park + continue), not a silent redesign. For architecture vs implementation detail, see `solve-issue`'s Autonomy model.
- **Never escalate scope to `protectedBranch`.** No PR, push, or merge targets `protectedBranch`.
## Output spec
<!-- KEEP THIS ICON LEGEND BYTE-IDENTICAL across solve-issue and solve-milestone. -->
**Icon legend:** ✅ merged · 🔨 building · ⏭️ queued · ⏸️ parked · 👁️ UI surface / visual evidence · ⚖️ judgment call · 🔴 Your move
### Template 1 - Run start / plan board
Show after Phase 0 triage completes.
```text
🚀 Milestone v[version] - [N] issues · [W] waves · [resolved mode: parallel | sequential (<reason>)] · ~[T]–[T2] min
develop ← integration PRs · profile: <H> heavy / <L> light
| Wave | Issue | Title | Risk | UI | Status |
|------|-------|--------------------------|-------|----|-------------|
| 1 | #201 | Background wave dispatch | heavy | - | 🔨 building |
| 2 | #203 | Status board templates | light | 👁️ | ⏭️ queued |
⏸️ Parked at triage: #202 - needs design (contradictory grouping spec)
▶ Wave 1 dispatched - the floor is yours.
```
Mode cell: `parallel`, or `sequential (<reason>)` where `<reason>` ∈ { `profile parallel:false`, `permission gap - see 🔴`, `test-isolation not confirmed` }.
### Template 2 - Status update at each wave boundary
Show after each Wave completes.
<!-- Structural mirror of solve-issue Template 2; keep column schema (Issue/Result/Gates/PR/Note) in sync. -->
```text
🌊 Wave [N] done · [T] min · milestone [done]/[total] ✅
| Issue | Result | Gates | PR | Note |
|-------|-----------|------------------|------|-------------------------|
| #201 | ✅ merged | 🧪✓ 🔍✓(2 fixed) | #301 | ⚖️ quarantined flaky E2E |
| #202 | ⏸️ parked | - | [#pr | -] | needs decision: new dep |
▶ Next: Wave 2 (#203 👁️, #204) - redirect or reprioritize before it lands.
```
PR cell: the PR number if the issue has one, else -.
Note cell: an issue sent through the Auto loop shows `remediated, cleared` (clean re-triage, label cleared), `remediated, still parked` (cap spent or still dirty), or `NEEDS_HUMAN, parked` (`skills/remediate-handoff.md (Park for good)`); Result still shows the state reached. No Auto loop → cell unchanged. A slow first build adds its note per `skills/solve-issue/SKILL.md (Slow first build:)`.
**Milestone end row** (`integrationGranularity: "milestone"` only). Once `skills/solve-milestone/milestone-end-gates.md`'s sequence finishes, print one Template-2-shaped table before Template 3:
```text
🏁 Milestone end · [T] min
| Issue | Result | Gates | PR | Note |
|---------------|---------|-------------|----|--------------------|
| milestone end | ✅ done | 🧪✓ 🔍✓ 🌐✓ | - | 2 fix dispatches |
```
Note cell: `<F> fix dispatches` (0-3); a cap-spent revert appends `; reverted #<n>`. No commits on the milestone branch → no row; any other resolved `integrationGranularity` never prints it. `## The run-scoped handler` path prints `⏸️ needs review` instead of `✅ done`, the Note cell naming the reason (an open finding, or an unattributed red gate).
Gates legend: 🧪 = unit suite · 🔍 = code review · 🌐 = E2E. Milestone end row: 🧪✓ 🔍✓, plus 🌐✓ only with a `ui`-labeled issue and `e2eTestCmd` defined; coherence review and preflight get no symbol, as in the per-issue Gates cell.
After this update every input the next wave needs is on disk (`.milestone-config/.runtime/wave-state.json`, `.milestone-config/triage-cache.json`, the milestone branch), so compaction here loses nothing; `session-resume` re-injects the checkpoint.
### Template 3 - Final results
The layout for `## Final summary` below; fill the metadata lines from that section's requirements.
```text
🏁 v[version] complete · [T] min · ✅ [M] merged · ⏸️ [P] parked
| Issue | Outcome | PR | Follow-up |
|-------|-----------|-------------|--------------------------------------------|
| #201 | ✅ merged | #301 | - |
| #203 | ✅ merged | #303 | 👁️ visual evidence attached |
| #202 | ⏸️ parked | [#pr | -] | clear `needs decision` (new dep) |
Judgment-call PRs: [list or "none"]
PRs missing Code Review section: [list or "none"]
Auto-resolved conflicts: [list or "none"]
Per-wave sizes: Wave 1 · [N] issues · [T] min | Wave 2 · …
🔴 Your move:
1. Sanity-check the ⚖️ judgment-call PRs and any PR listed as missing a Code Review section
2. Clear park labels → re-run
3. All merged → merge `integrationBranch` → `protectedBranch` with `--merge` (not squash), merging the release PR *before* tagging, then **back-merge `protectedBranch` → `integrationBranch`** (history-only, conflict-free) so `integrationBranch` stays tag-current and topologically even, close the milestone (`gh api -X PATCH repos/{owner}/{repo}/milestones/<number> -f state=closed`), deploy - full ordered runbook in `docs/consumer-setup.md` § "Releasing to your protected branch"
```
PR cell: the PR number if the issue has one, else -. Follow-up cell: the same auto-remediate outcome Template 2's Note cell records.
## Output style
Read `${CLAUDE_PLUGIN_ROOT}/skills/output-style.md` - this plugin's output contract. `## Terminal output` governs what this skill prints (including the `## Output spec` template rule); `## GitHub-facing prose`, `## When prose is the correct form`, and `## Evidence slots` govern every issue comment, PR body, and CHANGELOG entry it writes. **Plain English.** Write every response, document, and GitHub issue, milestone, comment, and PR body in plain, concise English. Never include hypothesis, conjecture, or defensive text.
Read `${CLAUDE_PLUGIN_ROOT}/skills/citation-format.md` - the one format every citation in those slots takes.
### Main-thread context
The orchestrator never `Read`s a persisted tool-result file (`~/.claude/projects/<session>/tool-results/*.txt`) whole, never `cat`s a `.project/` doc, the consumer brief, or a plugin reference file - it takes a section with `${CLAUDE_PLUGIN_ROOT}/scripts/read-doc-section.{sh,ps1}` or `sed -n`. A truncated tool result is re-run with a narrower command, never re-read. Everything an agent needs travels as a path in its brief, never pasted inline.
## Final summary
Use Template 3 as the layout. On completion or systemic-failure halt, report:
- **Issues built and merged** to `integrationBranch`, with PR links.
- **Issues parked** - per issue: number, title, park label, blocker reason, the Auto-loop outcome if attempted, and the open feature branch if applicable. Take the reason from tracked context (triage gap, STOP/PAUSE reason, or unmerged upstream). Not in active context → read `gh issue view <n> --json comments` and use the most recent matching comment, maybe from a prior run: opening `🔴 Triage` (triage-park), `🔴 Blocked` (dependency-hold), or `🔴 Parked` (build-park). gh returns comments oldest-first, so take the LAST match. No anchored match → report "park reason not recorded (pre-1.7.0 park format)". Never invent a reason.
- **PRs carrying a `judgment call` label**, flagged for post-run review.
- **PRs missing a `## Code Review` section**, flagged the same way, for review before the `integrationBranch` → `protectedBranch` merge.
- **Auto-resolved-conflict issues** (parallel mode) - those whose merge conflict the serial verified merge tail auto-resolved, for a human to sanity-check.
- **Per-Wave parallel-set sizes** (parallel mode).
- **The run ended because** no buildable issues remain - not because it is waiting on a human.
- The next human step: review parked issues; clear park labels once their blockers are resolved and re-run for the rest; all work merged → run Template 3's `🔴 Your move:` item 3 verbatim, then deploy manually.
**Output ordering (clean-completion path only):** do not emit Template 3 until `### Simplify pass` and step 6 have both completed or skipped cleanly (`skills/solve-milestone/changelog-authoring.md § 6.9 Surface in the final summary "Your move" section`). On the systemic-halt path both are skipped - emit Template 3 immediately.
### 6. Author the CHANGELOG entry
**Runs on the clean completion path only.** When the systemic-halt path reaches the Final summary, skip this step and go straight to `## Run-complete notification`.
**Guard - skip this step entirely if** either holds:
- The parked count for this run is greater than zero, OR
- The run ended via a systemic halt
The parked count comes from this run's **in-context tracking** - every issue that did not reach "merged": parked at build time, skipped on triage blockers, or excluded by a live blocker label (e.g. `blocked` from a prior run). It is Template 3's `⏸️ P` count. Do not re-derive via a live `gh issue list` query: it may find labels unrelated to this run.
Either condition holding → post _"Skipping CHANGELOG authoring - run did not fully complete (N parked)."_ and go straight to `## Run-complete notification` - except under `integrationGranularity: "milestone"`, where it skips only `changelog-authoring.md` steps 6.1-6.5: `milestone-granularity.md § Milestone end` still runs gates, push, and PR for whatever merged (`skills/solve-milestone/milestone-granularity.md (Steps 2 to 6 run for whatever did merge.)`).
**Guard passes** → read `${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/changelog-authoring.md` and run its steps 6.1–6.9 (idempotency check → PR summaries → categorize → theme → author the entry → branch name → doc-only PR → CI result → surface in the final summary). While the guard holds it is never read.
## Run-complete notification
After Template 3, emit a `PushNotification`:
- **Clean completion**: `🏁 <milestone-title> · ✅ M merged · ⏸️ P parked`, the counts from Template 3.
- **Systemic halt**: `🚨 Run halted - <reason>`, where `<reason>` is the systemic-failure description, e.g. "gh auth failure".
**Run-end cost record (additive, never-gating), survives context compaction.** No in-context aggregation: each dispatch appends its usage to disk as it returns; the run-end record reads back from that file alone, the last step here on both the clean-completion and systemic-halt paths.
1. **Append, per dispatch, as it returns.** Every Agent-dispatch this run - Phase 0 triage's own dispatches and each per-issue / per-wave `Agent(run_in_background: ...)` dispatch - pipes `{"agent":"<dispatch label>","tier":"<opus|sonnet>","totalTokens":<subagent_tokens>,"durationMs":<duration_ms>}` to `${CLAUDE_PLUGIN_ROOT}/scripts/write-cost-record.{sh,ps1} --append <milestone id>` as its last step, milestone-wide: one usage file spans every Wave, so Phase 1 needs none of its own (`skills/solve-milestone/parallel-waves.md (Hand the green set to Phase 2)`). An implementer or planner return carrying `output_file:` merges its measurement per `skills/solve-issue/SKILL.md (merges the object)`; that merge never gates (`.project/design-philosophy.md#Error & failure philosophy`).
2. **Finalize.** `${CLAUDE_PLUGIN_ROOT}/scripts/write-cost-record.{sh,ps1} --finalize <milestone id>` sums `totalTokens` per tier into `inputTokens` (`outputTokens`/`cacheReadTokens`/`cacheWriteTokens` at the 0 sentinel, `provenanceNote: "unsplit-total-as-input"` marking it a lower-bound), sums `durationMs` into `wallClockSeconds`, and writes the record to `.milestone-config/.runtime/cost-records/` with one more field, `agents`: the raw per-dispatch entries.
3. **Skip cleanly.** No usage file for this milestone (zero dispatches) → `--finalize` fails open on its own, one log line, no zero-value record. Writer script absent → silent no-op, one log line. Never fails the run.
Files in this skill
- SKILL.md
- abandoned-recovery.md
- blocked-label-clear.md
- changelog-authoring.md
- contingencies.md
- db-hazard-interview.md
- integration-granularity.md
- md-epic-parent-check.md
- milestone-granularity.md
- not-buildable.md
- parallel-waves.md
- sequential-loop.md
- simplify-pass.md
- trello-sync.md
- version-target.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.