agentleFS
kenmulfordkenmulford/milestone-driver/skills/solve-issue/SKILL.md

solve-issue

Skill2 starsChanged 15 days agoMITView on GitHub

This skill should be used when the user invokes "/milestone-driver:solve-issue <n>", or asks to "solve issue <n>", "fix issue <n>", or "drive issue <n>" through the milestone-driver gated procedure. Runs one GitHub issue end-to-end as an orchestrator - triage, dispatch the planner and approve its build packet, dispatch the implementer subagent (TDD, citations), unit + E2E gates, code review, PR to the integration branch, auto-merge on CI green, then close - never authoring application or test code on the main thread.

  • Commits and pushes

2 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions2 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 2

    1. edloidas/skills★ 2same name
    2. ypxing/coding-crew★ 1same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository3 other files it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

13 rules. About 0 are common, 0 are distinctive, and 13 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 13

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If ${CLAUDE_PLUGIN_ROOT} appears unexpanded, it is the parent of the skills/ directory that holds this file.

    line 5
  2. Very specific:

    Every ${CLAUDE_PLUGIN_ROOT}/scripts/*.{sh,ps1} invocation in this skill selects its host the same way: pwsh on Windows, bash elsewhere.

    line 17
  3. Very specific:

    Read the profile (see the plugin's docs/profile-schema.md).

    line 19
  4. Very specific:

    (d) runs inline; (a), (b) and (c) are resumes: - read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/resume-paths.md and follow its ### Resume …

    line 54
  5. Very specific:

    Create one TodoWrite item per numbered step below (on Codex, this and every Agent, Skill-tool and SendMessage use: skills/runtime-codex.md).

    line 59
  6. Very specific:

    Run gh issue view <n> with comments, redirected to the issue record <repo-root>/.milestone-config/.runtime/triage/issue-<n>.md by the …

    line 78
  7. Very specific:

    Source the docs root: Use projectDocs already resolved at step 1 (defaults to .project/).

    line 97
  8. Very specific:

    Pass the common.md path: Put the held common.md path into the ### 2 and ### 3 briefs.

    line 100
  9. Very specific:

    Act: Run the gate's act per the table row.

    line 162
  10. Very specific:

    After a fix, before committing: No in-scope findings → commit directly.

    line 183
  11. Very specific:

    Commit on the feature branch - the tests-green hook (PreToolUse on git commit) re-checks the suite.

    line 206
  12. Very specific:

    Skip cleanly: No usage file for this runId (e.g.

    line 231
  13. Very specific:

    Read ${CLAUDE_PLUGIN_ROOT}/skills/citation-format.md - the one format every citation in those slots takes.

    line 314

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

If ${CLAUDE_PLUGIN_ROOT} appears unexpanded, it is the parent of the skills/ directory that holds this file.

solve-issue - gated per-issue procedure

Run exactly one GitHub issue through a fixed, gated pipeline. The main thread acts only as an orchestrator: read, decide, dispatch, review, integrate. It never authors application or test code itself - the force-subagent hook enforces this mechanically. Orchestrate the superpowers:* skills for the inner loop. Before anything else, check #n's labels for md-epic: a parent issue takes ### Parent path (see skills/solve-issue/md-epic-fanout.md) instead of the pipeline below.

Contents

Before starting · The procedure (0 Triage · 1 Read the issue · Build profile resolution · Resolve cited project-docs sections · Resolve cited path (anchor) citations · 2 Evaluate the codebase for root cause · 3 Dispatch the implementer · 4 Verification gates - no step 5, it merged into 4 · 6 Review → integrate → close) · Run-end cost record · Autonomy model · Permission pre-flight gate · Milestone granularity · Wave granularity · Async mode · Parent-issue detection · Output spec (Template 1 · Template 2) · Output style · Non-negotiables

Before starting

Every ${CLAUDE_PLUGIN_ROOT}/scripts/*.{sh,ps1} invocation in this skill selects its host the same way: pwsh on Windows, bash elsewhere.

  1. Read the profile (see the plugin's docs/profile-schema.md).

    Profile-resolution decision pointBehavior
    Resolution order (transitional)Read <repo>/.milestone-config/driver.json first; if absent, fall back to the legacy root <repo>/milestone-driver.json.
    Both files exist.milestone-config/driver.json wins for the read.
    Migration (git mv)Deferred to step 3.5, so it rides the feature branch and does not trip the clean-tree precondition (step 2). Do not move here.
    Neither file exists, or integrationBranch / protectedBranch / sourceGlobs missingInvoke milestone-driver:setup to bootstrap it, then continue - do not fail.
    implementerAgentDefaults to milestone-driver:implementer when omitted.
    Optional keys - unitTestCmd, e2eTestCmd, e2eEnv, preflightCmd, domainSkills, nonNegotiables, projectDocsprojectDocs defaults to .project/; every other step is skipped cleanly when absent. domainSkills is expanded first (`${CLAUDE_PLUGIN_ROOT}/scripts/expand-domain-skills.<shps1> ~/.claude/plugins/cache <entry>…): its **stdout** is what the ### 2 and ### 3 briefs carry, never the raw profile value, and each unresolved: <entry>on stderr is named there asdomainSkills unresolved: <entry>. Empty stdout is an absent key: the brief omits domainSkills` and still names those entries.

    1.1. Self-heal the scratch-ignore (always, before any .milestone-config/ scratch write). Ensure a committed .milestone-config/.gitignore carrying exactly the block below; the directory also holds tracked config (driver.json, feeder.json), so never add a bare * or / rule, which swallows both. The suffix-scoped *-notice does not. Absent → create it (mkdir -p .milestone-config, then write the block). Present → do nothing. It rides the feature branch.

    <!-- Keep this block in sync with the committed .milestone-config/.gitignore in this repo and with solve-milestone / scripts/triage-cache.{sh,ps1} / hooks/tests-green.{sh,ps1}, feeder setup / plan. -->
    # 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/

    1.1.1. One-time notices. Immediately after the profile read: read ${CLAUDE_PLUGIN_ROOT}/skills/notices.md and, in file order, evaluate each section whose Skills field includes solve-issue, applying that section's Trigger → Text → Marker → Legacy fallback mechanics. Never evaluate a section scoped only to solve-milestone. 1.1.2. Remediate handoff (feeder only). Probe once: is /milestone-feeder:remediate resolvable in this session? Not resolvable → skip this sub-step: no question, no read, no error. Resolvable, and the caller supplied no answer → read ${CLAUDE_PLUGIN_ROOT}/skills/remediate-handoff.md and follow its ## The feeder-installed gate and ## The run-start question; hold the answer for the whole run. A caller-supplied answer (a solve-milestone run's own run-start question) is reused, never re-asked - the held-value shape step 0's Branch A uses for the triage result.

  2. Require a clean working tree (cold-start precondition) and a current local integrationBranch (git fetch, fast-forward). One exception, never stashed or discarded: step 3's probe finding an existing issue/<n>-* branch (committed or uncommitted prior work) skips this enforcement. Any other dirty state is a cold-start violation.
  3. Branch-state probe (resume an interrupted run). git fetch first, then classify prior progress from git + gh before cutting anything; first match wins:

    • (a) A PR exists for issue/<n>-*, merged or open (client-side filter: gh pr list --state all --limit 200 --json number,headRefName,state,url --jq '.[] | select(.headRefName | startswith("issue/<n>-"))').
    • (b) No PR; branch issue/<n>-* exists (local or remote) with commits ahead of integrationBranch.
    • (c) No PR; no commits ahead; the issue branch issue/<n>-* is checked out with uncommitted changes (the normal implementer contract).
    • (d) Otherwise (no branch, no PR, clean tree): cut a fresh feature branch from integrationBranch (e.g. issue/<n>-<slug>) - cold start: run ### 2. Evaluate the codebase for root cause, then ### 3. Dispatch the implementer with its PACKET:.

    (d) runs inline; (a), (b) and (c) are resumes - read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/resume-paths.md and follow its ### Resume paths.

    This derives resume-state entirely from git + gh; there is no checkpoint file. Never skip /code-review on a resumed path; a fix it triggers runs step 6's normal re-review and push cycle.

    3.5. Profile migration (run once, on the feature branch). .milestone-config/driver.json exists → use it. Else a legacy root milestone-driver.json exists → mkdir -p .milestone-config; git mv it to .milestone-config/driver.json (plain mv when untracked). Else: new project - setup creates the canonical file; hooks fail-open. Idempotent; both present → .milestone-config/driver.json wins, no move, no deletion. The move rides the issue's PR, not a separate commit.

  4. Create one TodoWrite item per numbered step below (on Codex, this and every Agent, Skill-tool and SendMessage use: skills/runtime-codex.md). Work them in order - do not skip or reorder.

The procedure

The park action, referenced by every park below. Post a comment on the issue in the park-comment shape (skills/output-style.md) opening with 🔴 Parked - and the reason (gh issue comment <n>); apply the named label, plus in progress when the feature branch has commits, via the apply-time helper (idempotent gh label create --force then gh issue edit <n> --add-label); leave the issue open; preserve the branch with any work done; clear the issue's dispatch-cap counters with ${CLAUDE_PLUGIN_ROOT}/scripts/reset-dispatch-cap.{sh,ps1} <repo-root> <n>, so a re-run after the park starts a fresh chain; and return.

0. Triage

Two branches - always run one:

Branch A - Explicit supply (reuse). Fires iff the caller supplied this issue's triage result at invocation time as named fields with actual values (e.g. issueStates["N"] = { blockers: false, label: null, advisories: [...], risk: "light" }, edges["N"] = [...]). Absent, label-only, or partial → Branch B. Use it directly - do not re-invoke milestone-driver:triage <n> - and proceed to the Blocker check.

Branch B - Standalone / fallback (the safe default, never an error). No explicit supply - including a result merely recalled from earlier context → invoke milestone-driver:triage <n> (single-issue mode) and use its return for the Blocker check.

Blocker check (both branches). A Blocker for this issue → park: triage has already posted the 🔴 Triage comment; VERIFY it exists (gh issue view <n> --comments) and post it if missing (idempotent); apply the recommended label from issueStates["<n>"].label - needs design for a design gap, needs decision for a non-design decision; do not proceed to step 1. All-clear or Advisory-only → proceed to step 1.

Held remediate answer is Auto (sub-step 1.1.2) → read ${CLAUDE_PLUGIN_ROOT}/skills/remediate-handoff.md and follow its ## The Auto loop instead of parking now: invoke /milestone-feeder:remediate <n>, re-run triage on the corrected body, and clear the park label when the re-triage comes back clean, then proceed to step 1. Attempt cap: 1 per issue per run. Its ## Park for good branches - remediate returns NEEDS_HUMAN, the re-triage is still dirty, the cap is spent, or the issue has no 🔴 Triage comment - run the park action above unchanged: comment opening 🔴 Parked - , label intact. No held answer, an answer of Leave-them-for-me, or the feeder absent → park immediately, as above.

1. Read the issue

Run gh issue view <n> with comments, redirected to the issue record <repo-root>/.milestone-config/.runtime/triage/issue-<n>.md by the skills/triage/SKILL.md (redirect its full record) command, which refreshes step 0's copy with comments posted since. Restate the acceptance criteria plainly before continuing.

common.md. A caller-supplied common.md path (a solve-milestone run) is reused, never re-assembled - the held-value shape sub-step 1.1.2 uses. None held → run the assembly call and its fail-open rule from skills/solve-milestone/SKILL.md (Assemble common.md) here, and hold its path.

Build profile resolution (resolved after step 0, governs steps 2–6)

Read issueStates["<n>"].risk from the step-0 result (held Phase 0 result in a milestone run, fresh single-issue return standalone): "light" or "heavy", defaulting to "heavy" when absent or inconclusive. That one read sets the brief token and the E2E gate's skip condition. Review effort and the cycle cap come from step 6.1.

ProfilePlanner and implementer briefE2E gate (step 4, E2E row)
LightInclude a risk:light token in the briefSkip when the issue touches no UI surface
Heavy (default)Standard TDD brief (no risk:light)Per step 4's E2E row (UI surface + e2eTestCmd)

The safety floor is unconditional for both profiles: triage (step 0) and the tests-green, force-subagent, and dispatch-cap hooks always run. Light relaxes ceremony only - it never skips verification.

Resolve cited project-docs sections (once, before dispatch)

Name the docs root and pass the common.md path once, here in the orchestrator; the planner quotes the .project/ sections the change depends on into the packet (skills/solve-issue/build-packet.md#Sections), so the implementer reads exactly those sections and never a whole doc. Runs after the build profile resolves, adding inputs to the step-2 and step-3 briefs.

  1. Source the docs root. Use projectDocs already resolved at step 1 (defaults to .project/). Do not re-resolve the profile here.
  2. Leave the anchors to the planner. The planner reads the issue's cited .project/<doc>#<section> anchors and quotes each section common.md does not carry into the packet's ## Rules; the main thread reads no section.
  3. Resolve the repo file index (once). Invoke ${CLAUDE_PLUGIN_ROOT}/scripts/build-file-index.{sh,ps1} once per run, never per-issue inside a milestone loop. Pipe the diff-scoped {"files":[...]} to stdin; it prints one <path> → <purpose> line per file. A ### 3 brief carrying no packet takes it as the resolved file index - the build-file-index output format, consumed, not re-derived.
  4. Pass the common.md path. Put the held common.md path into the ### 2 and ### 3 briefs; never paste its contents. Sub-steps 3 and 4 apply under both build profiles, unaltered by risk:light. The code-comment rule is agents/implementer.md rule 8; the brief does not restate it.

Degradation (no error, ever):

  • Absent projectDocs → .project/ (step 1).
  • Absent .project/ directory, or no cited anchors → no-op: no project grounding, no error, skipped cleanly like unitTestCmd/preflightCmd.
  • Missing/renamed cited anchor → a planner park (agents/planner.md#Park).
  • Absent/failed file-index resolver → no-op: no file index, no error.
  • assemble-common fails twice → no-op: the brief names no common.md path, no error; the implementer reads the four skills/output-style.md sections and citationFormatPath directly.

Resolve cited path (anchor) citations (once, before dispatch)

Resolve the issue's path (anchor) citations (skills/citation-format.md) once, here in the orchestrator, and thread the resolved table into every subagent brief this run composes - no subagent re-derives it. Runs with the block above, before ### 2. Evaluate the codebase for root cause. Paths are repo-root-relative: an issue has no directory, so there is no multi-base fallback.

  1. Extract by model judgment over the path (anchor) shape - never a regex. Apply skills/citation-format.md's span and position tests to the issue body + acceptance criteria.
  2. Resolve each citation once. Invoke ${CLAUDE_PLUGIN_ROOT}/scripts/resolve-citation.{sh,ps1} once per citation: resolve-citation.<sh|ps1> <file-path> <anchor-text>. Exit 0 prints PRIMARY <line> <text> then zero or more MATCH <line> <text>, TAB-delimited, in file order; matching is literal, case-sensitive, and line-scoped.
  3. Feed the printed rows into the ### 2 and ### 3 briefs as the resolved citations, as printed, unchanged; invent no new format.

Degradation (no error, ever):

  • No path (anchor) citation on the issue → no-op: no resolve-citation invocation and no error.
  • A cited anchor not found, or an unreadable file → resolve-citation fails loud (nonzero exit, naming the anchor and file on stderr, stdout empty). Surface it; never swallow it. Exit 2 is a wrong argument count or an empty anchor.

2. Evaluate the codebase for root cause

Dispatch milestone-driver:planner via the Agent tool, briefed with: the issue record path (step 1), the held step-0 result, the resolved build profile (a risk:light token under light), the profile keys sourceGlobs, the expanded domainSkills, nonNegotiables and projectDocs, the worktree path (the repo root standalone), the common.md path (omit when none is held), the resolved citations (omit any whose resolution was a no-op), citationFormatPath (the absolute path of ${CLAUDE_PLUGIN_ROOT}/skills/citation-format.md, always passed), the packet path <main-repo-root>/.milestone-config/.runtime/plans/issue-<n>.md beside common.md, never inside a worktree, and the packet contract path ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/build-packet.md. Every brief carries resolved absolute paths, never the token. A run may add project facts to the brief, never work. The brief carries step 3's scratch-hygiene and command-shape rules; the planner's park cases are agents/planner.md#Park. Planner dispatches are capped at 2 per issue, enforced by hooks/dispatch-cap.sh; a denied dispatch, or a return that is neither PARK nor PLANNED with a file at PACKET:, runs the park action with blocked, never a retry. A packet failing approval takes the one re-dispatch below.

  • STATUS: PLANNED → hold the PACKET: path and approve it with check-packet, never a whole-packet read: run ${CLAUDE_PLUGIN_ROOT}/scripts/check-packet.{sh,ps1} <packet> <worktree>, adding light under the light profile. Exit 0 → the packet is approved, the locked architecture for this issue. Nonzero → re-dispatch the planner once, same brief plus each FAIL line as one listed approval failure, per agents/planner.md (On a re-dispatch, fold the listed approval failures), and approve its new packet likewise. A second failure → run the park action with blocked, naming the failures; the planner cap of 2 is spent, so no further dispatch. A DOMAIN_SKILLS_INVOKED missing an expanded domainSkills name takes that same one re-dispatch, each missing name a listed failure; on exit 0, first copy the packet to issue-<n>.approved.md, which a denied or failing re-dispatch restores, approved. A name still missing goes to step 6.2.
  • STATUS: PARK → run the park action with the returned LABEL and REASON.

Hold the last planner return's DOMAIN_SKILLS_INVOKED line for step 6.2.

3. Dispatch the implementer

Dispatch the profile's implementerAgent (default milestone-driver:implementer; a project-level override uses that agent's own name as-is) via the Agent tool with no model on the first build, so its frontmatter tier applies, orchestrating superpowers:subagent-driven-development + superpowers:test-driven-development. Brief it like a colleague walking in cold: the issue, the approved PACKET: path, the profile, the common.md path (omit when none is held), the resolved citations (omit any whose resolution was a no-op), the repo root (that issue's worktree path in a parallel-mode run), and citationFormatPath; under the light profile the brief must include a risk:light token. Every implementer re-dispatch carries this brief, PACKET: included, plus the failure. Implementer dispatches are capped at 3 per issue (step 4's shared loop), and a non-converging issue parks like any other. The brief must also carry this scratch-hygiene rule: write scratch only under a path named for that issue or that agent, never the shared scratchpad directory, and report what a probe printed rather than writing a probe file to read back later. The brief must also carry this command-shape rule: absolute paths only, and never change directory (cd, pushd, or a subshell); git -C <dir> for git, absolute file paths for grep, cat, sed, and find; a command needing a working directory (unitTestCmd, preflightCmd, npm ci) runs with the absolute worktree path as its cwd.

The implementer is a leaf. It returns an uncommitted diff plus its report and dispatches no subagent of its own (docs/architecture.md → ## Dispatch topology).

Verify the report honors the implementer contract: least-code / reuse-first, TDD red→green observed (or a VERIFICATION (no test layer) section when no test layer exists or under risk:light), verified citations where citable sources exist, a Decision Log, a USER-FACING CHANGES block (with NEW_UI_ELEMENTS: yes|no, DESTRUCTIVE_OPS: yes|no, and POST_REVIEW_CHANGES: yes|no), and changes left uncommitted. The first COMPLETE return whose DOMAIN_SKILLS_INVOKED misses an expanded domainSkills name re-dispatches the implementer once, the re-dispatch brief plus the missing names: invoke them and fix what they contradict in the diff, no new RED run. A second miss or a denied dispatch continues, holding them for step 6.2. With unitTestCmd defined and CLAUDE_HOOK_DISABLE_TESTS_GREEN not 1, the GREEN evidence is a scoped run of only the spec file(s) the implementer added or touched (agents/implementer.md rule 3); the full-suite run is step 4's Unit gate below. Then apply the declaration gates:

  • NEW_UI_ELEMENTS: yes and the acceptance criteria are silent on the element's visual/UX detail → park with needs design, documenting the new elements and the direction needed.
  • DESTRUCTIVE_OPS: yes and the confirmation UX is unspecified → park with needs decision, documenting the missing confirm flow.

🔴 GATE - new dependency: the implementer reports that the optimal solution requires a new library or toolkit → park with needs decision, naming the library and its license / OSS status. Do not ask the operator interactively.

🔴 GATE - implementer STOPPED: STATUS: STOPPED carrying no PACKET_GAP: line (architecture conflict, scope overrun, out-of-scope edit, or missing/ambiguous brief) → park describing the conflict, with needs design for a design/spec conflict, needs decision for an architecture call, or blocked for an otherwise-unresolvable gate. PAUSED-FOR-APPROVAL indicates a new-dependency case and routes to the gate above.

A packet-gap stop re-plans: a stop carrying a PACKET_GAP: line, agents/implementer.md (A fact the packet lacks, or a quoted excerpt that does not match its file), re-dispatches the planner, step 2's brief plus that fact, per agents/planner.md (On a re-dispatch, fold the listed approval failures or the missing fact). Planner dispatches already at 2 (step 2 re-dispatched) → park blocked naming the gap, no dispatch. PLANNED → approve per step 2's check-packet run; approved → re-dispatch the implementer with the new PACKET: path and no model, then apply step 3's checks to its return; a failure parks blocked, no further planner dispatch. PARK → run the park action with its LABEL and REASON, no implementer re-dispatch. A denied planner or implementer dispatch parks blocked, never retried (step 4's shared loop).

4. Verification gates

There is no step 5; the next step is 6, since headings are citation anchors (.project/conventions.md (Keep ## headings stable)).

Unit, E2E, and preflight share one shape - act → verify → retry → park. /code-review is listed for visibility only (always-on, hence no trailing ? in its row): it keeps its own step-6.1 procedure and is not iterated by the shared loop below.

GateApplicabilityactCapPark / escape policy
Unit?unitTestCmd definedRun ${CLAUDE_PLUGIN_ROOT}/scripts/unit-gate.<sh|ps1> <repo-root> (pwsh on Windows, bash elsewhere), never unitTestCmd directly - it stages, runs unitTestCmd, and on green writes the same stamp hooks/tests-green.<sh|ps1> reads, so the commit-time hook does not re-run a suite this gate already proved green on this exact tree.Shared (below)Park blocked (or needs design if the plan is wrong).
E2E?Issue touches a UI surface and e2eTestCmd definedBug: run a targeted subset that proves the fix. Feature: implementer authors new E2E tests covering reasonable user stories, then runs them. Both against the profile's e2eEnv.Shared (below)Verified by other means (a DB assertion + an attached screenshot confirming the feature) → quarantine the flaky test, proceed, log the quarantine in the PR's Code Review section, apply judgment call. Not otherwise verified → park blocked.
/code-reviewUnconditional: always runs, no applicability flagSee step 6.1, its own in-scope-fix / park-trigger classificationStep 6.1's classifier verdict, per skills/review-depth.md § The ladderStep 6.1's own park-trigger classification (architecture deviation, shared-contract change, new dependency, out-of-scope edit, unmetable gate, material ambiguity), plus its second-cycle park rule
Preflight?preflightCmd defined as a literal commandRun the literal preflightCmd.Shared (below)Park blocked (or needs design if the plan is wrong).

Preflight's "github-ci" sentinel mode. When preflightCmd is the reserved sentinel "github-ci", the Preflight row above does not apply: no local command runs, no cap, no park. The PR's own CI is the preflight, gated by step 6.8's wait-for-green merge under whichever granularity is in effect (the per-issue PR, the wave PR, or the milestone PR), each with its red-CI handling. Read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/preflight-github-ci.md for the record.

🔴 GATE - shared loop (unit, E2E, preflight only):

  1. Act. Run the gate's act per the table row. Skip the gate cleanly, no error, when its applicability condition is not met (unitTestCmd absent; e2eTestCmd absent or no UI surface touched; preflightCmd absent).
  2. Verify. Invoke superpowers:verification-before-completion and report real output, never assertion - for all three gates.
  3. Retry. A failing gate re-dispatches the implementer (step 3's re-dispatch brief) with the failure and model: "opus" - or parks directly if the failure reveals the plan is wrong (see Autonomy). A preflight fix routes through step 6.1's After a fix, before committing branch set - ${CLAUDE_PLUGIN_ROOT}/scripts/classify-delta.{sh,ps1} picks the branch, and code-changed re-runs the unit gate, never unitTestCmd directly (skills/solve-issue/post-fix-commit.md § After a fix), and /code-review. Cap: 3 implementer dispatches per issue in total, however split, shared across every gate and the step-6.1 review loop, enforced by hooks/dispatch-cap.sh. A denied dispatch is the park signal, never a retry.
  4. Park. Still failing when the budget is spent → apply that gate's park/escape policy from the table above; the failed gate and its real output are the park comment's evidence slot. E2E's row is the one exception: verified-by-other-means quarantines and proceeds instead of parking.

Call sites. Unit and E2E run back-to-back immediately after the implementer dispatch (step 3). Preflight fires as the concluding action of step 6.1, after the /code-review loop converges and before version bump/commit.

6. Review → integrate → close

Coherence review (before the final /code-review). An optional read-only pass over the implementer's uncommitted diff. Run the review-depth classifier first (skills/review-depth.md § Running the classifier, <repo-root> as root): a shallow verdict skips this pass. Otherwise run it only when the coherenceReviewAgent - profile key, default-filled to milestone-coherence-reviewer:coherence-reviewer - is both present (dispatchable in this session) and configured (on Codex, present means its file resolves per skills/runtime-codex.md); absent/unavailable OR explicitly unconfigured → silently skip: no error, no prompt, at most one log line. When it runs, read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/coherence-review.md and follow its ### The pass.

  1. Resolve review depth, then review and resolve. Read ${CLAUDE_PLUGIN_ROOT}/skills/review-depth.md and follow it, passing <repo-root> as the classifier's root: it holds the verdict-to-effort mapping, the per-verdict cycle cap, the classifier's fail-open behavior, the re-classify-before-a-second-cycle rule, and the second-cycle park. Then run /code-review (superpowers:requesting-code-review) at that effort on the implementer's uncommitted changes, resolving findings autonomously per the Autonomy model, never pausing to ask the operator about an in-scope finding:

    • In-scope (cosmetic, naming, style, local reversible refactor, missing/weak test): take the pre-fix snapshot first - ${CLAUDE_PLUGIN_ROOT}/scripts/classify-delta.<sh|ps1> --snapshot <repo-root> (pwsh on Windows, bash elsewhere), holding the tree hash it prints; on nonzero exit hold nothing and still re-dispatch, the post-fix branch set names the move. Then re-dispatch the implementer (step 3's re-dispatch brief) to fix it with model: "opus" (the main thread cannot edit sourceGlobs - force-subagent); log it in the Decision Log. That hash is what the post-fix branch set below classifies against. Which findings the build profile fixes, and how a finding carrying no severity is scored, are skills/review-depth.md § Which findings get fixed.
    • Park trigger (architecture deviation; a shared contract/interface/schema change; a new dependency; edits outside the issue's file scope; an unmetable gate; material ambiguity): park with needs design, needs decision, or blocked. Do not commit.

    The orchestrator runs this review itself, dispatching the reviewers directly as leaves (docs/architecture.md → ## Dispatch topology), and never dispatches an agent that itself runs /code-review. Reviewing several issues concurrently pins the shape to exactly one reviewer leaf per issue (skills/solve-milestone/parallel-waves.md § Parallel mode - Phase 1: concurrent stage dispatch, Stage B). The gate is unconditional: its ### 4. Verification gates row carries no applicability flag, and hooks/code-review-gate.sh denies a gh pr create / gh pr merge whose PR body carries no ## Code Review section. The brief must also carry the step-3 scratch-hygiene and command-shape rules and citationFormatPath.

    The brief must also put comment text out of review scope: no finding at any severity about a comment's wording, density, or presence. One carve-out, in these terms and not redefined: a comment change altering a machine-read directive stays in scope, scripts/classify-delta.sh (is_directive <src>)'s enumeration deciding what counts as one.

    Omitting /code-review is not permitted. Skipped under any constraint (time, token budget, tool error, self-review substitution) → treat the omission as a park trigger and park with blocked, giving the reason.

    After a fix, before committing. No in-scope findings → commit directly. Otherwise at least one was fixed: read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/post-fix-commit.md and follow its ### After a fix: the document-only and classify-delta branches, and the tests-green behavior they rely on. The cycle cap that bounds the loop is skills/review-depth.md § The ladder.

    Preflight gate (concluding action of 6.1). Once the /code-review loop has converged, before version bump/commit, run the preflight gate - its applicability, act, cap, verify step, and park/escape policy are the Preflight row of ### 4. Verification gates and its shared loop.

  2. Assemble the Decision Log from the implementer's report for the PR body. Its slots are the Decision Log entry shape in skills/output-style.md - choice · rationale · citation · rejected alternatives, one entry per line. Carry planner DOMAIN_SKILLS_INVOKED: <value> and implementer DOMAIN_SKILLS_INVOKED: <value>, one line each, plus Domain skills missed: <names> when step 2 or 3 held any.

    Check every path (anchor) citation via ${CLAUDE_PLUGIN_ROOT}/scripts/resolve-citation.{sh,ps1} before posting. Resolves → post verbatim. Fails → correct that citation from code, or set it to none if no anchor exists. No other rewriting.

    Post the block on the issue as the Decision Log comment opening 📋 Decision Log, the implementer's CITATIONS block after it - the record the aggregate PR bodies' choice-only shape cites (skills/output-style.md (Choice-only Decision Log)). Probe gh issue view <n> --json comments for that opener: found, replace that comment's body (gh api -X PATCH repos/{owner}/{repo}/issues/comments/<id> -F body=@<file>), so a re-drive's entries land on the cited record; absent, create it (gh issue comment <n>). Never a second comment.

  3. Assemble the Code Review section for the PR body. Record whether /code-review ran, the finding count and severity per run (the 1st, and the 2nd if a re-review ran), and each finding's resolution (re-dispatched and resolved / accepted with rationale / triggered park). Its slots are the ## Code Review section shape in skills/output-style.md; the evidence slot is the ref each finding named (per skills/citation-format.md; at zero findings, the effort level used). Template:

    ## Code Review
    
    - /code-review run: yes | n/a - <reason> (`n/a` only for a PR carrying no `sourceGlobs` change; omission is a park trigger: a submitted PR always carries a real review; a parked run opens no PR)
    - Findings, one line per run: <count> in-scope finding(s) at <effort> effort
      - <finding> - <the ref it named, per skills/citation-format.md> → re-dispatched and resolved | accepted (rationale: <…>) | triggered park
      - … (one line per finding, or "none" when count is 0)
    - No park-triggering findings. | Park-triggering findings: <list>

    The version-bump annotation this section carries is the one step 6.4's resolved mode names.

  4. Version bump. Read versioning from the profile and select the mode: Version-free mode (versioning: false), Fail-safe degradation (versioned mode - versioning true or absent - but .claude-plugin/plugin.json does not exist), or Versioned mode (versioning true or absent, and .claude-plugin/plugin.json exists). Read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/version-bump.md and follow its ### Modes for that mode's mechanics, its Code Review annotation, and - under Versioned mode - the milestone-run vs standalone-run split.
  5. Commit on the feature branch - the tests-green hook (PreToolUse on git commit) re-checks the suite. Review-before-commit is enforced by audit trail (the mandatory Code Review section), not by a commit-time hook; hooks/code-review-gate.sh gates gh pr create / gh pr merge, not git commit.
  6. Push the feature branch and open a PR with --base <integrationBranch> (never protectedBranch - enforced by the no-push / no-pr-to-protected hooks and GitHub branch protection). Put the Decision Log and the Code Review section in the PR body. Add a judgment call label if any borderline autonomous call was made. Record its number (gh pr view --json number --jq .number) as step 8's <pr-number>.
  7. Auto-merge on green. Once CI is green, squash-merge for every issue, no UI/non-UI branch in front of it. This replaces the human-choice step of superpowers:finishing-a-development-branch.

    Every squash merge in this plugin passes an explicit subject and body, here and at every squash site citing this section.

    body="Decision Log and Code Review: PR #<pr-number>" \
      && title="$(gh pr view <pr-number> --json title --jq .title)" && [ -n "$title" ] \
      && gh pr merge <pr-number> --squash --delete-branch --subject "$title (#<pr-number>)" --body "$body"

    One Bash call, &&-joined - a variable does not survive a call boundary, and an empty title aborts before the merge. The title is read at merge time, never held, and every value these strings interpolate is a variable, so a $, a backtick, or a " in one is safe. $body is "Decision Log and Code Review: PR #<pr-number>" where the PR body carries a Decision Log, "Code Review: PR #<pr-number>" where it carries none.

    <pr-number> is written into the command as literal digits, never a variable. The merge gate's pr_arg regex reads the first token after merge and runs gh pr view on it (hooks/code-review-gate.sh (gh pr view "$pr_arg")); on a non-literal token that view fails and the gate falls open.

    Visual evidence (optional, never-gating), before that merge. Runs only when the PR's diff matches uiSurfaceGlobs. Then, with a visualCapture block carrying all three required keys - serverCmd, readyUrl, signInPath - on a sequential run (a parallel run defers the capture to the serial merge tail: skills/solve-milestone/parallel-waves.md (Deferred to the serial tail)), read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/visual-capture.md and follow its ### Capture flow. No UI surface, visualCapture absent or incomplete, or any failure in it → skip with one log line: no capture, no PR comment, no hold. The merge proceeds regardless.

  8. Confirm the issue is closed (a linked PR auto-closes it; otherwise gh issue close <n>) - unconditionally.

Run-end cost record (additive, never-gating)

Survives context compaction: an in-context sum is gone the moment compaction runs, so nothing is aggregated in context. Every dispatch's usage is appended to disk as it returns; the run-end record is read back from that disk file alone. Never blocks, parks, or changes any outcome (.project/design-philosophy.md#Error & failure philosophy).

  1. Append, per dispatch, as it returns. As soon as an Agent-dispatch report lands - planner (tier opus), implementer (tier sonnet, opus on a fix re-dispatch), /code-review, coherence-reviewer, direct triage; a background completion notification carries the same <usage> block - pipe {"agent":"<dispatch label>","tier":"<opus|sonnet>","totalTokens":<subagent_tokens>,"durationMs":<duration_ms>} to ${CLAUDE_PLUGIN_ROOT}/scripts/write-cost-record.{sh,ps1} --append <runId> (<runId> the issue branch / run id, held constant for the whole run). It appends one line to .milestone-config/.runtime/usage/<runId>.jsonl; a dispatch never skips this even mid-run. An implementer or planner return carrying an output_file: path first merges the object ${CLAUDE_PLUGIN_ROOT}/scripts/measure-dispatch.{sh,ps1} <output_file> prints into the piped one (jq -c -s '.[0] + .[1]' (Bash), or both objects through ConvertFrom-Json -AsHashtable, merged with +, then ConvertTo-Json -Compress (PowerShell)); no output_file:, or a nonzero exit, pipes the four-field object unchanged and never gates.
  2. Finalize, last action before returning to the caller at every terminal exit - every park (steps 0, 2, 3, 4, 6.1) and the step-6.9 close. Run ${CLAUDE_PLUGIN_ROOT}/scripts/write-cost-record.{sh,ps1} --finalize <runId>: it reads that .jsonl alone, sums totalTokens per tier into inputTokens (an auditable lower bound - outputTokens/cacheReadTokens/cacheWriteTokens at the 0 sentinel, provenanceNote: "unsplit-total-as-input"), sums durationMs into wallClockSeconds, and writes the record to .milestone-config/.runtime/cost-records/ carrying one more field, agents: the raw per-dispatch entries, for a per-agent durationMs breakdown the summed total alone cannot give back.
  3. Skip cleanly. No usage file for this runId (e.g. a triage-blocker park before any dispatch) → --finalize fails open on its own, one log line, no zero-value record. Writer script absent → silent no-op, one log line.

Autonomy model (Balanced)

Proceed autonomously (log on the PR): implementation choices within the approved architecture; reuse of existing helpers, styles, and conventions; test design; local reversible refactors; resolving in-scope /code-review findings (step 6.1).

PARK & continue (the autonomous runtime parks, never interactively waits): deviation from the approved architecture; any change to a shared contract, interface, base class, or DB schema used beyond this issue; a new dependency; edits outside the issue's expected file scope; a gate that cannot be met without a design change; material ambiguity in the issue's intent; /code-review omission or substitution for any reason (time, token budget, tool error, self-review); budget pressure is not an exception.

The park is ## The procedure's park action, comment opening 🔴 Parked - <reason>; its in progress label is the open-WIP signal the milestone loop reads, and that loop continues with independent, clean issues. Only a systemic failure (auth/gh failure, broken integrationBranch, missing tooling) halts the whole run. A standalone interactive solve-issue still parks durably; it may additionally narrate to the watching operator.

Additional park triggers (each a park, not silent resolution and not an interactive prompt):

  • The recorded/locked design is internally contradictory → park with needs design.
  • A self-noted risk about the approved design (e.g. "this list could get long at realistic data volumes") → park with needs design.

Architecture is locked at packet-approval time (step 2). The procedure executes approved architecture. If implementation proves the plan wrong → park, not pivot.

A change is architecture (→ park) if it touches any of: a component or data structure named in the approved packet; a shared contract, interface, base class, DB schema, or public API used by code outside this issue; data ownership or a cross-component boundary; a new external dependency; or any file outside this issue's stated scope. A change is an implementation detail (→ proceed, log in the Decision Log, step 6.2) if it is local to this issue's own files, changes no shared contract, and is reversible - a binding style, a private helper extracted in the same file, a local refactor, or test design. When the distinction is genuinely ambiguous, treat it as architecture and park.

Audit trail (always): a Decision Log on every PR, a Code Review section recording every /code-review run and its findings/resolutions, and a judgment call label on borderline calls.

Permission pre-flight gate

Runs once per run, before the first background dispatch - only when a leaf is about to be dispatched as Agent(run_in_background: true). A fully synchronous run SKIPS it entirely.

Background subagents auto-deny any tool call that would otherwise prompt, and a background leaf hitting an un-allowlisted tool fails outright with no interactive recovery. Before the first background dispatch: union permissions.allow across ~/.claude/settings.json, .claude/settings.json, and .claude/settings.local.json - absent or unreadable layers are skipped in the union, not treated as gaps. Synchronous fallback fires only when the union misses the required tool surface, or no layer is readable. With that union in hand, read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/permission-preflight.md and follow its ### Tool surface and response.

Milestone granularity (integrationGranularity: "milestone")

Resolved from the profile at step 1, not from an invocation token. integrationGranularity resolves to "milestone" (docs/profile-schema.md (How should built issues integrate?)) → read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/milestone-clauses.md and apply its ### Clauses, plus ${CLAUDE_PLUGIN_ROOT}/skills/solve-milestone/milestone-granularity.md for the branch model, integration-commit trailer, and resume query. Absent or invalid, with no milestone run holding a resolved value, resolves to "issue" here: neither read happens and the pipeline runs byte-unchanged. An explicit "issue" resolves the same way.

Wave granularity (integrationGranularity: "wave")

Resolved at the same step-1 profile read, not from an invocation token. integrationGranularity resolves to "wave" (docs/profile-schema.md (How should built issues integrate?)) → read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/wave-clauses.md and apply its ### Clauses. Those clauses apply to every issue in the Wave, UI included.

Async mode (--async): retired

--async is an interpreted token, not a parsed CLI flag - recognized by string presence in the invocation text - and inert: the pipeline runs on the caller's own main line, and the token is a no-op, never an error. No run reads ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/async-mode.md.

Parent-issue detection (md-epic)

Runs before anything else - before ## Before starting step 1 and before ### 0. Triage. Read #n's labels (gh issue view <n> --json labels, exact match against .labels[].name for the literal md-epic). Absent → this section does not apply; the pipeline runs byte-unchanged from ## Before starting step 1. Present → read ${CLAUDE_PLUGIN_ROOT}/skills/solve-issue/md-epic-fanout.md and follow its ### Parent path, which replaces the rest of this pipeline for this invocation.

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 the ### 0. Triage step completes.

🚀 Issue #201 - [title] · [risk: light | heavy] · [UI | non-UI]

| Issue | Title   | Risk   | UI | Status      |
|-------|---------|--------|----|-------------|
| #201  | [title] | [risk] | -  | 🔨 building |

▶ Building - the floor is yours.

Template 2 - Issue completion (terminal output)

<!-- Structural mirror of solve-milestone Template 2; keep column schema (Issue/Result/Gates/PR/Note) in sync. -->

One row per issue; emit only the row matching the actual outcome and suppress the others.

🏁 Issue #[n] · [T] min

| Issue | Result     | Gates | PR | Note                    |
|-------|------------|-------|----|-------------------------|
| #201  | ✅ merged  | 🔍✓(0 findings)  | #301 | -    |
| #202  | ⏸️ parked  | -                | [#pr | -] | [park label]      |
| #204  | ✅ committed | -              | -    | committed on its branch |

PR cell: show the PR number if the issue has one, else -. Gates legend: 🧪 = unit suite · 🔍 = code review · 🌐 = E2E Note cell: an issue the run sent through the Auto loop records its outcome there - remediated, cleared (re-triage clean, label cleared), remediated, still parked (cap spent or re-triage still dirty), or NEEDS_HUMAN, parked (skills/remediate-handoff.md (Park for good)); Result still shows the state the pipeline reached. No Auto loop → the cell is unchanged. Slow first build: an issue whose first implementer dispatch printed steps over 40 or minutesToFirstEdit over 2 adds <steps> steps, first edit <m> min (no minutesToFirstEdit: steps alone decides, shown as <steps> steps, no edit), joined to any Auto loop outcome by ; . Slow planner: an issue whose first planner dispatch printed steps over 25 adds planner <steps> steps, <minutes> min to the Note cell, joined to any other Note text by ; .

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 comment, Decision Log, and PR body. The two surfaces are distinct - terminal rules never reach GitHub. 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. skills/solve-milestone/SKILL.md § Main-thread context binds this skill too - it runs in-thread.

Non-negotiables

  • Gitflow. PRs target integrationBranch only - never protectedBranch.
  • Honor the profile's nonNegotiables (framework versions, platform targets).
  • The main thread never authors application or test code - always dispatch the implementer.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.