Use when creating or updating a feature plan document. Triggered by 'make a plan', 'plan this', 'flesh out the idea', or any request to scope and plan work before implementation.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add tomcounsell/ai --skill do-plan --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Do Plan?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/tomcounsell-do-plan)More formats (shields.io, HTML) on the badges page.
---
name: do-plan
description: "Use when creating or updating a feature plan document. Triggered by 'make a plan', 'plan this', 'flesh out the idea', or any request to scope and plan work before implementation."
allowed-tools: Read, Write, Edit, Glob, Bash, AskUserQuestion, ToolSearch, WebSearch, Agent
---
# Make a Plan (Shape Up Methodology)
## Repo Context Probe
If `docs/sdlc/do-plan.md` exists, read it and honor its declarations; otherwise use the generic defaults described below.
The context file is where a repo layers its planning automation onto this generic baseline: stage/status markers, a recon-validation gate, blast-radius and memory tools, the plans/infra directory conventions, required plan sections and frontmatter fields, the commit-on-main rule, and a plan-revising lock. When the file is absent (the common case in a foreign repo), this skill runs entirely on `git`, `gh`, the Read/Write tools, and optionally WebSearch — it writes a structured plan document and links a tracking issue, with no repo-specific tooling required.
Creates structured feature plans (by default under `docs/plans/`) following Shape Up principles: narrow the problem, set appetite, rough out the solution, identify rabbit holes, and define boundaries.
## What this skill does
1. Takes a vague or specific request and narrows it into a concrete plan
2. Writes a structured plan document in `docs/plans/{slug}.md`
3. Creates or links a GitHub issue for tracking
4. Sends the plan for review with open questions
## When to load sub-files
| Sub-file | Load when... |
|----------|-------------|
| `PLAN_TEMPLATE.md` | Writing the plan document (copy the template into `docs/plans/{slug}.md`) |
| `SCOPING.md` | The request is vague, a grab-bag, or needs narrowing before planning |
| `EXAMPLES.md` | Deciding how to respond to a user request (vague vs. grab-bag vs. good) |
## Cross-Repo Resolution
By default `gh` targets the repository of the current working directory. If the context file declares a cross-repo targeting mechanism (e.g. a `GH_REPO` env var), honor it so `gh` commands hit the intended repository.
## Quick Start Workflow
### Phase 0: Validate Recon (ISSUE → PLAN gate)
Before planning, verify the source issue has reconnaissance evidence — a
`## Recon Summary` (or equivalent) section grounding the issue's claims in the
actual code. If the context file declares a recon-validation gate, run it and do
not proceed until it passes. Otherwise, read the issue and confirm by hand that
its claims are evidence-backed. Plans built on unverified assumptions produce
rework.
### Phase 0.5: Freshness Check (has the world moved since the issue was filed?)
The recon validator in Phase 0 confirms evidence *exists* in the issue. This step confirms the evidence is *still true*. Issues can sit for hours, days, or weeks before being planned. In that window, code refactors, other PRs land, sibling systems change, and referenced file:line pointers drift. A plan built on stale premises will produce rework even if the recon was rigorous when written.
**Skip if:** the issue was filed within the last hour AND no commits have landed on main since then. Otherwise this step is mandatory.
For each **file:line reference** cited in the issue body (including the Recon Summary):
1. `Read` the exact file:line and confirm the code at that location still matches what the issue describes. Line numbers drift under refactors; the symbol may have been renamed, moved, or deleted entirely.
2. If the reference has drifted to a new location but the claim still holds, note the corrected file:line in the plan's Technical Approach section.
3. If the reference is gone and the underlying code was removed, flag it — the problem may already be fixed.
For each **cited sibling issue or PR** (e.g., "see #867", "blocked by #743", "related to #825"):
```bash
gh issue view <N> --json state,closedAt,title
gh pr view <N> --json state,mergedAt,title 2>/dev/null
```
1. If a blocker/related issue has closed or merged since filing, read its resolution — the landscape the current issue assumed may have shifted.
2. If a cited PR merged, `git log` the merge and check whether it changed files the current issue references.
For the **files most relevant to the issue** (the ones cited in the issue body or surfaced by Phase 1's blast-radius analysis):
```bash
ISSUE_CREATED=$(gh issue view <N> --json createdAt -q .createdAt)
git log --oneline --since="$ISSUE_CREATED" -- <file1> <file2> ...
```
1. If any commits touched those files since the issue was filed, read the diffs. Decide whether each change (a) is irrelevant, (b) partially addresses the problem, (c) changed the root cause, or (d) already fixes the problem.
2. Check `docs/plans/*.md` for active plans touching the same area (`ls -lt docs/plans/` then inspect the most recent few). Overlap with an active plan is a coordination signal, not necessarily a blocker — but it must be surfaced.
**For bug issues specifically**, reproduce the bug against current main — or, if reproduction is infeasible (e.g., the bug requires a production-only precondition), read the code path and confirm the defect is still present. If the bug is now unreproducible or the cited symptoms no longer occur, stop and ask the user whether to close the issue rather than plan a fix for a non-bug.
**Produce a `## Freshness Check` section** in the plan document capturing what was re-verified, with one of four dispositions:
| Disposition | Meaning | What to do |
|---|---|---|
| **Unchanged** | Nothing relevant has moved. Issue claims still hold. | Proceed to Phase 1. Note the commit SHA used as the baseline in the Freshness Check section. |
| **Minor drift** | Line numbers moved but claims still hold. A cited PR merged but didn't change the root cause. | Update file:line references in the plan. Proceed to Phase 1. Note drift details in the section. |
| **Major drift** | Root cause has changed, an adjacent system now handles the concern, or a prior PR already fixes it. | **Stop.** Report findings to the user and ask whether to close the issue, revise its scope, or proceed on a revised premise. Do NOT silently build a plan for a stale problem. |
| **Overlap** | An active plan in `docs/plans/` is already addressing the same area. | Surface the overlap. Ask whether to merge into the existing plan or coordinate. |
This section stays in the plan document as durable evidence of when and how the freshness check was performed — reviewers during critique and build can tell at a glance whether the plan's premises were verified at plan time.
### Phase 0.7: External Research (WebSearch)
Gather relevant external context before planning. This surfaces current documentation, ecosystem patterns, and known pitfalls that training data may not cover.
**Skip if:** The work is purely internal (no external libraries, APIs, or ecosystem patterns involved) — e.g., refactoring internal code, fixing a typo, or reorganizing files.
1. **Load the WebSearch tool** — WebSearch is a deferred tool; its schema must be loaded before use:
```
ToolSearch("select:WebSearch")
```
2. **Run 1-3 searches** targeting: (a) library/API documentation relevant to the approach, (b) ecosystem best practices, (c) known pitfalls, breaking changes, or migration guides. Keep only findings directly relevant to the technical approach.
3. **Save valuable findings for future reuse** — if the context file declares a memory/notes store, save each finding there (fire-and-forget — if it fails, continue without error). Otherwise the plan's `## Research` section is the only capture point.
4. **Write the `## Research` section** in the plan document: the queries used, key findings with source URLs, and how each finding informs the technical approach. If nothing useful was found, write: "No relevant external findings — proceeding with codebase context and training data."
### Phase 1: Flesh Out at High Level
1. **Understand the request** — gather evidence before scoping. Do NOT rely on inference; execute this checklist:
1. Read the full issue body (not just the title). Extract the Problem, Desired Outcome, and any Acceptance Criteria checklist items verbatim.
2. Read the `## Recon Summary` section of the issue if present. Extract the "Confirmed," "Revised," "Pre-requisites," and "Dropped" buckets — these are direct inputs to the plan's Solution and No-Gos sections.
3. Follow every cited sibling issue or PR referenced in the issue body. For each, summarize its relevance to the current work in one sentence. Record these under Prior Art.
2. **Narrow the problem** - Challenge vague requests (see `SCOPING.md` if needed)
3. **Blast radius analysis** - If the change involves code modifications, map the affected files. If the context file declares a blast-radius / code-impact tool, run it and route its results to plan sections (modify → **Solution**, dependency → **Risks**, test → **Success Criteria**, config → **Solution**, docs → **Documentation**, tangential coupling → **Rabbit Holes**). Otherwise use `git grep` and `Grep`/`Glob` over the symbols and paths the issue names to estimate the blast radius by hand. Skip if the change is purely documentation or process-related.
4. **Prior art search** - Search closed issues and merged PRs for related work. This prevents
proposing solutions that have already been tried (and failed) or re-solving problems that
already have working implementations.
```bash
# Search closed issues for related keywords
gh issue list --state closed --search "KEYWORDS_HERE" --limit 10 --json number,title,closedAt,url
# Search merged PRs for related work
gh pr list --state merged --search "KEYWORDS_HERE" --limit 10 --json number,title,mergedAt,url
```
Use results to fill the **Prior Art** section in the plan. If multiple prior attempts
addressed the same problem, also fill the **Why Previous Fixes Failed** section.
**Skip if:** Small appetite AND greenfield work (no existing code being modified).
4.5. **Expected-failure test search** - For bug fixes, search the test suite for expected-failure
markers related to the bug (pytest `xfail` in Python; the equivalent known-failure/skip
mechanism in the repo's test framework). These represent tests that document the bug but are
marked as expected failures. Python example:
```bash
# Search for xfail markers in tests (both decorator and runtime forms)
grep -rn 'pytest.mark.xfail\|pytest.xfail(' tests/ --include="*.py" | head -20
```
For each xfail found that relates to the bug being fixed:
- Add a task to the plan's **Step by Step Tasks**: "Convert TC{N} xfail to hard assertion"
- Document the test location in the **Success Criteria** section
- When the fix lands, the test should pass and the xfail marker must be removed
**IMPORTANT:** Pay special attention to **runtime `pytest.xfail()` calls** inside test bodies.
Unlike `@pytest.mark.xfail` decorators, runtime xfails short-circuit the test before reaching
assertions — so they silently pass even after the bug is fixed. These are invisible to pytest's
XPASS detection and MUST be explicitly listed as conversion targets in the plan.
**Skip if:** Not a bug fix, or no xfail tests found related to this bug.
4.7. **Infrastructure scan** - Scan `docs/infra/` for existing infrastructure constraints relevant to this work.
```bash
# Check for existing infra docs that might contain relevant constraints
ls docs/infra/*.md 2>/dev/null | head -20
```
Review any relevant INFRA docs for rate limits, API quotas, deployment constraints, or tool rules
that should inform the plan. Reference findings in the Solution and Risks sections.
**Skip if:** `docs/infra/` doesn't exist or contains no relevant docs.
5. **Data flow trace** - For changes involving multi-component interactions, trace the data
flow end-to-end through the system. Start from the entry point (user action, API call,
event trigger) and follow through each component, transformation, and storage layer.
```bash
# Read the entry point file
# Follow imports and function calls through the chain
# Document each transformation and handoff between components
```
Use results to fill the **Data Flow** section in the plan. This is critical for changes
that span multiple modules -- it prevents fixes applied at the wrong layer.
**Skip if:** Change is isolated to a single file/function, or is purely documentation/config.
6. **Failure analysis** - If the prior art search (step 4) found previous attempts to fix the
same problem, analyze why each attempt failed or was incomplete. Look for patterns:
- Was the root cause correctly identified?
- Was the fix applied at the right architectural layer?
- Did it address a symptom instead of the underlying cause?
- Did it introduce new problems while fixing the original?
Use results to fill the **Why Previous Fixes Failed** section (conditional -- only include
in the plan if prior failed fixes exist).
**Skip if:** No prior fixes found, or this is greenfield work.
7. **Set appetite** - Small / Medium / Large (see `SCOPING.md` for sizing guidance)
8. **Rough out solution** - Key components and flow, stay abstract
9. **Race condition analysis** - If the solution involves async operations, shared mutable state,
or cross-process data flows, identify timing hazards. For each: specify what data/state must
be established before dependent operations read it, and how the implementation prevents races.
Skip if the change is purely synchronous and single-threaded.
### Phase 1.5: Spike Resolution
Before writing the plan, resolve verifiable assumptions through time-boxed investigations.
1. **Identify assumptions** - Review the research from Phase 1 and list assumptions that could be validated by agents (prototyping, web research, code exploration)
2. **Enumerate spike tasks** - For each verifiable assumption, create a spike task:
```markdown
### spike-N: [Description of what to verify]
- **Assumption**: "[The assumption being tested]"
- **Method**: web-research | prototype | code-read
- **Agent Type**: Explore (code-read), general-purpose (web-research), builder in worktree (prototype)
- **Time cap**: 5 minutes agent time
- **Result**: [filled after spike completes]
- **Confidence**: [high | medium | low]
- **Impact if false**: [what changes in the plan]
```
3. **Dispatch spikes in parallel** - Use the P-Thread pattern (parallel Agent sub-agents) to run all spikes concurrently
4. **Appetite limits**:
- Small appetite: max 2 spikes
- Medium appetite: max 4 spikes
- Large appetite: uncapped
5. **Prototype isolation** - Prototype spikes MUST use `isolation: "worktree"` to avoid repo pollution. Each spike returns a yes/no/finding — no committed code, no half-implementations
6. **Collect results** - Aggregate spike findings into the `## Spike Results` section of the plan
7. **Filter Open Questions** - Only assumptions that spikes couldn't resolve go into Open Questions for the human
**Skip if:** No verifiable assumptions identified, or all assumptions require human judgment (business decisions, priority calls).
### Phase 2: Write Initial Plan
**Classification is mandatory** - every plan MUST include a `type:` field (bug, feature, or chore).
**Auto-Classification**: If the invoking environment supplies a pre-computed classification (e.g. the context file declares a `classification_type` carried in session context), use it as the default `type:` value. The user can always override.
Create `docs/plans/{slug}.md` using the template from `PLAN_TEMPLATE.md`.
**Conditional INFRA doc creation (only if the context file declares an infra-docs convention):** If the plan introduces new dependencies, services, external API calls, or deployment changes AND the repo keeps durable infrastructure docs, create one (this repo: `docs/infra/{slug}.md`) using this structure:
```markdown
# {Feature Name} — Infrastructure
## Current State
- [What infra exists today relevant to this work]
## New Requirements
- [New deps, services, API keys, config this plan adds]
- [Resource estimates: API quotas, storage, compute]
## Rules & Constraints
- [Rate limits, cost ceilings, API quotas]
- [Deployment topology requirements]
## Rollback Plan
- [How to revert infra changes if the feature is rolled back]
```
INFRA docs are NOT archived when plans ship — they accumulate in `docs/infra/` as durable infrastructure knowledge. Skip if the plan involves no infrastructure changes.
### Phase 2.5: Link or Create Tracking Issue
After writing the plan, **resolve the tracking issue first**, then push.
#### Step 1: Resolve repo and tracking issue
```bash
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
```
**Check for existing issue first!** If the plan was created in response to an existing GitHub issue (e.g., "make a plan for issue #42"), do NOT create a new issue. Instead, get its title and link the plan:
```bash
EXISTING_ISSUE=42
ISSUE_TITLE=$(gh issue view $EXISTING_ISSUE --json title -q .title)
gh issue edit $EXISTING_ISSUE --add-label "plan"
```
**Only create a NEW issue if** the plan was initiated from scratch (not from an existing issue).
```bash
TYPE=$(grep '^type:' docs/plans/{slug}.md | sed 's/type: *//' | tr -d ' ')
if [ -z "$TYPE" ]; then
echo "ERROR: Plan must have a 'type:' field in frontmatter (bug, feature, or chore)"
exit 1
fi
ISSUE_TITLE="{Feature Name}"
gh issue create \
--title "$ISSUE_TITLE" \
--label "plan" \
--label "$TYPE" \
--body "$(cat <<EOF
**Type:** {type} | **Appetite:** {appetite} | **Status:** Planning
---
This issue is for tracking and discussion. The plan document is the source of truth.
EOF
)"
```
#### Step 2: Push the plan
**Plans are documentation, not code. ALWAYS commit and push to main first.**
```bash
# MANDATORY: switch to main before committing the plan — regardless of current branch
git stash --include-untracked 2>/dev/null
git checkout main
git stash pop 2>/dev/null
git add docs/plans/{slug}.md && git commit -m "Plan: $ISSUE_TITLE"
git push
```
**CRITICAL: Plan PRs must NOT close the tracking issue.** The tracking issue stays open until the *implementation* PR merges with `Closes #N`. Never use closing keywords (Closes, Fixes, Resolves) when referencing the tracking issue in the plan PR body.
#### Step 3: Link plan to tracking issue
```bash
PLAN_LINK="https://github.com/${REPO}/blob/${PLAN_BRANCH}/docs/plans/{slug}.md"
if [ -n "$EXISTING_ISSUE" ]; then
# Prepend plan link to existing issue body
EXISTING_BODY=$(gh issue view $EXISTING_ISSUE --json body -q .body)
gh issue edit $EXISTING_ISSUE --body "**Plan:** ${PLAN_LINK}
${EXISTING_BODY}"
else
# Update the newly created issue with the plan link
ISSUE_NUM=$(gh issue list --state open --search "$ISSUE_TITLE" --json number -q '.[0].number')
gh issue edit $ISSUE_NUM --body "**Plan:** ${PLAN_LINK}
$(gh issue view $ISSUE_NUM --json body -q .body)"
fi
```
After linking or creating: update the plan's `tracking:` field and commit.
### Phase 2.6: Propagation Check
After all task steps are written and before committing, verify that task bullets are consistent with the current Technical Approach (or spike findings if no Technical Approach section exists).
1. **Re-read Technical Approach** — or, if the plan has no Technical Approach section, re-read the Spike Results section to get the current implementation findings
2. **Scan each task bullet** for encoding choices, library selections, function names, and pattern names
3. **Flag any task bullet that contradicts or predates spike findings** — common signs:
- A library name that was ruled out by a spike (e.g., a task still says "msgpack-encoded" after spike-2 confirmed `json.dumps()` is correct)
- A function name from a prior design that the spike replaced
- A pattern (e.g., `asyncio.to_thread()` vs. direct `await`) that contradicts the current approach
4. **Update divergent task steps** before committing — the task list must reflect the final spike conclusions, not intermediate assumptions
**This step is non-negotiable for plans with spikes.** For plans with no spikes (Small appetite, greenfield), it is a 30-second sanity check — scan once and move on.
### Phase 2.7: Sync Issue Comments into Plan
Before finalizing, check the tracking issue for comments that contain feedback, scope changes, or new context that should be incorporated into the plan.
```bash
# Extract issue number from plan frontmatter tracking URL
ISSUE_NUM=$(grep '^tracking:' docs/plans/{slug}.md | grep -oP '/issues/\K\d+')
if [ -n "$ISSUE_NUM" ]; then
# Get all comments with IDs, sorted chronologically
gh api repos/{owner}/{repo}/issues/${ISSUE_NUM}/comments \
--jq '.[] | {id: .id, author: .user.login, created: .created_at, body: .body}' 2>/dev/null
# Get the latest comment ID
LATEST_COMMENT_ID=$(gh api repos/{owner}/{repo}/issues/${ISSUE_NUM}/comments \
--jq '.[-1].id // empty' 2>/dev/null)
# Get the plan's recorded last_comment_id
PLAN_COMMENT_ID=$(grep '^last_comment_id:' docs/plans/{slug}.md | sed 's/last_comment_id: *//')
fi
```
**If new comments exist** (LATEST_COMMENT_ID != PLAN_COMMENT_ID):
1. Read each comment since the plan's `last_comment_id`
2. Incorporate relevant feedback into the plan (scope changes, new requirements, corrections)
3. Update `last_comment_id:` in the plan frontmatter to `LATEST_COMMENT_ID`
4. Commit the updated plan
**If no tracking issue or no comments**: Skip this step.
### Phase 3: Enumerate Questions
Plan critique is handled separately by `/do-plan-critique` (war room). This phase focuses only on surfacing questions that need human input before the critique step.
1. **Enumerate questions** - List all questions needing supervisor input
2. **Add questions to plan** - Append to "Open Questions" section
3. **Pre-send checklist**:
- [ ] Plan committed AND pushed (to `main` or `plan/{slug}` branch if main is protected)
- [ ] GitHub issue has `**Plan:** https://github.com/${REPO}/blob/${PLAN_BRANCH}/docs/plans/{slug}.md`
- [ ] Plan frontmatter has `tracking:` set to the issue URL
4. **Send reply**:
```
Plan draft created: docs/plans/{slug}.md
Tracking: {GitHub issue URL}
I've made the following key assumptions:
- [Assumption 1]
- [Assumption 2]
Please review the Open Questions section at the end of the plan and provide answers so I can finalize it.
```
### Phase 4: Finalize Plan
> **Never leave `docs/plans/` dirty across an await.** Plan docs commit directly on the shared main checkout, so an uncommitted edit sitting there while you wait on a subagent, a critique, or a human is exposed to every concurrent lane. A peer running `git pull --rebase` autostashes your work-in-progress, and if the pulled commits touch the same file the autostash-apply conflicts — at which point the peer has to quarantine the stash and someone has to prove by content diff which of stash-vs-commit was authoritative. That happened on 2026-08-07 (#2650, shape 1). Commit each revision as soon as it is coherent; a slightly noisy history costs nothing next to a reconciliation.
After receiving answers:
1. **Update plan** - Incorporate feedback, remove Open Questions section
2. **Mark as finalized** - Update frontmatter: `status: Ready`
**If this is a revision pass** (critique returned NEEDS REVISION / MAJOR REWORK / READY TO BUILD with concerns AND the plan has been revised based on critique findings):
2a. **Set `revision_applied: true` and `revision_applied_at: <timestamp>`** in the plan frontmatter — together these are the canonical signal that the plan has settled and the lock is no longer needed. `revision_applied_at` is an event-scoped ISO-8601 UTC timestamp (structural twin of the sticky boolean, #1760) that lets the router's convergence latch distinguish *this* settle-and-build revision from some later, unrelated `/do-plan` dispatch — write it in the SAME step as `revision_applied: true`, never as a follow-up edit:
```bash
REVISION_APPLIED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# In the plan frontmatter, set:
# revision_applied: true
# revision_applied_at: ${REVISION_APPLIED_AT}
# Then commit and push
git add docs/plans/{slug}.md && git commit -m "Plan revision ({slug}): address critique findings"
git push
```
2b. **Clear the plan-revising lock (only if the context file declares one)** — immediately after committing and pushing, if the repo uses a plan-revising lock to gate build dispatch, clear it so the pipeline can route to build. The lock and `revision_applied` must move together; both reflect "plan is settled". Follow the context file's exact invocation. In the generic case there is no lock — `revision_applied: true` in the frontmatter is the settled signal.
3. **Invite discussion**:
```
Plan finalized: docs/plans/{slug}.md
Tracking: {GitHub issue URL}
I think I'm done, but I'm supposed to ask — does anything feel off? Missed edge cases, wrong assumptions, anything that doesn't sit right? Now is the cheapest time to catch it.
```
## Output Location
All plans go to: `docs/plans/{slug}.md` — snake_case slugs (e.g. `async_meeting_reschedule.md`).
Plans are committed on `main` (see Phase 2.5 Step 2) — they are documentation, not code, and do not belong on feature branches. When the plan is *executed* (via `/do-build`), the build skill creates the feature branch.
## Status Tracking
Status and classification are tracked in the plan document's YAML frontmatter.
**Required Frontmatter Fields:**
- `status:` - Current state of the plan
- `type:` - Classification (bug, feature, or chore) - **MANDATORY**
**Status Values:**
- `Planning` - Initial draft being created
- `Ready` - Finalized and ready for implementation
- `In Progress` - Being implemented
- `Complete` - Shipped to production
- `Cancelled` - Not pursuing this
Update status as work progresses. Keep all tracking in the plan document itself.
**Tracking issue lifecycle:**
- When plan status changes to `Ready` or `In Progress`, update the GitHub issue status accordingly
- Issues are closed automatically when the **implementation PR** merges (via `Closes #N` in the do-build PR body) — do NOT close issues manually
- **Plan PRs (on protected branches) must NEVER close the tracking issue** — only the implementation PR should
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!