plot-sprint
Plot: Sprint
Sprints are not plans. Plans track what to build; sprints track when to ship it. Sprint files live in docs/sprints/. The initial skeleton commits directly to main; subsequent Planning-phase refinement may optionally happen on a sprint/<slug> branch with a draft PR for review. Principle 2 ("Plans merge before implementation") does not apply to sprints — sprints don't spawn implementation branches — but the refinement benefits from the same review surface.
Input: $ARGUMENTS determines the subcommand.
| Form | Action |
|---|---|
/plot-sprint |
Status (infer slug or list all) |
/plot-sprint <slug> |
Status of slug (or create if not found) |
/plot-sprint <slug> commit |
Lock sprint contents |
/plot-sprint <slug> start |
Begin the sprint |
/plot-sprint <slug> close |
End timebox, capture retro |
/plot-sprint <slug> add/remove/reprio |
Change sprint scope (see Scope Change) |
/plot-sprint <slug>: <goal> |
Create slug with goal |
Argument parsing: $ARGUMENTS = [<slug>] [<subcommand>] [<args>]
$ARGUMENTS[0]→ slug (first word, if present)$ARGUMENTS[1]→ subcommand:commit,start,close- Create detected by
:in slug token (e.g.week-1: Ship auth) - If no slug given: discover from
docs/sprints/active/— one active sprint → use it, multiple → list and ask, none → offer to create
Setup
Add a ## Plot Config section to the adopting project's CLAUDE.md:
## Plot Config
<!-- Optional: uncomment if using a GitHub Projects board -->
<!-- - **Project board:** owner/number (e.g. eins78/5) -->
- **Branch prefixes:** idea/, feature/, bug/, docs/, infra/
- **Plan directory:** docs/plans/
- **Active index:** docs/plans/active/
- **Delivered index:** docs/plans/delivered/
- **Sprint directory:** docs/sprints/
Model Guidance
| Steps | Min. Tier | Notes |
|---|---|---|
| Create, commit, start | Small | Git commands, templates, file ops |
| Status | Small | File existence checks for delivery state are mechanical; no judgment needed |
| Close | Mid | False-positive detection (cross-reference [x] against docs/plans/delivered/) plus existing checkbox parsing |
All sprint operations are structural (Small or Mid). No Frontier needed.
User interaction: Use
AskUserQuestion(Claude Code) /ask_question(Cursor) for all questions, proposals, and confirmations.
Sprint Lifecycle
flowchart LR
A["/plot-sprint<br/><slug>: <goal>"] -->|"⏸ drafting"| B["Planning"]
B -->|"⏳ /plot-sprint commit"| C["Committed"]
C -->|"⚡ /plot-sprint start"| D["Active"]
D -->|"⏳ /plot-sprint close"| E["Closed"]
Legend: ⚡ automate ASAP · ⏸ natural pause · ⏳ human-paced
Guardrails
Plan vs Sprint
Sprint files must not contain ## Design or ## Approach sections. If detected, warn: "This looks like a plan, not a sprint. Use /plot-idea for plans."
Phase Transitions
The Phase field is only updated by named subcommands: commit, start, close. Any other action — opening a PR for review, refining items, adjusting dates, fixing typos — leaves the phase unchanged. If the user says "start a PR for the sprint", that means open a draft PR for plan review, not /plot-sprint <slug> start. When in doubt, ask which subcommand the user means.
Subcommands
Create: /plot-sprint <slug>: <goal>
Create a new sprint in Planning phase.
Pacing: ⏸ natural pause (drafting)
1. Parse Input
Extract <slug> (before the colon) and <goal> (after the colon). Both are required.
- Slug: trimmed, lowercase, hyphens only
- Goal: the sprint goal as a sentence
If no colon or missing parts: "Usage: /plot-sprint <slug>: <goal>"
Multiline input: If $ARGUMENTS contains newlines, the first line is parsed for slug + goal as above. Any subsequent lines are treated as context for the goal (e.g., motivation, scope hints) and become the body of the ## Sprint Goal section in the new sprint file — not the one-line > <sprint goal> headline.
2. Determine ISO Week Prefix
Derive the ISO week prefix from today's date for the filename:
WEEK_PREFIX=$(date -u +%Y-W%V)
The sprint file will be named docs/sprints/${WEEK_PREFIX}-<slug>.md.
3. Pre-flight Checks
ls docs/sprints/${WEEK_PREFIX}-<slug>.md 2>/dev/null
If file exists: "Sprint <slug> already exists for week ${WEEK_PREFIX}."
4. Discover Active Plans
List active plans so the user can add them to the sprint:
ls docs/plans/active/ 2>/dev/null
If plans exist, present: "Found N active plans. Add any to this sprint?" List them and let the user select which to include (or none). Selected plans are added as [slug] items under the appropriate MoSCoW tier.
5. Create Sprint File
mkdir -p docs/sprints
Write docs/sprints/${WEEK_PREFIX}-<slug>.md using the template from skills/plot/templates/sprint.md, substituting <title> and <sprint goal>.
Item format: - [ ] [slug] description (plan reference) or - [ ] description (lightweight task).
Item Annotations
Plan-backed items carry HTML comment annotations for automation tracking:
- [ ] [slug] description <!-- pr: #N, status: draft, branch: feature/slug -->
| Field | Set by | Values |
|---|---|---|
pr |
/plot-approve |
PR number (#N) or none |
status |
/plot-approve, /plot-deliver, /plot-reject |
not-started, draft, open, merged, delivered, rejected |
branch |
/plot-approve |
Implementation branch name |
reviewed_at |
Review tracking | ISO 8601 timestamp |
review_sha |
Review tracking | HEAD SHA at time of review |
Annotations are created by /plot-approve and updated by /plot-deliver. The status subcommand reads them for enriched output.
Review Tracking
The review_sha annotation enables skip-if-unchanged review:
- Compare current HEAD SHA of the PR branch to the
review_shain the annotation - If same → no new commits since last review, skip
- If different → new commits, needs re-review
- If status is
merged→ never needs review
Use skills/plot/scripts/plot-review-status.sh <sprint-slug> to get review freshness for all sprint items as JSON.
Leave Start/End dates as placeholders — the user fills them during the Planning phase.
6. Update Plan Files
For each plan-backed item ([slug]) added in step 4, update the referenced plan file to record sprint membership:
- Resolve the plan file via
docs/plans/active/<slug>.md - Add
- **Sprint:** <sprint-slug>to its## Statussection (after the Phase line)
This enables sprint awareness in /plot-approve and /plot-deliver.
7. Commit Skeleton to Main
Commit the initial sprint skeleton directly to main:
git add docs/sprints/${WEEK_PREFIX}-<slug>.md docs/plans/
git commit -m "sprint: create <slug>"
git push
Refinement (fleshing out items, readiness assessments, deferrals) happens optionally on a sprint/<slug> branch with a draft PR — see "Refine via PR (optional)" below.
8. Summary
Print:
- Created:
docs/sprints/${WEEK_PREFIX}-<slug>.md - Sprint:
[*] Planning > [ ] Committed > [ ] Active > [ ] Closed - Plan files updated: N (if any)
- Next: add items, set dates, then
/plot-sprint <slug> commitwhen ready
Refine via PR (optional)
Once the skeleton is on main, sprint refinement can move to a sprint/<slug> feature branch with a draft PR. This is optional — small or solo sprints can stay on main. Use a PR when:
- Multiple stakeholders need to review scope before locking
- Readiness assessments or deferral decisions deserve their own commits in history
- The sprint is large enough that scope conversations benefit from inline comments
Phase stays Planning throughout the PR. The commit subcommand handles the phase bump and merge atomically (see Commit subcommand below).
Workflow:
git checkout -b sprint/<slug> origin/main- Refine the sprint file on the branch (one commit per substantive change — readiness, defer X, set dates)
gh pr create --draft --title "Sprint: <goal>" --body "..."— keep as draft while in Planning phase- Phase stays
Planningthroughout. Do NOT change the phase here. - When the team agrees: run
/plot-sprint <slug> commit(see Commit subcommand for PR-aware behavior).
Commit: /plot-sprint <slug> commit
Lock sprint contents. Team has agreed on what's in scope.
Pacing: ⏳ human-paced (team agreement)
1. Find Sprint File
ls docs/sprints/*-<slug>.md 2>/dev/null
If not found: "No sprint found for <slug>."
Read the sprint file. Check Phase field:
- If not
Planning: "Sprint is in<phase>phase, not Planning. Cannot commit."
2. Validate End Date
Check that the **End:** field has a real date (not the placeholder YYYY-MM-DD).
If missing or placeholder: "Set an end date before committing. Edit the sprint file directly."
3. Detect Sprint PR
Run gh pr list --head sprint/<slug> --json number,state,isDraft --jq '.[]'.
- PR exists and not merged: proceed to step 4a (PR-aware commit)
- No PR / PR already merged: proceed to step 4b (direct main commit)
4a. PR-Aware Commit
Update phase on the PR branch, push, mark ready, merge:
# Should already be on sprint/<slug> branch — confirm with: git branch --show-current
# If not, check it out worktree-safe: git checkout -b sprint/<slug> origin/sprint/<slug>
# Bump phase in the sprint file
# **Phase:** Planning → **Phase:** Committed
git add docs/sprints/*-<slug>.md
git commit -m "sprint: commit <slug>"
git push
gh pr ready <number> # if currently draft
gh pr merge <number> --merge --delete-branch
Default to merge commits (--merge) to preserve granular planning history (readiness, deferrals, scope changes are valuable context). If the project's CLAUDE.md specifies a different merge strategy, follow that instead. Do NOT default to --squash — it collapses the planning trail.
The merge itself is the "scope locked" transition. No follow-up commit on main needed.
4b. Direct Main Commit (no PR)
# Bump phase in the sprint file
# **Phase:** Planning → **Phase:** Committed
git add docs/sprints/*-<slug>.md
git commit -m "sprint: commit <slug>"
git push
5. Summary
Print:
- Committed:
<slug> - Sprint:
[ ] Planning > [x] Committed > [ ] Active > [ ] Closed - End date:
<end date> - Items: N must-haves, N should-haves, N could-haves
- Next:
/plot-sprint <slug> startwhen the sprint begins
Start: /plot-sprint <slug> start
Begin the sprint. Creates the active symlink.
Pacing: ⚡ automate ASAP (mechanical transition)
1. Find and Validate Sprint File
Find sprint file, check Phase is Committed.
2. Create Active Symlink
mkdir -p docs/sprints/active
ln -s ../${WEEK_PREFIX}-<slug>.md docs/sprints/active/<slug>.md
3. Update Phase
Change **Phase:** Committed → **Phase:** Active
4. Commit
git add docs/sprints/*-<slug>.md docs/sprints/active/<slug>.md
git commit -m "sprint: start <slug>"
git push
5. Summary
Print:
- Started:
<slug> - Sprint:
[ ] Planning > [ ] Committed > [x] Active > [ ] Closed - End date:
<end date> - Active symlink:
docs/sprints/active/<slug>.md - Next: work on sprint items. When timebox ends,
/plot-sprint <slug> close
Close: /plot-sprint <slug> close
End the timebox. Check MoSCoW completeness and capture retrospective.
Pacing: ⏳ human-paced (retrospective)
1. Find and Validate Sprint File
Find sprint file, check Phase is Active.
2. MoSCoW Completeness Check
Parse the sprint file for checkbox items in each tier:
- Count checked
- [x]vs unchecked- [ ]items per tier - For each
[x]item with a[slug]reference, verify the plan is actually delivered:- Check
docs/plans/delivered/<slug>.md(file or symlink) exists - If the plan is in
docs/plans/active/<slug>.mdor missing entirely, this is a false-positive completion
- Check
Present results, listing any false positives before the totals:
⚠ False-positive completions detected:
- [slug] — checked but plan is in active/ (not delivered)
- [slug] — checked but plan file not found
Must Have: 2/4 complete (1 false positive)
Should Have: 1/2 complete
Could Have: 0/1 complete
If false positives exist, do NOT proceed to close until the user resolves them. Present three options:
- Run
/plot-deliver <slug>for each false-positive item (preferred — actually delivers the plan) - Uncheck the box in the sprint file (acknowledges it's not really done)
- Override and close anyway (last resort) — requires logging a one-liner reason in the sprint file's
## Notes > ### Scope Changessection, mirroring the existing scope-change log convention. Format:- YYYY-MM-DD: Closed with [slug] marked complete despite plan not delivered — <reason>
After false positives are resolved, continue with the regular Must-Have completeness flow:
If must-haves are incomplete, present three options:
- Close anyway (must-haves stay unchecked in place)
- Move incomplete must-haves to Deferred — move each unchecked
- [ ]line from### Must Haveto### Deferred, preserving the original text - Hold off (don't close yet)
3. Capture Retrospective
Ask the user: "Add a retrospective? (optional)"
If yes, prompt for:
- What went well?
- What could improve?
- Action items for next sprint?
Fill the ## Retrospective section using the template from skills/plot/templates/retrospective.md. Include the Metrics subsection with actual counts from step 2.
4. Update Phase and Remove Symlink
Change **Phase:** Active → **Phase:** Closed
git rm docs/sprints/active/<slug>.md
git add docs/sprints/*-<slug>.md
git commit -m "sprint: close <slug>"
git push
5. Summary
Print:
- Closed:
<slug> - Sprint:
[ ] Planning > [ ] Committed > [ ] Active > [x] Closed - Must-haves: N/M complete
- Deferred: N items (if any moved)
- Retrospective: captured / skipped
- Suggested next actions:
- Review the retrospective action items
- Carry deferred items to the next sprint:
/plot-sprint <new-slug>: <goal> - If all planned work is delivered:
/plot-releaseto cut a release
Scope Change
Scope changes are allowed during Active (or Committed) sprints. All changes are logged in the sprint file's ## Notes > ### Scope Changes section for traceability.
Adding items mid-sprint:
- Add the new
- [ ]item to the appropriate MoSCoW tier - Log:
- YYYY-MM-DD: Added [slug] to Must/Should/Could — <reason> - If plan-backed (
[slug]), update the plan's Sprint field
Removing or deferring items:
- Move the item to the
### Deferredsection (do not delete — preserve history) - Log:
- YYYY-MM-DD: Deferred [slug] from Must — <reason>
Changing MoSCoW tier:
- Move the item between tier sections (e.g., Must → Should)
- Log:
- YYYY-MM-DD: Reprioritized [slug] Must → Should — <reason>
Commit scope changes directly to main with message: sprint: scope change <slug>.
Status: /plot-sprint or /plot-sprint <slug>
Show sprint status.
1. Resolve Slug
If slug provided ($ARGUMENTS[0] present, no subcommand):
- Find sprint file:
ls docs/sprints/*-<slug>.md 2>/dev/null - Found → show status for that sprint (step 2)
- Not found → "No sprint
<slug>found. Create it with/plot-sprint <slug>: <goal>"
If no arguments:
- List active sprints:
ls docs/sprints/active/ 2>/dev/null - One active sprint → use it, show status (step 2)
- Multiple → list all active sprints with summary
- None → "No active sprints. Create one with
/plot-sprint <slug>: <goal>"
2. For Each Sprint
Read the sprint file and display:
- Sprint name and goal
- Phase
- Time remaining (days until end date; "ended N days ago" if past)
- MoSCoW progress: Must N/M, Should N/M, Could N/M
- For plan-backed items with annotations: show PR number, status, and branch
- False-positive flag: for
[x]items with[slug]refs, if the plan is not indocs/plans/delivered/, prefix the line with⚠and note(plan not delivered). This makes the discrepancy visible during routine status checks, not just at close time.
3. Summary
## Active Sprints
- `<slug>` — "<goal>" | [*] Active | 3 days remaining | Must: 2/4 | Should: 1/2 | Could: 0/1
Common Mistakes
| Mistake | Effect | Prevention |
|---|---|---|
Closing a sprint with [x] [slug] items whose plans are still in active/ |
Sprint reads as complete but plans remain undelivered; /plot-deliver later reverts the plan to Draft |
Step 2 of close runs a false-positive check; resolve via /plot-deliver or uncheck before closing |
| Squash-merging a sprint planning PR | Readiness/defer/dates collapse into one commit; reasoning lost | Default to --merge (matches plot-approve for plan PRs) — squash is for messy WIP, not planning |