Implement a GitHub Story and all its Task sub-issues using a spec-driven loop: the AC become failing tests written by test-writer, then ponytail code makes them pass: resolve → (per task: explore → propose → failing tests [test-writer] → apply [with ponytail] → verify [+ mutation check] → commit → archive) → PR. Use when the user provides a Story issue number, a Story ID like 'BE-AUTH-1' from a backlog generated by prd-to-github-backlog, a single Task ID like 'BE-AUTH-1-T1', or says 'implemen...
Installs into .claude/skills of the current project.
Are you the author of Spec Apply?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/orrrrli-spec-apply)
---
name: spec-apply
stacks: [all]
description: "Implement a GitHub Story and all its Task sub-issues using a spec-driven loop: the AC become failing tests written by test-writer, then ponytail code makes them pass: resolve → (per task: explore → propose → failing tests [test-writer] → apply [with ponytail] → verify [+ mutation check] → commit → archive) → PR. Use when the user provides a Story issue number, a Story ID like 'BE-AUTH-1' from a backlog generated by prd-to-github-backlog, a single Task ID like 'BE-AUTH-1-T1', or says 'implement story #N', 'work on BE-AUTH-1', 'list my stories', or 'spec-apply N'."
trigger: /spec-apply
---
# /spec-apply [issue#] (with Ponytail Principles)
Implement a **Story** by driving **all of its Task sub-issues** through the phases below, one at a time,
**committing after each task passes verification**. Never skip a phase. If given a single Task ID,
implement just that one task through the same phases. **Applies ponytail principles during the Apply phase** to favor the simplest, most minimal solution that works.
## Execution model
```
Phase 0 Resolve → Story# + ordered list of its open Task sub-issues; set up the Story worktree;
AC missing/untestable → business-analyst drafts them ⏸ STOP for approval
FOR each Task (in dependency order):
Phase 1 Explore (no stop gate — report and continue)
Phase 2 Propose ⏸ STOP: wait for OK before writing code
Phase 2.5 Tests → test-writer turns the AC into failing tests; they become read-only
Phase 3 Apply → Apply with ponytail principles (laziest solution that works)
Phase 4 Verify → spec-verify (code-reviewer) [+ security-auditor]; on failure STOP the Story
Phase 4.5 Commit (one conventional commit per task)
Phase 5 Archive (comment + close the Task issue)
Phase 6 Finish → push the Story branch and open one PR for the Story
```
## Prerequisites
```bash
gh auth status # must succeed with repo + project scope
```
---
## Naming convention (how the backlog encodes the hierarchy)
`prd-to-github-backlog` creates every issue with the title `"<ID>: <title>"`, and the
**ID itself encodes the parent chain** — so you never have to guess relationships:
| Level | ID convention | Example title | Labels |
|-------|--------------------|----------------------------|------------------------------|
| Epic | `EPIC-<SLUG>` | `EPIC-AUTH: Authentication`| `type:epic` |
| Story | `<AREA>-<EPIC>-<n>`| `BE-AUTH-1: Login endpoint`| `type:story`, `area:<area>` |
| Task | `<story-id>-T<k>` | `BE-AUTH-1-T1: Add route` | `type:task`, `area:<area>` |
Derivation rules:
- **Task → Story:** strip the trailing `-T<k>`. `BE-AUTH-1-T1` → story `BE-AUTH-1`.
- **Story → Epic:** the story's `<EPIC>` segment maps to epic `EPIC-<EPIC>`. `BE-AUTH-1` → `EPIC-AUTH`.
- Tasks and stories also have **real GitHub sub-issue parent links** — use those as the source of truth.
---
## Phase 0 — Resolve the Story and its tasks
**Goal:** Turn whatever the user gave you (raw `#N`, a Story ID like `BE-AUTH-1`, a Task ID like
`BE-AUTH-1-T1`, or "list my stories") into a concrete **Story issue number**, its **Epic number**,
and the **ordered list of its open Task sub-issues**. Then set up the Story worktree.
Resolve via the **manifest as a cache, verified against the live board** (the chosen strategy):
1. **Read config from the manifest** if present (`manifest.json` at repo root or alongside the
backlog skill output). Pull `owner`, `repo`, `project_number`, and the `epics[].stories[].tasks[]`
tree. The manifest IDs let you map a Story ID → Epic ID and enumerate its Tasks offline.
- If there is **no manifest**, fall back to pure `gh` label/title queries below.
2. **List stories** (use when the user says "list my stories" or gives ambiguous input):
```bash
gh issue list --repo <owner/repo> --label type:story --state open --limit 100
```
3. **Resolve the input → a Story issue number** (exact title prefix match):
```bash
# If given a Story ID (BE-AUTH-1):
gh issue list --repo <owner/repo> --label type:story --state all \
--search "in:title <STORY_ID>" --json number,title \
--jq '.[] | select(.title | startswith("<STORY_ID>:")) | .number'
# If given a Task ID (BE-AUTH-1-T1): strip the trailing -T<k> to get the Story ID, then as above.
# If given a raw #N: check its type:* label. If type:task, walk up to its parent story.
```
4. **Enumerate the Story's Task sub-issues** (source of truth = live sub-issue links):
```bash
gh api repos/<owner/repo>/issues/<story#>/sub_issues \
--jq '.[] | {number, title, state}'
```
Keep only **open** tasks. **Order them** by: (a) `depends_on` in the manifest/task body, else
(b) ascending `-T<k>` suffix. Skip tasks already closed and note them.
5. **Verify against the live board** — confirm the story's parent epic matches the manifest:
```bash
gh api repos/<owner/repo>/issues/<story#>/parent --jq '.number' # epic number
```
If the live links disagree with the manifest, **trust the live board** and note the drift.
6. **Resolve the base branch** — do NOT hardcode it. Take it from the project config in this order:
manifest `base_branch` → repo default (`gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'`)
→ `main`. Call it `<BASE>` and reuse it in Phase 6.
7. **Set up the Story worktree** — one worktree and one branch per Story, so parallel sessions
never share a working tree. Every task commits onto `feat/<STORY_ID>`. Never `git checkout` in
the current tree and never `cd`.
```bash
git fetch origin <BASE>
main=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
git branch --show-current
git worktree list --porcelain
```
- Current branch is `feat/<STORY_ID>` → you are in the Story worktree. `git pull --ff-only` if it
has an upstream, then continue.
- `feat/<STORY_ID>` is checked out in another worktree → **STOP**: "Open a session in `<path>` and
run `/spec-apply <STORY_ID>` there."
- Otherwise create it (the post-checkout hook links the local Claude harness into it):
```bash
git worktree add -b feat/<STORY_ID> "$main-<STORY_ID>" origin/<BASE> # new branch
git worktree add "$main-<STORY_ID>" feat/<STORY_ID> # branch already exists
```
Then **STOP**: "Worktree ready at `<path>`. Open a session there and run `/spec-apply <STORY_ID>`."
8. **Check the Story's acceptance criteria** — once per Story, since every task and `spec-verify`
are judged against them:
```bash
gh issue view <story#> --repo <owner/repo> --json body --jq '.body'
```
Testable means Given/When/Then with an observable outcome. If they are present and testable,
continue. If they are missing or untestable (e.g. "works well", "good UX"):
- Dispatch `business-analyst`. It cannot see this conversation or run `gh`, so the prompt
carries the Story body and the Epic body verbatim, plus the paths to `CLAUDE.md` and
`business/rules.md`. Ask for Given/When/Then criteria returned in the report, no files written.
- Show them to the user. **STOP** and wait for approval or edits.
- Once approved, append them to the Story under `## Acceptance criteria` so `spec-verify` reads
the same ones:
```bash
gh issue view <story#> --repo <owner/repo> --json body --jq '.body' > /tmp/story-<story#>.md
# append the approved criteria to that file, then:
gh issue edit <story#> --repo <owner/repo> --body-file /tmp/story-<story#>.md
```
**Single-task input:** if the user explicitly named one Task ID/number and asked for just that,
set the task list to that one task (still in the Story worktree).
**Exit gate:** State the resolved `Story #<n> (<STORY_ID>)`, `Epic #<n> (<EPIC_ID>)`, the base branch
`<BASE>`, the working branch `feat/<STORY_ID>` and its worktree path, and the **ordered task list** (`Task #<n> (<TASK_ID>)`
each). If the input matched
zero or many stories, list the candidates and ask which one. Then begin the per-task loop at Phase 1
with the first task.
---
> The phases below run **once per task** in the ordered list from Phase 0. `<task#>` is the current
> task; `<story#>` / `<epic#>` stay fixed for the whole run.
## Phase 1 — Explore
**Goal:** Understand the current task before touching a single file. Use the `Task #`, `Story #`, and
`Epic #` resolved in Phase 0.
1. Fetch the Task issue:
```bash
gh issue view <task#> --repo <owner/repo>
```
2. Fetch the parent Story (the sub-issue parent resolved in Phase 0):
```bash
gh issue view <story#> --repo <owner/repo>
```
Read the **acceptance criteria** (Given/When/Then). These are the exit criteria for Phase 4.
3. Fetch the grandparent Epic for broader context:
```bash
gh issue view <epic#> --repo <owner/repo>
```
4. **Read the project context index** — find it using this fallback order:
```
CLAUDE.md (standard fill-context structure — root of the repo)
→ .agents/CONTEXT.md (alt structure)
→ AGENTS.md (alt structure)
```
Once you find the index, read it to locate these files (most will be linked in the index):
- `business/overview.md`, `business/rules.md` — domain constraints, success metrics
- `domains/[entity].md` — detailed model for each entity this task touches
- `architecture/overview.md`, `architecture/integrations.md` — patterns, stack, external services
- `engineering/standards.md`, `engineering/testing.md` — coding conventions, test approach
**Always read the context files; do not skip because the task "looks simple."**
5. Locate the relevant code — find the files, components, and layers that this task touches. Report exact file paths and line numbers.
**Report (no stop gate):** State in plain language, then continue straight to Phase 2:
- What this task requires
- Which files will change and why
- Any risks or ambiguities
---
## Phase 2 — Propose
**Goal:** Define the approach before writing code.
Produce a short implementation plan:
- What interfaces / signatures change (if any)
- Which layer(s) are touched (domain / application / infrastructure / presentation)
- Whether new files are needed
- Whether any dependency is required — if so, name it explicitly and wait for approval
- Any architectural decision that deviates from existing patterns — flag it and ask
- Responsibilities: list each distinct one (business rule, persistence, security, external
call, presentation) and where it lives: an existing component, a new one with its reason,
or a framework capability (e.g. ASP.NET Identity, NextAuth). Security never lives in an
ad-hoc check when the framework provides it.
- Behaviors to test: one line per AC this task covers. A task with no testable behavior
(config, scaffolding) says so here and skips Phase 2.5.
**Exit gate:** Present the plan. STOP and wait for explicit user confirmation before writing any code.
---
## Phase 2.5 — Tests
**Goal:** Turn the AC into failing tests before any implementation exists, written by an
agent that does not implement.
1. Dispatch `test-writer`. Its prompt stands alone: the AC verbatim, the approved
interface and behavior lines from Phase 2, the paths to `CLAUDE.md` and
`engineering/testing.md`, and the test command. Ask it to:
- Cover each behavior line, plus the negative and boundary cases the AC imply.
- Derive every expected value from the AC, never from existing code.
- If the interface does not exist yet, add only a signature stub from Phase 2 that
throws not-implemented. No logic.
- Run the tests and report each one RED with its failure reason.
2. Check the reasons: a test red for the wrong reason (typo, broken fixture) goes back
to `test-writer` once.
3. Stage the test files (`git add <test files>`). From here they are read-only for Phase 3.
---
## Phase 3 — Apply (with Ponytail Principles)
**Goal:** Implement exactly what was proposed. Nothing more. **Apply ponytail principles: favor the laziest solution that actually works.**
Rules:
- Only modify files identified in Phase 2
- **Make the Phase 2.5 tests pass. Never edit, skip, delete or weaken them**, and never
touch test or CI config to get green. If a test looks wrong, STOP and report it: a wrong
test usually means an ambiguous AC, and that is the user's call.
- **Apply ponytail principles during implementation:**
1. **Does this need to exist at all?** Speculative need = skip it, say so in one line. (YAGNI)
2. **Already in this codebase?** A helper, util, type, or pattern that already lives here → reuse it. Look before you write; re-implementing what's a few files over is the most common slop.
3. **Stdlib does it?** Use it.
4. **Native platform feature covers it?** `<input type="date">` over a picker lib, CSS over JS, DB constraint over app code.
5. **Already-installed dependency solves it?** Use it. Never add a new one for what a few lines can do.
6. **Can it be one line?** One line.
7. **Only then:** the minimum code that works.
- **Follow the coding conventions and patterns** from:
- `engineering/standards.md` (naming, structure, test strategy)
- `business/rules.md` (domain constraints that code must enforce)
- `domains/[entity].md` (entity-specific rules and relationships)
- No new dependencies without asking
- No refactoring outside the task scope — note suggestions after completing
- If you discover something unexpected mid-implementation, STOP and report it
- **Mark deliberate simplifications with a `ponytail:` comment** (e.g., `# ponytail: using existing util instead of new class`)
- **Bug fix = root cause, not symptom.** Before you edit, grep every caller of the function you're about to touch. The lazy fix IS the root-cause fix: one guard in the shared function is a smaller diff than a guard in every caller.
After each file change, state: file path + what changed + why.
**Output pattern:** `[code] → skipped: [X], add when [Y].`
---
## Phase 4 — Verify
**Goal:** Prove the task is done against the Story's acceptance criteria, with a reviewer that did
not write the code.
1. **Self-check first** (cheap, before spending a review):
- [ ] Existing tests still pass (`npm run test` / `dotnet test` / equivalent)
- [ ] Phase 2.5 tests are green and untouched (`git diff -- <test files>` is empty)
- [ ] No files outside Phase 2 + 2.5 scope were modified
2. **Independent review**, uncommitted, in ONE message so they run concurrently:
- Invoke the `spec-verify` skill with `<TASK_ID>`. It runs in a fresh `code-reviewer` subagent,
reads the Story's AC from GitHub, reviews the uncommitted diff, runs the tests, and returns
PASS/FAIL per criterion.
- **Also** dispatch `security-auditor` when the diff touches auth, sessions/tokens, permissions,
payments, webhooks, or untrusted input. Its prompt must stand alone: the Story's AC verbatim,
the diff command (`git diff HEAD`), and the paths to `CLAUDE.md` and `engineering/standards.md`.
3. **Act on the reports:** reviewers never edit. Fix blocking findings yourself; list minor ones in
the Phase 5 comment instead of fixing them.
**Failure policy:** If `spec-verify` returns FAIL or the security-auditor reports a critical/high
issue, make **one** attempt to fix it in Phase 3 and verify again. If it still fails, **STOP the
entire Story** here — do NOT commit the failing task, leave
already-committed tasks in place, and report: which task failed, why, and what's left. Do not move on
to the next task.
---
## Phase 4.5 — Commit
**Goal:** One commit per task, only after Verify passes.
```bash
git add <files from Phase 2 + 2.5 scope>
git commit -m "<type>(<TASK_ID>): <Imperative summary>"
# e.g. feat(BE-FOUND-1-T1): Scaffold Go module layout
```
Rules:
- **Conventional Commits format** (`feat`, `fix`, `refactor`, `test`, …) — projects set up by
`init-project.sh` reject any other subject through the git `commit-msg` hook that
`core.hooksPath` points to in clawd-harness (`global/githooks/`).
Pick the type that matches the task.
- **Subject line only — no body, no trailers.** The scope is the Task ID. The task is closed in
Phase 5 with `gh issue close`, and the Story PR body lists `Closes #<task#>`, so the commit needs
no `Closes` line.
- **Do NOT add any Claude/AI co-author trailer** (no `Co-Authored-By: Claude`). Author is the user only.
- Commit **only** the files this task changed (they should match Phase 2 + 2.5 scope).
---
## Phase 5 — Archive (per task)
**Goal:** Close the loop on GitHub for this task, then advance the loop.
1. Comment on the Task issue with a brief summary of what was implemented:
```bash
gh issue comment <task#> --repo <owner/repo> --body "Implemented: <one-line summary>. Commit: <sha>. Files changed: <list>. Minor review notes: <list or none>."
```
2. Close the task issue:
```bash
gh issue close <task#> --repo <owner/repo>
```
3. **Advance:** if more tasks remain in the Phase 0 list, return to Phase 1 for the next task.
Otherwise continue to Phase 6.
---
## Phase 6 — Finish the Story
**Goal:** Ship all completed tasks as one Story PR.
1. Push the Story branch:
```bash
git push -u origin feat/<STORY_ID>
```
2. Open **one PR named after the Story** — title = the Story's full issue title
(`<STORY_ID>: <story title>`):
```bash
gh pr create --repo <owner/repo> --base <BASE> --head feat/<STORY_ID> \
--title "<STORY_ID>: <story title>" \
--body-file <scratchpad>/pr.md
```
Write `pr.md` per `~/.claude/workflows.md` "Pull Requests". `## What` names Story #<story#>
(<STORY_ID>) and ends with one `Closes #<task#>` line per task. A skipped or failed task goes
under `## Not changed`.
Do NOT add a Claude/AI co-author or generated-by trailer to the PR.
3. Report done: the PR URL, the list of tasks committed/closed, and anything skipped or left for a
follow-up. Close with: "After merge, remove the worktree: delete its harness symlinks first,
then `git worktree remove <path>`."
---
## Hard rules
- **One Story per invocation.** The unit of work is a Story and all its open Tasks. If the user names
multiple stories, ask which one to start with.
- **Never implement an Epic directly.** If given an Epic number/ID, list its Story sub-issues and ask
which Story to run.
- **Always read the project context** (Phase 1, step 4). Do not skip because the task "looks simple."
This ensures code conforms to domain rules, business constraints, and engineering standards.
- **Work task-by-task inside the Story** — full Explore→Verify→Commit→Archive per task before moving
on. Never batch multiple tasks into one commit.
- **The Propose gate is per task:** stop and wait for approval before writing code for each task.
- **Tests are written before the code, by `test-writer`** (Phase 2.5), and Phase 3 never
edits them.
- **Stop on first verify failure:** one fix attempt, then halt the whole Story and report. Committed
tasks stay; the rest wait.
- If the Story `depends_on` another issue (manifest / Story body), check that dependency is closed
before starting Phase 3 of the first task.
- **No AI co-authorship** on commits or the PR (no `Co-Authored-By: Claude`, no generated-by trailer).
- **Ponytail principles must be applied during Phase 3 (Apply)** — favor simplicity, reuse, and minimalism.