@ikunin/sprintpilot 1.0.3 → 1.0.5

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.
@@ -8,23 +8,19 @@ You do NOT hardcode the workflow sequence. After each completed skill, read its
8
8
 
9
9
  **Git integration** is additive. If `_Sprintpilot/manifest.yaml` doesn't exist or `git.enabled: false`, all git operations are silently skipped and this workflow behaves identically to the stock autopilot.
10
10
 
11
- ### Shell portability (IMPORTANT)
11
+ ### Shell portability
12
12
 
13
- Sprintpilot runs under any LLM CLI (Claude Code, Gemini CLI, Cursor, etc.) on any OS. The shell that executes commands may be **bash, zsh, PowerShell, or cmd** depending on platform and CLI. Shell-specific idioms will fail silently when the wrong shell is used.
13
+ The executing shell may be bash, zsh, PowerShell, or cmd translate bash idioms as needed:
14
14
 
15
- **When you encounter bash-style idioms below, translate them to your shell.** The table applies to **external commands** (like `git`); cmdlets have slightly different conventions.
15
+ | Bash | PowerShell |
16
+ |---|---|
17
+ | `A && B` | `A; if ($LASTEXITCODE -eq 0) { B }` |
18
+ | `A \|\| true` | `A; $LASTEXITCODE = 0` |
19
+ | `2>/dev/null` | `2>$null` |
20
+ | `rm -rf <dir>` | `Remove-Item -Recurse -Force <dir>` |
21
+ | `if [ -f X ]; then ... fi` | `if (Test-Path -PathType Leaf X) { ... }` |
16
22
 
17
- | Bash idiom | PowerShell equivalent | Meaning |
18
- |---|---|---|
19
- | `A && B` | `A; if ($LASTEXITCODE -eq 0) { B }` (or separate commands, guarding B manually) | Run B only if A succeeded |
20
- | `A \|\| true` | `A; $LASTEXITCODE = 0` (or `try { A } catch {}` for cmdlets) | Run A, ignore failures |
21
- | `2>/dev/null` | `2>$null` | Suppress stderr |
22
- | `rm -rf <dir>` | `Remove-Item -Recurse -Force <dir>` | Recursive delete |
23
- | `if [ -f X ]; then ... fi` | `if (Test-Path -PathType Leaf X) { ... }` | File-exists check (regular file, not dir) |
24
-
25
- **Safer:** when in doubt, use the cross-platform Node helpers under `_Sprintpilot/scripts/`. For ad-hoc file ops, invoke Node inline: `node -e "require('fs').rmSync('<path>', {recursive: true, force: true})"`.
26
-
27
- If a step below uses `&&` to chain "run B only on A's success", and you cannot express that in one line, **run the commands separately and STOP if any step fails** — do not proceed past a failed step.
23
+ For cross-platform file ops prefer the Node helpers under `_Sprintpilot/scripts/`, or inline: `node -e "require('fs').rmSync('<path>', {recursive: true, force: true})"`. When a step chains commands with `&&` and you cannot express it in one line, run them separately and STOP on any failure.
28
24
 
29
25
  ---
30
26
 
@@ -75,55 +71,20 @@ For everything else: decide, document briefly, continue.
75
71
 
76
72
  ## DECISION LOGGING
77
73
 
78
- Every non-trivial decision made during autopilot execution MUST be logged to `{decision_log_file}`. This creates an audit trail the user reviews at the end of each session.
79
-
80
- ### When to log
81
-
82
- Log a decision whenever you:
83
- - Choose an architecture pattern, data structure, or design approach (`architecture`)
84
- - Select a test strategy or skip a test category (`test-strategy`)
85
- - Add, remove, or substitute a dependency (`dependency`)
86
- - Dismiss a code review finding (`review-triage`)
87
- - Accept and apply a code review finding (`review-accept`)
88
- - Recover from a HALT condition (`halt-recovery`)
89
- - Implement something not explicitly in the story spec (`scope`)
90
- - Apply a workaround for a tool limitation or false positive (`workaround`)
74
+ Log every non-trivial decision to `{decision_log_file}` (skip routine actions running tests, staging files, creating branches). Create the file on first decision; update `last_updated` on every append.
91
75
 
92
- Do NOT log routine actions (running tests, staging files, creating branches).
93
-
94
- ### File format
95
-
96
- Initialize `{decision_log_file}` on first decision (if it does not exist):
76
+ **Categories:** `architecture`, `test-strategy`, `dependency`, `review-triage` (dismissed finding), `review-accept` (applied fix), `halt-recovery`, `scope` (outside story spec), `workaround`.
77
+ **Impact:** `low` (reversible/cosmetic), `medium` (affects one component), `high` (cross-cutting or deviates from spec).
78
+ **Phase format:** `{skill}:{sub_phase}` — e.g. `dev-story:RED`, `code-review:triage`, `autopilot:routing`.
97
79
 
80
+ **File schema:**
98
81
  ```yaml
99
- generated: {current_date}
100
- last_updated: {current_datetime}
101
-
102
- decisions: []
82
+ generated: {date}
83
+ last_updated: {datetime}
84
+ decisions:
85
+ - { id, timestamp, story, phase, category, decision, rationale, impact }
103
86
  ```
104
87
 
105
- Append each decision as a new entry:
106
-
107
- ```yaml
108
- - id: {auto_increment}
109
- timestamp: "{current_datetime_iso8601}"
110
- story: "{current_story or sprint-level}"
111
- phase: "{skill}:{sub_phase}"
112
- category: {architecture|test-strategy|dependency|review-triage|review-accept|halt-recovery|scope|workaround}
113
- decision: "{what was decided — one line}"
114
- rationale: "{why — one line}"
115
- impact: {low|medium|high}
116
- ```
117
-
118
- **Phase format:** `dev-story:RED`, `dev-story:GREEN`, `code-review:triage`, `code-review:patch`, `autopilot:init`, `autopilot:routing`, etc.
119
-
120
- **Impact levels:**
121
- - `low` — easily reversible, cosmetic, or standard practice
122
- - `medium` — affects behavior but contained to one story/component
123
- - `high` — cross-cutting, hard to reverse, or deviates from spec
124
-
125
- Always update `last_updated` when appending.
126
-
127
88
  ---
128
89
 
129
90
  ## SKILL AUTOMATABLE REFERENCE
@@ -138,6 +99,7 @@ All BMAD skills are fully automatable (auto-continue past menus, derive decision
138
99
  | `bmad-create-ux-design` | Automatable if PRD exists; BLOCKER if no PRD |
139
100
  | `bmad-party-mode` | Skip — inherently interactive |
140
101
  | `bmad-brainstorming` | Skip — inherently interactive |
102
+ | `bmad-retrospective` | Under autopilot, handled per `autopilot.retrospective_mode`: `auto` (default — inline artifact, no external skill call), `stop` (pause so user runs `/bmad-retrospective` interactively, then resumes autopilot), or `skip` (not recommended). The external skill is NOT invoked from autopilot because it enters a multi-persona discussion loop under some CLIs. |
141
103
 
142
104
  ---
143
105
 
@@ -155,6 +117,8 @@ Resolve:
155
117
  - `project_root` = absolute path of current working directory (store for later use)
156
118
  - `session_story_limit` is loaded below from `modules/autopilot/config.yaml` (default: 3)
157
119
 
120
+ **`{state_file}` schema** (referenced as `STATE_FIELDS` below): `last_updated`, `current_story`, `current_bmad_step`, `completed_skill`, `next_skill`, `session_stories_done`, `stories_remaining`, `git_enabled`, `platform`, `in_worktree`, `pr_base`. Always update `last_updated` on every write.
121
+
158
122
  ### Git integration bootstrap
159
123
 
160
124
  <action>Check if `{project-root}/_Sprintpilot/manifest.yaml` exists</action>
@@ -173,13 +137,15 @@ Resolve:
173
137
  </action>
174
138
  <action>Read `{project-root}/_Sprintpilot/modules/autopilot/config.yaml` (if present) and set:
175
139
  - `{{session_story_limit}}` from `autopilot.session_story_limit` (default: 3). A value of 0 disables the limit (run until sprint complete).
176
- If the file or key is missing, fall back to 3.
140
+ - `{{retrospective_mode}}` from `autopilot.retrospective_mode` (default: `auto`). Valid values: `auto` | `stop` | `skip`. Any unknown value falls back to `auto`.
141
+ If the file or either key is missing, fall back to the defaults above.
177
142
  </action>
178
143
  </check>
179
144
 
180
145
  <check if="manifest does NOT exist">
181
146
  <action>Set `{{git_enabled}}` = false</action>
182
147
  <action>Set `{{session_story_limit}}` = 3</action>
148
+ <action>Set `{{retrospective_mode}}` = `auto`</action>
183
149
  <action>Log: "No _Sprintpilot/manifest.yaml found — running stock autopilot (no git)"</action>
184
150
  </check>
185
151
 
@@ -197,6 +163,17 @@ Resolve:
197
163
  <action>STOP</action>
198
164
  </check>
199
165
 
166
+ <action>**Check for `origin` remote** — run: `git remote get-url origin`
167
+ If the command fails (exit code != 0), no `origin` remote is configured. Set `{{has_origin}}` = false.
168
+ Otherwise set `{{has_origin}}` = true.
169
+ </action>
170
+ <check if="{{has_origin}} is false">
171
+ <action>Log: "WARN: no `origin` remote configured — running in local-only mode. Remote operations (fetch, push, PR, branch reconciliation) will be skipped. Add a remote later with: `git remote add origin <url>`"</action>
172
+ <action>Set `{{push_auto}}` = false</action>
173
+ <action>Set `{{create_pr}}` = false</action>
174
+ <action>Set `{{platform}}` = "git_only"</action>
175
+ </check>
176
+
200
177
  <action>**Lock file** — run: `node {{project_root}}/_Sprintpilot/scripts/lock.js acquire`
201
178
  Output will be one of:
202
179
  - `ACQUIRED:<session-id>` → proceed
@@ -232,7 +209,8 @@ Resolve:
232
209
  </action>
233
210
 
234
211
  <action>**Branch reconciliation** — detect pushed-but-unmerged story branches.
235
- Run as separate commands — **if `git fetch origin` fails (network/auth), STOP branch reconciliation and log a warning; do not operate on stale local refs**:
212
+ Skip this entire section if `{{has_origin}}` is false (no remote nothing to reconcile).
213
+ Run as separate commands — **if `git fetch origin` fails (no remote/network/auth), STOP branch reconciliation and log a warning; do not operate on stale local refs**:
236
214
  1. `git fetch origin`
237
215
  2. `git branch -r --list "origin/{{branch_prefix}}*"`
238
216
  For each remote branch:
@@ -315,26 +293,27 @@ Resolve:
315
293
  - Advance to next non-done story
316
294
  - Update `{state_file}` with reconciled values
317
295
  </action>
296
+
297
+ <!-- Resume from a `retrospective_mode: stop` pause. -->
298
+ <check if="{state_file}.paused_at is epic-complete-awaiting-retrospective">
299
+ <action>Set `{{paused_epic_id}}` from `{state_file}.paused_epic_id`. Check if epic `{{paused_epic_id}}` is `done` in `{status_file}` OR an artifact exists at `{implementation_artifacts}/retrospectives/epic-{{paused_epic_id}}-*.md`.</action>
300
+ <check if="epic is done OR retrospective artifact exists">
301
+ <action>Clear `paused_at`, `paused_epic_id`, `next_action` from `{state_file}`. Log: "Epic {{paused_epic_id}} retrospective detected — resuming autopilot".</action>
302
+ </check>
303
+ <check if="epic is NOT done AND no retrospective artifact">
304
+ <action>Report: "Autopilot still paused — epic {{paused_epic_id}} retrospective not yet done. Run `/bmad-retrospective` interactively, then re-run `/sprint-autopilot-on`. (To bypass: set `retrospective_mode` to `auto` or `skip` in `_Sprintpilot/modules/autopilot/config.yaml`.)" Then STOP.</action>
305
+ </check>
306
+ </check>
307
+
318
308
  <goto step="2">Jump to execution loop with reconciled state</goto>
319
309
  </check>
320
310
 
321
311
  <check if="state_file does NOT exist">
322
- <action>Check if `{status_file}` exists if not, invoke `bmad-sprint-planning` first</action>
323
-
324
- <check if="{{git_enabled}} AND status_file did not exist (sprint-planning just ran)">
325
- <action>Run `git fetch origin` to ensure remote refs are current</action>
326
- <action>Initialize `{git_status_file}` with git_integration block:
327
- ```yaml
328
- # Sprintpilot — Git Status
329
- git_integration:
330
- enabled: true
331
- base_branch: {git.base_branch from config}
332
- platform: {{platform}}
333
-
334
- stories:
335
- ```
336
- Note: this is the addon's own file — NEVER write git fields to sprint-status.yaml.
337
- </action>
312
+ <action>Check if `{status_file}` exists. If NOT, do NOT jump to `bmad-sprint-planning` (Phase 4 skill, requires Phase 1–3 artifacts). Invoke `bmad-help` — "No sprint-status.yaml found. What is the current phase and which skill should run first?" — and set `{{next_skill}}` from its response. Expected routing: no PRD → `bmad-create-prd` (BLOCKER); PRD → `bmad-create-architecture`; architecture → `bmad-create-epics-and-stories`; epics → `bmad-sprint-planning`.</action>
313
+
314
+ <check if="{{git_enabled}} AND status_file did not exist AND {{next_skill}} is bmad-sprint-planning (planning just completed earlier in this flow)">
315
+ <action>Run `git fetch origin` warn + skip on failure (no remote/auth/network), do not abort bootstrap.</action>
316
+ <action>Initialize `{git_status_file}` (addon-owned — NEVER write git fields to sprint-status.yaml) with: `git_integration: { enabled: true, base_branch: <from config>, platform: {{platform}} }` and empty `stories:`.</action>
338
317
  </check>
339
318
 
340
319
  <action>Read `{status_file}` — find all stories not yet `done`</action>
@@ -345,21 +324,7 @@ Resolve:
345
324
  - `{{session_stories_done}}` = 0
346
325
  </action>
347
326
  <action>Create master task: "Sprintpilot — Full Sprint Execution" → `in_progress`</action>
348
- <action>Write initial `{state_file}`:
349
- ```yaml
350
- last_updated: {current_datetime}
351
- current_story: null
352
- current_bmad_step: null
353
- completed_skill: bmad-help
354
- next_skill: {{next_skill}}
355
- session_stories_done: 0
356
- stories_remaining: [list from sprint-status]
357
- git_enabled: {{git_enabled}}
358
- platform: {{platform}}
359
- in_worktree: false
360
- pr_base: {{base_branch}}
361
- ```
362
- </action>
327
+ <action>Write initial `{state_file}` with STATE_FIELDS: `current_story = null`, `current_bmad_step = null`, `completed_skill = bmad-help`, `session_stories_done = 0`, `stories_remaining = [from sprint-status]`, `in_worktree = false`, `pr_base = {{base_branch}}`.</action>
363
328
  <action>Report to user:
364
329
  ```
365
330
  Sprintpilot ON
@@ -369,6 +334,7 @@ Resolve:
369
334
  Git integration: {{git_enabled}}
370
335
  Platform: {{platform}}
371
336
  Session limit: {{session_story_limit}} stories, then checkpoint + new session
337
+ Retrospective mode: {{retrospective_mode}}
372
338
 
373
339
  Beginning autonomous execution. I will only stop for true blockers or session checkpoints.
374
340
  ```
@@ -451,90 +417,33 @@ Resolve:
451
417
 
452
418
  <!-- GIT: Enter worktree before dev-story -->
453
419
  <check if="{{git_enabled}} AND {{next_skill}} is bmad-dev-story">
454
- <action>**Sanitize branch name** run:
455
- `node {{project_root}}/_Sprintpilot/scripts/sanitize-branch.js "{{current_story}}" --prefix "{{branch_prefix}}" --max-length 60`
456
- Output: sanitized name (without prefix). Set `{{branch_name}}` = output.
457
- Full branch ref will be `{{branch_prefix}}{{branch_name}}`.
458
- </action>
459
-
460
- <action>**Check if branch already registered** in `{status_file}` for this story.
461
- If yes AND worktree already exists → skip creation (idempotent).
462
- If yes AND no worktree → recovery mode (see health check).
463
- If no → proceed with creation.
464
- </action>
420
+ <action>**Sanitize branch name**: `node {{project_root}}/_Sprintpilot/scripts/sanitize-branch.js "{{current_story}}" --prefix "{{branch_prefix}}" --max-length 60`. Set `{{branch_name}}` = output. Full ref: `{{branch_prefix}}{{branch_name}}`.</action>
465
421
 
466
- <action>**Prepare for worktree** determine the correct branch point.
467
- Run: `git fetch origin`
422
+ <action>**Idempotency check** — if branch is already registered in `{status_file}` for this story AND its worktree exists, skip creation. If registered without worktree → recovery mode (see health check). Otherwise proceed.</action>
468
423
 
469
- Check if there is a previous story in this epic with a pushed but unmerged branch (PR pending):
470
- - Read `{git_status_file}` for earlier stories in the same epic
471
- - Find the latest story branch where `push_status` = "pushed" AND `pr_url` is a valid URL
472
- - Check if that branch has been merged to `{{base_branch}}`: `git merge-base --is-ancestor origin/{{branch_prefix}}<prev-branch> origin/{{base_branch}}`
424
+ <action>**Pick branch point.** If `{{has_origin}}` is true: `git fetch origin` (warn + continue on failure). If `{{has_origin}}` is false: skip fetch, use local refs.
473
425
 
474
- If an unmerged previous story branch exists:
475
- - Branch from it: `git checkout origin/{{branch_prefix}}<prev-branch>`
476
- - Set `{{pr_base}}` = `{{branch_prefix}}<prev-branch>` (PR should target previous story, not main)
477
- - Log: "Branching from {{branch_prefix}}<prev-branch> (PR pending merge)"
478
- Otherwise:
479
- - Branch from base: `git checkout origin/{{base_branch}}`
480
- - Set `{{pr_base}}` = `{{base_branch}}`
426
+ Read `{git_status_file}` for earlier stories in this epic; find the latest with `push_status = "pushed"` AND a valid `pr_url`; check if merged to base: `git merge-base --is-ancestor origin/{{branch_prefix}}<prev-branch> origin/{{base_branch}}`.
427
+ - If unmerged previous story exists (requires `{{has_origin}}`): `git checkout origin/{{branch_prefix}}<prev-branch>`, set `{{pr_base}}` = `{{branch_prefix}}<prev-branch>`.
428
+ - Otherwise: `git checkout origin/{{base_branch}}` (or local `{{base_branch}}` if no origin), set `{{pr_base}}` = `{{base_branch}}`.
481
429
 
482
- (Detached HEAD is fine — the worktree add below creates a new branch from HEAD)
430
+ Detached HEAD is fine — `git worktree add` below creates a new branch from HEAD.
483
431
  </action>
484
432
 
485
- <action>**Create worktree** using standard git commands (works in any coding agent):
486
- ```
487
- git worktree add "{{project_root}}/.worktrees/{{current_story}}" -b "{{branch_prefix}}{{branch_name}}" 2>&1
488
- ```
489
- This creates `.worktrees/{{current_story}}/` with a new branch `{{branch_prefix}}{{branch_name}}` from HEAD.
490
-
491
- If worktree add fails (branch already exists):
492
- ```
493
- git worktree add "{{project_root}}/.worktrees/{{current_story}}" "{{branch_prefix}}{{branch_name}}" 2>&1
494
- ```
433
+ <action>**Create worktree.** Try: `git worktree add "{{project_root}}/.worktrees/{{current_story}}" -b "{{branch_prefix}}{{branch_name}}" 2>&1`. If it fails because the branch already exists, retry without `-b`: `git worktree add "{{project_root}}/.worktrees/{{current_story}}" "{{branch_prefix}}{{branch_name}}" 2>&1`.
495
434
 
496
- **If both fail** (disk full, permissions, etc.):
497
- - Log: "WARN: git worktree add failed — continuing without worktree isolation"
498
- - Set `{{in_worktree}}` = false
499
- - Create branch manually: `git checkout -b {{branch_prefix}}{{branch_name}}`
500
- If checkout also fails (branch already exists): `git checkout {{branch_prefix}}{{branch_name}}`
501
- If both fail: HALT — "Could not create or switch to branch {{branch_prefix}}{{branch_name}}"
502
- - Continue with the skill invocation in PROJECT_ROOT (no isolation)
503
- - Git operations (commit, push, PR) still work on the branch
435
+ If both fail (disk/permissions): log "WARN: worktree add failed — continuing without isolation", set `{{in_worktree}}` = false, and fall back to branch-only mode: `git checkout -b {{branch_prefix}}{{branch_name}}` (retry without `-b` if branch exists). HALT only if the checkout also fails. Git push/PR still work on the branch.
504
436
  </action>
505
437
 
506
438
  <check if="worktree add succeeded">
507
- <action>**Change working directory** to the worktree:
508
- `cd {{project_root}}/.worktrees/{{current_story}}`
509
- All subsequent file operations and commands MUST use this directory.
510
- Set `{{worktree_path}}` = `{{project_root}}/.worktrees/{{current_story}}`
511
- </action>
512
-
513
- <action>**Init submodules** if needed.
514
- First check for `.gitmodules` (use your file-exists tool, or `node -e "process.exit(require('fs').existsSync('.gitmodules')?0:1)"`). If not present, skip this step.
515
- If present, run `git submodule update --init --recursive` (give it ~30 seconds). If the command fails or hangs, warn "Submodule init failed (may need auth). Continuing without." and proceed.
516
- </action>
517
-
439
+ <action>`cd {{project_root}}/.worktrees/{{current_story}}`. All subsequent commands run from here. Set `{{worktree_path}}` = this path.</action>
440
+ <action>**Init submodules** if `.gitmodules` exists (check with your file-exists tool or `node -e "process.exit(require('fs').existsSync('.gitmodules')?0:1)"`). Run `git submodule update --init --recursive` (~30s). On failure/hang: warn "Submodule init failed (may need auth). Continuing." and proceed.</action>
518
441
  <action>Set `{{in_worktree}}` = true</action>
519
442
  </check>
520
- <action>Update `{state_file}` (write to worktree copy since we're now IN the worktree)</action>
443
+ <action>Update `{state_file}` (write to the worktree copy since cwd is now the worktree)</action>
521
444
  </check>
522
445
 
523
- <action>Update `{state_file}`:
524
- ```yaml
525
- last_updated: {current_datetime}
526
- current_story: {{current_story}}
527
- current_bmad_step: executing
528
- completed_skill: {previous skill}
529
- next_skill: {{next_skill}}
530
- session_stories_done: {{session_stories_done}}
531
- stories_remaining: {{stories_remaining}}
532
- git_enabled: {{git_enabled}}
533
- platform: {{platform}}
534
- in_worktree: {{in_worktree}}
535
- pr_base: {{pr_base}}
536
- ```
537
- </action>
446
+ <action>Update `{state_file}` with STATE_FIELDS (set `current_bmad_step = executing`, `completed_skill = <previous skill>`).</action>
538
447
 
539
448
  <!-- Autopilot menu handling rules apply — see AUTOPILOT RULES section above -->
540
449
 
@@ -586,8 +495,12 @@ pr_base: {{pr_base}}
586
495
  <goto step="7">Mark story done</goto>
587
496
  </check>
588
497
 
589
- <check if="{{completed_skill}} was bmad-retrospective">
590
- <action>Log: "Epic retrospective complete BMAD skills will update sprint-status.yaml directly"</action>
498
+ <check if="{{completed_skill}} is retrospective-auto">
499
+ <action>Log: "Epic retrospective generated inline by autopilot sprint-status.yaml updated"</action>
500
+ </check>
501
+
502
+ <check if="{{completed_skill}} is retrospective-skip">
503
+ <action>Log: "Epic retrospective skipped per config — sprint-status.yaml updated inline"</action>
591
504
  </check>
592
505
 
593
506
  <check if="{{completed_skill}} was bmad-create-epics-and-stories">
@@ -601,7 +514,8 @@ pr_base: {{pr_base}}
601
514
  </check>
602
515
 
603
516
  <check if="{{completed_skill}} was bmad-sprint-planning AND {{git_enabled}}">
604
- <action>Run `git fetch origin`</action>
517
+ <action>If `{{has_origin}}` is true, run `git fetch origin` (log a warning on failure and continue — do not abort).
518
+ If `{{has_origin}}` is false, skip the fetch.</action>
605
519
  <action>Initialize `{git_status_file}` if it doesn't exist (with git_integration block)</action>
606
520
  </check>
607
521
 
@@ -617,22 +531,10 @@ pr_base: {{pr_base}}
617
531
  </action>
618
532
  </check>
619
533
 
620
- <!-- GIT: Commit planning artifacts to main after planning skills -->
621
534
  <check if="{{git_enabled}} AND {{completed_skill}} is a planning skill (bmad-create-prd, bmad-create-architecture, bmad-create-ux-design, bmad-create-epics-and-stories, bmad-sprint-planning, bmad-check-implementation-readiness, bmad-create-story)">
622
- <action>**Commit planning artifacts to main** — keep track of all planning decisions in git.
623
- Stage all changed artifacts (ignore errors — any of these paths may not yet exist):
624
- ```
625
- git add _bmad-output/planning-artifacts/ _bmad-output/implementation-artifacts/ _bmad-output/stories/
626
- ```
627
- Check if there's anything staged; if yes, commit:
628
- ```
629
- git diff --cached --quiet
630
- ```
631
- If that exits non-zero (there are staged changes), run: `git commit -m "docs: {{completed_skill}} artifacts"`
632
- Then push (log a warning if push fails; do not halt autopilot):
633
- ```
634
- git push origin {{base_branch}}
635
- ```
535
+ <action>**Commit planning artifacts to main.**
536
+ 1. `git add _bmad-output/planning-artifacts/ _bmad-output/implementation-artifacts/ _bmad-output/stories/` (ignore missing-path errors)
537
+ 2. If `git diff --cached --quiet` exits non-zero: `git commit -m "docs: {{completed_skill}} artifacts"` then `git push origin {{base_branch}}` (warn on push failure, do not halt).
636
538
  </action>
637
539
  </check>
638
540
 
@@ -643,32 +545,17 @@ pr_base: {{pr_base}}
643
545
 
644
546
  <step n="5" goal="Determine next skill — from skill output first, bmad-help as fallback">
645
547
 
646
- <action>Read the output of `{{completed_skill}}`</action>
647
-
648
- <check if="output contains 'Next Steps', 'What to do next', 'Run next', or equivalent">
649
- <action>Extract `{{next_skill}}` from that section</action>
650
- <action>Log: "Next step from skill output: {{next_skill}}"</action>
651
- </check>
548
+ <action>Read the output of `{{completed_skill}}`. If it contains "Next Steps", "What to do next", "Run next", or equivalent, extract `{{next_skill}}` from that section. Otherwise invoke `bmad-help` — "{{completed_skill}} just finished. What is the next required workflow step?" — and extract `{{next_skill}}` from its response. Log the source ("skill output" vs "bmad-help fallback").</action>
652
549
 
653
- <check if="output contains NO clear next step">
654
- <action>Invoke `bmad-help` "{{completed_skill}} just finished. What is the next required workflow step?"</action>
655
- <action>Extract `{{next_skill}}` from bmad-help response</action>
656
- <action>Log: "Next step from bmad-help fallback: {{next_skill}}"</action>
657
- </check>
658
-
659
- <check if="{{next_skill}} is null, empty, or signals completion (no further steps / sprint done / all done)">
660
- <action>**Verify against source of truth**re-read `{status_file}` and check for any story with status != "done"</action>
661
- <check if="undone stories exist in status_file">
662
- <action>Set `{{current_story}}` = first undone story from `{status_file}`</action>
663
- <action>Determine `{{next_skill}}` based on that story's current status and BMAD step:
664
- - If story has no story file yet → `bmad-create-story`
665
- - If story file exists but status is `ready-for-dev` → `bmad-check-implementation-readiness`
666
- - If story is `in-progress` and `current_bmad_step` is before `code-review` (i.e. RED or GREEN phase) → `bmad-dev-story`
667
- - If story is `in-progress` and `current_bmad_step` is `code-review` or later → `bmad-code-review`
668
- - Otherwise → invoke `bmad-help` for precise determination
669
- </action>
670
- <action>Log: "next_skill was empty but undone stories remain — resolved to {{next_skill}} for {{current_story}}"</action>
671
- </check>
550
+ <check if="{{next_skill}} is null, empty, or signals completion">
551
+ <action>**Verify against source of truth** — re-read `{status_file}`. If undone stories exist, set `{{current_story}}` = first one and determine `{{next_skill}}`:
552
+ - No story file → `bmad-create-story`
553
+ - Story file + status `ready-for-dev` `bmad-check-implementation-readiness`
554
+ - Status `in-progress` and `current_bmad_step` before `code-review` → `bmad-dev-story`
555
+ - Status `in-progress` and `current_bmad_step` ≥ `code-review` → `bmad-code-review`
556
+ - Else invoke `bmad-help` for precise determination
557
+ Log: "next_skill was empty but undone stories remain resolved to {{next_skill}} for {{current_story}}".
558
+ </action>
672
559
  <check if="all stories in status_file are done">
673
560
  <goto step="10">Sprint complete</goto>
674
561
  </check>
@@ -779,37 +666,15 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
779
666
  </action>
780
667
 
781
668
  <check if="{{create_pr}} is false OR {{platform}} is git_only OR {{pr_url}} is null or SKIPPED">
782
- <action>**Merge story branch to main** tracked and retryable.
783
- Run:
784
- ```
785
- git checkout -B {{base_branch}} origin/{{base_branch}}
786
- git merge {{branch_prefix}}{{branch_name}} --no-edit
787
- ```
788
- If succeeds:
789
- - `git push origin {{base_branch}}`
790
- - Set `{{merge_status}}` = "merged"
791
- If fails (conflict):
792
- - `git merge --abort`
793
- - `git fetch origin`
794
- - `git checkout -B {{base_branch}} origin/{{base_branch}}`
795
- - Retry merge once: `git merge {{branch_prefix}}{{branch_name}} --no-edit`
796
- - If retry succeeds: push, set `{{merge_status}}` = "merged"
797
- - If retry fails: set `{{merge_status}}` = "failed"
798
- Log: "WARN: merge failed for {{current_story}} — will retry on next boot"
799
-
800
- If `{{merge_status}}` == "failed":
801
- Log warning but do NOT halt. The branch is pushed and preserved.
802
- Boot reconciliation (INITIALIZATION branch reconciliation) will retry on next session.
803
-
804
- Note: `{{merge_status}}` is persisted by the full sync-status.js call later in this step (via `--merge-status`). Do NOT call sync-status.js separately here — it does full block replacement and would destroy other fields.
669
+ <action>**Merge story branch to main.** If `{{has_origin}}` is false (local-only), substitute `origin/{{base_branch}}` → `{{base_branch}}` and skip all `git push origin` / `git fetch origin` calls below.
670
+ 1. `git checkout -B {{base_branch}} origin/{{base_branch}}` then `git merge {{branch_prefix}}{{branch_name}} --no-edit`.
671
+ 2. On success: `git push origin {{base_branch}}`, set `{{merge_status}}` = "merged".
672
+ 3. On conflict: `git merge --abort`, `git fetch origin`, re-checkout base, retry merge once. On retry success: push + merged. On retry failure: `{{merge_status}}` = "failed", log warning, continue — the branch is preserved and boot reconciliation retries next session.
673
+
674
+ `{{merge_status}}` is persisted by the sync-status.js call later in this step (via `--merge-status`). Do NOT call sync-status.js here — it does full block replacement and would destroy other fields.
805
675
  </action>
806
676
  <check if="{{cleanup_on_merge}} is true">
807
- <action>**Cleanup worktree** for merged story branch was merged locally, worktree is no longer needed. Ignore failures from the remove (the worktree may already be gone):
808
- ```
809
- git worktree remove .worktrees/{{current_story}} --force
810
- git worktree prune
811
- ```
812
- </action>
677
+ <action>**Cleanup worktree** (ignore failuresmay already be gone): `git worktree remove .worktrees/{{current_story}} --force` then `git worktree prune`</action>
813
678
  </check>
814
679
  </check>
815
680
  <check if="{{pr_url}} is a valid URL (not null, not SKIPPED)">
@@ -818,29 +683,11 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
818
683
  <action>Log: "Story {{current_story}} pushed — PR awaiting review: {{pr_url}}"</action>
819
684
  </check>
820
685
 
821
- <!-- Commit all implementation artifacts and status updates to main after each story -->
822
- <action>**Commit story completion artifacts to main** — ensure main always reflects current sprint state.
823
- ```
824
- git checkout -B {{base_branch}} origin/{{base_branch}}
825
- ```
826
- </action>
827
-
828
- <action>**Write git status** to addon's own file (NEVER modify sprint-status.yaml) — runs AFTER checkout to base branch so the file persists in the working tree for the commit below:
829
- `node {{project_root}}/_Sprintpilot/scripts/sync-status.js --story "{{current_story}}" --git-status-file "{{project_root}}/_bmad-output/implementation-artifacts/git-status.yaml" --branch "{{branch_prefix}}{{branch_name}}" --commit "{{story_commit}}" --patch-commits "{{patch_commits_csv}}" --push-status "{{push_status}}" --merge-status "{{merge_status}}" --pr-url "{{pr_url}}" --lint-result "{{lint_result}}" --worktree "{{project_root}}/.worktrees/{{current_story}}" --platform "{{platform}}" --base-branch "{{base_branch}}"`
830
- This writes to `git-status.yaml` (addon-owned). Sprint-status.yaml is BMAD-owned — updated by BMAD skills only.
831
- </action>
832
-
833
- <action>**Stage and commit artifacts** — explicitly include git-status.yaml and decision-log.yaml. Ignore errors from the `git add` (any listed path may not yet exist):
834
- ```
835
- git add _bmad-output/implementation-artifacts/sprint-status.yaml _bmad-output/implementation-artifacts/git-status.yaml _bmad-output/implementation-artifacts/autopilot-state.yaml _bmad-output/implementation-artifacts/decision-log.yaml _bmad-output/stories/ _bmad-output/planning-artifacts/
836
- ```
837
- Check if anything is staged: `git diff --cached --quiet`. If that exits non-zero, commit:
838
- `git commit -m "docs: story {{current_story}} done — {{test_count}} tests{{#if pr_url}}, PR: {{pr_url}}{{/if}}"`
839
- Then push (log a warning if push fails; do not halt autopilot):
840
- ```
841
- git push origin {{base_branch}}
842
- ```
843
- This ensures sprint-status.yaml, git-status.yaml, story files, and any updated artifacts are on main even when story code is on a PR branch.
686
+ <action>**Commit story artifacts to main** keeps main in sync even when story code is on a PR branch.
687
+ 1. `git checkout -B {{base_branch}} origin/{{base_branch}}`
688
+ 2. Write git-status.yaml (addon-owned — never touch sprint-status.yaml): `node {{project_root}}/_Sprintpilot/scripts/sync-status.js --story "{{current_story}}" --git-status-file "{{project_root}}/_bmad-output/implementation-artifacts/git-status.yaml" --branch "{{branch_prefix}}{{branch_name}}" --commit "{{story_commit}}" --patch-commits "{{patch_commits_csv}}" --push-status "{{push_status}}" --merge-status "{{merge_status}}" --pr-url "{{pr_url}}" --lint-result "{{lint_result}}" --worktree "{{project_root}}/.worktrees/{{current_story}}" --platform "{{platform}}" --base-branch "{{base_branch}}"`
689
+ 3. Stage artifacts (ignore errors for missing paths): `git add _bmad-output/implementation-artifacts/sprint-status.yaml _bmad-output/implementation-artifacts/git-status.yaml _bmad-output/implementation-artifacts/autopilot-state.yaml _bmad-output/implementation-artifacts/decision-log.yaml _bmad-output/stories/ _bmad-output/planning-artifacts/`
690
+ 4. If `git diff --cached --quiet` exits non-zero: `git commit -m "docs: story {{current_story}} done — {{test_count}} tests{{#if pr_url}}, PR: {{pr_url}}{{/if}}"` then `git push origin {{base_branch}}` (warn on push failure, do not halt).
844
691
  </action>
845
692
  </check>
846
693
 
@@ -858,36 +705,37 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
858
705
 
859
706
  <action>Check if ALL stories in this epic are `done`</action>
860
707
  <check if="epic complete">
861
- <action>Create task "[epic] retrospective" `in_progress`</action>
862
- <action>INVOKE `bmad-retrospective` using Skill tool (retrospective skill updates sprint-status.yaml itself)</action>
863
- <action>Mark retrospective task → `completed`</action>
864
- <action>Set `{{completed_skill}}` = `bmad-retrospective`</action>
708
+ <action>Resolve `{{epic_id}}` (e.g. "1") and `{{epic_title}}` from `{status_file}` for the current epic</action>
709
+ <action>Create task "[epic {{epic_id}}] retrospective" `in_progress`</action>
710
+
711
+ <!-- Retrospective: driven by `autopilot.retrospective_mode`. The external
712
+ `bmad-retrospective` skill is NEVER invoked from autopilot (multi-persona
713
+ discussion loop under some CLIs). -->
714
+
715
+ <check if="{{retrospective_mode}} is auto">
716
+ <action>Collect from `{status_file}` for epic `{{epic_id}}`: done stories `{ story-key, title, test_pass_count, patch_count }`, epic title, dates if present.</action>
717
+ <action>Collect decision-log entries for epic `{{epic_id}}` (match `story` prefix `{{epic_id}}-` or `phase: autopilot:*` tagged to this epic). Identify open risks / carry-over notes from any story `notes`/`risks` fields or `workaround` decisions for this epic.</action>
718
+ <action>Ensure `{implementation_artifacts}/retrospectives/` exists. Read template `{{project_root}}/_Sprintpilot/templates/epic-retrospective.md`, fill mustache placeholders, write to `{implementation_artifacts}/retrospectives/epic-{{epic_id}}-retrospective.md`.</action>
719
+ <action>Update `{status_file}`: `epics.{{epic_id}}.status = done`, `.retrospective_path = <file>`, `.completed_at = {current_date}`.</action>
720
+ <action>Append decision-log entry: `{ category: workaround, decision: "retrospective generated inline", rationale: "retrospective_mode=auto", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`.</action>
721
+ <action>Mark retrospective task → `completed`. Set `{{completed_skill}}` = `retrospective-auto`.</action>
722
+ </check>
865
723
 
866
- <!-- GIT: Epic completion — suggest merge, cleanup worktrees -->
867
- <check if="{{git_enabled}}">
868
- <action>**List all epic PR/MR URLs** from `{status_file}` for this epic's stories</action>
869
- <action>Report:
870
- ```
871
- Epic complete — PR/MR summary:
872
- {{#each epic_stories}}
873
- - {{story-key}}: {{pr_url}}
874
- {{/each}}
724
+ <check if="{{retrospective_mode}} is stop">
725
+ <action>Update `{state_file}`: `paused_at = epic-complete-awaiting-retrospective`, `paused_epic_id = {{epic_id}}`, `next_action = "run /bmad-retrospective interactively for epic {{epic_id}}, then re-run /sprint-autopilot-on"`.</action>
726
+ <action>Append decision-log entry: `{ category: workaround, decision: "paused for interactive retrospective", rationale: "retrospective_mode=stop", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`. Mark retrospective task → `completed`.</action>
727
+ <action>Report: "Autopilot paused — epic {{epic_id}} complete, retrospective handed off. Run `/bmad-retrospective` interactively for epic {{epic_id}}, then re-run `/sprint-autopilot-on`. State saved to: {state_file}." Then STOP.</action>
728
+ </check>
875
729
 
876
- Ready to merge. Review PRs and confirm when ready.
877
- ```
878
- </action>
730
+ <check if="{{retrospective_mode}} is skip">
731
+ <action>Update `{status_file}`: `epics.{{epic_id}}.status = done`, `.retrospective_path = null`, `.retrospective_skipped = true`, `.completed_at = {current_date}`.</action>
732
+ <action>Append decision-log entry: `{ category: workaround, decision: "retrospective skipped", rationale: "retrospective_mode=skip (NOT RECOMMENDED)", impact: medium, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`. Mark retrospective task → `completed`. Set `{{completed_skill}}` = `retrospective-skip`.</action>
733
+ </check>
734
+
735
+ <check if="{{git_enabled}}">
736
+ <action>**Epic PR summary** — list all epic PR/MR URLs from `{status_file}` and report as "Epic complete — PR/MR summary: [list]. Ready to merge — review PRs and confirm when ready."</action>
879
737
  <check if="{{cleanup_on_merge}} is true">
880
- <action>**Cleanup worktrees** for completed stories:
881
- For each story in this epic:
882
- 1. Check if worktree at `.worktrees/{{story-key}}` exists
883
- 2. Check if clean: `git -C .worktrees/{{story-key}} status --porcelain`
884
- 3. If clean → `git worktree remove .worktrees/{{story-key}}` + `git worktree prune`
885
- Update `{git_status_file}` for this story: `worktree_cleaned: true`
886
- 4. If dirty → warn user, skip cleanup
887
- </action>
888
- </check>
889
- <check if="{{cleanup_on_merge}} is false">
890
- <action>Log: "Worktree cleanup skipped (git.worktree.cleanup_on_merge = false)"</action>
738
+ <action>**Cleanup worktrees** for completed stories. For each: if `.worktrees/{{story-key}}` exists, check cleanliness via `git -C .worktrees/{{story-key}} status --porcelain`. If clean → `git worktree remove` + `git worktree prune` and set `worktree_cleaned: true` in `{git_status_file}`. If dirty → warn and skip.</action>
891
739
  </check>
892
740
  </check>
893
741
  </check>
@@ -904,21 +752,7 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
904
752
 
905
753
  <step n="8" goal="Save state and continue">
906
754
 
907
- <action>Update `{state_file}`:
908
- ```yaml
909
- last_updated: {current_datetime}
910
- current_story: {{current_story}}
911
- current_bmad_step: {{current_bmad_step}}
912
- completed_skill: {{completed_skill}}
913
- next_skill: {{next_skill}}
914
- session_stories_done: {{session_stories_done}}
915
- stories_remaining: {{stories_remaining}}
916
- git_enabled: {{git_enabled}}
917
- platform: {{platform}}
918
- in_worktree: {{in_worktree}}
919
- pr_base: {{pr_base}}
920
- ```
921
- </action>
755
+ <action>Update `{state_file}` with STATE_FIELDS.</action>
922
756
 
923
757
  <goto step="2">Continue execution loop</goto>
924
758
 
@@ -953,21 +787,7 @@ pr_base: {{pr_base}}
953
787
  </action>
954
788
  </check>
955
789
 
956
- <action>Update `{state_file}`:
957
- ```yaml
958
- last_updated: {current_datetime}
959
- current_story: {{current_story}}
960
- current_bmad_step: {{current_bmad_step}}
961
- completed_skill: {{completed_skill}}
962
- next_skill: {{next_skill}}
963
- session_stories_done: {{session_stories_done}}
964
- stories_remaining: {{stories_remaining}}
965
- git_enabled: {{git_enabled}}
966
- platform: {{platform}}
967
- in_worktree: {{in_worktree}}
968
- pr_base: {{pr_base}}
969
- ```
970
- </action>
790
+ <action>Update `{state_file}` with STATE_FIELDS.</action>
971
791
 
972
792
  <action>Read `{decision_log_file}` — count medium/high decisions from this session's stories</action>
973
793
 
@@ -1017,133 +837,53 @@ No work will be repeated.
1017
837
  <action>Run full test suite — report `N/N passed`</action>
1018
838
 
1019
839
  <!-- Generate project documentation after sprint completion -->
1020
- <action>**Generate documentation** — create or update project README and docs.
1021
- Invoke `bmad-document-project` skill to auto-generate documentation from the completed implementation.
1022
- If the skill is not available or fails, generate a minimal README.md:
1023
- - Project name and description (from product brief / PRD)
1024
- - How to install (`npm install` / `pip install` / etc.)
1025
- - How to run (`npm start` / the launch command)
1026
- - How to test (`npm test`)
1027
- - Architecture overview (from architecture doc if it exists)
1028
- </action>
1029
-
1030
- <!-- GIT: Commit documentation and final artifacts to main -->
1031
- <check if="{{git_enabled}}">
1032
- <action>**Commit final artifacts and documentation to main**. Run each step; if an early step fails, STOP and log — don't proceed past a failed step. `git add` may fail for missing optional paths (`docs/`, `README.md`); ignore those path-specific errors. Failure of the final push should log a warning but not halt autopilot:
1033
- ```
1034
- git checkout -B {{base_branch}} origin/{{base_branch}}
1035
- git add _bmad-output/ README.md docs/
1036
- ```
1037
- Check if anything is staged: `git diff --cached --quiet`. If that exits non-zero, commit:
1038
- `git commit -m "docs: project documentation and final artifacts"`
1039
- Then: `git push origin {{base_branch}}`
1040
- </action>
1041
- </check>
1042
-
1043
- <action>Read `{status_file}` and collect:
1044
- - All completed stories grouped by epic, with their story titles
1045
- - Total story count, total epic count
1046
- - Final test count
1047
- - If git_enabled: all PR/MR URLs, patch counts, dismissed findings per story
840
+ <action>**Resolve stack** — set `{{stack}}` = `{ name, install_cmd, run_cmd, test_cmd }` using the first successful source:
841
+
842
+ 1. **`project-context.md`** (glob `**/project-context.md`, canonical `{output_folder}/project-context.md`) extract from "Technology Stack & Versions" and any install/run/test subsections.
843
+ 2. **`architecture.md`** (`{planning_artifacts}/architecture.md`) extract from "Tech Stack" / "Runtime" / "Build & Deploy" / "Commands" sections.
844
+ 3. **Manifest heuristics** map manifest file stack idiomatic commands:
845
+
846
+ | Manifest | Stack | install / run / test |
847
+ |---|---|---|
848
+ | `package.json` | Node/JS/TS | `<pm> install` / `<pm> run start\|dev\|serve` (or `node <bin>`) / `<pm> test` — `<pm>` = `pnpm`/`yarn`/`bun`/`npm` by lockfile |
849
+ | `pyproject.toml`, `requirements.txt`, `setup.py` | Python | `pip install -r requirements.txt` (or `-e .`) / Django `python manage.py runserver` → Flask → FastAPI `uvicorn app:app` → `python main.py`/`app.py` / `pytest` |
850
+ | `go.mod` | Go | `go mod download` / `go run .` (or `./cmd/<name>`) / `go test ./...` |
851
+ | `Cargo.toml` | Rust | `cargo build` / `cargo run` (or `--bin <name>`) / `cargo test` |
852
+ | `pom.xml` | Java/Kotlin (Maven) | `mvn install` / `mvn spring-boot:run` or `mvn exec:java` / `mvn test` |
853
+ | `build.gradle(.kts)` | Java/Kotlin (Gradle) | `./gradlew build` / `./gradlew bootRun` or `run` / `./gradlew test` |
854
+ | `Gemfile` | Ruby | `bundle install` / `rails server` or `bundle exec ruby <entry>` / `bundle exec rspec` |
855
+ | `*.csproj`/`*.sln` | .NET | `dotnet restore` / `dotnet run` (or `--project`) / `dotnet test` |
856
+ | `composer.json` | PHP | `composer install` / `php artisan serve` (Laravel) or `php -S localhost:8000 -t public` / `vendor/bin/phpunit` |
857
+ | `mix.exs` | Elixir | `mix deps.get` / `mix phx.server` or `mix run --no-halt` / `mix test` |
858
+ | (none of the above) | Explicit launcher | `./run.sh`/`./run_gui.sh`/`./start.sh`, `make run\|start\|dev`, `docker compose up`, `docker build` + `docker run` |
859
+
860
+ 4. **No match** — all fields `null`. Downstream omits the line; never guess.
861
+
862
+ Set `{{launch_cmd}}` = `{{stack.run_cmd}}`.
863
+ If `{{stack}}` came from (3) and `project-context.md` exists without stack info, log: "Consider running `bmad-generate-project-context` to capture stack commands."
1048
864
  </action>
1049
865
 
1050
- <action>Read `{decision_log_file}` and collect:
1051
- - All decisions with impact `medium` or `high`
1052
- - Count of `review-accept` entries (patches applied)
1053
- - Count of `review-triage` entries (findings dismissed)
1054
- - Total review rounds (count of code-review invocations)
1055
- - Per-story summary: patches applied and findings dismissed
1056
- </action>
866
+ <action>**Generate documentation** — invoke `bmad-document-project`. If unavailable or it fails, write a minimal README using `{{stack}}`: project name + description (from brief/PRD); install/run/test lines for each non-null `{{stack.*_cmd}}` (omit lines where null); architecture overview if `architecture.md` exists.</action>
1057
867
 
1058
- <action>Find the app launch command by checking (in order):
1059
- 1. `run_gui.sh` or `run.sh` in the project root
1060
- 2. `main.py` in the project root
1061
- 3. Check `pyproject.toml`, `package.json`, or `setup.py` for scripts
1062
- Record as `{{launch_cmd}}`
1063
- </action>
1064
-
1065
- <!-- GIT: Final worktree cleanup — safety net for any worktrees not cleaned during epic completion -->
1066
868
  <check if="{{git_enabled}}">
1067
- <action>**Cleanup all remaining worktrees**:
1068
- Run: `git worktree list --porcelain`
1069
- For each worktree that is NOT the main worktree, run the following — log and continue on failure; some worktrees may already be gone:
1070
- `git worktree remove <path> --force`
1071
- Then: `git worktree prune`
869
+ <action>**Commit final artifacts + docs to main.**
870
+ 1. `git checkout -B {{base_branch}} origin/{{base_branch}}`
871
+ 2. `git add _bmad-output/ README.md docs/` (ignore missing-path errors)
872
+ 3. If `git diff --cached --quiet` exits non-zero: `git commit -m "docs: project documentation and final artifacts"` then `git push origin {{base_branch}}` (warn on push failure).
1072
873
  </action>
1073
874
  </check>
1074
875
 
1075
- <!-- GIT: Release lock -->
876
+ <action>**Collect report data** from `{status_file}` (stories grouped by epic with titles, totals, final test count; PR/MR URLs, patch/dismissed counts per story if git_enabled) and `{decision_log_file}` (medium/high-impact decisions; counts of `review-accept`, `review-triage`, code-review rounds; per-story patches-applied / findings-dismissed).</action>
877
+
1076
878
  <check if="{{git_enabled}}">
879
+ <action>**Cleanup remaining worktrees** (safety net): `git worktree list --porcelain` → for each non-main worktree: `git worktree remove <path> --force` then `git worktree prune` (log + continue on failure).</action>
1077
880
  <action>Release lock: `node {{project_root}}/_Sprintpilot/scripts/lock.js release`</action>
1078
881
  </check>
1079
882
 
1080
883
  <action>Delete `{state_file}` — sprint complete</action>
1081
884
  <action>Mark master task "Sprintpilot — Full Sprint Execution" → `completed`</action>
1082
885
 
1083
- <action>Report (use exact format):
1084
- ```
1085
- ╔═══════════════════════════════════════════════════════════════╗
1086
- ║ BMAD AUTOPILOT — REPORT ║
1087
- ╚═══════════════════════════════════════════════════════════════╝
1088
-
1089
- SUMMARY
1090
- Stories completed : {{done_count}}/{{total_stories}}
1091
- Epics completed : {{done_epics}}/{{total_epics}}
1092
- Total tests : {{N}}/{{N}} passed
1093
- {{#if git_enabled}}
1094
- Platform : {{platform}}
1095
- {{/if}}
1096
-
1097
- STORIES
1098
- {{#each epic}}
1099
- Epic {{epic_number}}: {{epic_title}}
1100
- {{#each stories}}
1101
- ✓ {{story-key}} — {{test_count}} tests{{#if pr_url}} PR: {{pr_url}}{{/if}}
1102
- {{/each}}
1103
- {{/each}}
1104
- {{#if remaining_stories}}
1105
- Not started:
1106
- {{#each remaining_stories}}
1107
- · {{story-key}}
1108
- {{/each}}
1109
- {{/if}}
1110
-
1111
- DECISIONS REQUIRING REVIEW (high/medium impact)
1112
- {{#each medium_high_decisions}}
1113
- #{{id}} [{{impact}}] {{story}} / {{phase}}
1114
- {{decision}}
1115
- → {{rationale}}
1116
- {{/each}}
1117
- {{#if no_medium_high_decisions}}
1118
- None — all decisions were low-impact.
1119
- {{/if}}
1120
-
1121
- Full log: {decision_log_file}
1122
-
1123
- REVIEW FINDINGS APPLIED
1124
- Patches applied : {{total_patches}}
1125
- Findings dismissed : {{total_dismissed}}
1126
- Review rounds : {{total_review_rounds}}
1127
-
1128
- CODE REVIEW SUMMARY (per story)
1129
- {{#each completed_stories}}
1130
- {{story-key}} : {{patches_applied}} patches applied, {{findings_dismissed}} dismissed
1131
- {{/each}}
1132
-
1133
- WHAT TO DO NEXT
1134
- 1. Review decisions marked medium/high above
1135
- {{#if has_pr_urls}}
1136
- 2. Merge open PRs: {{pr_urls_list}}
1137
- {{/if}}
1138
- {{#if launch_cmd}}
1139
- {{next_number}}. Run the app: {{launch_cmd}}
1140
- {{/if}}
1141
- {{next_number}}. Manual smoke test checklist:
1142
- {{#each completed_stories}}
1143
- · [{{story-key}}] {{smoke_test_suggestion}}
1144
- {{/each}}
1145
- ```
1146
- </action>
886
+ <action>Read template `{{project_root}}/_Sprintpilot/templates/sprint-report.txt`, fill mustache placeholders with the collected data, and print the result verbatim as the final message.</action>
1147
887
 
1148
888
  </step>
1149
889