sprint-end
Skill metrics: Emit a start event to the activity log:
echo "{\"type\":\"skill\",\"event\":\"start\",\"skill\":\"sprint-end\",\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> docs/sessions/.activity-log.jsonl
Ending the sprint. Discovering and wrapping up all work on the current branch.
Profile-Adaptive Behavior
Read the **Profile:** line from CLAUDE.md to determine the active project profile.
Skip: Step 2 quality agent dispatch, Step 3 documentation updates (epics, backlog, metrics, context, architecture, PRD review, debt register), ground rules compliance check, ADR compliance check. Tests must still pass — safety is not reduced.
Progress tracking: Create step-level tasks:
- "Discover sprint state" — activeForm: "Discovering sprint state..."
- "Run quality gates" — activeForm: "Running quality gates..." — blockedBy: [1]
- "Update documentation" — activeForm: "Updating docs..." — blockedBy: [2]
- "Commit documentation artifacts" — activeForm: "Committing docs..." — blockedBy: [3]
- "Push and create PR" — activeForm: "Creating PR..." — blockedBy: [4]
- "Wait for CI" — activeForm: "Waiting for CI..." — blockedBy: [5]
- "Merge and clean up" — activeForm: "Merging to main..." — blockedBy: [6]
At each step boundary, mark current task completed and next task in_progress.
Process Flow (authoritative — prose below is supporting detail)
START → 1. Discover Sprint State (from git, no assumptions)
→ [On main or no commits?] → STOP (nothing to ship)
→ 2. Quality Gates (tests, test protection, quality agents)
→ [All gates pass?]
→ NO: Fix issues → re-run gates
→ YES: 3. Documentation Updates (epics, backlog, progress)
→ 3.5. Commit Documentation Artifacts
→ 4. Push and Create PR
→ 5. Wait for CI
→ [CI green?]
→ NO: Fix → push → re-check
→ YES: 6. Merge and Clean Up (squash, delete branch, worktree)
→ 7. Sprint Complete Summary → DONE
Failure State Persistence
At step boundaries, write docs/sessions/.failure-state.md with YAML frontmatter so the Stop hook and /continue can programmatically detect incomplete workflows.
At workflow start (Step 1 entry):
---
status: active
skill: sprint-end
phase: "1"
phase_name: "Discover Sprint State"
started_at: "[ISO-8601 timestamp from date -u +%Y-%m-%dT%H:%M:%SZ]"
story: "[branch name or sprint identifier]"
branch: "[from git branch --show-current]"
next_action: "Discover sprint state from git"
files_modified: []
---
## Context
[Free-form notes — stories in sprint, quality gate status, PR state]
At each step transition: Update the frontmatter fields: phase, phase_name, next_action, and append to files_modified. Update the Context section with current progress.
On successful completion (Step 7 — Sprint Complete): Delete .failure-state.md — clean state means no failure to recover from.
1. Discover Sprint State
No assumptions about previous context. Discover everything from git.
Check story completion status: Read docs/progress.md for the current story status. If it shows a phase earlier than "DONE" (e.g., "Phase 3 — tests pass, self-review pending"), the last story was only partially completed. Factor this into quality gates — run the missing steps as part of sprint-end rather than flagging them as new issues.
Read the Default branch from CLAUDE.md's Git Workflow section. If not set, detect at runtime:
DEFAULT_BRANCH=$(grep -oP '^\- \*\*Default branch:\*\* \K\S+' CLAUDE.md 2>/dev/null)
if [ -z "$DEFAULT_BRANCH" ]; then
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
fi
if [ -z "$DEFAULT_BRANCH" ]; then
for branch in main master develop; do
if git show-ref --verify --quiet "refs/heads/$branch"; then DEFAULT_BRANCH="$branch"; break; fi
done
fi
Discover from git (using the detected default branch):
git branch --show-current
git log $DEFAULT_BRANCH..HEAD --oneline
git diff --stat $DEFAULT_BRANCH...HEAD
git diff --name-only $DEFAULT_BRANCH...HEAD
If on the default branch: There is no sprint to end. Inform user and stop.
If no commits ahead of the default branch: Nothing to ship. Inform user and stop.
If in a worktree: Detect with git rev-parse --git-common-dir. Note the worktree path for cleanup in step 6.
Child stream check (parallel work): Discover any streams fanned out from this branch by /parallel-work:
# Branches whose recorded parent is the current branch.
# Note: git canonicalizes config keys to lowercase in --get-regexp output.
git config --get-regexp '^branch\..*\.exosuitparent$' 2>/dev/null \
| awk -v P="$(git branch --show-current)" '$2==P {print $1}' \
| sed 's/^branch\.//; s/\.exosuitparent$//'
For each child branch found, count work not yet merged into this branch:
git rev-list --count HEAD..<child-branch>
- If any child has unmerged commits: STOP and report:
"Stream
<child>has [N] commits not merged into this sprint branch. Run/merge-upinside its worktree first, or explicitly confirm abandoning that work." Proceed only when every child is merged or the user has explicitly abandoned it. - If all children are merged (count 0): note them, with their worktree paths from
git worktree list --porcelain, for cleanup in step 6.
Analyze: branch name, all commits since branching, all files changed, stories completed (parse from commit messages).
1.5. Pre-Ship Smoke Test (Optional)
Check CLAUDE.md Commands for a dev: command. If one is configured:
- Offer: "Before running quality gates, want to do a quick visual check of the app?"
- If user accepts: run the dev command in background (
run_in_background: true), show whatever output it produces (URLs, status, CLI output — do NOT assume localhost), let user verify - If user finds issues: fix them before proceeding to quality gates
- If user declines or no dev command exists: skip and proceed to quality gates
This catches visual/UX issues that automated tests don't cover. Advisory, not blocking.
2. Quality Gates
Mindset: Assume there are problems. Your job is to find them. Your first assessment is almost never "all clear."
- Standard quality gates (Recommended) — description: "Code quality + test validation + security audit"
- All quality gates — description: "All 5 quality agents + independent verification"
- Run test suite only — description: "Execute all tests, verify zero failures"
- Test count protection — description: "Verify test count did not decrease from last sprint"
- Ground rules compliance — description: "Check against GROUND_RULES.md principles"
- UAT coverage check — description: "Check UAT test case pass/fail summary (advisory, does not block)"
- Skip all quality gates — description: "Skip all gates (not recommended)"
If the user deselects any: "Note: skipping gates may allow issues to reach main. All gates recommended for production sprints."
Quality Agent Dispatch
Dispatch quality gates via /quality-check:
- Standard selection:
/quality-check(defaults to code + tests + security based on profile) - All gates:
/quality-check --all - Custom:
/quality-check --code --security(any combination of flags)
The /quality-check skill handles parallel agent dispatch, profile-aware defaults, and unified reporting. See .claude/skills/quality-check/SKILL.md for details.
Do NOT use native agents (.claude/agents/code-reviewer.md, .claude/agents/security-analyst.md) for static quality gates — use the skill-based dispatch through /quality-check.
Exception — integration-tester: For "All quality gates" or strict profile, /quality-check --all automatically includes the integration-tester agent.
Run only selected gates. The HARD-GATE still applies: if /quality-check returns FAIL, do NOT proceed.
Read references/quality-gates.md for detailed checks (tests, test protection, quality agents with scope-based scaling, recovery). For error recovery during this step, consult references/error-recovery.md — search for ## Step 2.
Compliance ledger: Record results in the sprint spec's ## Outcome → Ground rules field (e.g., "5/5 checked, Clean" or "5/5 checked, 1 violation: [description]"). This data is available to /retrospective for longitudinal compliance analysis.
2.5 UAT Coverage Check (Advisory)
- Parse the Dashboard table (or scan directory for test case statuses)
- Count: total, pass, fail, untested, blocked, partial
- Identify critical/high priority cases that are ❌ Fail or ⬜ Untested
- Report:
UAT Coverage: X/Y pass | Z untested | W fail Critical untested: [list or "none"] - WARN (do not block) if critical cases are untested or failing — include warning in PR body
3. Documentation Updates
Based on what was done in the sprint, update relevant documentation:
- Epic file (
docs/reference/backlog/E##-*.md):- In the story checklist: change
- [ ] ID — Title (P#, in-progress)→- [x] ID — Title (P#, done) - In story detail sections: update
**Status:** done - Legacy format: mark as
[DONE]if epic uses old markers - Emit story lifecycle event for each completed story:
echo "{\"type\":\"story\",\"event\":\"status-change\",\"id\":\"<story-id>\",\"from\":\"review\",\"to\":\"done\",\"story_type\":\"<type>\",\"size\":\"<size>\",\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> docs/sessions/.activity-log.jsonl
- In the story checklist: change
- BACKLOG_INDEX.md: Update story counts per priority group. Update the Backlog Health section: recalculate Definition of Ready %, check for zombie stories (any story with
created:date >2 sprint cycles old still not done) - Sprint spec (
docs/sprints/sprint-N.md):- Update all story statuses to final state (✅ done or ⏭️ carried over)
- Fill in the
## Outcomesection with metrics:- Goal achieved: yes/no — did the sprint goal succeed?
- Stories completed: X/Y
- Throughput: X stories (count of ✅)
- Cycle time: average days per completed story (from git log first/last commit per story)
- Sprint churn: % of stories added or removed mid-sprint vs original plan (compare current stories table against the initial commit of the sprint spec:
git show $(git log --oneline --diff-filter=A -- docs/sprints/sprint-N.md | tail -1 | cut -d' ' -f1):docs/sprints/sprint-N.md) - Tests: before → after (+delta)
- Coverage: before% → after%
- Ground rules: X/Y checked, Z violations or "Clean"
- PR: #number
- Merged: today's date
- progress.md:
- Update
## Current Sprintto show completed state - Collect sprint satisfaction: Ask the developer to rate LLM output quality this sprint (1–5). Use AskUserQuestion: "Sprint satisfaction (1–5)? 1=significant rework needed, 3=acceptable with corrections, 5=excellent, minimal intervention"
- Compute sprint metrics from git and activity log:
- Tasks: count of ✅ stories
- Cycle time: avg days per story (git log first/last commit dates per story)
- Change failure rate: post-merge fixes / total changes this sprint (count commits with "fix" type after initial implementation)
- Test coverage Δ: coverage change on new/modified code (from CI output or test runner)
- Code churn ratio: run
git log --numstatto find lines modified within 14 days of creation / total lines (or usescripts/pm/metrics.sh --churn) - AI effectiveness: parse
docs/sessions/.activity-log.jsonl— compute skill success rate and context reset frequency (or usescripts/pm/metrics.sh --ai-effectiveness) - Sprint satisfaction: from user input above (X/5 format)
- Append row to
## Sprint History:| N | [goal] | ✅/❌ | X | X.Xd | X% | +X% | 0.XX | 0.XX | X/5 | #N | - Update
## Metricstable: For each metric row, update the Current column with this sprint's value. Recompute Trend sparklines from the last 6 Sprint History rows (use ▁▂▃▄▅▆▇█ — map values to 8 levels; for lower-is-better metrics like cycle time/CFR/churn, invert so up=improving). Compute Status using BOTH absolute and relative thresholds:- Absolute: 🟢 if within target, 🟡 if within 120% of target, 🔴 if beyond 120%. For "≥" targets, invert.
- Relative: Also compute % change from the 3-sprint rolling average in Sprint History. If metric worsened >50% from average, set status to at least 🟡 regardless of absolute position. If >100% worse, set to 🔴. Example: CFR at 14% (target ≤15%) is 🟢 by absolute threshold, but if the 3-sprint average was 7%, the 100% increase makes it 🔴.
- Take the worst status between absolute and relative checks.
- Sprint note: If any metric is 🟡 or 🔴, write a one-sentence explanation of the likely cause. Include Δ3avg context when relative change triggered the status (e.g., "CFR doubled from 3-sprint average despite being within absolute target"). Check for three-sprint trends (3 consecutive sprints in same direction = strong signal requiring action). When code churn ratio is 🟡 or 🔴, run
scripts/pm/metrics.sh --churnand include the top 3 hotspot files in the note (e.g., "Churn 🟡 — hotspots: src/auth/session.ts (7 changes), src/api/routes.ts (5 changes)"). - Update
## Next Stepswith post-sprint actions
- Update
- CLAUDE.md: Update Current Focus if epic status changed
- Project context (
docs/context/): If sprint changes affect architecture, patterns, or tech stack, incrementally update the relevant context files (usegit diff $DEFAULT_BRANCH...HEAD --name-onlyto identify affected areas). Updateupdated:timestamps in YAML frontmatter. - System patterns (
docs/context/system-patterns.md): Review commit messages and story plans from this sprint for pattern-related changes. If any story introduced a new implementation pattern, established a new convention, or changed the error handling/testing approach, update the relevant section. Add new implementation recipes when a repeated entity type was added for the first time (e.g., first API endpoint, first background job). Remove patterns for approaches no longer used. - Architecture doc (
docs/architecture/ARCHITECTURE.md): If any story in this sprint changed architecture (check the Update Triggers section), verify the doc was updated during story-cycle Phase 4e. If not, update it now and setLast Verifieddate to today. - SBOM (informational): If CycloneDX or Syft tools are available, generate or update
sbom.jsonto reflect current dependencies. If no SBOM tool is available, skip — note "SBOM generation: no tool available" in the PR body. This is informational, not blocking. - Technical debt register (
docs/technical-debt.md):- If quality gates (step 2) identified issues logged to the debt register, ensure each item has the full format (category, severity, origin, quantified impact, interest rate, effort, resolution plan)
- For debt introduced by AI-assisted code this sprint, set
origin: ai-generated - For debt resolved by sprint stories, move items to "Resolved" section with actual effort and sprint reference
- Update the header:
Active items: X | Resolved this quarter: Y - Record in sprint spec
## Outcome: Debt delta: +N added / -N resolved (net: +/-N). This data feeds into/retrospectivefor trend analysis.
PRD Living Document Review
If docs/reference/PRD_SUMMARY.md exists, review it against sprint learnings:
- Section 9 (Open questions): Were any assumptions validated or invalidated this sprint? Update their Status (Assumed → Validated/Invalidated). Were any open questions answered? Update Status (Open → Resolved).
- Section 5 (Requirements): Did any requirement's scope change during implementation? If acceptance criteria were added, modified, or found insufficient, note the delta.
- Section 3 (Success criteria): Can any criteria now be measured? Note baseline values if available from test output.
- Scope creep check: Compare stories delivered against PRD Section 5 requirements. Flag stories that don't trace to any PRD requirement — these may indicate scope creep or legitimate new requirements that the PRD should absorb.
If changes are needed, update PRD_SUMMARY.md and bump the version in the header comment.
Persona Assumption Update
If docs/context/personas.md exists and user-facing stories were delivered this sprint: check the Persona Assumptions table. Were any assumptions validated or invalidated by this sprint's work? Update their Confidence column (ASSUMED → CONFIRMED or INVALIDATED) with a brief note of the evidence. Bump the updated: date in frontmatter.
Skip when: No user-facing stories were delivered (pure infrastructure/refactoring sprint), or personas.md doesn't exist.
3.5 Commit Documentation Artifacts
Commit all documentation changes as a separate commit. This ensures documentation is preserved even if the push or PR step fails or is skipped.
# Check if there are uncommitted documentation changes
if ! git diff --quiet docs/ CLAUDE.md 2>/dev/null || \
! git diff --quiet --cached docs/ CLAUDE.md 2>/dev/null || \
git ls-files --others --exclude-standard docs/ | grep -q .; then
git add docs/ CLAUDE.md
git commit -m "docs(sprint): update sprint documentation and session artifacts
Co-Authored-By: Claude <noreply@anthropic.com>"
fi
If there are no documentation changes, skip this step gracefully. This commit happens regardless of whether push will be performed.
PR Size Check
LINES_CHANGED=$(git diff --stat $DEFAULT_BRANCH...HEAD | tail -1 | grep -oE '[0-9]+ insertion|[0-9]+ deletion' | grep -oE '[0-9]+' | paste -sd+ | bc 2>/dev/null || echo "0")
If LINES_CHANGED exceeds 400 lines, warn:
Large PR detected ([LINES_CHANGED] lines changed). Research shows PRs in the 200–400 line range have 40% fewer defects, and each additional 100 lines adds ~25 minutes of review time. Consider breaking this into stacked PRs for more effective review.
Offer to proceed as-is or help split the PR.
By story: Create one PR per completed story. Cherry-pick each story's commits to a new branch:
git checkout $DEFAULT_BRANCH git checkout -b feat/<story-description> git cherry-pick <story-commit-hashes> git push -u origin feat/<story-description> gh pr create --title "<type>(<scope>): <story summary>"Repeat for each story. Each PR should be ≤400 LOC.
By layer: Split into backend/frontend/infrastructure PRs if changes span layers.
By module: Split into separate PRs per module or package boundary.
Create split PRs in dependency order so CI passes on each. Reference the sprint spec in each PR for context. See docs/reference/GIT_WORKFLOW.md → Stacked PRs for advanced tooling.
4. Push and Create PR
git push -u origin $(git branch --show-current)
Create PR with GitHub CLI. The repository includes a PR template (.github/pull_request_template.md) — fill in its sections rather than providing a raw body:
gh pr create --title "<type>(<scope>): <summary>" --body "$(cat <<'EOF'
## Type of Change
- [x] <matching type from template>
## Summary
<1-3 sentences summarizing what was done and why>
## Changes
<list of key changes, grouped by area>
## Test Evidence
<paste test output from step 2>
## Quality Gates
- [x] All tests pass
- [x] Test count did not decrease
- [x] Code quality agent reviewed
- [x] Test validator agent reviewed
- [x/n/a] Security audit reviewed
- [x] No hardcoded secrets introduced
- [x] Coding standards followed
## Self-Review Checklist
- [x] I read the diff and it matches the intent
- [x] No debug code or TODOs left behind
- [x] Error handling is appropriate
- [x] New code follows existing patterns
- [x/n/a] Documentation updated where needed
## AI Assistance
- **AI-assisted:** <list components where AI generated or significantly modified code>
- **Human-written:** <list components written or heavily edited by hand, or "N/A">
- **AI review focus:** <specific areas where reviewers should check for AI anti-patterns>
Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
5. Wait for CI
Note: The framework includes a Claude PR Review workflow (.github/workflows/claude-pr-review.yml). If configured with an ANTHROPIC_API_KEY secret, it runs automated code review on PRs. Check its status alongside other CI checks.
No CI detected — the local quality gates in step 2 serve as verification. Proceed to merge.
Human Review Handling
gh pr view --json reviewRequests,reviews
If human review is required:
- List required reviewers from CODEOWNERS or branch protection rules
- Remind reviewers of Layer 4 focus (see
docs/reference/TEAM_WORKFLOW.md→ Code Review Process):- Architecture alignment with documented ADRs
- Business logic correctness — right problem solved?
- Intent verification — matches the story spec?
- AI-specific anti-patterns: phantom deps, code duplication, tautological tests, over-engineering
- Request reviews if not already requested:
gh pr edit --add-reviewer <reviewer> - Present to user:
**Human review required before merge.** - Reviewer(s): [list] - Status: [Pending / Approved / Changes Requested] Options: → Wait for review (recommended) → Continue to other work while waiting (`/story-cycle` or `/sprint-start --worktree`) - If "Changes Requested": guide user through addressing feedback, pushing updates, and re-requesting review
If no human review required: Proceed to merge.
6. Merge and Clean Up
Child stream cleanup (before the merge, while still on the sprint branch): For each merged child stream noted in step 1:
git worktree remove <child-worktree-path> # fails if the worktree is dirty — resolve first
git branch -d <child-branch> # safe delete works: the child is merged into this branch
git config --remove-section branch.<child-branch> 2>/dev/null || true
This must happen before switching to the default branch — after the squash merge, git branch -d would no longer recognize the children as merged. For any stream the user chose to abandon (unmerged commits), leave its branch in place and report it: safe delete will refuse, and force-deleting branches is blocked by the framework's git hooks on purpose. The user can delete it manually once they are certain.
Once CI is green (or local gates passed) and any required reviews are complete:
gh pr merge --squash --delete-branch
git checkout $DEFAULT_BRANCH
git pull origin $DEFAULT_BRANCH
git worktree prune
Worktree cleanup (if running in a worktree detected in step 1):
WORKTREE_PATH=$(pwd)
cd <main-worktree-path>
git worktree remove "$WORKTREE_PATH"
git worktree prune
Inform the user that the worktree has been removed and they should close the Claude Code instance that was using it.
Verify clean state:
git status
git log --oneline -3
7. Sprint Complete
Skill metrics: Emit a completion event:
echo "{\"type\":\"skill\",\"event\":\"end\",\"skill\":\"sprint-end\",\"outcome\":\"success\",\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> docs/sessions/.activity-log.jsonl
### Sprint Complete
**Sprint [N]: [goal]**
**Goal achieved:** [yes/no]
**Branch:** `sprint-<number>` (merged and deleted)
**PR:** #<number> (<url>)
**Stories:** [completed]/[total] delivered
**Throughput:** [X] stories | **Churn:** [X]%
**Tests:** [total] passing ([+delta] vs main) | **Coverage:** [X]%
**Carried over:** [count] stories
- [Goal-critical]: [list — these threaten the sprint goal, prioritize in next sprint]
- [Non-critical]: [list — correct prioritization, schedule when ready]
Non-critical carry-over is a positive signal — it means the team correctly prioritized goal-critical work over lower-priority items.
**Main is clean and up to date.**
**Sprint spec:** `docs/sprints/sprint-<number>.md`
**Next Steps:**
→ `/retrospective` — review sprint metrics and process
→ `/sprint-start` — begin the next sprint
→ `/handoff` — if ending the session
Graceful Degradation
| Dependency | If Missing |
|---|---|
| Sub-agents | Run quality checks manually in the main context |
| CI pipeline | Local quality gates (step 2) serve as verification |
| Test runner | Warn user, skip test count delta, note in PR body |
| Linter | Skip lint check, note in PR body |
| Type checker | Skip typecheck, note in PR body |
gh CLI |
Push manually, create PR via web UI |
Project State Adaptation
Read CLAUDE.md Commands section before running quality gates (or use the discover-commands micro-component from .claude/prompts/discover-commands.md):
Rules
- NEVER merge without CI passing (or local gates if no CI)
- NEVER force push or skip hooks
- NEVER merge without running quality agents
- NEVER merge if test count decreased (without explicit user approval)
- ALWAYS discover state from git — assume no prior context
- ALWAYS update documentation for completed stories
- ALWAYS squash merge to keep main history clean
- ALWAYS clean up worktrees after merge
- Follow coding standards in
docs/reference/CODING_STANDARDS.md