Skip to content
Back to skills

Epic Flywheel

ASecurity

Drive an entire epic to completion semi-autonomously — runs the per-story flywheel (create → dev → review) for every story with granular commits, then a single epic-boundary verification + manual-test-planning pass. Use when the user says "epic flywheel", "run the whole epic", "flywheel the epic", or "run epic {N} autonomously".

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 19, 2026
ai-agentsgoswiftshellbashsqltestingcode-reviewgit

Works with

  • terminal

Security analysis

A100/100

Scanned September 19, 2026

npx -y skills add rterakedis/leanwheel-skills --skill epic-flywheel --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Epic Flywheel?

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

Security grade badge for Epic Flywheel
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/rterakedis-epic-flywheel/badge)](https://www.skillsdirectory.com/skills/rterakedis-epic-flywheel)

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: epic-flywheel
description: Drive an entire epic to completion semi-autonomously — runs the per-story flywheel (create → dev → review) for every story with granular commits, then a single epic-boundary verification + manual-test-planning pass. Use when the user says "epic flywheel", "run the whole epic", "flywheel the epic", or "run epic {N} autonomously".
---

# Epic Flywheel Skill

**Goal:** Take a whole epic from "stories not started" to "all stories implemented, reviewed, and verified together" with minimal steering. The per-story automated gates (Build & Test, evals RUN, invariant verification) still stop the loop on a real compounding bug; the **manual / integration test pass is deferred to the epic boundary** (DD-30).

**Relationship to `/story-flywheel`:** epic-flywheel is the autonomous, epic-scoped layer above story-flywheel. It reuses the same three subagents and per-phase model routing (story-flywheel → **Subagent Delegation & Model Routing**; not duplicated here). What epic-flywheel adds: (1) commit-per-step so a bad story can be unraveled, (2) within-epic auto-advance on clean stories, (3) an **Epic Boundary Gate** that runs build+test+evals+invariants across the whole epic and **HALTs for help** on any failure, (4) continuous deferred-item re-homing, and (5) a deduplicated **rolled-up test plan** that first subtracts what automation already proves, then splits the remainder into simulator-runnable vs physical-device-required.

---

## Iron rules

1. **HALT, don't push forward.** A red Build & Test Gate, a failing eval, an unverifiable invariant, or a dev-story HALT stops the loop and asks the user for help. Never carry a broken story into the next one.
2. **Commit every step.** create → commit → dev → commit → review+patch → commit. The granular trail is what lets you `git bisect`/revert to the precise step that went wrong.
3. **Don't accumulate context.** The orchestrating thread holds only short structured reports from subagents — never full story files or source. The only LLM-heavy work this thread does itself is the one-time test-plan dedup at the epic boundary, over collected plan text only.
4. **Deferred means re-homed, not forgotten.** Every `[Defer]` finding gets a home (slotted as an AC or a remediation story) via the `deferred` skill, and the Epic Boundary Gate sweeps for any orphan.

---

## Activation

1. **Pick the epic.** Same discovery as story-flywheel (sort by epic number embedded in milestone *title*, not GitHub milestone ID; fall back to `docs/epics.md`). Accept `/epic-flywheel {N}` to force one. Announce: "Epic-flywheel for Epic {N}: {title}. {X} stories. I'll run them with per-story gates, commit each step, then do one verification + test-planning pass at the end."

2. **Delegation mode + model routing.** Identical to story-flywheel (→ **Subagent Delegation & Model Routing**): detect `swift_project`, prefer subagent-delegation mode (`lw-story-creator` / `lw-story-developer` / `lw-story-reviewer`), `model: opus` override only for Phase 2 on Swift (never `fable` — the flywheel's cost ceiling is Opus); inline on the session model only when subagents are unavailable.

3. **Detect the Apple platform set.** Set `apple_project = true` if `swift_project`. Read `docs/setup/manifest` / `docs/setup/swift/` and `docs/ux/EXPERIENCE.md` only enough to learn which platforms ship (iOS/iPadOS/macOS). This governs the simulator-vs-physical split at the boundary. Non-Apple projects get a generic "automated/local-runnable vs manual" split instead.

4. **Commit authorization (required for autonomy).** Ask once:
   > Epic-flywheel commits after each step (create / dev / review) so the trail is granular and unravel-able. Authorize automatic commits for this run? (yes / no — if no, I'll pause for you to commit at each step.)

   Detect the commit command: prefer `scripts/commit-push.sh "<msg>"` if it exists in the project, else plain `git add -A && git commit`. Push only if the project's script pushes (don't introduce pushing where the project doesn't).

5. **Autonomy level.** Default is **auto-advance within the epic**: any story whose per-story gates are fully green (no `UNRESOLVED`, PASS rubric, green Build & Test, evals pass, invariants verified) advances without a checkpoint. The loop still stops on red/HALT. Offer the stricter alternative:
   > Default: I auto-advance on clean stories and only stop for problems or the epic boundary. Prefer a checkpoint after *every* story instead? (auto / every-story)

---

## The Per-Story Loop

For each story in the epic, in story order. Spawn each phase as its subagent (cold start; it owns the heavy reading). The orchestrator captures only the structured report fields.

**"Spawn" = a literal Agent tool call** (`subagent_type` = `lw-story-creator` / `lw-story-developer` / `lw-story-reviewer` / `lw-docs-sync`), per story-flywheel → **Spawning a phase** — if a step ends with no Agent call in the transcript, the step did not run. Its **non-return rule** and PID/artifact wait loops apply here unchanged (DD-21): a report missing its required fields means the phase hasn't returned — resume the subagent via SendMessage, don't advance.

### GitHub tracking is orchestrator-owned (do not delegate-and-hope)

The orchestrator drives every issue transition itself with the deterministic script (DD-22); subagents are not relied on for this:

```
backlog → ready-for-dev → in-progress → review → done (+ closed)
```

At each transition point in the steps below, run one line — it adds the target label, strips every stale status label, and self-verifies:

```bash
bash scripts/gh-track.sh transition {issue#} {label}    # ready-for-dev|in-progress|review
bash scripts/gh-track.sh close {issue#} "Story {e}.{s} complete"   # final: done + close
```

Get `{issue#}` from the create-story report or `grep '^github_issue:' {story_file}`. If it's `0`/missing the issue was never created — run github-tracking **CREATE-ISSUE** first, don't silently skip. If the script is absent, fall back to the github-tracking TRANSITION/CLOSE-ISSUE ops. If `gh` is unavailable the script prints `skip: gh unavailable` and exits 0 — note that once in the boundary report rather than claiming issues were updated.

### Step 1 — Create Story → commit
Spawn `lw-story-creator` with `{epic}.{story}`. Capture `STORY FILE`, `EPIC CONTEXT`, `COMPLEXITY`, `CLARIFICATIONS NEEDED`, `PREREQUISITES`, `DESIGN GAP`.
- **Epic context gate (zero-token, orchestrator-owned):** run `[ -f docs/epics/epic-{N}-context.md ]`. The creator must have generated or reused it (report field `EPIC CONTEXT`). If the file is absent, re-spawn `lw-story-creator` with an explicit prompt to run create-story's **Generate Cache** section for Epic {N}, then re-check. Never advance to Step 2 or commit while the file is missing (DD-23).
- **Clarification Gate (the one mandatory human pause inside a story):** if the report lists *material* clarifications, surface them now and wait for answers; record them into the story file. One-default ambiguities are recorded as stated assumptions and do not pause.
- **Cross-story prerequisite check:** if `PREREQUISITES` names a runtime artifact owned by a *later* story in this or another epic, flag a sequencing risk now — the legitimate "not built yet" case belongs at story-design time, not as a fake bug later.
- **Track:** `gh-track.sh transition {issue#} ready-for-dev`.
- **Commit:** `story {epic}.{story}: create` (stages the story file + the epic context cache if generated/updated this step + any tracking/epics edits).

### Step 2 — Dev Story → commit
**Track first:** `gh-track.sh transition {issue#} in-progress` before spawning, so a long dev pass shows the right state. Then spawn `lw-story-developer` (`model: opus` only if `swift_project`) with the story file path. It runs the full dev-story workflow: implementation, **Build & Test Gate** (verify by running), **evals RUN** (if `docs/evals/`), and invariant + design verification, then stops at `review`. Capture `STATUS`, `BUILD & TEST`, `BUILD/TEST ITERATIONS`, `EVALS`, `INVARIANTS`, `INFRA TOUCHED`, `UNRESOLVED`, `TESTING PLAN`, `REVIEW HANDOFF`.
- **On HALT or red gate:** stop the loop. Report which story and why; do **not** commit a red story. Resume with `/epic-flywheel {N}` after the blocker is fixed.
- **Operational doc sync (cheap, orchestrator-owned):** the developer does not run docs-sync (DD-24). If `INFRA TOUCHED: yes`, spawn **`lw-docs-sync`** (Haiku) with the story path and op `OPERATIONAL`; capture `DOCS UPDATED`. Skip the spawn when `INFRA TOUCHED: no`. The doc edits land in the dev commit below.
- **Track:** on green, `gh-track.sh transition {issue#} review`.
- **Commit (only if gate green):** `story {epic}.{story}: dev` (includes any docs-sync edits).
- **Testing-plan shape gate (zero-token):** the report's `TESTING PLAN` must carry both sub-fields, `AUTOMATED:` and `MANUAL:` (dev-story → *Testing Plan (required report field)*). Missing either → non-return: resume the subagent via SendMessage for the complete field; do not stash a half plan.
- **Stash the TESTING PLAN** for the boundary roll-up — both sub-fields, text only. Append one block per story to a scratch list `docs/epics/.epic-{N}-test-plans.md`:
  ```
  ## {epic}.{story} — {title}
  AUTOMATED: {names}
  MANUAL: {tagged lines}
  ```
  The `AUTOMATED` names are the subtract list the boundary greps against.

### Step 3 — Code Review + patch → commit
Per story-flywheel → Phase 3 (not duplicated here): **always** spawn `lw-story-reviewer` with the developer's `REVIEW HANDOFF` as its prompt, verbatim. It emits the SCORE rubric line, auto-patches `patch` findings, applies `fix-now` findings within code-review's Triage ceiling (recorded in Review Findings, not logged as deferred), logs `defer` via the `deferred` skill (re-homing each — slot as AC or remediation story), and **re-verifies green**. `decision-needed` findings surface to the user.
- **Deferred re-homing check:** confirm every `[Defer]` from this story landed in `docs/deferred-items.md` with a `Scheduled As` target. An orphan is a loop bug — fix before advancing.
- **Track:** on green, `gh-track.sh close {issue#} "Story {epic}.{story} complete"` (applies `done` + closes — milestone progress ticks up here).
- **Commit (only if green after patches):** `story {epic}.{story}: review+patch`. If patches couldn't resolve, leave status `in-progress`, don't commit, HALT.

### Step 4 — Advance or checkpoint
- **auto mode (default):** if the story is fully green, append the per-story roll-up via `scripts/ledger.sh story-flywheel …` (per story-flywheel → Observability — written every story, silent advance included) and advance silently to the next story. If anything is non-green, stop.
- **every-story mode:** present the standard story-flywheel Phase-4 checkpoint and wait.
- When the last story in the epic finishes → **Epic Boundary Gate**.

---

## Epic Boundary Gate

All stories implemented, reviewed, and individually green. Now verify the epic *as a whole* and prepare the human test pass. **Any failure here HALTs and asks for help — do not start the next epic.**

### 1. Epic Build & Test Gate (whole project)
Run a full, unfiltered build + test — not story-scoped:
- Apple: `xcodebuild -quiet … build test` (and `swift build -q && swift test -q` for SPM targets).
- Web: the project's quiet build + test scripts.
Use the project CLAUDE.md `## Quiet commands` invocations when present; always `tee` the full log to `.leanwheel/logs/` and keep only the tail in context (dev-story → Build & Test Gate).
- Else: the documented project command.
Red build / any failing test → **HALT**: report the failing target/test output and ask the user how to proceed.

### 2. Evals RUN — full cumulative set
Run `bash scripts/evals.sh --quiet` — every epic, not just this one (per evals → RUN). Any regression → **HALT**, listing the failing cases.

### 3. Invariant verification sweep
Collect the `### Invariant Verification` blocks recorded by dev-story across this epic's stories (read the short blocks, not full files). Any invariant left `[ ] UNVERIFIED` (no test, no cited enforcing `file:line`) → **HALT** and surface it for the user (DD-14).

### 4. Deferred sweep
Two-pass, mirroring `/retrospective`:
- **Pass 1:** scan this epic's story files for `[Defer]` entries not present in `docs/deferred-items.md`; LOG-AND-SCHEDULE any orphan so it gets a home.
- **Pass 2:** verify every logged deferred item has a non-empty `Scheduled As` pointing at open work. Report the count re-homed.

### 4a. CLAUDE.md budget check (zero-token, non-blocking)
`wc -l CLAUDE.md` > 300 → report as a finding in the boundary report: root CLAUDE.md is over the 300-line budget; demote (T2→T1) or move to a nested CLAUDE.md rather than append (DD-54; `/retrospective`'s CLAUDE.md tier-audit step).

### 4b. Tracking reconcile (safety net)
Reconcile the whole epic's issues against story frontmatter:
```bash
bash scripts/gh-track.sh sync "<story-glob>"            # dry-run diff
bash scripts/gh-track.sh sync "<story-glob>" --apply    # if the diff is non-empty
```
A clean diff (`0 to-change`) is the proof every issue landed in the right state. Report the count fixed in the boundary report. If the project predates the script, call the github-tracking SYNC op instead.

### 4c. Architecture promotion (canonical-doc sync, cheap)
Call the Agent tool with `subagent_type: "lw-docs-sync"` (Haiku) and a prompt naming op `PROMOTE` and Epic {N} (fallback: execute the docs-sync **PROMOTE** op inline if subagents are unavailable). It harvests project-canonical learnings from `docs/epics/epic-{N}-context.md` and appends the durable ones to `docs/architecture.md` (idempotent; also `docs/sql/` / `docs/maintainer/` when present) — see DD-58. Zero-token when the context file has nothing canonical; never touches `docs/setup/*` guidance. Report the count promoted in the boundary report.

### 5. Rolled-up, deduplicated, **subtracted** Test Plan (the manual pass)
Read the accumulated `docs/epics/.epic-{N}-test-plans.md` scratch list (collected plan text only — no source). Then:

0. **Every flow opens with a "Starting state" prerequisites block** before step 1 — the required data and settings state the tester must have in place. Steps then follow real usage order, not story order.
   - **Setup commands are part of the starting state.** When reaching the state needs a terminal command (`scripts/sim.sh launch …`, `xcrun simctl …`, a dev-server or seed script), print the exact command in the block, carrying every flag the app needs to honour it — a launch argument the app only reads in combination with another (e.g. an entitlement override applied only under `--uitest`/`--seed`) must appear with its companions; a bare flag the app silently ignores produces a false finding. When unsure, grep the launch-argument handler (testability foundation) rather than guessing.
1. **Deduplicate & merge** the `MANUAL` items across stories into end-to-end flows (e.g. five stories each touching the cart → one "complete a purchase" flow plus the per-story edge cases that aren't covered by the flow).
2. **Enumerate edge cases** the individual story plans listed, deduped against the flows.
3. **Subtract what is already automated (mandatory, zero-token grep + one judgment pass; DD-31).** For each candidate human step:
   - Collect the union of the stashed `AUTOMATED` names, then grep the project's **UI-test flows**, **unit test target**, and **`docs/evals/epic-*.md`** (cumulative, every epic) for the step's surface or assertion — identifiers, flow/suite/case names, the string asserted on.
   - **Covered** → remove it from the checkbox list. It is listed by name in the flow's `Automated — do not re-test:` line instead.
   - **Not covered** → it stays, unless a test could clearly pin it — then say so in `## Notes` as an automation gap (`/e2e-tests` candidate), and keep it as a human step for this pass only.
   - Record `{s}` = the number of candidate steps subtracted and `{g}` = the number of automation gaps flagged; both go in the boundary report.

   > **Rule: a test plan step that restates an existing automated assertion is a defect of this step.** Section A may contain only (a) assertions with no flow/eval coverage and (b) visual/layout-judgment passes — design-eye, Dynamic Type, dark mode, copy tone. Everything else is either already pinned (subtract it) or should be (flag it).
4. **Classify every remaining test** by where it can run:
   - **Simulator / local-runnable** — anything exercisable in the iOS/iPadOS/macOS simulator (or, for web, a local dev server / headless browser). UI flows, navigation, state, layout, Dynamic Type, light/dark, most logic.
   - **Physical-device-required** — needs real hardware or a paid/org capability: camera & photo capture, real push notifications (APNs on device), Face ID / Touch ID, background location, Bluetooth / NFC / HealthKit sensors, real network conditions, thermal/perf, StoreKit on-device purchase, anything gated behind an **org-based developer account / provisioning** the user doesn't yet have. The stashed `MANUAL` tags (`device-only`, `sandbox-only`) route here directly.

Write the result to `docs/epics/epic-{N}-test-plan.md`:

````markdown
# Epic {N} — {title}: Test Plan
_Rolled up from {X} story plans on {date}: {a} human steps after subtracting {s} already-automated, {b} physical-device._
_To log a finding: add an **indented** plain bullet (`-` or `*`, no checkbox) directly under the relevant step — e.g. `  - shows wrong total`. Leave the `- [ ]` step lines as checkboxes (check them off as you pass them). Then run `/harvest-findings {N}` to capture and schedule the findings._
_If a step turns out to be already covered by a passing test, note it as a finding too (`  - already automated: {flow}`) — it is a plan defect the retro counts._

## A. Simulator / local-runnable (do now)
### Flow: {name}
**Automated — do not re-test:** {flow/suite/eval names that already prove this flow, e.g. `UpgradeSheetFlow`, `CustomerLimitServiceTests`, `E12-03`}
**Starting state:** {required data, settings state — everything that must be true before step 1}
```bash
scripts/sim.sh launch --uitest --seed heavy --route {route}   # every flag the app needs; bare flags are ignored
```
- [ ] {step the automation cannot see} → {expected} [visual-judgment | setup-unreachable]
### Edge cases
- [ ] {case} → {expected}

## B. Physical-device pass (DEFERRED — requires org developer account)
> These cannot run on the current developer account / simulator. Batch them for the
> physical-device test session once the org-based account and provisioning are in place.
- [ ] {test} — requires: {camera | APNs push | Face ID | …}

## Notes
- {anything ambiguous the tester should confirm}
- Automation gaps (should be pinned, `/e2e-tests` candidates): {list or "none"}
````

The `Automated — do not re-test:` line is mandatory on every section-A flow (write `none` only when the flow genuinely has no coverage — and then expect the flow to be an automation gap).

Also **append section B's items to a persistent cross-epic backlog** `docs/testing/physical-device-backlog.md` (create if absent), tagged with the epic. Delete the `.epic-{N}-test-plans.md` scratch file after writing.

### 5b. Squash-merge to main — Apple / manual-test epics (conditional)

**Only when the manual test pass needs a build the user drives** (`apple_project = true`, or any epic whose test plan must be built/run outside the agent — e.g. Xcode/TestFlight, a native desktop app). Skip for web/library epics where the agent runs the tests itself.

If the project's `CLAUDE.md` prescribes merge-at-boundary (look for an "Epic-boundary merge" rule), **do it now**, after gates 1–4c are green and the test plan (step 5) is written: squash-merge the epic branch to `main`, then remove the worktree (rationale: DD-25).

```bash
gh pr create --fill --base main --head feature/<epic-slug>
gh pr merge --squash --delete-branch                       # run from the PRIMARY tree, not the worktree
git -C <primary> worktree remove <worktree-path>; git -C <primary> pull --ff-only origin main
```
Note: `gh pr merge` may fail its local checkout step if run from inside the worktree — the remote merge still lands; finish the worktree-remove + `pull` from the primary tree. If the project's `CLAUDE.md` has no merge-at-boundary rule, do NOT merge — leave the branch open and only offer the PR in the boundary report.

### 6. Boundary report
```
─────────────────────────────────────────────
EPIC {N} COMPLETE & VERIFIED — {X} stories
─────────────────────────────────────────────
Build & Test (whole project): {green | HALTED}
Evals (cumulative): {p}/{t} command pass
Invariants: {v}/{t} verified
Deferred re-homed this epic: {n} (0 orphans)
Architecture learnings promoted: {n} → docs/architecture.md
Test plan: docs/epics/epic-{N}-test-plan.md
  {a} human steps (after subtracting {s} already-automated), {b} physical-device
  • Simulator/local (section A): {a}  ← run these now; each flow lists what NOT to re-test
  • Physical-device (section B): {b}  ← deferred to org-account pass
  • Automation gaps flagged: {g}  ← /e2e-tests candidates
Commits this epic: {count} (granular: create/dev/review per story)
─────────────────────────────────────────────
RECOMMENDED FLOW:
  1. Work through the simulator/local test plan above, recording findings inline
     under each scenario in docs/epics/epic-{N}-test-plan.md.
  2. → Run /harvest-findings {N} to capture those findings, schedule the in-scope
       ones as story {N}.{last+1}, and reset the plan for re-test.
  3. → Then run /retrospective for Epic {N} to capture learnings
       and update conventions BEFORE starting Epic {N+1}. It also condenses
       Epic {N} in docs/epics.md (epic-archive CONDENSE) — story detail
       collapses to a summary row; the full spec stays in each story file.

Next:
  • "test"      — walk me through the simulator test plan now
  • "harvest"   — run /harvest-findings {N} (do this after testing, before retro)
  • "retro"     — run the retrospective for Epic {N} (do this after harvesting)
  • "continue"  — skip retro and start epic-flywheel on Epic {N+1}
  • "stop"      — end here
─────────────────────────────────────────────
```
Append the epic-level roll-up via `scripts/ledger.sh epic-flywheel --story epic-{N} --models … --build-test … --evals …`. Wait for the user — the boundary is always a human gate.

**Retrospective reminder is mandatory.** The Epic Boundary Gate always surfaces the retrospective prompt — never close an epic silently.

**`/retrospective` is human-in-the-loop even here.** It asks retrospective's `## Seven Questions` one at a time and waits for answers; autonomously generating the retro from git history is forbidden. If the user picks `"continue"` (skip retro), confirm once: "Starting Epic {N+1} without a retrospective for Epic {N} — the learnings/conventions from this epic won't be captured. Proceed?" Honor their choice, but make the skip explicit. If the user runs `"test"` first, then returns, re-surface the retrospective reminder before advancing to the next epic.

**Last epic of the phase.** If no later epic remains in `docs/epics.md`, add one more line to the recommended flow after step 3: `4. → Optionally /epic-archive cut-release {version} to archive this phase's epics.md and seed a fresh one for the next phase.` An offer only — never cut a release automatically.

---

## Exit Conditions

- **All epics complete:** every milestone closed / every story done → final "ALL EPICS COMPLETE" message; suggest `/retrospective` and `/status`.
- **HALT (gate failure or dev-story blocker):** loop stops, reports the exact story/step and the failing output, and asks for help. Resume with `/epic-flywheel {N}` (or `/epic-flywheel {epic}.{story}` to re-enter at a story). The granular commits mean the user can `git reset`/revert to the last good step.
- **User "stop":** graceful exit; report stories remaining and the resume command.

## Resuming

`/epic-flywheel` with no arg detects in-flight state from GitHub labels / `Status:` fields (same rules as story-flywheel): `in-progress` → resume at Step 2; `review` → resume at Step 3; otherwise start at the first incomplete story. If all stories are done but no `epic-{N}-test-plan.md` exists, resume directly at the Epic Boundary Gate.

---

## Notes

- **Token posture:** every per-epic gate is zero-token (build/test/evals are shell commands; the invariant and deferred sweeps read short recorded blocks). The only model-heavy step is the once-per-epic test-plan dedup + subtract, bounded over plan text plus grep hits.
- **Why subtract before writing the plan:** DD-31. **Why defer manual testing to the boundary:** DD-30. **Why commit-per-step:** a bisectable, unravel-able history instead of one giant squash.

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.