@ikunin/sprintpilot 2.2.31 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +232 -413
- package/_Sprintpilot/Sprintpilot.md +76 -6
- package/_Sprintpilot/bin/autopilot.js +734 -68
- package/_Sprintpilot/lib/orchestrator/action-ledger.js +208 -0
- package/_Sprintpilot/lib/orchestrator/adapt.js +93 -15
- package/_Sprintpilot/lib/orchestrator/profile-rules.js +7 -16
- package/_Sprintpilot/lib/orchestrator/sprint-plan.js +488 -0
- package/_Sprintpilot/lib/orchestrator/state-store.js +9 -5
- package/_Sprintpilot/lib/orchestrator/user-command-applier.js +78 -0
- package/_Sprintpilot/lib/orchestrator/user-commands.js +114 -0
- package/_Sprintpilot/lib/orchestrator/verify.js +10 -17
- package/_Sprintpilot/manifest.yaml +4 -1
- package/_Sprintpilot/modules/autopilot/profiles/_base.yaml +18 -4
- package/_Sprintpilot/modules/git/config.yaml +15 -9
- package/_Sprintpilot/modules/ma/config.yaml +29 -27
- package/_Sprintpilot/scripts/dispatch-layer.js +12 -15
- package/_Sprintpilot/scripts/infer-dependencies.js +706 -254
- package/_Sprintpilot/scripts/log-timing.js +6 -10
- package/_Sprintpilot/scripts/merge-shards.js +21 -23
- package/_Sprintpilot/scripts/post-green-gates.js +3 -1
- package/_Sprintpilot/scripts/resolve-dag.js +452 -280
- package/_Sprintpilot/scripts/sprint-plan.js +1068 -0
- package/_Sprintpilot/scripts/state-shard.js +13 -5
- package/_Sprintpilot/scripts/summarize-timings.js +2 -3
- package/_Sprintpilot/skills/sprint-autopilot-on/SKILL.md +30 -2
- package/_Sprintpilot/skills/sprint-autopilot-on/workflow.orchestrator.md +36 -10
- package/_Sprintpilot/skills/sprintpilot-dependency-graph/SKILL.md +63 -0
- package/_Sprintpilot/skills/sprintpilot-dependency-graph/workflow.md +227 -0
- package/_Sprintpilot/skills/sprintpilot-plan-sprint/SKILL.md +67 -0
- package/_Sprintpilot/skills/sprintpilot-plan-sprint/workflow.md +435 -0
- package/_Sprintpilot/skills/sprintpilot-sprint-progress/SKILL.md +53 -0
- package/_Sprintpilot/skills/sprintpilot-sprint-progress/workflow.md +169 -0
- package/lib/commands/install.js +186 -10
- package/package.json +1 -1
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# Sprintpilot — Sprint Plan Generation
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Build (or refresh) `_bmad-output/implementation-artifacts/sprint-plan.yaml`
|
|
6
|
+
— the authoritative sprint plan that drives autopilot story selection,
|
|
7
|
+
DAG-aware reordering, and external-tracker integration. Infers per-epic
|
|
8
|
+
+ cross-epic dependencies via piped LLM envelopes (validated server-side
|
|
9
|
+
by `infer-dependencies.js`), lets the user curate which stories belong
|
|
10
|
+
in the active plan, and persists everything atomically.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
| Artifact | Path | Required |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| BMad sprint-status | `_bmad-output/implementation-artifacts/sprint-status.yaml` | yes |
|
|
17
|
+
| BMad epics | `_bmad-output/planning-artifacts/epics.md` | yes |
|
|
18
|
+
| BMad architecture | `_bmad-output/planning-artifacts/architecture.md` | yes |
|
|
19
|
+
|
|
20
|
+
If any required prerequisite is missing, halt with a `user_prompt`
|
|
21
|
+
naming the missing file and the BMad skill that produces it
|
|
22
|
+
(`bmad-sprint-planning`, `bmad-create-epics-and-stories`,
|
|
23
|
+
`bmad-create-architecture`). Do NOT attempt the workflow without them.
|
|
24
|
+
|
|
25
|
+
## Outputs
|
|
26
|
+
|
|
27
|
+
| File | Location | Purpose |
|
|
28
|
+
|------|----------|---------|
|
|
29
|
+
| `sprint-plan.yaml` | `_bmad-output/implementation-artifacts/` | Authoritative plan (read by autopilot + resolve-dag) |
|
|
30
|
+
| `sprint-plan-dag.mmd` | `_bmad-output/implementation-artifacts/` | Rendered mermaid DAG (refreshed on every plan write) |
|
|
31
|
+
| Archived legacy | `.archive/dependencies.yaml.migrated` | If a pre-v2.3.0 `_Sprintpilot/sprints/dependencies.yaml` existed |
|
|
32
|
+
|
|
33
|
+
## Conventions used below
|
|
34
|
+
|
|
35
|
+
- `<root>` = the project root passed via `--project-root` (or `cwd`).
|
|
36
|
+
- All scripts live under `_Sprintpilot/scripts/` — invoke via `node <path>`.
|
|
37
|
+
- LLM envelopes are JSON; you produce them, the script validates them.
|
|
38
|
+
- On any 3-iteration validation failure (per-epic OR cross-epic), the
|
|
39
|
+
skill writes `_bmad-output/implementation-artifacts/sprint-plan.yaml.partial`
|
|
40
|
+
with the last attempted envelope + errors header, writes the sentinel
|
|
41
|
+
`.sprint-plan-validation-failed`, and halts with a `user_prompt`
|
|
42
|
+
asking the user to inspect the partial.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Step 1 — Load Inputs
|
|
47
|
+
|
|
48
|
+
<action>Verify the three required artifacts exist:
|
|
49
|
+
- `_bmad-output/implementation-artifacts/sprint-status.yaml`
|
|
50
|
+
- `_bmad-output/planning-artifacts/epics.md`
|
|
51
|
+
- `_bmad-output/planning-artifacts/architecture.md`
|
|
52
|
+
|
|
53
|
+
If any is missing, halt with a `user_prompt` naming what's missing.</action>
|
|
54
|
+
|
|
55
|
+
<action>Read each into memory. Parse epics.md for the list of epic IDs.
|
|
56
|
+
Parse sprint-status.yaml for the list of story keys under
|
|
57
|
+
`development_status:` (or `stories:` for the alternate shape).</action>
|
|
58
|
+
|
|
59
|
+
<action>Read the existing plan if present:
|
|
60
|
+
```
|
|
61
|
+
node _Sprintpilot/scripts/sprint-plan.js read --project-root <root>
|
|
62
|
+
```
|
|
63
|
+
Note: `exists: true/false` + the full plan body when present. Used by
|
|
64
|
+
Step 3 to compute staleness and decide which epics need re-inference.</action>
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Step 2 — Migrate Legacy Dependencies (One-Shot)
|
|
69
|
+
|
|
70
|
+
<action>Check whether a pre-v2.3.0 sidecar exists at
|
|
71
|
+
`_Sprintpilot/sprints/dependencies.yaml`. If it does AND `sprint-plan.yaml`
|
|
72
|
+
is absent OR has an empty `dependencies.stories` block, run the
|
|
73
|
+
one-shot migration:
|
|
74
|
+
```
|
|
75
|
+
node _Sprintpilot/scripts/infer-dependencies.js migrate --project-root <root>
|
|
76
|
+
```
|
|
77
|
+
The migrate command imports the legacy `stories:` + `overrides:` blocks
|
|
78
|
+
into the new plan, drops the legacy `epics:` block with a warning,
|
|
79
|
+
and moves the old file to `.archive/dependencies.yaml.migrated`.
|
|
80
|
+
|
|
81
|
+
If `migrated: false` and `reason: 'no_legacy_file'` — proceed silently.
|
|
82
|
+
If `migrated: false` for any other reason — surface the message in a
|
|
83
|
+
`user_prompt` and ask whether to continue with a fresh plan.</action>
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Step 3 — Staleness Check
|
|
88
|
+
|
|
89
|
+
<action>Compute which epics need re-inference. The cheap way is to
|
|
90
|
+
ask the orchestrator helper:
|
|
91
|
+
```
|
|
92
|
+
node -e "
|
|
93
|
+
const m = require('./_Sprintpilot/lib/orchestrator/sprint-plan.js');
|
|
94
|
+
console.log(JSON.stringify(m.planStaleness({projectRoot: process.cwd()})));
|
|
95
|
+
"
|
|
96
|
+
```
|
|
97
|
+
Returns `{ stale: bool, reason, missing_keys?, removed_keys? }`.
|
|
98
|
+
|
|
99
|
+
Decision matrix:
|
|
100
|
+
|
|
101
|
+
| stale | reason | action |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| false | — | Skip Step 4 entirely (plan is fresh). Go to Step 5 unless invoked via /sprintpilot-plan-sprint with a "rebuild from scratch" intent. |
|
|
104
|
+
| true | `missing` | Run Step 4 for EVERY epic in epics.md. |
|
|
105
|
+
| true | `migration_needed` | Step 2 should have handled this — re-check; if still missing, run Step 4 for every epic. |
|
|
106
|
+
| true | `added_stories` | Run Step 4 only for the epics whose stories appear in `missing_keys`. |
|
|
107
|
+
| true | `removed_stories` | Run Step 4 for the epics affected (a missing story means the per-epic graph for that epic is stale). |
|
|
108
|
+
| true | `corrupt` | Halt with a `user_prompt` showing the corruption error + offering archive+regenerate. |
|
|
109
|
+
|
|
110
|
+
For hand-authored plans (no AUTO-INFERRED marker AND user-direct invocation
|
|
111
|
+
of /sprintpilot-plan-sprint without explicit "rebuild" intent), confirm
|
|
112
|
+
with a `user_prompt` before regenerating: "An existing plan has hand
|
|
113
|
+
edits in the `dependencies` block. Proceed with regeneration (loses the
|
|
114
|
+
edits) or skip per-epic inference and just refresh DAG render?"</action>
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Step 4 — Per-Epic Inference
|
|
119
|
+
|
|
120
|
+
<action>For each epic that needs (re-)inference, run a tight loop:
|
|
121
|
+
|
|
122
|
+
1. Generate the prompt:
|
|
123
|
+
```
|
|
124
|
+
node _Sprintpilot/scripts/infer-dependencies.js scaffold-prompt --epic <id> --project-root <root>
|
|
125
|
+
```
|
|
126
|
+
Stdout is the literal prompt — feed it back to yourself in chat as
|
|
127
|
+
the "next user message" to infer the dependencies. Read the four
|
|
128
|
+
files the prompt names; do not improvise based on memory.
|
|
129
|
+
|
|
130
|
+
2. Produce a JSON envelope of the EXACT shape:
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"version": 1,
|
|
134
|
+
"epic": "<id>",
|
|
135
|
+
"dependencies": { "<story-key>": ["<dep-key>", ...] },
|
|
136
|
+
"rationale": { "<story-key>": "1 sentence quoting the AC/file/architecture line" }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
Stories with NO inbound deps are omitted from `dependencies`. Every
|
|
140
|
+
listed key needs a non-empty rationale. Cross-epic edges go through
|
|
141
|
+
Step 5 — don't put them here.
|
|
142
|
+
|
|
143
|
+
3. Validate via dry-run:
|
|
144
|
+
```
|
|
145
|
+
echo '<envelope>' | node _Sprintpilot/scripts/infer-dependencies.js dry-run --epic <id> --project-root <root>
|
|
146
|
+
```
|
|
147
|
+
On `valid: true`, proceed. On `valid: false`, the response carries
|
|
148
|
+
an `errors[]` array — fix the envelope and retry. Max 3 iterations
|
|
149
|
+
per epic. On 3rd failure, save the partial + sentinel and halt
|
|
150
|
+
(see "Conventions" above).
|
|
151
|
+
|
|
152
|
+
4. Commit the envelope:
|
|
153
|
+
```
|
|
154
|
+
echo '<envelope>' | node _Sprintpilot/scripts/infer-dependencies.js write --epic <id> --project-root <root>
|
|
155
|
+
```
|
|
156
|
+
`wrote: true, edges_inferred, edges_added, edges_removed` confirms
|
|
157
|
+
success. The script writes into `plan.dependencies.stories.*` while
|
|
158
|
+
preserving entries for other epics and the `overrides:` block.</action>
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Step 5 — Cross-Epic Detection
|
|
163
|
+
|
|
164
|
+
<action>Now that per-epic edges are in place, ask the LLM whether
|
|
165
|
+
any edges cross epic boundaries:
|
|
166
|
+
|
|
167
|
+
1. Generate the cross-epic prompt:
|
|
168
|
+
```
|
|
169
|
+
node _Sprintpilot/scripts/infer-dependencies.js scaffold-prompt --cross-epic --project-root <root>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
2. Produce a JSON envelope:
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"version": 1,
|
|
176
|
+
"cross_epic_deps": [
|
|
177
|
+
{ "from_story": "<key>", "to_story": "<key>", "rationale": "<≤200 chars>" }
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
`from_story` depends on `to_story`. Both keys must belong to
|
|
182
|
+
DIFFERENT epics. If no cross-epic deps detected, send
|
|
183
|
+
`cross_epic_deps: []`.
|
|
184
|
+
|
|
185
|
+
3. Validate via dry-run:
|
|
186
|
+
```
|
|
187
|
+
echo '<envelope>' | node _Sprintpilot/scripts/infer-dependencies.js dry-run --cross-epic --project-root <root>
|
|
188
|
+
```
|
|
189
|
+
Validator checks: keys exist in sprint-status, from/to epics differ,
|
|
190
|
+
rationale present (≤200 chars), no duplicate of per-epic edges,
|
|
191
|
+
no cycle in the combined graph. Max 3 iterations.
|
|
192
|
+
|
|
193
|
+
4. Present each surviving edge to the user with its rationale:
|
|
194
|
+
```
|
|
195
|
+
Cross-epic edge detected:
|
|
196
|
+
2-1-foo depends on 1-3-add-auth
|
|
197
|
+
Rationale: needs auth context from 1-3 before integration
|
|
198
|
+
[a] accept [r] reject [s] skip remaining edges
|
|
199
|
+
```
|
|
200
|
+
Accept-all, reject-all, or per-edge. Rejected edges drop out;
|
|
201
|
+
accepted edges go through to write.
|
|
202
|
+
|
|
203
|
+
5. Commit accepted edges:
|
|
204
|
+
```
|
|
205
|
+
echo '<accepted envelope>' | node _Sprintpilot/scripts/infer-dependencies.js write-cross-epic --project-root <root>
|
|
206
|
+
```</action>
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Step 6 — Issue Tracker Setup (Optional)
|
|
211
|
+
|
|
212
|
+
<action>Ask the user (single user_prompt):
|
|
213
|
+
> "Do you want to link stories to an external issue tracker
|
|
214
|
+
> (Jira / Linear / GitHub / GitLab)? [y/N]"
|
|
215
|
+
|
|
216
|
+
On `n` or skip — proceed to Step 7 without writing the `issue_tracker:`
|
|
217
|
+
block.
|
|
218
|
+
|
|
219
|
+
On `y` — collect:
|
|
220
|
+
- `provider`: one of `jira`, `linear`, `github`, `gitlab`
|
|
221
|
+
- `base_url`: full URL prefix (e.g., `https://co.atlassian.net`)
|
|
222
|
+
- `project_key`: the tracker's project key (e.g., `PROJ` for Jira)
|
|
223
|
+
|
|
224
|
+
Then call the sprint-plan primitive. Currently the simplest path is
|
|
225
|
+
to read the plan, set `plan.issue_tracker = { provider, base_url, project_key }`,
|
|
226
|
+
and write the whole plan back via `node _Sprintpilot/scripts/sprint-plan.js write`
|
|
227
|
+
(piping the modified plan to stdin). The script validates schema and writes
|
|
228
|
+
atomically.</action>
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Step 7 — Issue ID Capture (Optional)
|
|
233
|
+
|
|
234
|
+
<action>Ask the user (single user_prompt):
|
|
235
|
+
> "Capture external issue IDs for each epic/story? [Y]es / [n]o
|
|
236
|
+
> / [s]kip remaining for this epic / [p]attern (sequential IDs)"
|
|
237
|
+
|
|
238
|
+
On `n` — proceed to Step 8 without setting any `issue_id` fields.
|
|
239
|
+
|
|
240
|
+
On `Y` — loop through epics and stories. For each entity:
|
|
241
|
+
> "Issue ID for epic 1 (or skip)?"
|
|
242
|
+
> "Issue ID for story 1-3-add-auth (or skip)?"
|
|
243
|
+
|
|
244
|
+
`p` mode prompts once for a prefix (e.g., `PROJ-`) + starting number;
|
|
245
|
+
the skill assigns sequential IDs (`PROJ-100`, `PROJ-101`, …).
|
|
246
|
+
|
|
247
|
+
**Validation rules** — `setIssueId` rejects these inputs (re-prompt if
|
|
248
|
+
they appear; don't retry the same value):
|
|
249
|
+
- Any of: `[ ] < > | ; & \n \r` or ASCII control characters
|
|
250
|
+
- Any Unicode RTL/LTR override marks (``–``, ``–``)
|
|
251
|
+
- Length > 200 chars
|
|
252
|
+
|
|
253
|
+
Legitimate tracker IDs from Jira (`PROJ-101`), Linear (`LIN-42`),
|
|
254
|
+
GitHub (`org/repo#123`), and GitLab don't contain any of these. If a
|
|
255
|
+
user enters something like `PROJ;101` or pastes a URL with embedded
|
|
256
|
+
brackets, the validator throws — show the error and ask for a clean
|
|
257
|
+
ID. Don't loop on the same broken input.
|
|
258
|
+
|
|
259
|
+
For each captured ID, update the plan via read → mutate → write. The
|
|
260
|
+
relevant primitive is `setIssueId(entity_key, issue_id, { projectRoot })`
|
|
261
|
+
in `sprint-plan.js`; from this skill the simplest path is to bulk-edit
|
|
262
|
+
the plan in memory and write it once at the end of the loop.
|
|
263
|
+
|
|
264
|
+
Bulk skip options ensure this step doesn't become tedious on sprints
|
|
265
|
+
with many stories.</action>
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Step 8 — Finalize Dependencies in Plan
|
|
270
|
+
|
|
271
|
+
<action>By this point Steps 4 + 5 have written `plan.dependencies.stories`
|
|
272
|
+
and `plan.cross_epic_deps`. Step 6/7 may have edited `issue_tracker`
|
|
273
|
+
and per-entity `issue_id`. Re-read the plan once to confirm:
|
|
274
|
+
```
|
|
275
|
+
node _Sprintpilot/scripts/sprint-plan.js read --project-root <root>
|
|
276
|
+
```
|
|
277
|
+
The plan should validate cleanly. If `exists: true, error: ...` →
|
|
278
|
+
something went wrong; halt with the error.</action>
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Step 9 — Build the Sprint-Wide DAG
|
|
283
|
+
|
|
284
|
+
<action>Compute the topological layering for presentation:
|
|
285
|
+
```
|
|
286
|
+
node _Sprintpilot/scripts/resolve-dag.js graph --project-root <root>
|
|
287
|
+
```
|
|
288
|
+
Returns `{ nodes, edges, layers, width, cycle }`. If `cycle.length > 0`,
|
|
289
|
+
the combined intra + cross-epic graph has a cycle (validator should
|
|
290
|
+
have caught this earlier; if reached, halt with the offending nodes
|
|
291
|
+
and ask the user to remove the bad edge).</action>
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Step 10 — Present the DAG
|
|
296
|
+
|
|
297
|
+
<action>Render a text-mode topological tree to the user:
|
|
298
|
+
```
|
|
299
|
+
Layer 1 (parallel-eligible width: <N>):
|
|
300
|
+
- 1-1-bootstrap
|
|
301
|
+
- 2-1-foo ← cross-epic upstream of 1-3
|
|
302
|
+
Layer 2:
|
|
303
|
+
- 1-2-models
|
|
304
|
+
Layer 3:
|
|
305
|
+
- 1-3-add-auth (depends on: 1-1, 1-2, 2-1)
|
|
306
|
+
...
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Highlight cross-epic edges with `←` or `cross-epic →` annotations.
|
|
310
|
+
Show summary stats: total stories, total epics, max layer width
|
|
311
|
+
(indicates parallel potential for v2.4.0), count of missing issue IDs.</action>
|
|
312
|
+
|
|
313
|
+
<action>Also write the mermaid DAG file for visual review:
|
|
314
|
+
```
|
|
315
|
+
node _Sprintpilot/scripts/resolve-dag.js render --format mermaid --project-root <root>
|
|
316
|
+
```
|
|
317
|
+
Report the path to the user:
|
|
318
|
+
> "DAG rendered to `_bmad-output/implementation-artifacts/sprint-plan-dag.mmd`
|
|
319
|
+
> — preview in any markdown viewer (GitHub, VS Code with Mermaid Preview, etc.)."</action>
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Step 11 — Curation
|
|
324
|
+
|
|
325
|
+
<action>Ask the user which stories belong in this sprint plan:
|
|
326
|
+
> "Which stories do you want to run in this sprint?
|
|
327
|
+
> Default: ALL non-done stories.
|
|
328
|
+
> [Enter] accept default [e] edit selection [a:KEY] add [r:KEY] remove"
|
|
329
|
+
|
|
330
|
+
`Default: ALL non-done` means every story in sprint-status whose
|
|
331
|
+
status is not `done`. Excluded stories carry `plan_status: excluded`
|
|
332
|
+
in the plan — they remain visible in the file for context (e.g., as
|
|
333
|
+
upstreams of included stories) but are NOT picked by the queue resolver.</action>
|
|
334
|
+
|
|
335
|
+
<action>On `e` (edit), present a numbered list with `[x]` for included
|
|
336
|
+
and `[ ]` for excluded; the user toggles entries by number.</action>
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Step 12 — Validate Selection
|
|
341
|
+
|
|
342
|
+
<action>For each story marked included, every transitive upstream
|
|
343
|
+
(intra-epic AND cross-epic) must be either ALSO included OR already
|
|
344
|
+
done in sprint-status. Compute via the orchestrator helper:
|
|
345
|
+
```
|
|
346
|
+
node -e "
|
|
347
|
+
const m = require('./_Sprintpilot/lib/orchestrator/sprint-plan.js');
|
|
348
|
+
const plan = require('./_Sprintpilot/scripts/sprint-plan.js').read({projectRoot: process.cwd()});
|
|
349
|
+
const proposed = [/* user-selected keys */];
|
|
350
|
+
console.log(JSON.stringify(m.validateOrdering(proposed, plan, {projectRoot: process.cwd()})));
|
|
351
|
+
"
|
|
352
|
+
```
|
|
353
|
+
Returns `{ valid, violations: [{story, upstream, suggestion}] }`.
|
|
354
|
+
|
|
355
|
+
On `valid: false`, present each violation:
|
|
356
|
+
> "Story `1-3-add-auth` (included) depends on `1-1-bootstrap` which is
|
|
357
|
+
> not in the plan and not done. Options:
|
|
358
|
+
> [a] add `1-1-bootstrap` to the plan
|
|
359
|
+
> [r] remove `1-3-add-auth` from the plan
|
|
360
|
+
> [x] exclude `1-3-add-auth` (keeps it visible but won't run)"
|
|
361
|
+
|
|
362
|
+
Loop until `valid: true`. Excluded stories carry `plan_status: excluded`
|
|
363
|
+
and the validator treats them as terminal.</action>
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Step 13 — Write Plan
|
|
368
|
+
|
|
369
|
+
<action>Build the final plan object in memory:
|
|
370
|
+
- `schema_version: 1`
|
|
371
|
+
- `source: 'skill' | 'auto' | 'cli'` (use `auto` when invoked via
|
|
372
|
+
`template_slots.auto: true`, `skill` otherwise)
|
|
373
|
+
- `plan_id`: keep existing if re-running; generate fresh on first
|
|
374
|
+
curation
|
|
375
|
+
- `epics: []` — per-epic metadata captured in Step 1 (id, title)
|
|
376
|
+
- `stories: []` — per-story entries with `key`, `epic`, `title`,
|
|
377
|
+
`plan_status` (`pending` for included, `excluded` for excluded),
|
|
378
|
+
`issue_id` (from Step 7), `priority` (1-indexed in topological order),
|
|
379
|
+
`upstream` (denormalized from `plan.dependencies.stories.<key>.depends_on`),
|
|
380
|
+
`cross_epic_upstream` (denormalized from `plan.cross_epic_deps`),
|
|
381
|
+
`added_by: 'skill'`, `added_at`
|
|
382
|
+
- `dependencies`, `cross_epic_deps`, `overrides`, `notes` — preserved
|
|
383
|
+
from Steps 4-7
|
|
384
|
+
- `status.last_run_outcome: 'success'`, `status.last_run_at: <now>`,
|
|
385
|
+
`status.last_error: null`
|
|
386
|
+
|
|
387
|
+
Pipe to write:
|
|
388
|
+
```
|
|
389
|
+
echo '<plan-json>' | node _Sprintpilot/scripts/sprint-plan.js write --project-root <root>
|
|
390
|
+
```
|
|
391
|
+
The script validates schema, stamps `generated`, and atomically writes
|
|
392
|
+
via tmp+rename.</action>
|
|
393
|
+
|
|
394
|
+
<action>Re-render the DAG to reflect any curation changes:
|
|
395
|
+
```
|
|
396
|
+
node _Sprintpilot/scripts/resolve-dag.js render --format mermaid --project-root <root>
|
|
397
|
+
```</action>
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Step 14 — Report
|
|
402
|
+
|
|
403
|
+
<action>Summarize what was written:
|
|
404
|
+
> "Sprint plan written to
|
|
405
|
+
> `_bmad-output/implementation-artifacts/sprint-plan.yaml`.
|
|
406
|
+
>
|
|
407
|
+
> - **N** stories planned (M epics)
|
|
408
|
+
> - **X** cross-epic edges
|
|
409
|
+
> - **Y** stories excluded (kept for context)
|
|
410
|
+
> - **Z** stories without issue IDs
|
|
411
|
+
>
|
|
412
|
+
> First 5 in execution order:
|
|
413
|
+
> 1. 1-1-bootstrap
|
|
414
|
+
> 2. 1-2-models
|
|
415
|
+
> 3. ...
|
|
416
|
+
>
|
|
417
|
+
> Run `/sprint-autopilot-on` to begin execution, or
|
|
418
|
+
> `/sprintpilot-plan-sprint` again to refine."</action>
|
|
419
|
+
|
|
420
|
+
<action>If invoked via `template_slots.auto: true` or `replan: true`,
|
|
421
|
+
keep the summary shorter (1-2 sentences) and return cleanly so the
|
|
422
|
+
autopilot session resumes; do not block on confirmation.</action>
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Failure modes
|
|
427
|
+
|
|
428
|
+
| Symptom | Recovery |
|
|
429
|
+
|---|---|
|
|
430
|
+
| Prerequisite artifact missing | Halt with `user_prompt` naming the missing file + the BMad skill that produces it. |
|
|
431
|
+
| 3 consecutive validation failures (per-epic OR cross-epic) | Save `sprint-plan.yaml.partial` + `.sprint-plan-validation-failed` sentinel; halt asking the user to inspect. |
|
|
432
|
+
| Cycle in combined intra+cross graph | Halt with the offending nodes; ask the user to remove or re-rationalize the bad edge. |
|
|
433
|
+
| Plan write fails (disk full, permission) | The atomic tmp+rename is all-or-nothing — no torn state. Halt with the error message. |
|
|
434
|
+
| `sprint-plan.yaml` corrupt on entry to Step 3 | Offer archive+regenerate via `user_prompt`. |
|
|
435
|
+
| Skill invoked but `sprint-status.yaml` is empty | No stories to plan; halt and ask the user to run `bmad-sprint-planning` first. |
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sprintpilot-sprint-progress
|
|
3
|
+
description: 'Synthesize current sprint execution progress with LLM-layered judgment. Reads sprint-plan.yaml, sprint-status.yaml, and the autopilot ledger via `autopilot progress`; highlights stalls, repeated verify failures, retry loops, and stuck phases; suggests the next action (continue, narrow to a specific story, reorder, replan, abort). Use when you want a quick health check between autopilot sessions without scrolling through the raw ledger.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## STOP — read this entire file before doing anything
|
|
7
|
+
|
|
8
|
+
This skill is a **read-only diagnostic**. It does not change sprint-plan.yaml,
|
|
9
|
+
sprint-status.yaml, or any persisted autopilot state. The goal is to produce
|
|
10
|
+
a concise human-readable summary of where execution stands and what (if
|
|
11
|
+
anything) the user should do about it.
|
|
12
|
+
|
|
13
|
+
Follow **`./workflow.md`** verbatim. The three steps it lists are:
|
|
14
|
+
|
|
15
|
+
1. Collect the structured snapshot via `autopilot progress --json`.
|
|
16
|
+
2. Pull recent ledger entries for halt / verify / step context.
|
|
17
|
+
3. Synthesize a brief progress report with LLM judgment + one
|
|
18
|
+
recommended next action.
|
|
19
|
+
|
|
20
|
+
### Never improvise
|
|
21
|
+
|
|
22
|
+
- **No file writes.** This skill is observational. If the user wants to
|
|
23
|
+
reorder, add stories, mark something skipped, or replan — point them
|
|
24
|
+
at the appropriate user_input command or `/sprintpilot-plan-sprint`.
|
|
25
|
+
Don't try to fix things from this skill.
|
|
26
|
+
- **No state interpretation beyond what's in the ledger.** Don't
|
|
27
|
+
speculate about why a verify failed — just report what the ledger
|
|
28
|
+
says and recommend the user look at the relevant file.
|
|
29
|
+
- **No long-form output by default.** Default mode is ≤15 lines. If
|
|
30
|
+
the user asks for more detail, narrow to a story and read its
|
|
31
|
+
recent step events.
|
|
32
|
+
|
|
33
|
+
### When to invoke
|
|
34
|
+
|
|
35
|
+
- Between autopilot sessions to check whether anything halted that
|
|
36
|
+
needs intervention.
|
|
37
|
+
- When you (the LLM) just finished a long batch of stories and want
|
|
38
|
+
to know if anything's stuck before suggesting next moves.
|
|
39
|
+
- When the user asks "how's the sprint going?" / "where are we?" /
|
|
40
|
+
"is autopilot stuck?".
|
|
41
|
+
|
|
42
|
+
### When NOT to invoke
|
|
43
|
+
|
|
44
|
+
- For machine-readable output: call
|
|
45
|
+
`node _Sprintpilot/bin/autopilot.js progress --json` directly. This
|
|
46
|
+
skill adds LLM synthesis on top — pure JSON consumers don't need it.
|
|
47
|
+
- For full sprint reports: that's `bmad-sprint-status` (BMad-native,
|
|
48
|
+
reads sprint-status.yaml only).
|
|
49
|
+
- For build / deploy / CI status: not this skill's domain.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
Follow the instructions in ./workflow.md.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Sprintpilot — Sprint Progress Check
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Produce a concise health check of the current sprint's autopilot
|
|
6
|
+
execution. Reads the structured progress snapshot, recent halts /
|
|
7
|
+
verify failures, and step-level events; layers brief judgment on top
|
|
8
|
+
to highlight what (if anything) needs attention.
|
|
9
|
+
|
|
10
|
+
## Outputs
|
|
11
|
+
|
|
12
|
+
- A ≤15-line human-readable summary printed to chat.
|
|
13
|
+
- A single recommended next action (or "nothing to do — autopilot is
|
|
14
|
+
healthy / idle").
|
|
15
|
+
|
|
16
|
+
No file writes. No state mutations.
|
|
17
|
+
|
|
18
|
+
## Conventions
|
|
19
|
+
|
|
20
|
+
- `<root>` = project root (where `_bmad-output/` lives).
|
|
21
|
+
- All shell-outs use `node` (no global install assumed).
|
|
22
|
+
- On any error (no plan, missing ledger, etc.), degrade gracefully —
|
|
23
|
+
print what you DO know and skip the parts you don't. The user gets
|
|
24
|
+
a partial answer rather than a halt.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Step 1 — Collect the Structured Snapshot
|
|
29
|
+
|
|
30
|
+
<action>Run the progress CLI in JSON mode:
|
|
31
|
+
```
|
|
32
|
+
node _Sprintpilot/bin/autopilot.js progress --project-root <root> --json
|
|
33
|
+
```
|
|
34
|
+
Parse the response. Key fields:
|
|
35
|
+
- `plan_present` — false → project running in sprint-status order; the
|
|
36
|
+
rest of the analysis is naturally lighter.
|
|
37
|
+
- `plan_id`
|
|
38
|
+
- `current_story` / `current_step`
|
|
39
|
+
- `sprint_progress` — `{ total, done, pending, skipped, excluded, source }`
|
|
40
|
+
- `recent_events` — last 3 `story_step_*` ledger entries.
|
|
41
|
+
|
|
42
|
+
Don't fail if the command exits non-zero (e.g., missing project root).
|
|
43
|
+
Capture stderr and treat as "unknown progress" — proceed with Step 2
|
|
44
|
+
which still produces useful output.</action>
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 2 — Pull Recent Halt / Verify Context
|
|
49
|
+
|
|
50
|
+
<action>Read the tail of the ledger to identify any unresolved
|
|
51
|
+
halts, verify rejections, or repeated step-failure loops. Inline node
|
|
52
|
+
is the lightest path:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
node -e "
|
|
56
|
+
const l = require('./_Sprintpilot/lib/orchestrator/action-ledger.js');
|
|
57
|
+
const entries = l.read({projectRoot: process.cwd()}, {limit: 40});
|
|
58
|
+
const interesting = entries.filter(e =>
|
|
59
|
+
e.kind === 'halt' ||
|
|
60
|
+
e.kind === 'verify_rejected' ||
|
|
61
|
+
e.kind === 'plan_exhausted' ||
|
|
62
|
+
e.kind === 'plan_reorder_rejected' ||
|
|
63
|
+
e.kind === 'auto_derive_emitted' ||
|
|
64
|
+
e.kind === 'plan_migrated'
|
|
65
|
+
);
|
|
66
|
+
process.stdout.write(JSON.stringify(interesting));
|
|
67
|
+
"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Look for:
|
|
71
|
+
- **`halt` with reason in {`autopilot_lock_held`, `worktree_orphans_detected`,
|
|
72
|
+
`plan_exhausted`, `user_pause`, `user_replan_sprint`, `user_abort_sprint`}**
|
|
73
|
+
— autopilot is stopped and needs user attention.
|
|
74
|
+
- **`verify_rejected` with `consecutive >= 3`** — autopilot is stuck in
|
|
75
|
+
a retry loop; the LLM may need to re-read the failing artifact.
|
|
76
|
+
- **`plan_reorder_rejected`** — a recent reorder violated the DAG;
|
|
77
|
+
the user has unresolved input pending.
|
|
78
|
+
- **Repeated `story_step_started` for the same story+phase without
|
|
79
|
+
matching `story_step_completed`** — phase entered but never finished;
|
|
80
|
+
could indicate a wedged session.
|
|
81
|
+
|
|
82
|
+
Don't fail if the ledger is empty (greenfield project, never run).
|
|
83
|
+
Just note "no execution history yet" and proceed.</action>
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Step 3 — Synthesize the Report
|
|
88
|
+
|
|
89
|
+
<action>Render a single brief block to chat following this template
|
|
90
|
+
(omit any line that doesn't apply):
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Sprint progress
|
|
94
|
+
Plan: <plan_id> — <done>/<total> done (<pending> pending,
|
|
95
|
+
<skipped> skipped, <excluded> excluded)
|
|
96
|
+
Bar: [===== ] <pct>%
|
|
97
|
+
Tracker: <linked>/<total> stories linked to <provider> (<project_key>) ← only when issue_tracker set
|
|
98
|
+
Current: <story_key> [<issue_id>] (step: <phase>) OR "idle"
|
|
99
|
+
↑ issue_id bracket only when set on this story
|
|
100
|
+
Recent: <kind> <story> [<issue_id>] / <phase> (<elapsed>s ago)
|
|
101
|
+
<kind> <story> [<issue_id>] / <phase> (<elapsed>s ago)
|
|
102
|
+
|
|
103
|
+
Health: <one of: HEALTHY | STALLED | NEEDS-INPUT | EXHAUSTED | NO-PLAN>
|
|
104
|
+
Reason: <one short sentence>
|
|
105
|
+
Suggest: <one concrete next action OR "continue running">
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The `autopilot progress --json` response carries the lookup data:
|
|
109
|
+
- `current_issue_id` — the issue_id of the currently-running story (or null).
|
|
110
|
+
- `issue_tracking` — `{provider, project_key, base_url, total, linked, coverage}`
|
|
111
|
+
when an issue_tracker is configured; null otherwise (omit the Tracker
|
|
112
|
+
line entirely when null — don't surface zeros as noise).
|
|
113
|
+
- Each `recent_events[]` entry carries an `issue_id` field (or null).
|
|
114
|
+
|
|
115
|
+
Always include the `[<issue_id>]` bracket when the field is non-null;
|
|
116
|
+
omit it when null. Don't write "[no issue]" or similar — silence
|
|
117
|
+
communicates "not tracked" cleanly.
|
|
118
|
+
|
|
119
|
+
**Health classification:**
|
|
120
|
+
|
|
121
|
+
| Signal | Health | Suggest |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `plan_present=false` AND no halts in last 40 | NO-PLAN | "Continue in sprint-status order, or run /sprintpilot-plan-sprint to enable dependency-aware ordering." |
|
|
124
|
+
| Most-recent halt is `plan_exhausted` | EXHAUSTED | "Run /sprintpilot-plan-sprint to add more stories, or `autopilot start --no-auto-plan` to continue in sprint-status order." |
|
|
125
|
+
| Most-recent halt is `user_pause` | NEEDS-INPUT | "Resume with `autopilot start`." |
|
|
126
|
+
| Most-recent halt is `user_replan_sprint` | NEEDS-INPUT | "Next `autopilot start` will invoke /sprintpilot-plan-sprint." |
|
|
127
|
+
| `verify_rejected` with `consecutive >= 3` in last 5 entries | STALLED | "Inspect the failing artifact named in `verify_result.issues`; consider `user_input { kind: 'force_continue' }` only if you've manually resolved the issue." |
|
|
128
|
+
| `plan_reorder_rejected` more recent than any subsequent reorder_queue | NEEDS-INPUT | "Reorder violations exist; revise the order to respect the DAG before sending another reorder_queue." |
|
|
129
|
+
| No halts, current_story present, current_step is a valid phase | HEALTHY | "Continue running; nothing requires attention." |
|
|
130
|
+
| No halts, no current_story, sprint_progress.pending > 0 | HEALTHY | "Run `autopilot start` to pick up the next pending story." |
|
|
131
|
+
| No halts, no current_story, sprint_progress.pending == 0 | HEALTHY | "Sprint complete; consider running the bmad-retrospective skill if not already done." |
|
|
132
|
+
|
|
133
|
+
Default to HEALTHY when classification is ambiguous — don't manufacture
|
|
134
|
+
alarm.</action>
|
|
135
|
+
|
|
136
|
+
<action>If the user invoked the skill with a story name argument
|
|
137
|
+
(e.g., `/sprintpilot-sprint-progress 1-3-add-auth`), also call:
|
|
138
|
+
```
|
|
139
|
+
node _Sprintpilot/bin/autopilot.js progress --project-root <root> --story <story-key> --json
|
|
140
|
+
```
|
|
141
|
+
And append a one-block "Story detail" section showing that story's
|
|
142
|
+
plan entry. When `issue_id` is non-null, prominently display it on its
|
|
143
|
+
own line (it's the primary cross-reference back to the user's issue
|
|
144
|
+
tracker):
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
Story: <story_key>
|
|
148
|
+
Issue: <issue_id> ← omit line when null
|
|
149
|
+
Epic: <epic>
|
|
150
|
+
Plan status: <plan_status>
|
|
151
|
+
BMad status: <bmad_status>
|
|
152
|
+
Priority: <priority>
|
|
153
|
+
Current step: <current_step> ← omit when not running
|
|
154
|
+
Completed: <completed_at> ← omit when not done
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Do not repeat the full sprint summary in this mode — just the focused
|
|
158
|
+
story block.</action>
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Failure modes
|
|
163
|
+
|
|
164
|
+
| Symptom | Recovery |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `autopilot progress` exits non-zero (missing project root, etc.) | Capture stderr; print "Progress CLI unavailable: <stderr first line>"; still attempt Step 2. |
|
|
167
|
+
| Ledger file missing | Print "No execution history yet — sprint hasn't started"; skip Step 2 analysis; suggest `autopilot start`. |
|
|
168
|
+
| Plan file corrupt | Print "sprint-plan.yaml unreadable (run `node _Sprintpilot/scripts/sprint-plan.js read --project-root .` to inspect)"; do NOT auto-archive — that's user's call. |
|
|
169
|
+
| Recent ledger has 0 entries | Note "Ledger is empty — autopilot hasn't run yet or was reset"; skip halt analysis. |
|