@ikunin/sprintpilot 1.0.4 → 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.
- package/_Sprintpilot/manifest.yaml +1 -1
- package/_Sprintpilot/skills/sprint-autopilot-on/workflow.md +135 -532
- package/_Sprintpilot/skills/sprintpilot-update/workflow.md +2 -1
- package/_Sprintpilot/templates/epic-retrospective.md +24 -0
- package/_Sprintpilot/templates/sprint-report.txt +60 -0
- package/package.json +1 -1
|
@@ -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
|
|
11
|
+
### Shell portability
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The executing shell may be bash, zsh, PowerShell, or cmd — translate bash idioms as needed:
|
|
14
14
|
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`)
|
|
91
|
-
|
|
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):
|
|
97
|
-
|
|
98
|
-
```yaml
|
|
99
|
-
generated: {current_date}
|
|
100
|
-
last_updated: {current_datetime}
|
|
101
|
-
|
|
102
|
-
decisions: []
|
|
103
|
-
```
|
|
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.
|
|
104
75
|
|
|
105
|
-
|
|
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`.
|
|
106
79
|
|
|
80
|
+
**File schema:**
|
|
107
81
|
```yaml
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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}
|
|
82
|
+
generated: {date}
|
|
83
|
+
last_updated: {datetime}
|
|
84
|
+
decisions:
|
|
85
|
+
- { id, timestamp, story, phase, category, decision, rationale, impact }
|
|
116
86
|
```
|
|
117
87
|
|
|
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
|
|
@@ -156,6 +117,8 @@ Resolve:
|
|
|
156
117
|
- `project_root` = absolute path of current working directory (store for later use)
|
|
157
118
|
- `session_story_limit` is loaded below from `modules/autopilot/config.yaml` (default: 3)
|
|
158
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
|
+
|
|
159
122
|
### Git integration bootstrap
|
|
160
123
|
|
|
161
124
|
<action>Check if `{project-root}/_Sprintpilot/manifest.yaml` exists</action>
|
|
@@ -200,6 +163,17 @@ Resolve:
|
|
|
200
163
|
<action>STOP</action>
|
|
201
164
|
</check>
|
|
202
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
|
+
|
|
203
177
|
<action>**Lock file** — run: `node {{project_root}}/_Sprintpilot/scripts/lock.js acquire`
|
|
204
178
|
Output will be one of:
|
|
205
179
|
- `ACQUIRED:<session-id>` → proceed
|
|
@@ -235,7 +209,8 @@ Resolve:
|
|
|
235
209
|
</action>
|
|
236
210
|
|
|
237
211
|
<action>**Branch reconciliation** — detect pushed-but-unmerged story branches.
|
|
238
|
-
|
|
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**:
|
|
239
214
|
1. `git fetch origin`
|
|
240
215
|
2. `git branch -r --list "origin/{{branch_prefix}}*"`
|
|
241
216
|
For each remote branch:
|
|
@@ -319,30 +294,14 @@ Resolve:
|
|
|
319
294
|
- Update `{state_file}` with reconciled values
|
|
320
295
|
</action>
|
|
321
296
|
|
|
322
|
-
<!-- Resume from a `retrospective_mode: stop` pause.
|
|
323
|
-
The user was told to run /bmad-retrospective interactively. If they
|
|
324
|
-
did, the epic is now `done` in {status_file} (or a retrospective
|
|
325
|
-
artifact exists). Otherwise, re-issue the instructions and halt. -->
|
|
297
|
+
<!-- Resume from a `retrospective_mode: stop` pause. -->
|
|
326
298
|
<check if="{state_file}.paused_at is epic-complete-awaiting-retrospective">
|
|
327
|
-
<action>Set `{{paused_epic_id}}` from `{state_file}.paused_epic_id
|
|
328
|
-
<action>Check whether epic `{{paused_epic_id}}` is now `done` in `{status_file}` OR an artifact exists at `{implementation_artifacts}/retrospectives/epic-{{paused_epic_id}}-*.md`</action>
|
|
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>
|
|
329
300
|
<check if="epic is done OR retrospective artifact exists">
|
|
330
|
-
<action>Clear `paused_at`, `paused_epic_id`,
|
|
331
|
-
<action>Log: "Epic {{paused_epic_id}} retrospective detected — resuming autopilot"</action>
|
|
301
|
+
<action>Clear `paused_at`, `paused_epic_id`, `next_action` from `{state_file}`. Log: "Epic {{paused_epic_id}} retrospective detected — resuming autopilot".</action>
|
|
332
302
|
</check>
|
|
333
303
|
<check if="epic is NOT done AND no retrospective artifact">
|
|
334
|
-
<action>Report:
|
|
335
|
-
```
|
|
336
|
-
Autopilot still paused — epic {{paused_epic_id}} retrospective not yet done.
|
|
337
|
-
|
|
338
|
-
Run `/bmad-retrospective` interactively for epic {{paused_epic_id}},
|
|
339
|
-
then re-run `/sprint-autopilot-on` to continue.
|
|
340
|
-
|
|
341
|
-
(To bypass, edit _Sprintpilot/modules/autopilot/config.yaml and set
|
|
342
|
-
retrospective_mode to `auto` or `skip`.)
|
|
343
|
-
```
|
|
344
|
-
</action>
|
|
345
|
-
<action>STOP</action>
|
|
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>
|
|
346
305
|
</check>
|
|
347
306
|
</check>
|
|
348
307
|
|
|
@@ -350,22 +309,11 @@ Resolve:
|
|
|
350
309
|
</check>
|
|
351
310
|
|
|
352
311
|
<check if="state_file does NOT exist">
|
|
353
|
-
<action>Check if `{status_file}` exists
|
|
354
|
-
|
|
355
|
-
<check if="{{git_enabled}} AND status_file did not exist
|
|
356
|
-
<action>Run `git fetch origin`
|
|
357
|
-
<action>Initialize `{git_status_file}` with git_integration
|
|
358
|
-
```yaml
|
|
359
|
-
# Sprintpilot — Git Status
|
|
360
|
-
git_integration:
|
|
361
|
-
enabled: true
|
|
362
|
-
base_branch: {git.base_branch from config}
|
|
363
|
-
platform: {{platform}}
|
|
364
|
-
|
|
365
|
-
stories:
|
|
366
|
-
```
|
|
367
|
-
Note: this is the addon's own file — NEVER write git fields to sprint-status.yaml.
|
|
368
|
-
</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>
|
|
369
317
|
</check>
|
|
370
318
|
|
|
371
319
|
<action>Read `{status_file}` — find all stories not yet `done`</action>
|
|
@@ -376,21 +324,7 @@ Resolve:
|
|
|
376
324
|
- `{{session_stories_done}}` = 0
|
|
377
325
|
</action>
|
|
378
326
|
<action>Create master task: "Sprintpilot — Full Sprint Execution" → `in_progress`</action>
|
|
379
|
-
<action>Write initial `{state_file}
|
|
380
|
-
```yaml
|
|
381
|
-
last_updated: {current_datetime}
|
|
382
|
-
current_story: null
|
|
383
|
-
current_bmad_step: null
|
|
384
|
-
completed_skill: bmad-help
|
|
385
|
-
next_skill: {{next_skill}}
|
|
386
|
-
session_stories_done: 0
|
|
387
|
-
stories_remaining: [list from sprint-status]
|
|
388
|
-
git_enabled: {{git_enabled}}
|
|
389
|
-
platform: {{platform}}
|
|
390
|
-
in_worktree: false
|
|
391
|
-
pr_base: {{base_branch}}
|
|
392
|
-
```
|
|
393
|
-
</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>
|
|
394
328
|
<action>Report to user:
|
|
395
329
|
```
|
|
396
330
|
Sprintpilot ON
|
|
@@ -483,90 +417,33 @@ Resolve:
|
|
|
483
417
|
|
|
484
418
|
<!-- GIT: Enter worktree before dev-story -->
|
|
485
419
|
<check if="{{git_enabled}} AND {{next_skill}} is bmad-dev-story">
|
|
486
|
-
<action>**Sanitize branch name
|
|
487
|
-
`node {{project_root}}/_Sprintpilot/scripts/sanitize-branch.js "{{current_story}}" --prefix "{{branch_prefix}}" --max-length 60`
|
|
488
|
-
Output: sanitized name (without prefix). Set `{{branch_name}}` = output.
|
|
489
|
-
Full branch ref will be `{{branch_prefix}}{{branch_name}}`.
|
|
490
|
-
</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>
|
|
491
421
|
|
|
492
|
-
<action>**
|
|
493
|
-
If yes AND worktree already exists → skip creation (idempotent).
|
|
494
|
-
If yes AND no worktree → recovery mode (see health check).
|
|
495
|
-
If no → proceed with creation.
|
|
496
|
-
</action>
|
|
497
|
-
|
|
498
|
-
<action>**Prepare for worktree** — determine the correct branch point.
|
|
499
|
-
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>
|
|
500
423
|
|
|
501
|
-
|
|
502
|
-
- Read `{git_status_file}` for earlier stories in the same epic
|
|
503
|
-
- Find the latest story branch where `push_status` = "pushed" AND `pr_url` is a valid URL
|
|
504
|
-
- 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.
|
|
505
425
|
|
|
506
|
-
|
|
507
|
-
-
|
|
508
|
-
-
|
|
509
|
-
- Log: "Branching from {{branch_prefix}}<prev-branch> (PR pending merge)"
|
|
510
|
-
Otherwise:
|
|
511
|
-
- Branch from base: `git checkout origin/{{base_branch}}`
|
|
512
|
-
- 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}}`.
|
|
513
429
|
|
|
514
|
-
|
|
430
|
+
Detached HEAD is fine — `git worktree add` below creates a new branch from HEAD.
|
|
515
431
|
</action>
|
|
516
432
|
|
|
517
|
-
<action>**Create worktree
|
|
518
|
-
```
|
|
519
|
-
git worktree add "{{project_root}}/.worktrees/{{current_story}}" -b "{{branch_prefix}}{{branch_name}}" 2>&1
|
|
520
|
-
```
|
|
521
|
-
This creates `.worktrees/{{current_story}}/` with a new branch `{{branch_prefix}}{{branch_name}}` from HEAD.
|
|
522
|
-
|
|
523
|
-
If worktree add fails (branch already exists):
|
|
524
|
-
```
|
|
525
|
-
git worktree add "{{project_root}}/.worktrees/{{current_story}}" "{{branch_prefix}}{{branch_name}}" 2>&1
|
|
526
|
-
```
|
|
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`.
|
|
527
434
|
|
|
528
|
-
|
|
529
|
-
- Log: "WARN: git worktree add failed — continuing without worktree isolation"
|
|
530
|
-
- Set `{{in_worktree}}` = false
|
|
531
|
-
- Create branch manually: `git checkout -b {{branch_prefix}}{{branch_name}}`
|
|
532
|
-
If checkout also fails (branch already exists): `git checkout {{branch_prefix}}{{branch_name}}`
|
|
533
|
-
If both fail: HALT — "Could not create or switch to branch {{branch_prefix}}{{branch_name}}"
|
|
534
|
-
- Continue with the skill invocation in PROJECT_ROOT (no isolation)
|
|
535
|
-
- 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.
|
|
536
436
|
</action>
|
|
537
437
|
|
|
538
438
|
<check if="worktree add succeeded">
|
|
539
|
-
<action
|
|
540
|
-
`
|
|
541
|
-
All subsequent file operations and commands MUST use this directory.
|
|
542
|
-
Set `{{worktree_path}}` = `{{project_root}}/.worktrees/{{current_story}}`
|
|
543
|
-
</action>
|
|
544
|
-
|
|
545
|
-
<action>**Init submodules** if needed.
|
|
546
|
-
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.
|
|
547
|
-
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.
|
|
548
|
-
</action>
|
|
549
|
-
|
|
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>
|
|
550
441
|
<action>Set `{{in_worktree}}` = true</action>
|
|
551
442
|
</check>
|
|
552
|
-
<action>Update `{state_file}` (write to worktree copy since
|
|
443
|
+
<action>Update `{state_file}` (write to the worktree copy since cwd is now the worktree)</action>
|
|
553
444
|
</check>
|
|
554
445
|
|
|
555
|
-
<action>Update `{state_file}
|
|
556
|
-
```yaml
|
|
557
|
-
last_updated: {current_datetime}
|
|
558
|
-
current_story: {{current_story}}
|
|
559
|
-
current_bmad_step: executing
|
|
560
|
-
completed_skill: {previous skill}
|
|
561
|
-
next_skill: {{next_skill}}
|
|
562
|
-
session_stories_done: {{session_stories_done}}
|
|
563
|
-
stories_remaining: {{stories_remaining}}
|
|
564
|
-
git_enabled: {{git_enabled}}
|
|
565
|
-
platform: {{platform}}
|
|
566
|
-
in_worktree: {{in_worktree}}
|
|
567
|
-
pr_base: {{pr_base}}
|
|
568
|
-
```
|
|
569
|
-
</action>
|
|
446
|
+
<action>Update `{state_file}` with STATE_FIELDS (set `current_bmad_step = executing`, `completed_skill = <previous skill>`).</action>
|
|
570
447
|
|
|
571
448
|
<!-- Autopilot menu handling rules apply — see AUTOPILOT RULES section above -->
|
|
572
449
|
|
|
@@ -637,7 +514,8 @@ pr_base: {{pr_base}}
|
|
|
637
514
|
</check>
|
|
638
515
|
|
|
639
516
|
<check if="{{completed_skill}} was bmad-sprint-planning AND {{git_enabled}}">
|
|
640
|
-
<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>
|
|
641
519
|
<action>Initialize `{git_status_file}` if it doesn't exist (with git_integration block)</action>
|
|
642
520
|
</check>
|
|
643
521
|
|
|
@@ -653,22 +531,10 @@ pr_base: {{pr_base}}
|
|
|
653
531
|
</action>
|
|
654
532
|
</check>
|
|
655
533
|
|
|
656
|
-
<!-- GIT: Commit planning artifacts to main after planning skills -->
|
|
657
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)">
|
|
658
|
-
<action>**Commit planning artifacts to main
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
git add _bmad-output/planning-artifacts/ _bmad-output/implementation-artifacts/ _bmad-output/stories/
|
|
662
|
-
```
|
|
663
|
-
Check if there's anything staged; if yes, commit:
|
|
664
|
-
```
|
|
665
|
-
git diff --cached --quiet
|
|
666
|
-
```
|
|
667
|
-
If that exits non-zero (there are staged changes), run: `git commit -m "docs: {{completed_skill}} artifacts"`
|
|
668
|
-
Then push (log a warning if push fails; do not halt autopilot):
|
|
669
|
-
```
|
|
670
|
-
git push origin {{base_branch}}
|
|
671
|
-
```
|
|
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).
|
|
672
538
|
</action>
|
|
673
539
|
</check>
|
|
674
540
|
|
|
@@ -679,32 +545,17 @@ pr_base: {{pr_base}}
|
|
|
679
545
|
|
|
680
546
|
<step n="5" goal="Determine next skill — from skill output first, bmad-help as fallback">
|
|
681
547
|
|
|
682
|
-
<action>Read the output of `{{completed_skill}}
|
|
683
|
-
|
|
684
|
-
<check if="output contains 'Next Steps', 'What to do next', 'Run next', or equivalent">
|
|
685
|
-
<action>Extract `{{next_skill}}` from that section</action>
|
|
686
|
-
<action>Log: "Next step from skill output: {{next_skill}}"</action>
|
|
687
|
-
</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>
|
|
688
549
|
|
|
689
|
-
<check if="
|
|
690
|
-
<action
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
<action>Set `{{current_story}}` = first undone story from `{status_file}`</action>
|
|
699
|
-
<action>Determine `{{next_skill}}` based on that story's current status and BMAD step:
|
|
700
|
-
- If story has no story file yet → `bmad-create-story`
|
|
701
|
-
- If story file exists but status is `ready-for-dev` → `bmad-check-implementation-readiness`
|
|
702
|
-
- If story is `in-progress` and `current_bmad_step` is before `code-review` (i.e. RED or GREEN phase) → `bmad-dev-story`
|
|
703
|
-
- If story is `in-progress` and `current_bmad_step` is `code-review` or later → `bmad-code-review`
|
|
704
|
-
- Otherwise → invoke `bmad-help` for precise determination
|
|
705
|
-
</action>
|
|
706
|
-
<action>Log: "next_skill was empty but undone stories remain — resolved to {{next_skill}} for {{current_story}}"</action>
|
|
707
|
-
</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>
|
|
708
559
|
<check if="all stories in status_file are done">
|
|
709
560
|
<goto step="10">Sprint complete</goto>
|
|
710
561
|
</check>
|
|
@@ -815,37 +666,15 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
|
|
|
815
666
|
</action>
|
|
816
667
|
|
|
817
668
|
<check if="{{create_pr}} is false OR {{platform}} is git_only OR {{pr_url}} is null or SKIPPED">
|
|
818
|
-
<action>**Merge story branch to main
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
git
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
If succeeds:
|
|
825
|
-
- `git push origin {{base_branch}}`
|
|
826
|
-
- Set `{{merge_status}}` = "merged"
|
|
827
|
-
If fails (conflict):
|
|
828
|
-
- `git merge --abort`
|
|
829
|
-
- `git fetch origin`
|
|
830
|
-
- `git checkout -B {{base_branch}} origin/{{base_branch}}`
|
|
831
|
-
- Retry merge once: `git merge {{branch_prefix}}{{branch_name}} --no-edit`
|
|
832
|
-
- If retry succeeds: push, set `{{merge_status}}` = "merged"
|
|
833
|
-
- If retry fails: set `{{merge_status}}` = "failed"
|
|
834
|
-
Log: "WARN: merge failed for {{current_story}} — will retry on next boot"
|
|
835
|
-
|
|
836
|
-
If `{{merge_status}}` == "failed":
|
|
837
|
-
Log warning but do NOT halt. The branch is pushed and preserved.
|
|
838
|
-
Boot reconciliation (INITIALIZATION branch reconciliation) will retry on next session.
|
|
839
|
-
|
|
840
|
-
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.
|
|
841
675
|
</action>
|
|
842
676
|
<check if="{{cleanup_on_merge}} is true">
|
|
843
|
-
<action>**Cleanup worktree**
|
|
844
|
-
```
|
|
845
|
-
git worktree remove .worktrees/{{current_story}} --force
|
|
846
|
-
git worktree prune
|
|
847
|
-
```
|
|
848
|
-
</action>
|
|
677
|
+
<action>**Cleanup worktree** (ignore failures — may already be gone): `git worktree remove .worktrees/{{current_story}} --force` then `git worktree prune`</action>
|
|
849
678
|
</check>
|
|
850
679
|
</check>
|
|
851
680
|
<check if="{{pr_url}} is a valid URL (not null, not SKIPPED)">
|
|
@@ -854,29 +683,11 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
|
|
|
854
683
|
<action>Log: "Story {{current_story}} pushed — PR awaiting review: {{pr_url}}"</action>
|
|
855
684
|
</check>
|
|
856
685
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
git
|
|
861
|
-
|
|
862
|
-
</action>
|
|
863
|
-
|
|
864
|
-
<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:
|
|
865
|
-
`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}}"`
|
|
866
|
-
This writes to `git-status.yaml` (addon-owned). Sprint-status.yaml is BMAD-owned — updated by BMAD skills only.
|
|
867
|
-
</action>
|
|
868
|
-
|
|
869
|
-
<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):
|
|
870
|
-
```
|
|
871
|
-
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/
|
|
872
|
-
```
|
|
873
|
-
Check if anything is staged: `git diff --cached --quiet`. If that exits non-zero, commit:
|
|
874
|
-
`git commit -m "docs: story {{current_story}} done — {{test_count}} tests{{#if pr_url}}, PR: {{pr_url}}{{/if}}"`
|
|
875
|
-
Then push (log a warning if push fails; do not halt autopilot):
|
|
876
|
-
```
|
|
877
|
-
git push origin {{base_branch}}
|
|
878
|
-
```
|
|
879
|
-
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).
|
|
880
691
|
</action>
|
|
881
692
|
</check>
|
|
882
693
|
|
|
@@ -897,134 +708,34 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
|
|
|
897
708
|
<action>Resolve `{{epic_id}}` (e.g. "1") and `{{epic_title}}` from `{status_file}` for the current epic</action>
|
|
898
709
|
<action>Create task "[epic {{epic_id}}] retrospective" → `in_progress`</action>
|
|
899
710
|
|
|
900
|
-
<!-- Retrospective
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
NEVER invoked from autopilot — it enters a multi-persona discussion
|
|
904
|
-
loop under some CLIs. -->
|
|
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). -->
|
|
905
714
|
|
|
906
715
|
<check if="{{retrospective_mode}} is auto">
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
<action>
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
<action>Collect decision-log entries for epic `{{epic_id}}` from `{decision_log_file}` (match on `story` prefix `{{epic_id}}-` or `phase: autopilot:*` entries tagged to this epic)</action>
|
|
914
|
-
<action>Identify open risks / carry-over notes from sprint-status (any story with `notes` or `risks` fields, any `workaround` decisions in the log for this epic)</action>
|
|
915
|
-
<action>Ensure directory `{implementation_artifacts}/retrospectives/` exists</action>
|
|
916
|
-
<action>Write `{implementation_artifacts}/retrospectives/epic-{{epic_id}}-retrospective.md` using this template:
|
|
917
|
-
```markdown
|
|
918
|
-
# Epic {{epic_id}} — {{epic_title}} — Retrospective
|
|
919
|
-
|
|
920
|
-
**Completed:** {current_date}
|
|
921
|
-
**Stories done:** {{n_done}}/{{n_total}}
|
|
922
|
-
|
|
923
|
-
## Stories
|
|
924
|
-
{{#each stories}}
|
|
925
|
-
- **{{story-key}}** — {{title}}
|
|
926
|
-
- Tests: {{test_pass_count}}
|
|
927
|
-
- Patches applied: {{patch_count}}
|
|
928
|
-
{{/each}}
|
|
929
|
-
|
|
930
|
-
## Key decisions
|
|
931
|
-
{{#each decisions}}
|
|
932
|
-
- [{{impact}}] {{category}}: {{decision}} — {{rationale}}
|
|
933
|
-
{{/each}}
|
|
934
|
-
|
|
935
|
-
## Risks carried forward
|
|
936
|
-
{{#each open_risks}}
|
|
937
|
-
- {{risk}}
|
|
938
|
-
{{/each}}
|
|
939
|
-
|
|
940
|
-
## Notes
|
|
941
|
-
Generated inline by Sprintpilot autopilot per `autopilot.retrospective_mode: auto`.
|
|
942
|
-
```
|
|
943
|
-
</action>
|
|
944
|
-
<action>Update `{status_file}`:
|
|
945
|
-
- `epics.{{epic_id}}.status` = `done`
|
|
946
|
-
- `epics.{{epic_id}}.retrospective_path` = the retrospective file path (relative to project root)
|
|
947
|
-
- `epics.{{epic_id}}.completed_at` = {current_date}
|
|
948
|
-
</action>
|
|
949
|
-
<action>Append decision-log entry:
|
|
950
|
-
`{ category: workaround, decision: "retrospective generated inline", rationale: "autopilot.retrospective_mode=auto — avoids external skill's multi-persona loop", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`
|
|
951
|
-
</action>
|
|
952
|
-
<action>Mark retrospective task → `completed`</action>
|
|
953
|
-
<action>Set `{{completed_skill}}` = `retrospective-auto`</action>
|
|
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>
|
|
954
722
|
</check>
|
|
955
723
|
|
|
956
724
|
<check if="{{retrospective_mode}} is stop">
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
<action>Update `{state_file}`:
|
|
961
|
-
- `paused_at` = `epic-complete-awaiting-retrospective`
|
|
962
|
-
- `paused_epic_id` = `{{epic_id}}`
|
|
963
|
-
- `next_action` = "run /bmad-retrospective interactively for epic {{epic_id}}, then re-run /sprint-autopilot-on"
|
|
964
|
-
</action>
|
|
965
|
-
<action>Append decision-log entry:
|
|
966
|
-
`{ category: workaround, decision: "paused for interactive retrospective", rationale: "autopilot.retrospective_mode=stop", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`
|
|
967
|
-
</action>
|
|
968
|
-
<action>Mark retrospective task → `completed` (handed off to user)</action>
|
|
969
|
-
<action>Report:
|
|
970
|
-
```
|
|
971
|
-
Autopilot paused — epic {{epic_id}} complete, retrospective handed off.
|
|
972
|
-
|
|
973
|
-
Per `autopilot.retrospective_mode: stop` in
|
|
974
|
-
_Sprintpilot/modules/autopilot/config.yaml, autopilot does not run the
|
|
975
|
-
retrospective automatically.
|
|
976
|
-
|
|
977
|
-
To continue:
|
|
978
|
-
1. Run `/bmad-retrospective` interactively for epic {{epic_id}}
|
|
979
|
-
2. When done, run `/sprint-autopilot-on` to resume with the next epic
|
|
980
|
-
|
|
981
|
-
State saved to: {state_file}
|
|
982
|
-
```
|
|
983
|
-
</action>
|
|
984
|
-
<action>STOP</action>
|
|
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>
|
|
985
728
|
</check>
|
|
986
729
|
|
|
987
730
|
<check if="{{retrospective_mode}} is skip">
|
|
988
|
-
|
|
989
|
-
<action>
|
|
990
|
-
- `epics.{{epic_id}}.status` = `done`
|
|
991
|
-
- `epics.{{epic_id}}.retrospective_path` = null
|
|
992
|
-
- `epics.{{epic_id}}.retrospective_skipped` = true
|
|
993
|
-
- `epics.{{epic_id}}.completed_at` = {current_date}
|
|
994
|
-
</action>
|
|
995
|
-
<action>Append decision-log entry:
|
|
996
|
-
`{ category: workaround, decision: "retrospective skipped", rationale: "autopilot.retrospective_mode=skip (NOT RECOMMENDED)", impact: medium, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`
|
|
997
|
-
</action>
|
|
998
|
-
<action>Mark retrospective task → `completed` (skipped)</action>
|
|
999
|
-
<action>Set `{{completed_skill}}` = `retrospective-skip`</action>
|
|
1000
|
-
<action>Log: "Epic {{epic_id}} retrospective skipped per config — continuing with next epic"</action>
|
|
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>
|
|
1001
733
|
</check>
|
|
1002
734
|
|
|
1003
|
-
<!-- GIT: Epic completion — suggest merge, cleanup worktrees -->
|
|
1004
735
|
<check if="{{git_enabled}}">
|
|
1005
|
-
<action>**
|
|
1006
|
-
<action>Report:
|
|
1007
|
-
```
|
|
1008
|
-
Epic complete — PR/MR summary:
|
|
1009
|
-
{{#each epic_stories}}
|
|
1010
|
-
- {{story-key}}: {{pr_url}}
|
|
1011
|
-
{{/each}}
|
|
1012
|
-
|
|
1013
|
-
Ready to merge. Review PRs and confirm when ready.
|
|
1014
|
-
```
|
|
1015
|
-
</action>
|
|
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>
|
|
1016
737
|
<check if="{{cleanup_on_merge}} is true">
|
|
1017
|
-
<action>**Cleanup worktrees** for completed stories:
|
|
1018
|
-
For each story in this epic:
|
|
1019
|
-
1. Check if worktree at `.worktrees/{{story-key}}` exists
|
|
1020
|
-
2. Check if clean: `git -C .worktrees/{{story-key}} status --porcelain`
|
|
1021
|
-
3. If clean → `git worktree remove .worktrees/{{story-key}}` + `git worktree prune`
|
|
1022
|
-
Update `{git_status_file}` for this story: `worktree_cleaned: true`
|
|
1023
|
-
4. If dirty → warn user, skip cleanup
|
|
1024
|
-
</action>
|
|
1025
|
-
</check>
|
|
1026
|
-
<check if="{{cleanup_on_merge}} is false">
|
|
1027
|
-
<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>
|
|
1028
739
|
</check>
|
|
1029
740
|
</check>
|
|
1030
741
|
</check>
|
|
@@ -1041,21 +752,7 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
|
|
|
1041
752
|
|
|
1042
753
|
<step n="8" goal="Save state and continue">
|
|
1043
754
|
|
|
1044
|
-
<action>Update `{state_file}
|
|
1045
|
-
```yaml
|
|
1046
|
-
last_updated: {current_datetime}
|
|
1047
|
-
current_story: {{current_story}}
|
|
1048
|
-
current_bmad_step: {{current_bmad_step}}
|
|
1049
|
-
completed_skill: {{completed_skill}}
|
|
1050
|
-
next_skill: {{next_skill}}
|
|
1051
|
-
session_stories_done: {{session_stories_done}}
|
|
1052
|
-
stories_remaining: {{stories_remaining}}
|
|
1053
|
-
git_enabled: {{git_enabled}}
|
|
1054
|
-
platform: {{platform}}
|
|
1055
|
-
in_worktree: {{in_worktree}}
|
|
1056
|
-
pr_base: {{pr_base}}
|
|
1057
|
-
```
|
|
1058
|
-
</action>
|
|
755
|
+
<action>Update `{state_file}` with STATE_FIELDS.</action>
|
|
1059
756
|
|
|
1060
757
|
<goto step="2">Continue execution loop</goto>
|
|
1061
758
|
|
|
@@ -1090,21 +787,7 @@ pr_base: {{pr_base}}
|
|
|
1090
787
|
</action>
|
|
1091
788
|
</check>
|
|
1092
789
|
|
|
1093
|
-
<action>Update `{state_file}
|
|
1094
|
-
```yaml
|
|
1095
|
-
last_updated: {current_datetime}
|
|
1096
|
-
current_story: {{current_story}}
|
|
1097
|
-
current_bmad_step: {{current_bmad_step}}
|
|
1098
|
-
completed_skill: {{completed_skill}}
|
|
1099
|
-
next_skill: {{next_skill}}
|
|
1100
|
-
session_stories_done: {{session_stories_done}}
|
|
1101
|
-
stories_remaining: {{stories_remaining}}
|
|
1102
|
-
git_enabled: {{git_enabled}}
|
|
1103
|
-
platform: {{platform}}
|
|
1104
|
-
in_worktree: {{in_worktree}}
|
|
1105
|
-
pr_base: {{pr_base}}
|
|
1106
|
-
```
|
|
1107
|
-
</action>
|
|
790
|
+
<action>Update `{state_file}` with STATE_FIELDS.</action>
|
|
1108
791
|
|
|
1109
792
|
<action>Read `{decision_log_file}` — count medium/high decisions from this session's stories</action>
|
|
1110
793
|
|
|
@@ -1154,133 +837,53 @@ No work will be repeated.
|
|
|
1154
837
|
<action>Run full test suite — report `N/N passed`</action>
|
|
1155
838
|
|
|
1156
839
|
<!-- Generate project documentation after sprint completion -->
|
|
1157
|
-
<action>**
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
<
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
`
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
- All completed stories grouped by epic, with their story titles
|
|
1182
|
-
- Total story count, total epic count
|
|
1183
|
-
- Final test count
|
|
1184
|
-
- If git_enabled: all PR/MR URLs, patch counts, dismissed findings per story
|
|
1185
|
-
</action>
|
|
1186
|
-
|
|
1187
|
-
<action>Read `{decision_log_file}` and collect:
|
|
1188
|
-
- All decisions with impact `medium` or `high`
|
|
1189
|
-
- Count of `review-accept` entries (patches applied)
|
|
1190
|
-
- Count of `review-triage` entries (findings dismissed)
|
|
1191
|
-
- Total review rounds (count of code-review invocations)
|
|
1192
|
-
- Per-story summary: patches applied and findings dismissed
|
|
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."
|
|
1193
864
|
</action>
|
|
1194
865
|
|
|
1195
|
-
<action
|
|
1196
|
-
1. `run_gui.sh` or `run.sh` in the project root
|
|
1197
|
-
2. `main.py` in the project root
|
|
1198
|
-
3. Check `pyproject.toml`, `package.json`, or `setup.py` for scripts
|
|
1199
|
-
Record as `{{launch_cmd}}`
|
|
1200
|
-
</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>
|
|
1201
867
|
|
|
1202
|
-
<!-- GIT: Final worktree cleanup — safety net for any worktrees not cleaned during epic completion -->
|
|
1203
868
|
<check if="{{git_enabled}}">
|
|
1204
|
-
<action>**
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
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).
|
|
1209
873
|
</action>
|
|
1210
874
|
</check>
|
|
1211
875
|
|
|
1212
|
-
|
|
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
|
+
|
|
1213
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>
|
|
1214
880
|
<action>Release lock: `node {{project_root}}/_Sprintpilot/scripts/lock.js release`</action>
|
|
1215
881
|
</check>
|
|
1216
882
|
|
|
1217
883
|
<action>Delete `{state_file}` — sprint complete</action>
|
|
1218
884
|
<action>Mark master task "Sprintpilot — Full Sprint Execution" → `completed`</action>
|
|
1219
885
|
|
|
1220
|
-
<action>
|
|
1221
|
-
```
|
|
1222
|
-
╔═══════════════════════════════════════════════════════════════╗
|
|
1223
|
-
║ BMAD AUTOPILOT — REPORT ║
|
|
1224
|
-
╚═══════════════════════════════════════════════════════════════╝
|
|
1225
|
-
|
|
1226
|
-
SUMMARY
|
|
1227
|
-
Stories completed : {{done_count}}/{{total_stories}}
|
|
1228
|
-
Epics completed : {{done_epics}}/{{total_epics}}
|
|
1229
|
-
Total tests : {{N}}/{{N}} passed
|
|
1230
|
-
{{#if git_enabled}}
|
|
1231
|
-
Platform : {{platform}}
|
|
1232
|
-
{{/if}}
|
|
1233
|
-
|
|
1234
|
-
STORIES
|
|
1235
|
-
{{#each epic}}
|
|
1236
|
-
Epic {{epic_number}}: {{epic_title}}
|
|
1237
|
-
{{#each stories}}
|
|
1238
|
-
✓ {{story-key}} — {{test_count}} tests{{#if pr_url}} PR: {{pr_url}}{{/if}}
|
|
1239
|
-
{{/each}}
|
|
1240
|
-
{{/each}}
|
|
1241
|
-
{{#if remaining_stories}}
|
|
1242
|
-
Not started:
|
|
1243
|
-
{{#each remaining_stories}}
|
|
1244
|
-
· {{story-key}}
|
|
1245
|
-
{{/each}}
|
|
1246
|
-
{{/if}}
|
|
1247
|
-
|
|
1248
|
-
DECISIONS REQUIRING REVIEW (high/medium impact)
|
|
1249
|
-
{{#each medium_high_decisions}}
|
|
1250
|
-
#{{id}} [{{impact}}] {{story}} / {{phase}}
|
|
1251
|
-
{{decision}}
|
|
1252
|
-
→ {{rationale}}
|
|
1253
|
-
{{/each}}
|
|
1254
|
-
{{#if no_medium_high_decisions}}
|
|
1255
|
-
None — all decisions were low-impact.
|
|
1256
|
-
{{/if}}
|
|
1257
|
-
|
|
1258
|
-
Full log: {decision_log_file}
|
|
1259
|
-
|
|
1260
|
-
REVIEW FINDINGS APPLIED
|
|
1261
|
-
Patches applied : {{total_patches}}
|
|
1262
|
-
Findings dismissed : {{total_dismissed}}
|
|
1263
|
-
Review rounds : {{total_review_rounds}}
|
|
1264
|
-
|
|
1265
|
-
CODE REVIEW SUMMARY (per story)
|
|
1266
|
-
{{#each completed_stories}}
|
|
1267
|
-
{{story-key}} : {{patches_applied}} patches applied, {{findings_dismissed}} dismissed
|
|
1268
|
-
{{/each}}
|
|
1269
|
-
|
|
1270
|
-
WHAT TO DO NEXT
|
|
1271
|
-
1. Review decisions marked medium/high above
|
|
1272
|
-
{{#if has_pr_urls}}
|
|
1273
|
-
2. Merge open PRs: {{pr_urls_list}}
|
|
1274
|
-
{{/if}}
|
|
1275
|
-
{{#if launch_cmd}}
|
|
1276
|
-
{{next_number}}. Run the app: {{launch_cmd}}
|
|
1277
|
-
{{/if}}
|
|
1278
|
-
{{next_number}}. Manual smoke test checklist:
|
|
1279
|
-
{{#each completed_stories}}
|
|
1280
|
-
· [{{story-key}}] {{smoke_test_suggestion}}
|
|
1281
|
-
{{/each}}
|
|
1282
|
-
```
|
|
1283
|
-
</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>
|
|
1284
887
|
|
|
1285
888
|
</step>
|
|
1286
889
|
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
|
|
35
35
|
6. **Install the update.** On confirmation, run:
|
|
36
36
|
```bash
|
|
37
|
-
npx @ikunin/sprintpilot@
|
|
37
|
+
npx @ikunin/sprintpilot@{latest} install --yes
|
|
38
38
|
```
|
|
39
|
+
(`{latest}` is the version resolved in step 2 — e.g., `npx @ikunin/sprintpilot@1.0.4 install --yes`.)
|
|
39
40
|
Stream the output so the user can see progress.
|
|
40
41
|
|
|
41
42
|
7. **Verify.** Read `_Sprintpilot/manifest.yaml` again and confirm the version updated:
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Epic {{epic_id}} — {{epic_title}} — Retrospective
|
|
2
|
+
|
|
3
|
+
**Completed:** {current_date}
|
|
4
|
+
**Stories done:** {{n_done}}/{{n_total}}
|
|
5
|
+
|
|
6
|
+
## Stories
|
|
7
|
+
{{#each stories}}
|
|
8
|
+
- **{{story-key}}** — {{title}}
|
|
9
|
+
- Tests: {{test_pass_count}}
|
|
10
|
+
- Patches applied: {{patch_count}}
|
|
11
|
+
{{/each}}
|
|
12
|
+
|
|
13
|
+
## Key decisions
|
|
14
|
+
{{#each decisions}}
|
|
15
|
+
- [{{impact}}] {{category}}: {{decision}} — {{rationale}}
|
|
16
|
+
{{/each}}
|
|
17
|
+
|
|
18
|
+
## Risks carried forward
|
|
19
|
+
{{#each open_risks}}
|
|
20
|
+
- {{risk}}
|
|
21
|
+
{{/each}}
|
|
22
|
+
|
|
23
|
+
## Notes
|
|
24
|
+
Generated inline by Sprintpilot autopilot per `autopilot.retrospective_mode: auto`.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
2
|
+
║ BMAD AUTOPILOT — REPORT ║
|
|
3
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
4
|
+
|
|
5
|
+
SUMMARY
|
|
6
|
+
Stories completed : {{done_count}}/{{total_stories}}
|
|
7
|
+
Epics completed : {{done_epics}}/{{total_epics}}
|
|
8
|
+
Total tests : {{N}}/{{N}} passed
|
|
9
|
+
{{#if git_enabled}}
|
|
10
|
+
Platform : {{platform}}
|
|
11
|
+
{{/if}}
|
|
12
|
+
|
|
13
|
+
STORIES
|
|
14
|
+
{{#each epic}}
|
|
15
|
+
Epic {{epic_number}}: {{epic_title}}
|
|
16
|
+
{{#each stories}}
|
|
17
|
+
✓ {{story-key}} — {{test_count}} tests{{#if pr_url}} PR: {{pr_url}}{{/if}}
|
|
18
|
+
{{/each}}
|
|
19
|
+
{{/each}}
|
|
20
|
+
{{#if remaining_stories}}
|
|
21
|
+
Not started:
|
|
22
|
+
{{#each remaining_stories}}
|
|
23
|
+
· {{story-key}}
|
|
24
|
+
{{/each}}
|
|
25
|
+
{{/if}}
|
|
26
|
+
|
|
27
|
+
DECISIONS REQUIRING REVIEW (high/medium impact)
|
|
28
|
+
{{#each medium_high_decisions}}
|
|
29
|
+
#{{id}} [{{impact}}] {{story}} / {{phase}}
|
|
30
|
+
{{decision}}
|
|
31
|
+
→ {{rationale}}
|
|
32
|
+
{{/each}}
|
|
33
|
+
{{#if no_medium_high_decisions}}
|
|
34
|
+
None — all decisions were low-impact.
|
|
35
|
+
{{/if}}
|
|
36
|
+
|
|
37
|
+
Full log: {decision_log_file}
|
|
38
|
+
|
|
39
|
+
REVIEW FINDINGS APPLIED
|
|
40
|
+
Patches applied : {{total_patches}}
|
|
41
|
+
Findings dismissed : {{total_dismissed}}
|
|
42
|
+
Review rounds : {{total_review_rounds}}
|
|
43
|
+
|
|
44
|
+
CODE REVIEW SUMMARY (per story)
|
|
45
|
+
{{#each completed_stories}}
|
|
46
|
+
{{story-key}} : {{patches_applied}} patches applied, {{findings_dismissed}} dismissed
|
|
47
|
+
{{/each}}
|
|
48
|
+
|
|
49
|
+
WHAT TO DO NEXT
|
|
50
|
+
1. Review decisions marked medium/high above
|
|
51
|
+
{{#if has_pr_urls}}
|
|
52
|
+
2. Merge open PRs: {{pr_urls_list}}
|
|
53
|
+
{{/if}}
|
|
54
|
+
{{#if launch_cmd}}
|
|
55
|
+
{{next_number}}. Run the app: {{launch_cmd}}
|
|
56
|
+
{{/if}}
|
|
57
|
+
{{next_number}}. Manual smoke test checklist:
|
|
58
|
+
{{#each completed_stories}}
|
|
59
|
+
· [{{story-key}}] {{smoke_test_suggestion}}
|
|
60
|
+
{{/each}}
|
package/package.json
CHANGED