Skip to content
Back to skills

Solve Milestone

ASecurity

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 concurren...

  • 2 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 3, 2026
ai-agentsgoreactcode-reviewgitapi

Works with

  • terminal
  • cli
  • api

Security analysis

A100/100

Pro scans all 15 files and shows the line behind each finding

Scanned September 25, 2026

npx -y skills add kenmulford/milestone-driver --skill solve-milestone --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Solve Milestone?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Solve Milestone
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kenmulford-solve-milestone/badge)](https://www.skillsdirectory.com/skills/kenmulford-solve-milestone)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.md32.2 KB
  • abandoned-recovery.md4.9 KB
  • blocked-label-clear.md2.2 KB
  • changelog-authoring.md14.3 KB
  • contingencies.md7.7 KB
  • db-hazard-interview.md2.2 KB
  • integration-granularity.md14.5 KB
  • md-epic-parent-check.md2.2 KB
  • milestone-granularity.md22.8 KB
  • not-buildable.md3.2 KB
  • parallel-waves.md39.2 KB
  • sequential-loop.md6.8 KB
  • simplify-pass.md10.5 KB
  • trello-sync.md17.4 KB
  • version-target.md2.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.