Sprint Planning skill for Claude Code
Commit to what the data says, not what feels good. Claude sets a sprint goal written as an outcome, collects availability and runs a bundled script that scales the 20th-percentile and median velocity by this sprint's capacity, then subtracts re-estimated carry-over. Stories are pulled in goal and dependency order up to the commit number, stretch work is labeled apart, and a written drop order decides what goes first when the sprint gets hit.
- name
sprint-planning - size169 lines · ≈1,800 tokens
- files2
- licenseMIT
- updated
What Claude sees before loading it
This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.
description: Plans sprints and iterations a team can actually finish — a sprint goal tied to an outcome, capacity from real availability, a commitment range from past velocity computed by a bundled script, a ready and ordered backlog, explicit carry-over handling, and a refinement cadence that keeps two sprints of stories ready. Use when preparing or running sprint planning, setting a sprint goal, working out team capacity or how many points to commit, dealing with chronic carry-over, refining a backlog, or replanning mid-sprint after scope changes. Try asking
- Plan next sprint: two people are on holiday and we carried over 8 points
- We carry over a third of our work every sprint; figure out why and fix our planning
- Write a sprint goal and pick stories from this backlog for a 2-week sprint
Use it when
- Preparing or facilitating sprint planning
- Working out how many points the team should commit
- Carry-over keeps happening sprint after sprint
- Urgent work arrives mid-sprint and the plan needs adjusting
Skip it when
- You need a delivery date for a whole project, use work-estimation instead
- Stories are still vague epics without acceptance criteria, use user-stories first
How to check the result
Committed points sit at or below the script's commit number, every story meets the definition of ready, and the plan states the goal, stretch items and drop order.
The common mistake it prevents
Filling the sprint to 100% of best-case velocity so the first bug or support escalation guarantees carry-over.
A sprint plan is done well when the team leaves with one sentence everyone can repeat (the sprint goal), a set of ready stories sized to measured capacity with a buffer, and a clear rule for what gets dropped first if things go wrong. Success is the goal met and carry-over under ~10%, not the maximum number of points pulled in.
Ground rules
- The sprint goal is an outcome (“Finance admins can export a month of invoices”), not a list of tickets. Roughly 60–80% of the committed work should serve it.
- Commit from data: median past velocity scaled by this sprint’s availability. Never plan from hoped-for velocity or “we’ll push harder”.
- Only stories meeting the definition of ready enter the sprint (see user-stories).
- Leave room for the unplanned: a 10–20% reserve for bugs, support, and review load, unless history shows the velocity already absorbs it.
- Carry-over is re-estimated for the remaining work and competes for priority like any other item. It does not get an automatic first slot.
- The team commits; the PM proposes priorities. Capacity numbers are not negotiable by stakeholders.
Workflow
- [ ] 1. Before the meeting: refine the top of the backlog, check readiness, collect availability
- [ ] 2. Review the last sprint: goal met? velocity, carry-over and why
- [ ] 3. Compute capacity and the commitment range (script below)
- [ ] 4. PM proposes the sprint goal and ordered candidate stories
- [ ] 5. Team pulls stories until the commit number; confirm the goal still fits
- [ ] 6. Break stories into tasks for the first days; surface dependencies and risks
- [ ] 7. Agree the drop order and the stretch items
- [ ] 8. Publish the plan; set the mid-sprint check
Meeting timebox
| Sprint length | Planning timebox | Refinement per week |
|---|---|---|
| 1 week | ≤ 1 h | 30–45 min |
| 2 weeks | ≤ 2 h | 1 h |
| 3–4 weeks | ≤ 4 h | 1–2 h |
If planning regularly overruns, refinement is not happening; stories are being designed in the planning meeting.
Capacity and commitment
Collect availability per person: allocation to this team, days off, and unusual load compared with the sprints in the velocity history (on-call week, interviews, training, release duty). Then:
python3 scripts/sprint_capacity.py team.csv --sprint-days 10 --velocity 31,28,35,24,30 --carryover 5
team.csv columns: member,allocation,days_off,other_days. The script prints
available vs. normal person-days, the capacity ratio, and a commitment range: the
20th-percentile and median velocity scaled by capacity ratio and --reserve
(default 0.10), minus re-estimated carry-over. It warns when velocity varies more than
30% between sprints or when carry-over exceeds 25% of median velocity.
Worked example (5 people, 10-day sprint, Raj off 3 days, Tom on call 2 days, Mei at 50%): capacity ratio 84%, velocity median 30, P20 27 → commit ~16 points of new work after 5 points of carry-over, stretch ~18.
| Situation | Rule |
|---|---|
| New team, < 3 sprints of history | Commit to ~60% of what feels possible; measure |
| Velocity CV > 30% | Commit at the low number; investigate the swings in the retro |
| Team composition changed | Scale by capacity ratio for one sprint, then re-baseline |
| Holiday-heavy sprint | Capacity ratio handles it; do not “make it up” next sprint |
| No story points | Count stories of similar size (throughput) instead; same method |
Sprint goal
Pattern: By the end of the sprint, <persona> can <capability>, so that <outcome>.
| Weak | Better |
|---|---|
| “Finish INV-1 through INV-6” | “Finance admins can export a month of invoices to CSV and open it in Excel without fixes” |
| “Work on onboarding” | “New teams see a setup checklist and we can measure step completion” |
| “Bugs and tech debt” | “Checkout error rate below 0.5% at p95 load” |
The goal guides trade-offs mid-sprint: when something must drop, keep what serves the goal.
Ordering and selection
- Carry-over that still serves this goal (re-estimated).
- Stories for the goal, in dependency order, starting with the thinnest end-to-end slice.
- Fixed commitments (incident follow-ups, security fixes) sized and listed.
- Other priorities from the top of the backlog.
- Stretch items, explicitly labeled, never counted toward the commitment.
Stop pulling when the sum reaches the commit number. Then ask: “If one story blows up, which do we drop?” and write the drop order.
Carry-over
| Carry-over % of committed | Likely cause | Fix |
|---|---|---|
| < 10% | Normal variance | None |
| 10–25% | Stories too large, or work started late in the sprint | Split to ≤ 1/4 velocity; limit work in progress |
| > 25% two sprints running | Over-commitment or hidden work | Commit at P20; make unplanned work visible on the board |
Root-cause each carried item in one line (blocked, underestimated, started late, deprioritized). Patterns go to the retrospective (see retrospectives).
Backlog refinement
- Keep 1.5–2 sprints of stories ready at all times.
- In refinement: clarify, write acceptance criteria, split, size. Do not assign people.
- Size with the whole team; big disagreements (e.g. 2 vs. 8) mean different understandings, so discuss before re-voting.
- Stories with unknowns large enough to block sizing become time-boxed spikes (see technical-spike).
Mid-sprint changes
- New urgent work enters only by removing work of equal size; the PM picks what leaves.
- Check progress at the midpoint: if under ~40% of committed points are done or in review, drop stretch items now and raise risks to stakeholders.
- If the sprint goal becomes obsolete, end or replan the sprint openly rather than quietly swapping everything.
Gotchas
- Planning to 100%. No slack means any interruption causes carry-over.
- Points as performance targets. Teams inflate estimates; velocity rises while output does not. Use points only for forecasting.
- Comparing velocity across teams. Points are team-relative.
- Assigning all tasks up front. Locks people into silos; let people pull work and swarm on the goal.
- Too much in progress. Five stories at 80% delivers nothing. Limit work in progress to roughly people + 1 and finish before starting.
- Silent scope additions from stakeholders via direct messages. Route everything through the PM and the swap rule.
Verification
- The sprint goal is one outcome sentence the whole team can repeat.
- Committed points ≤ the script’s commit number; stretch items labeled separately.
- Every committed story meets the definition of ready and is ≤ 1/4 of velocity.
- Drop order and dependencies are written in the plan.
- After the sprint: goal met or not, carry-over %, and causes recorded.
Output contract
# Sprint <n>: <dates>
Goal: <outcome sentence>
Capacity: <available>/<normal> person-days (<ratio>%) — velocity median <x>, P20 <y>
Commitment: <points> (incl. <carry-over> re-estimated carry-over) Stretch: <points>
| Order | Story | Size | Serves goal? | Owner(s) | Depends on |
|---|---|---|---|---|---|
Drop order if at risk: <story>, <story>
Risks and dependencies: ...
Mid-sprint check: <date>
Last sprint: goal <met/missed>, velocity <x>, carry-over <y%> — causes: ... Bundled files
Loaded only when a step in the skill needs them. Review them here before installing.
scripts/sprint_capacity.py
#!/usr/bin/env python3
"""Sprint capacity and commitment range from availability and past velocity.
Usage:
python3 sprint_capacity.py team.csv --sprint-days 10 --velocity 31,28,35,24,30
[--carryover 5] [--reserve 0.10]
team.csv columns (header required): member,allocation,days_off,other_days
allocation : fraction of this person's time on this team, 0-1 (blank = 1)
days_off : PTO, holidays, conferences inside the sprint
other_days : on-call, interview loops, training, release duty
--velocity : completed points (or items) from the last 3-8 sprints, oldest first,
each delivered at roughly normal capacity
--carryover : points of unfinished work rolling into this sprint (re-estimated
for the remaining work only)
--reserve : extra fraction held back beyond the interrupts already reflected
in past velocity, e.g. a known migration or launch (default 0.10)
Prints available person-days vs. a normal sprint, the capacity ratio, and a
commitment range = velocity quantiles x capacity ratio x (1 - reserve), minus
carry-over. Warns when velocity is volatile (coefficient of variation > 0.3).
Standard library only. Exits 1 on bad input.
"""
from __future__ import annotations
import argparse
import csv
import statistics
import sys
def fail(msg: str) -> None:
print(f"error: {msg}", file=sys.stderr)
sys.exit(1)
def quantile(xs: list[float], q: float) -> float:
s = sorted(xs)
pos = (len(s) - 1) * q
lo = int(pos)
hi = min(lo + 1, len(s) - 1)
return s[lo] + (s[hi] - s[lo]) * (pos - lo)
def main() -> None:
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("team")
ap.add_argument("--sprint-days", type=float, required=True)
ap.add_argument("--velocity", required=True)
ap.add_argument("--carryover", type=float, default=0.0)
ap.add_argument("--reserve", type=float, default=0.10)
a = ap.parse_args()
try:
vel = [float(v) for v in a.velocity.split(",") if v.strip()]
except ValueError:
fail("--velocity must be comma-separated numbers")
if len(vel) < 3:
fail("give at least 3 past sprints of velocity")
if not 0 <= a.reserve < 0.6:
fail("--reserve must be in [0, 0.6)")
if a.sprint_days <= 0:
fail("--sprint-days must be > 0")
normal = available = 0.0
lines = []
try:
with open(a.team, newline="", encoding="utf-8-sig") as fh:
reader = csv.DictReader(fh)
need = {"member", "allocation", "days_off", "other_days"}
if not need <= set(reader.fieldnames or []):
fail(f"team.csv needs columns {sorted(need)}")
for i, row in enumerate(reader, start=2):
name = (row["member"] or "").strip()
if not name:
continue
try:
alloc = float(row["allocation"] or 1)
off = float(row["days_off"] or 0)
other = float(row["other_days"] or 0)
except ValueError:
fail(f"line {i} ({name}): non-numeric value")
if not 0 <= alloc <= 1 or off < 0 or other < 0:
fail(f"line {i} ({name}): allocation 0-1, days >= 0")
if off + other > a.sprint_days:
fail(f"line {i} ({name}): days_off + other_days exceed sprint length")
full = a.sprint_days * alloc
avail = (a.sprint_days - off - other) * alloc
normal += full
available += avail
lines.append((name, alloc, full, avail))
except OSError as exc:
fail(str(exc))
if normal == 0:
fail("no team capacity found")
ratio = available / normal
mean = statistics.mean(vel)
cv = statistics.stdev(vel) / mean if mean else 0
factor = ratio * (1 - a.reserve)
low = quantile(vel, 0.2) * factor - a.carryover
mid = quantile(vel, 0.5) * factor - a.carryover
w = max(len(n) for n, *_ in lines)
print(f"{'member':<{w}} alloc normal available")
for name, alloc, full, avail in lines:
print(f"{name:<{w}} {alloc:>5.2f} {full:>6.1f} {avail:>9.1f}")
print(f"{'total':<{w}} {normal:>6.1f} {available:>9.1f} capacity ratio {ratio:.0%}")
print(f"\nVelocity last {len(vel)}: {', '.join(f'{v:g}' for v in vel)} "
f"(median {quantile(vel, 0.5):g}, P20 {quantile(vel, 0.2):g}, CV {cv:.0%})")
print(f"Adjustment: x{ratio:.2f} capacity x{1 - a.reserve:.2f} reserve; minus {a.carryover:g} carry-over")
print(f"\nCommit (high confidence): {max(low, 0):.0f} points of new work")
print(f"Stretch (about 50/50): {max(mid, 0):.0f} points of new work")
if cv > 0.3:
print("warning: velocity varies > 30% sprint to sprint; commit to the low number and find the cause")
if a.carryover and a.carryover > 0.25 * quantile(vel, 0.5):
print("warning: carry-over exceeds 25% of median velocity; stories are probably too big")
if low <= 0:
print("warning: no room for new work after carry-over and reserve")
sys.exit(0)
if __name__ == "__main__":
main()
About this skill
Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.