@kennethsolomon/shipkit 3.7.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +181 -257
  2. package/package.json +1 -1
  3. package/skills/sk:fast-track/SKILL.md +80 -0
  4. package/skills/sk:gates/SKILL.md +97 -0
  5. package/skills/sk:retro/SKILL.md +124 -0
  6. package/skills/sk:reverse-doc/SKILL.md +116 -0
  7. package/skills/sk:scope-check/SKILL.md +93 -0
  8. package/skills/sk:setup-claude/SKILL.md +55 -0
  9. package/skills/sk:setup-claude/scripts/apply_setup_claude.py +207 -6
  10. package/skills/sk:setup-claude/templates/.claude/agents/e2e-tester.md +46 -0
  11. package/skills/sk:setup-claude/templates/.claude/agents/linter.md +53 -0
  12. package/skills/sk:setup-claude/templates/.claude/agents/perf-auditor.md +43 -0
  13. package/skills/sk:setup-claude/templates/.claude/agents/security-auditor.md +47 -0
  14. package/skills/sk:setup-claude/templates/.claude/agents/test-runner.md +42 -0
  15. package/skills/sk:setup-claude/templates/.claude/rules/api.md.template +14 -0
  16. package/skills/sk:setup-claude/templates/.claude/rules/frontend.md.template +15 -0
  17. package/skills/sk:setup-claude/templates/.claude/rules/laravel.md.template +15 -0
  18. package/skills/sk:setup-claude/templates/.claude/rules/react.md.template +14 -0
  19. package/skills/sk:setup-claude/templates/.claude/rules/tests.md.template +16 -0
  20. package/skills/sk:setup-claude/templates/.claude/settings.json.template +76 -0
  21. package/skills/sk:setup-claude/templates/.claude/statusline.sh +50 -0
  22. package/skills/sk:setup-claude/templates/CLAUDE.md.template +18 -1
  23. package/skills/sk:setup-claude/templates/hooks/log-agent.sh +24 -0
  24. package/skills/sk:setup-claude/templates/hooks/pre-compact.sh +44 -0
  25. package/skills/sk:setup-claude/templates/hooks/session-start.sh +53 -0
  26. package/skills/sk:setup-claude/templates/hooks/session-stop.sh +33 -0
  27. package/skills/sk:setup-claude/templates/hooks/validate-commit.sh +81 -0
  28. package/skills/sk:setup-claude/templates/hooks/validate-push.sh +43 -0
  29. package/skills/sk:setup-claude/templates/tasks/cross-platform.md.template +31 -0
  30. package/skills/sk:setup-optimizer/SKILL.md +2 -1
@@ -0,0 +1,50 @@
1
+ #!/bin/bash
2
+ # Claude Code statusline: Shows persistent status in CLI
3
+ # Receives JSON on stdin with context_window, model info
4
+ # Outputs a single formatted line
5
+
6
+ INPUT=$(cat)
7
+
8
+ # Parse context and model — use jq if available
9
+ if command -v jq >/dev/null 2>&1; then
10
+ MODEL=$(echo "$INPUT" | jq -r '.model // "unknown"' 2>/dev/null)
11
+ CTX_USED=$(echo "$INPUT" | jq -r '.context_window.used // 0' 2>/dev/null)
12
+ CTX_TOTAL=$(echo "$INPUT" | jq -r '.context_window.total // 1' 2>/dev/null)
13
+ else
14
+ MODEL="unknown"
15
+ CTX_USED=0
16
+ CTX_TOTAL=1
17
+ fi
18
+
19
+ # Calculate context percentage
20
+ if [ "$CTX_TOTAL" -gt 0 ] 2>/dev/null; then
21
+ CTX_PCT=$((CTX_USED * 100 / CTX_TOTAL))
22
+ else
23
+ CTX_PCT=0
24
+ fi
25
+
26
+ # Branch
27
+ BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "none")
28
+
29
+ # Current workflow step
30
+ STEP="—"
31
+ if [ -f "tasks/workflow-status.md" ]; then
32
+ NEXT_LINE=$(grep -E ">>\s*next\s*<<" "tasks/workflow-status.md" 2>/dev/null | head -1)
33
+ if [ -n "$NEXT_LINE" ]; then
34
+ # Extract step number and name from table row
35
+ STEP_NUM=$(echo "$NEXT_LINE" | grep -oE '^\|[[:space:]]*[0-9]+' | grep -oE '[0-9]+')
36
+ STEP_NAME=$(echo "$NEXT_LINE" | sed 's/.*| *>> next << *|.*//' | sed 's/|.*//;s/^ *//;s/ *$//')
37
+ if [ -n "$STEP_NUM" ]; then
38
+ STEP="Step ${STEP_NUM}"
39
+ fi
40
+ fi
41
+ fi
42
+
43
+ # Task name from todo.md
44
+ TASK="—"
45
+ if [ -f "tasks/todo.md" ]; then
46
+ TASK=$(head -1 "tasks/todo.md" 2>/dev/null | sed 's/^# TODO.*— //' | cut -c1-40)
47
+ fi
48
+
49
+ # Output single line
50
+ echo "[${CTX_PCT}%] ${MODEL} | ${STEP} | ${BRANCH} | ${TASK}"
@@ -272,6 +272,21 @@ Use `run_in_background: true` for tasks that don't block your next step:
272
272
  ```
273
273
  <!-- END:sub-agent-patterns -->
274
274
 
275
+ ## Cross-Platform Tracking
276
+
277
+ This project may have a companion codebase (web <-> mobile). During implementation, **log every change that affects the other platform** to `tasks/cross-platform.md`.
278
+
279
+ **When to log:**
280
+ - API contract changes (new/modified endpoints, payload shapes, auth)
281
+ - Data model changes (new fields, type changes, validation rules)
282
+ - Business logic that must behave identically on both platforms
283
+ - UI/UX flows that should have parity
284
+ - Platform-specific deviations (intentional differences)
285
+
286
+ **How to log:** Append a new section using the template in `tasks/cross-platform.md`. Include enough context that a developer in the other codebase can implement without guessing.
287
+
288
+ **When to review:** At the start of every task, check `tasks/cross-platform.md` for pending items targeting this codebase.
289
+
275
290
  ## Project Memory
276
291
 
277
292
  Read these files at the start of every task:
@@ -279,12 +294,14 @@ Read these files at the start of every task:
279
294
  - `tasks/lessons.md` — past mistakes and how to avoid them
280
295
  - `tasks/todo.md` — current plan
281
296
  - `tasks/tech-debt.md` — known shortcuts, deferred work, and areas to revisit
297
+ - `tasks/cross-platform.md` — pending changes from the other codebase
282
298
 
283
299
  Write to these files continuously:
284
300
  - `tasks/progress.md` — every attempt, error, and resolution
285
301
  - `tasks/findings.md` — anything important discovered mid-task
302
+ - `tasks/cross-platform.md` — any change that impacts the other platform
286
303
 
287
- **Never overwrite** `tasks/lessons.md` or `tasks/security-findings.md`.
304
+ **Never overwrite** `tasks/lessons.md`, `tasks/security-findings.md`, or `tasks/cross-platform.md`.
288
305
 
289
306
  ## Lessons Capture
290
307
 
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+ # Claude Code SubagentStart hook: Log agent invocations for audit trail
3
+ # Tracks which agents are being used and when
4
+ #
5
+ # Input schema (SubagentStart):
6
+ # { "agent_id": "agent-abc123", "agent_name": "linter", ... }
7
+
8
+ INPUT=$(cat)
9
+
10
+ # Parse agent name
11
+ if command -v jq >/dev/null 2>&1; then
12
+ AGENT_NAME=$(echo "$INPUT" | jq -r '.agent_name // "unknown"' 2>/dev/null)
13
+ else
14
+ AGENT_NAME=$(echo "$INPUT" | grep -oE '"agent_name"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"agent_name"[[:space:]]*:[[:space:]]*"//;s/"$//')
15
+ [ -z "$AGENT_NAME" ] && AGENT_NAME="unknown"
16
+ fi
17
+
18
+ TIMESTAMP=$(date +%Y%m%d_%H%M%S)
19
+
20
+ # Append to audit log (create tasks/ dir if needed)
21
+ mkdir -p tasks 2>/dev/null
22
+ echo "$TIMESTAMP | Agent invoked: $AGENT_NAME" >> "tasks/agent-audit.log" 2>/dev/null
23
+
24
+ exit 0
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ # Claude Code PreCompact hook: Preserve session state before context compression
3
+ # Outputs critical state so Claude retains it after compaction
4
+
5
+ echo "=== Pre-Compaction State Snapshot ==="
6
+
7
+ # Workflow status
8
+ if [ -f "tasks/workflow-status.md" ]; then
9
+ echo ""
10
+ echo "--- workflow-status.md ---"
11
+ cat "tasks/workflow-status.md" 2>/dev/null | head -30
12
+ TOTAL_LINES=$(wc -l < "tasks/workflow-status.md" 2>/dev/null | tr -d ' ')
13
+ if [ "$TOTAL_LINES" -gt 30 ]; then
14
+ echo " ... ($TOTAL_LINES total lines)"
15
+ fi
16
+ fi
17
+
18
+ # Git status
19
+ echo ""
20
+ echo "--- Uncommitted Changes ---"
21
+ STAGED=$(git diff --cached --name-only 2>/dev/null)
22
+ UNSTAGED=$(git diff --name-only 2>/dev/null)
23
+ UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null)
24
+
25
+ [ -n "$STAGED" ] && echo "Staged:" && echo "$STAGED" | sed 's/^/ /'
26
+ [ -n "$UNSTAGED" ] && echo "Unstaged:" && echo "$UNSTAGED" | sed 's/^/ /'
27
+ [ -n "$UNTRACKED" ] && echo "Untracked:" && echo "$UNTRACKED" | sed 's/^/ /'
28
+
29
+ if [ -z "$STAGED" ] && [ -z "$UNSTAGED" ] && [ -z "$UNTRACKED" ]; then
30
+ echo " (clean)"
31
+ fi
32
+
33
+ # Log compaction event
34
+ if [ -d "tasks" ]; then
35
+ echo "" >> "tasks/progress.md" 2>/dev/null
36
+ echo "### [$(date +%Y-%m-%d)] Context compaction occurred at $(date +%H:%M:%S)" >> "tasks/progress.md" 2>/dev/null
37
+ fi
38
+
39
+ echo ""
40
+ echo "--- Recovery ---"
41
+ echo "Read tasks/workflow-status.md to restore current step."
42
+ echo "Read tasks/progress.md for recent work."
43
+ echo "==================================="
44
+ exit 0
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+ # Claude Code SessionStart hook: Load project context at session start
3
+ # Outputs context that Claude sees when a session begins
4
+
5
+ echo "=== ShipKit — Session Context ==="
6
+
7
+ # Current branch
8
+ BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
9
+ if [ -n "$BRANCH" ]; then
10
+ echo "Branch: $BRANCH"
11
+ echo ""
12
+ echo "Recent commits:"
13
+ git log --oneline -5 2>/dev/null | while read -r line; do
14
+ echo " $line"
15
+ done
16
+ fi
17
+
18
+ # Current workflow step from workflow-status.md
19
+ if [ -f "tasks/workflow-status.md" ]; then
20
+ echo ""
21
+ NEXT_STEP=$(grep -E ">>\s*next\s*<<" "tasks/workflow-status.md" 2>/dev/null | head -1)
22
+ if [ -n "$NEXT_STEP" ]; then
23
+ echo "Workflow: $NEXT_STEP"
24
+ else
25
+ echo "Workflow: all steps complete or not started"
26
+ fi
27
+ fi
28
+
29
+ # Tech debt count
30
+ if [ -f "tasks/tech-debt.md" ]; then
31
+ TOTAL=$(grep -c "^### \[" "tasks/tech-debt.md" 2>/dev/null || echo 0)
32
+ RESOLVED=$(grep -c "^Resolved:" "tasks/tech-debt.md" 2>/dev/null || echo 0)
33
+ UNRESOLVED=$((TOTAL - RESOLVED))
34
+ if [ "$UNRESOLVED" -gt 0 ]; then
35
+ echo "Tech Debt: $UNRESOLVED unresolved item(s)"
36
+ fi
37
+ fi
38
+
39
+ # Code health (skip on large codebases to keep session start fast)
40
+ if [ -d "src" ]; then
41
+ FILE_COUNT=$(find src/ -type f 2>/dev/null | head -1001 | wc -l | tr -d ' ')
42
+ if [ "$FILE_COUNT" -le 1000 ]; then
43
+ TODO_COUNT=$(grep -r "TODO" src/ 2>/dev/null | wc -l | tr -d ' ')
44
+ FIXME_COUNT=$(grep -r "FIXME" src/ 2>/dev/null | wc -l | tr -d ' ')
45
+ if [ "$TODO_COUNT" -gt 0 ] || [ "$FIXME_COUNT" -gt 0 ]; then
46
+ echo ""
47
+ echo "Code health: ${TODO_COUNT} TODOs, ${FIXME_COUNT} FIXMEs in src/"
48
+ fi
49
+ fi
50
+ fi
51
+
52
+ echo "==================================="
53
+ exit 0
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ # Claude Code Stop hook: Log session accomplishments
3
+ # Runs when a Claude Code session ends
4
+
5
+ # Only log if tasks/ directory exists (ShipKit project)
6
+ if [ ! -d "tasks" ]; then
7
+ exit 0
8
+ fi
9
+
10
+ TIMESTAMP=$(date +%Y-%m-%d\ %H:%M:%S)
11
+
12
+ # Get current branch
13
+ BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
14
+
15
+ # Count commits in this session (last hour)
16
+ RECENT_COMMITS=$(git log --since="1 hour ago" --oneline 2>/dev/null | wc -l | tr -d ' ')
17
+
18
+ # Get current workflow step
19
+ CURRENT_STEP=""
20
+ if [ -f "tasks/workflow-status.md" ]; then
21
+ CURRENT_STEP=$(grep -E ">>\s*next\s*<<" "tasks/workflow-status.md" 2>/dev/null | head -1 | sed 's/.*| //' | sed 's/ |.*//')
22
+ fi
23
+
24
+ # Append session end to progress.md
25
+ {
26
+ echo ""
27
+ echo "### [$TIMESTAMP] Session ended"
28
+ echo "- Branch: $BRANCH"
29
+ echo "- Commits this session: $RECENT_COMMITS"
30
+ [ -n "$CURRENT_STEP" ] && echo "- Next step: $CURRENT_STEP"
31
+ } >> "tasks/progress.md" 2>/dev/null
32
+
33
+ exit 0
@@ -0,0 +1,81 @@
1
+ #!/bin/bash
2
+ # Claude Code PreToolUse hook: Validates git commit commands
3
+ # Receives JSON on stdin with tool_input.command
4
+ # Exit 0 = allow, Exit 2 = block (stderr shown to Claude)
5
+ #
6
+ # Validates: conventional commit format, debug statements, hardcoded secrets
7
+
8
+ INPUT=$(cat)
9
+
10
+ # Parse command — use jq if available, fall back to grep
11
+ if command -v jq >/dev/null 2>&1; then
12
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
13
+ else
14
+ COMMAND=$(echo "$INPUT" | grep -oE '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"command"[[:space:]]*:[[:space:]]*"//;s/"$//')
15
+ fi
16
+
17
+ # Only process git commit commands
18
+ if ! echo "$COMMAND" | grep -qE '^git[[:space:]]+commit'; then
19
+ exit 0
20
+ fi
21
+
22
+ WARNINGS=""
23
+
24
+ # Extract commit message from -m flag
25
+ COMMIT_MSG=$(echo "$COMMAND" | grep -oE '\-m[[:space:]]+"[^"]*"' | sed 's/-m[[:space:]]*"//;s/"$//' || echo "")
26
+ if [ -z "$COMMIT_MSG" ]; then
27
+ COMMIT_MSG=$(echo "$COMMAND" | grep -oE "\-m[[:space:]]+'[^']*'" | sed "s/-m[[:space:]]*'//;s/'$//" || echo "")
28
+ fi
29
+
30
+ # Validate conventional commit format (type(scope): message or type: message)
31
+ if [ -n "$COMMIT_MSG" ]; then
32
+ if ! echo "$COMMIT_MSG" | grep -qE '^(feat|fix|docs|refactor|chore|test|style|perf|ci|build|revert)(\([a-zA-Z0-9_-]+\))?(!)?:[[:space:]].+'; then
33
+ WARNINGS="$WARNINGS\nCOMMIT: Message does not follow conventional commit format: type(scope): message"
34
+ fi
35
+ fi
36
+
37
+ # Check staged files for debug statements and secrets
38
+ STAGED=$(git diff --cached --name-only 2>/dev/null)
39
+ if [ -n "$STAGED" ]; then
40
+ # Read full staged diff once (avoids repeated git subprocess calls per file)
41
+ FULL_DIFF=$(git diff --cached 2>/dev/null)
42
+
43
+ while IFS= read -r file; do
44
+ if [ -f "$file" ]; then
45
+ FILE_DIFF=$(echo "$FULL_DIFF" | sed -n "/^diff --git a\/$file /,/^diff --git /p")
46
+ # JavaScript/TypeScript debug
47
+ if echo "$file" | grep -qE '\.(js|ts|jsx|tsx)$'; then
48
+ if echo "$FILE_DIFF" | grep -E '^\+.*console\.(log|debug|warn)\(' | grep -qv '//.*console'; then
49
+ WARNINGS="$WARNINGS\nDEBUG: $file has console.log/debug/warn in staged changes"
50
+ fi
51
+ if echo "$FILE_DIFF" | grep -qE '^\+.*debugger'; then
52
+ WARNINGS="$WARNINGS\nDEBUG: $file has debugger statement in staged changes"
53
+ fi
54
+ fi
55
+ # PHP debug
56
+ if echo "$file" | grep -qE '\.php$'; then
57
+ if echo "$FILE_DIFF" | grep -qE '^\+.*(dd\(|dump\(|var_dump\(|print_r\()'; then
58
+ WARNINGS="$WARNINGS\nDEBUG: $file has dd/dump/var_dump in staged changes"
59
+ fi
60
+ fi
61
+ # Python debug
62
+ if echo "$file" | grep -qE '\.py$'; then
63
+ if echo "$FILE_DIFF" | grep -qE '^\+.*(breakpoint\(\)|pdb\.set_trace\(\))'; then
64
+ WARNINGS="$WARNINGS\nDEBUG: $file has breakpoint/pdb in staged changes"
65
+ fi
66
+ fi
67
+ fi
68
+ done <<< "$STAGED"
69
+
70
+ # Check for potential hardcoded secrets (reuse FULL_DIFF)
71
+ if echo "$FULL_DIFF" | grep -qE '^\+.*(PRIVATE_KEY|SECRET_KEY|API_KEY|PASSWORD|TOKEN)[[:space:]]*=.*[a-zA-Z0-9]{16,}'; then
72
+ WARNINGS="$WARNINGS\nSECRET: Staged changes may contain hardcoded secrets. Review before committing."
73
+ fi
74
+ fi
75
+
76
+ # Print warnings (non-blocking) and allow commit
77
+ if [ -n "$WARNINGS" ]; then
78
+ echo -e "=== Commit Validation Warnings ===$WARNINGS\n================================" >&2
79
+ fi
80
+
81
+ exit 0
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Claude Code PreToolUse hook: Warns on push to protected branches
3
+ # Receives JSON on stdin with tool_input.command
4
+ # Exit 0 = allow (warnings only), Exit 2 = block
5
+ #
6
+ # Warns on: push to main/master/production/release, force push
7
+
8
+ INPUT=$(cat)
9
+
10
+ # Parse command
11
+ if command -v jq >/dev/null 2>&1; then
12
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
13
+ else
14
+ COMMAND=$(echo "$INPUT" | grep -oE '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"command"[[:space:]]*:[[:space:]]*"//;s/"$//')
15
+ fi
16
+
17
+ # Only process git push commands
18
+ if ! echo "$COMMAND" | grep -qE '^git[[:space:]]+push'; then
19
+ exit 0
20
+ fi
21
+
22
+ WARNINGS=""
23
+
24
+ # Check for protected branch names
25
+ for branch in main master production release; do
26
+ if echo "$COMMAND" | grep -qE "(origin[[:space:]]+${branch}|[[:space:]]${branch}$)"; then
27
+ WARNINGS="$WARNINGS\nPROTECTED: Pushing to '${branch}' — this is a protected branch. Confirm this is intentional."
28
+ fi
29
+ done
30
+
31
+ # Check for force push
32
+ if echo "$COMMAND" | grep -qE '\-\-force([[:space:]]|$)|\-f([[:space:]]|$)'; then
33
+ WARNINGS="$WARNINGS\nFORCE: Using --force push. This rewrites remote history and is destructive."
34
+ fi
35
+ if echo "$COMMAND" | grep -qE '\-\-force-with-lease'; then
36
+ WARNINGS="$WARNINGS\nFORCE: Using --force-with-lease. Safer than --force but still rewrites history."
37
+ fi
38
+
39
+ if [ -n "$WARNINGS" ]; then
40
+ echo -e "=== Push Validation Warnings ===$WARNINGS\n================================" >&2
41
+ fi
42
+
43
+ exit 0
@@ -0,0 +1,31 @@
1
+ # Cross-Platform Changes
2
+
3
+ Track changes that need to be replicated in the other codebase (web <-> mobile).
4
+
5
+ <!--
6
+ Format: One section per feature/task.
7
+ Each entry should have enough context for a developer (or Claude session)
8
+ in the other codebase to implement without guessing.
9
+ -->
10
+
11
+ <!-- TEMPLATE — copy this block for each new feature:
12
+
13
+ ## [Feature Name] (YYYY-MM-DD)
14
+ Source: [web|mobile] — the codebase where this was implemented first
15
+
16
+ ### API Changes
17
+ - [ ] Description of endpoint/payload/auth changes
18
+
19
+ ### Data Model Changes
20
+ - [ ] New fields, renamed fields, type changes, validation rules
21
+
22
+ ### Behavior Changes
23
+ - [ ] Business logic that must match across platforms
24
+
25
+ ### UI/UX Parity
26
+ - [ ] Screens, flows, or interactions that should match
27
+
28
+ ### Platform-Specific Notes
29
+ - [ ] Anything that differs intentionally between web and mobile
30
+
31
+ -->
@@ -39,7 +39,7 @@ The single command to keep your CLAUDE.md current. Diagnoses problems, updates t
39
39
 
40
40
  Before making any changes, runs a diagnostic pass on the existing CLAUDE.md:
41
41
 
42
- - **Missing sections** — checks for essential sections (Workflow, Sub-Agent Patterns, Project Memory, Lessons Capture, Testing, Commands, etc.)
42
+ - **Missing sections** — checks for essential sections (Workflow, Sub-Agent Patterns, Cross-Platform Tracking, Project Memory, Lessons Capture, Testing, Commands, etc.)
43
43
  - **Stale content** — detects outdated info (stale model/route counts, removed dependencies, old command names like `/laravel-lint` instead of `/sk:lint`)
44
44
  - **Inconsistencies** — compares documented vs actual project state (directories, scripts, workflows)
45
45
  - **Section completeness** — flags sections that exist but are empty or have only placeholder text
@@ -63,6 +63,7 @@ Read → Explore → Design → Accessibility → Plan → Branch → Migrate
63
63
  - Step completion summary rule (NON-NEGOTIABLE)
64
64
  - Bug fix flow section
65
65
  - Sub-Agent Patterns section (if missing)
66
+ - Cross-Platform Tracking section (if missing)
66
67
  - Project Memory section (if missing)
67
68
  - Lessons Capture section (if missing)
68
69
  - Testing TDD section (if missing)