Run the full story development loop (create → dev → review) repeatedly until the epic is complete. Use when the user says "story flywheel", "run flywheel", or "run the loop".
Installs into .claude/skills of the current project.
Are you the author of Story Flywheel?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/rterakedis-story-flywheel)
---
name: story-flywheel
description: Run the full story development loop (create → dev → review) repeatedly until the epic is complete. Use when the user says "story flywheel", "run flywheel", or "run the loop".
---
# Story Flywheel Skill
**Goal:** Drive the epic to completion without manual story-by-story triggering. Automates create-story → dev-story → code-review in sequence, pausing only for human decisions and commit sign-off between stories.
**Iron rule:** Never start the next story until the user has committed and given explicit go-ahead.
---
## Activation
1. Determine which epic to run. Check in order:
- Explicit argument: `/story-flywheel {epic_num}` or `/story-flywheel {epic_num}-{story_num}`
- Active milestone with `in-progress` or `review` issues (resume in flight)
- Lowest epic-numbered milestone that still has open issues (see epic discovery below)
- Ask: "Which epic should I run the flywheel on?"
**Epic discovery — sort by epic number in title, not by GitHub milestone ID:**
GitHub milestone IDs are assigned in creation order and do not reflect intentional epic sequence. Epics may be reordered or inserted later, so always derive sequence from the number embedded in the milestone title (e.g., `"Epic 3 — ..."` → epic 3).
```bash
# List all open milestones, extract epic number from title, sort by it, pick lowest
gh api repos/{owner}/{repo}/milestones \
--jq '[.[] | select(.open_issues > 0)
| {title:.title, gh_num:.number,
epic_num: (.title | capture("Epic (?P<n>[0-9]+)").n | tonumber)}]
| sort_by(.epic_num) | .[0]'
```
Use the returned `title` and `gh_num` for all subsequent milestone-scoped queries. If the title does not match the `"Epic N"` pattern, fall back to `docs/epics.md` ordering.
2. If a specific starting story was given, start there. Otherwise find the **first incomplete story** in the epic by querying within that milestone only:
```bash
# Use milestone's GitHub number (gh_num) — never sort open issues globally
gh api "repos/{owner}/{repo}/issues?milestone={gh_num}&state=open" \
--jq '[.[] | select(all(.labels[]; .name != "in-progress") and all(.labels[]; .name != "review"))]
| sort_by(.number) | .[0] | {number:.number, title:.title}'
```
Fall back to reading `docs/epics.md` if GitHub unavailable.
3. Announce: "Starting flywheel for Epic {N}: {title}. {X} stories remaining. First up: {epic}.{story} — {story_title}."
4. **Determine delegation mode + model routing.** Set `swift_project = true` if `docs/setup/swift/` exists OR an `.xcodeproj` / `.xcworkspace` / `Package.swift` is present in the repo; otherwise `false`.
Check whether the leanwheel subagents are available (the plugin ships `lw-story-creator`, `lw-story-developer`, `lw-story-reviewer` as agent types).
- **Subagents available (default, preferred):** run in **subagent-delegation mode** — each phase is spawned as its subagent via the Agent tool, with the model selected automatically (see Subagent Delegation & Model Routing). No manual model switching. Announce once:
> Running the flywheel with subagent delegation — each phase runs in its own context with its model chosen automatically (Sonnet for create/review{; Opus for dev-story since this is a Swift project | ; Sonnet for dev-story too, per swift_project}). You'll only be asked to weigh in at clarifications, the per-story checkpoint, and epic boundaries.
- **Subagents unavailable (fallback):** run every phase inline on the current session model — no mid-session model switching (see Fallback below).
---
## Subagent Delegation & Model Routing
Default mode. Each phase is delegated to its subagent via the Agent tool. Two wins on the Pro plan: **model routing is automatic** (no `/model` dance), and **context is isolated** — each phase's heavy doc/code reading happens in a throwaway subagent window, so this orchestrating thread only accumulates each subagent's short structured report, not three phases of file reads.
Per-phase routing (Conserve-Opus baseline, dynamic Swift exception; Opus is also the **cost ceiling** — a Fable session is never inherited by a phase spawn):
| Phase | subagent_type | Python / Web | Swift / SwiftUI | Why |
|---|---|---|---|---|
| 1 — Create Story | `lw-story-creator` | Sonnet (pinned) | Sonnet (pinned) | Story authoring is routine. |
| 2 — Dev Story | `lw-story-developer` | **Sonnet** (pinned) | **Opus** (override) | The hard phase gets the expert, not the specialist: Opus caps per-story cost while still clearing the Build & Test Gate first-try. Empirical — DD-20; the ledger's `bt_iterations` by model (reported by `/retrospective`) is the evidence for changing this. |
| 3 — Code Review | `lw-story-reviewer` | Sonnet (pinned) | Sonnet (pinned) | Adversarial reading in a fresh context, every story (DD-75); the Build & Test Gate is the correctness backstop, not the model. |
**How to set the model:** the subagent defs pin `model: sonnet`. Pass a per-spawn `model` override on the Agent call **only** for Phase 2 when `swift_project = true` (`model: opus`) — never `fable` (usage-tier ceiling: flywheel throughput on Fable burns a week's budget in one epic). If the user opts out for the run ("conserve everything", "stay on Sonnet"), drop the Opus override too and note it. Never ask the user to `/model`-switch mid-run — it busts the prompt cache (DD-20).
**Effort routing (second axis, static):** set only via `effort:` in the agent defs — there is no per-spawn override. Every runner pins it (creator `medium`, developer and reviewer `high`, `lw-docs-sync` `low`) so a session's effort never leaks into a phase. Never step the developer down for budget; that is the model axis's job (DD-73).
**Spawning a phase = one literal Agent tool call.** Everywhere this skill says "spawn", make an actual Agent tool invocation — never a prose description of delegation, and never doing the phase's work inline in this thread:
```
Agent tool call:
subagent_type: "lw-story-creator" | "lw-story-developer" | "lw-story-reviewer" | "lw-docs-sync"
model: "opus" # ONLY Phase 2 when swift_project = true — omit on every other spawn (defs pin Sonnet); never "fable"
prompt: the story identifier/path + any context the subagent needs (it starts cold)
```
**Never hand a subagent a command known to hang or flake** (e.g. a flaky full test run) — give it the targeted/known-good invocation. An agent handed a hanging command spawns a watcher and early-returns, orphaning its work.
Wait for the subagent's report, then act on its structured fields. Do **not** re-do the phase's work in this thread — the subagent owns it. If a phase ends with no Agent call in the transcript (only narration like "the creator subagent will handle this"), the phase did not run — go back and make the call. Inline execution is legal only in the documented fallback when subagents are unavailable.
**A report missing its required fields is a non-return.** Each subagent owes a fixed set of report fields — `STORY FILE`/`EPIC CONTEXT` for the creator, `STATUS`/`BUILD & TEST` for the developer, the `RUBRIC` line and `STATUS` for the reviewer (see each agent def's "Report back"). If a subagent's final message is missing any of these — including a message that only narrates what it's about to do next, e.g. "I'll stop polling now and wait for the test suite to notify me" — the phase has **not** returned, no matter how long it ran. Resume it with SendMessage rather than proceeding: ask it to finish and emit the missing fields. Never advance to the next phase, the checkpoint, or a commit on a narration-only message standing in for a report.
**Wait loops key on a specific PID or artifact, never on "no matching process anywhere."** When a subagent (or this thread) waits on a long-running command — a full test suite, a build — poll the specific PID it started or a completion marker/artifact that command produces. Never a condition like "no process of this kind is running" (e.g. a `ps aux | grep xcodebuild` emptiness check): the orchestrator or a sibling story may run the same tool concurrently, so that condition can stay permanently false and the wait never ends.
---
## Fallback: subagents unavailable
Run every phase inline on the **current session model**. Do not issue model-switch prompts: changing `/model` (or `/effort`) mid-conversation busts the prompt cache, so a per-phase Sonnet→Opus→Sonnet dance costs more than it saves. If the user wants the Swift dev phase on a stronger model than the session is running, tell them once at activation — "start the flywheel in a fresh session on {model} for this epic" — and proceed on the current model if they decline. Inline phases execute `skills/create-story/SKILL.md`, `skills/dev-story/SKILL.md`, and `skills/code-review/SKILL.md` directly; the checkpoint, gates, and report fields are unchanged.
---
## The Loop
Repeat until the epic is complete (see **Exit Conditions**):
### Phase 1 — Create Story
**Subagent mode:** spawn `lw-story-creator` (default model: Sonnet) via the Agent tool. Prompt it with the story identifier (`{epic}.{story}`) so create-story skips identification.
**Fallback mode:** execute `skills/create-story/SKILL.md` inline on the session model.
- Wait for the story file to be written and GitHub issue updated.
- From the subagent report, capture `STORY FILE`, `EPIC CONTEXT`, `COMPLEXITY`, `CLARIFICATIONS NEEDED`, `PREREQUISITES`, `DESIGN GAP`.
- **Epic context check (zero-token):** confirm `docs/epics/epic-{N}-context.md` exists on disk (`EPIC CONTEXT` says whether it was generated or reused this phase). If missing, re-spawn the creator with an explicit prompt to run create-story's **Generate Cache** section before proceeding — the cache is a Phase-1 deliverable; every later story in the epic depends on it for token economy and the `## Prior Story Learnings` channel.
- **Clarification surfacing:** if the report lists material clarifications (the subagent assumed defaults rather than guessing silently), present them to the user now as a human-decision pause before Phase 2 — this is the Clarification Gate surfacing at the orchestration layer. Record the user's answers back into the story file (or confirm the assumed defaults) before proceeding.
- Do not proceed to Phase 2 until the story file path is in hand and clarifications are resolved.
### Phase 2 — Dev Story
**Subagent mode:** spawn `lw-story-developer` via the Agent tool with the story file path. Pass `model: opus` **only if `swift_project`** (otherwise the pinned Sonnet). Instruct it to run the full dev-story workflow including the Build & Test Gate, the evals RUN via `scripts/evals.sh` (if `docs/evals/` exists), and invariant/design verification. It stops at `review`; it does not review its own diff.
**Fallback mode:** execute `skills/dev-story/SKILL.md` inline on the session model.
- From the report capture `STATUS`, `BUILD & TEST`, `BUILD/TEST ITERATIONS`, `EVALS`, `INVARIANTS`, `INFRA TOUCHED`, `UNRESOLVED`, `REVIEW HANDOFF`.
- **Operational doc sync (cheap, orchestrator-owned):** the developer does **not** run docs-sync (it would land on the dev model). If the report's `INFRA TOUCHED` is `yes`, spawn **`lw-docs-sync`** (Haiku) via the Agent tool with the story file path and op `OPERATIONAL`; capture its `DOCS UPDATED` return for the checkpoint/ledger. Skip the spawn entirely when `INFRA TOUCHED: no` (zero cost). Fallback if subagents are unavailable: execute the docs-sync OPERATIONAL op inline.
- Do not proceed until `STATUS` is `review`/`done` (or HALT).
**On HALT:** Stop the flywheel. Report: "Flywheel paused — dev-story halted on {epic}.{story}: {reason}. Resolve the blocker and resume with `/story-flywheel {epic}.{story}`."
### Phase 3 — Code Review
**Always spawn `lw-story-reviewer`** — every story, clean or not. The author never reviews its own diff (DD-75).
- Its prompt is the developer's `REVIEW HANDOFF`, verbatim — pointers only. Never add a summary of the dev pass or hints about what to check.
- It runs every code-review pass, emits the **SCORE rubric line**, auto-patches `patch` findings, applies `fix-now` findings within the ceiling, logs `defer` via the deferred skill, and **re-verifies green**.
- `decision-needed` findings surface in its report — present them to the user, then send the answers back to the same reviewer to apply.
- **Fallback mode** (no subagents): execute `skills/code-review/SKILL.md` inline on the session model. That review shares the author's context, so record `review=inline-fallback` in the roll-up and say so at the checkpoint.
**On unresolvable patches:** Do not proceed to Phase 4. Leave story status `in-progress`, report which items need attention, and stop the flywheel. Resume with `/story-flywheel {epic}.{story}`.
### Phase 4 — Human Checkpoint
After code review completes, surface a consolidated checkpoint before any commit:
```
─────────────────────────────────────────────
STORY {epic}.{story} CHECKPOINT
─────────────────────────────────────────────
Status: code review complete
VERIFICATION
{Build & Test: green | manual-required | red} · {evals: P/T or n/a} · {rubric gate: PASS/FAIL or n/a} · {invariants: V/T or n/a} · build/test iterations: {n}
DECISIONS MADE THIS STORY
{list each [Decision] finding and the answer recorded — "none" if clean}
PATCHES APPLIED
{list each [Patch] auto-applied — "none" if clean}
FIX-NOW APPLIED
{list each [Fix-Now] applied outside the ACs, with file:line — "none" if clean}
DEFERRED ITEMS
{list each [Defer] item with its D-ID and scheduled story — "none" if clean}
TESTING PLAN
AUTOMATED: {flow/suite/eval names that now pin this story's behavior — names only, or "none"}
MANUAL: {only what no test can exercise — one line per item, `{step} → {expected} [visual-judgment | device-only | sandbox-only | setup-unreachable]`, with the exact setup command (all flags) when one is needed. "none — fully automated" or "none — no user-visible surface changed." Both sub-fields are mandatory — see dev-story → Testing Plan (required report field).}
UNRESOLVED ITEMS (action required before committing)
{list any [ ] findings that could not be auto-patched — empty means none}
─────────────────────────────────────────────
Review the changes above, then:
• Commit when satisfied
• Type "continue" to start the next story
• Type "stop" to end the flywheel here
• Type "retry" to re-run code review on this story
─────────────────────────────────────────────
```
**Observability:** before presenting the checkpoint, append the story-level roll-up via `scripts/ledger.sh` (never hand-write the JSON; the script no-ops if `docs/metrics/` is absent and never reads the ledger into context):
`bash scripts/ledger.sh story-flywheel --story {id} --models create={m},dev={m},review={m|inline-fallback},docs-sync={m|skipped} --build-test green|red --evals P/T [--rubric-gate PASS|FAIL] [--deferred n] [--unresolved n] [--tests "1139/85 suites"]`
The per-phase `dev-story` / `code-review` lines are written by those skills/subagents; this roll-up is written every story, in both checkpoint and auto-pilot flows — a story without one is a loop bug.
**Wait for user response.** Do not proceed until user explicitly types one of the above commands or equivalent.
- **"continue"** (or equivalent like "next", "go", "lgtm"): advance to next story
- **"stop"** (or equivalent like "done", "pause", "exit"): exit flywheel gracefully (see Exit)
- **"retry"**: re-run `skills/code-review/SKILL.md` on the current story, then show checkpoint again
If unresolved items exist, note them prominently in the checkpoint and wait — do not auto-advance even if user says "continue" without acknowledging them. Ask: "There are {N} unresolved items above. Confirm you want to continue anyway, or address them first?"
### Phase 5 — Advance to Next Story
After "continue":
1. Query for the next incomplete story in the epic:
```bash
gh issue list --milestone "{milestone_title}" --state open \
--json number,title,labels --jq 'sort_by(.number) | .[0]'
```
Fall back to scanning `docs/epics.md` for the next story with `Status: not started` or no file.
2. If a next story exists in the **same epic**: announce "Moving to {epic}.{next_story}: {title}." and return to **Phase 1**.
3. If no stories remain in the current epic: proceed to **Phase 5b — Epic Boundary Gate**.
### Phase 5b — Epic Boundary Gate
All stories in the current epic are done. Before starting a new epic, present:
```
─────────────────────────────────────────────
EPIC {N} COMPLETE — {X} stories done
─────────────────────────────────────────────
All stories in Epic {N} are implemented and reviewed.
What would you like to do next?
• "retro" — run a retrospective for Epic {N} before continuing
• "continue" — skip retrospective and start Epic {N+1}
• "stop" — end the flywheel here
─────────────────────────────────────────────
```
**Wait for user response.**
- **"retro"**: Execute `skills/retrospective/SKILL.md` for the completed epic. After it finishes, prompt again:
```
Retrospective complete. Ready to start Epic {N+1}: {title}.
• "continue" — start the flywheel on Epic {N+1}
• "stop" — end the flywheel here
```
Wait for response, then act accordingly.
- **"continue"**: Identify the first incomplete story in the next epic and return to **Phase 1** with that story.
- **"stop"**: Exit gracefully (see **User-Stopped**).
---
## Exit Conditions
### Epic Complete
All epics and stories are done (no remaining open issues or stories in `docs/epics.md`):
```
─────────────────────────────────────────────
ALL EPICS COMPLETE
─────────────────────────────────────────────
Every story across all epics is implemented and reviewed.
Next steps:
• Run /retrospective if you haven't already
• Check /status for overall project state
─────────────────────────────────────────────
```
### User-Stopped
When user types "stop":
```
Flywheel stopped after {epic}.{story}. {X} stories remain in Epic {N}.
Resume anytime with: /story-flywheel {epic}.{next_story}
```
### HALT (dev-story blocker)
Reported inline as described in Phase 2. Flywheel does not auto-resume.
---
## Resuming Mid-Epic
`/story-flywheel` with no argument will detect the in-flight story:
- If a story has `Status: in-progress` or GitHub label `in-progress`: resume at Phase 2 (dev-story).
- If a story has `Status: review` or GitHub label `review`: resume at Phase 3 (code-review), then proceed to Phase 4 (checkpoint).
- If no in-flight story: start from the first incomplete story.
---
## Notes
- **Toward hands-off runs.** Subagent delegation already collapses the human touch-points to three: the Phase-1 clarification surfacing, the Phase-4 checkpoint, and the epic-boundary gate. To go further, the user may pre-authorize **auto-pilot**: at the start they say "auto-continue on clean stories" — then for any story whose checkpoint has *no* `UNRESOLVED` items, a PASS rubric gate, and a green Build & Test Gate, the flywheel commits (if the user also pre-authorized commits) or pauses only to let the user commit, then advances without waiting for "continue". Any story with unresolved items, a FAIL gate, a red build, or a HALT **always** stops for the human regardless of auto-pilot. This is how the user gets "the app builds while I just steer at checkpoints" without losing the safety gates. Default remains: pause every checkpoint.
- The flywheel does not commit unless the user pre-authorizes it. You own the commit gate between stories.
- GitHub tracking is handled by the sub-skills (create-story, dev-story, code-review) — flywheel does not call tracking ops directly.
- If GitHub is unavailable, flywheel falls back to `docs/epics.md` Status fields for all story discovery.
- Deferred items are logged and scheduled by `skills/deferred/SKILL.md` (called via code-review). SCHEDULE tries SLOT-INTO-BACKLOG first — injecting the item as an AC on the best matching not-started backlog story — and only creates a new remediation story if no suitable slot exists. The flywheel surfaces deferred items in the Phase 4 checkpoint with their D-ID and the story they were slotted into or created as.