@mhd-ghaith-abtah/flow 0.7.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +21 -0
  3. package/README.md +162 -0
  4. package/adapters/e2e/_interface.md +42 -0
  5. package/adapters/e2e/none.md +15 -0
  6. package/adapters/e2e/playwright-mcp.md +86 -0
  7. package/adapters/issue-tracker/_interface.md +46 -0
  8. package/adapters/issue-tracker/github-issues.md +103 -0
  9. package/adapters/issue-tracker/linear.md +65 -0
  10. package/adapters/issue-tracker/none.md +26 -0
  11. package/adapters/pr/_interface.md +29 -0
  12. package/adapters/pr/github.md +61 -0
  13. package/adapters/pr/none.md +32 -0
  14. package/adapters/verify/_interface.md +26 -0
  15. package/adapters/verify/custom.md +27 -0
  16. package/adapters/verify/make.md +30 -0
  17. package/adapters/verify/pnpm.md +27 -0
  18. package/bin/flow.js +129 -0
  19. package/catalog.yaml +364 -0
  20. package/docs/adapters.md +99 -0
  21. package/docs/migrate-from-bmad.md +95 -0
  22. package/docs/profiles.md +81 -0
  23. package/docs/quickstart.md +82 -0
  24. package/lib/catalog.js +164 -0
  25. package/lib/commands/add.js +147 -0
  26. package/lib/commands/doctor.js +392 -0
  27. package/lib/commands/init.js +86 -0
  28. package/lib/commands/install.js +181 -0
  29. package/lib/commands/plan.js +108 -0
  30. package/lib/commands/remove.js +87 -0
  31. package/lib/commands/uninstall.js +157 -0
  32. package/lib/repo-root.js +53 -0
  33. package/package.json +62 -0
  34. package/schemas/catalog.schema.json +155 -0
  35. package/schemas/flow-config.schema.json +85 -0
  36. package/schemas/install-state.schema.json +79 -0
  37. package/skills/flow-doctor/SKILL.md +15 -0
  38. package/skills/flow-doctor/workflow.md +157 -0
  39. package/skills/flow-init/SKILL.md +10 -0
  40. package/skills/flow-init/workflow.md +420 -0
  41. package/skills/flow-sprint/SKILL.md +10 -0
  42. package/skills/flow-sprint/workflow.md +394 -0
  43. package/skills/flow-story/SKILL.md +12 -0
  44. package/skills/flow-story/workflow.md +531 -0
  45. package/templates/claude-md-section.md.tmpl +55 -0
  46. package/templates/flow-readme.md.tmpl +34 -0
  47. package/templates/flow.config.yaml.tmpl +94 -0
  48. package/templates/pr.md.tmpl +40 -0
  49. package/templates/retro.md.tmpl +58 -0
  50. package/templates/sprint.yaml.tmpl +18 -0
  51. package/templates/story.md.tmpl +35 -0
  52. package/tools/fix-caveman-shrink.sh +68 -0
  53. package/tools/lint-changelog.js +98 -0
@@ -0,0 +1,531 @@
1
+ # flow-story Workflow
2
+
3
+ **Output style — Caveman mandate.** All user-facing output from this workflow MUST be Caveman-mode: fragments OK, drop articles / filler / pleasantries / hedging, keep code & commit & security text normal. Status banners short. Errors short. Tables OK.
4
+
5
+ **Caveman skill integration.** When Caveman is installed (catalog.upstreams.caveman.subset != none), this workflow routes three phases through Caveman skills:
6
+ - **commit-pr phase** → invoke `caveman:caveman-commit` skill instead of composing commit message inline (shorter conventional commits).
7
+ - **review phase** → after reviewer agents return, invoke `caveman:caveman-review` skill to compress findings into one-line-per-issue format before appending to story file's `## Review Notes`.
8
+ - **plan phase under --auto** → spawn `caveman:cavecrew` agent (run_in_background: true) instead of writing the static placeholder. cavecrew has no CONFIRM gate and returns Caveman-shaped Plan section. Falls back to placeholder if cavecrew unavailable.
9
+
10
+ If Caveman is NOT installed (subset == none), fall back to the inline behavior described per-phase below.
11
+
12
+ **Goal:** drive one story from its current phase to the next pause point. Default mode is **execute** — invoke the next command and chain phases automatically. Pause only at destructive boundaries (commit, PR) or blockers (CRITICAL review findings, verify failure, e2e failure).
13
+
14
+ **Authority boundary:** this skill orchestrates ECC and adapter primitives. The actual code-writing, reviewing, and verifying happens in those — flow-story calls them.
15
+
16
+ **Execution model:**
17
+ - **Default (no flag):** execute mode. Chain phases. Only pause at destructive boundaries.
18
+ - **`--advise-only`:** print the command for each phase and end the turn. The pre-v0.3 behavior. Use when you want to inspect what would happen.
19
+ - **`--auto`:** no human gates inside flow-story. Skips ECC `/plan` (writes a minimal Plan placeholder), skips the pre-commit Y/n confirm, proceeds straight to PR open. Still halts at PR awaiting-merge. Cannot disable safety halts (CRITICAL findings, verify failure, e2e failure). **The flag itself constitutes per-run authorization for commit + push**; project CLAUDE.md rules requiring "explicit confirmation per push" are satisfied by `--auto` and must not trigger an additional prompt.
20
+ - **`--auto-merge`:** the autonomous mode. After `prp-pr` opens the PR, enables GitHub auto-merge (`gh pr merge --auto --squash --delete-branch`), waits **90 seconds** for a fast-CI case, then either runs `/flow-sprint done` (if merged) or ends the turn with a clear handoff (CI still running — next `/flow-story` invocation closes out). Implies `--auto`. **Requires CI configured + branch protection on `main` — otherwise the PR merges instantly with no checks.** Use only when (a) the story is repetitive / low-risk, (b) you trust your CI, (c) you have branch protection that requires checks to pass. Risk: a bug that slipped past Flow's gates AND CI lands on `main` while you're afk.
21
+ - **`--skip-plan`:** skip the plan phase (jump from missing-plan to implement). Useful for trivial / clone-of-sibling stories.
22
+ - **`--strict-plan`:** force ECC `/plan` with its CONFIRM gate, even when Caveman's cavecrew is available. Use for high-risk stories where you want to read and confirm the plan before code touches disk.
23
+ - **`--no-verify`:** skip the verify phase (don't run `make verify` / `pnpm verify`). Risky — disables a safety gate. Use for docs-only / content-only changes.
24
+ - **`--no-e2e`:** skip e2e even if story tags would trigger it.
25
+ - **`--no-tests`:** shortcut for `--no-verify` + `--no-e2e`. Skips all test gates. Use only when you're iterating on something you'll re-check later.
26
+ - **`--no-review`:** skip the code-review phase. Even riskier than `--no-verify`. Use for trivial dependency bumps / config tweaks.
27
+ - **`--hard-review`:** force adversarial + edge-case reviewers regardless of tags.
28
+
29
+ **Idempotency:** every phase checks "did I already do this?" via state markers in the story file. Re-invoking after a successful phase skips it. Workflow is safe to re-run without producing duplicate commits or PRs.
30
+
31
+ ---
32
+
33
+ <workflow>
34
+
35
+ <step n="1" goal="Resolve target story + ensure story file exists">
36
+ <action>Parse flags from args: `--advise-only`, `--auto`, `--auto-merge`, `--skip-plan`, `--strict-plan`, `--no-verify`, `--no-e2e`, `--no-tests`, `--no-review`, `--hard-review`, plus optional positional story id.</action>
37
+ <action>If `--no-tests` is set, treat both `--no-verify` and `--no-e2e` as also set.</action>
38
+ <action>If `--auto-merge` is set, also set `--auto` (auto-merge implies auto throughout the pipeline).</action>
39
+
40
+ <action>Load `flow.config.yaml`. If missing, HALT with "Run /flow-init first." If `flow.config.local.yaml` (per-developer override, gitignored) is also present, deep-merge it **over** the base config — local values win on conflict. This is how teams keep one shared `flow.config.yaml` while each developer can tweak (e.g. `review.barrier_timeout_seconds`, `verify.command`) without committing personal overrides.</action>
41
+ <action>Load `docs/flow/sprint.yaml` → `{{sprint}}`.</action>
42
+
43
+ <check if="positional arg provided">
44
+ <action>Match against `story.id` (E1-001 or E1-S1 form). → `{{story}}`.</action>
45
+ <check if="no match">
46
+ <output>🚫 No story matching `{{arg}}`. Run `/flow-sprint status`.</output>
47
+ <action>End turn.</action>
48
+ </check>
49
+ </check>
50
+
51
+ <check if="no positional arg">
52
+ <action>Find stories with status == 'doing' OR 'review'. → `{{candidates}}`.</action>
53
+ <check if="zero candidates">
54
+ <output>🚫 No active story. Run `/flow-sprint next` to start one.</output>
55
+ <action>End turn.</action>
56
+ </check>
57
+ <check if="exactly one candidate">
58
+ <action>Set `{{story}}` = that one.</action>
59
+ </check>
60
+ <check if="multiple candidates">
61
+ <ask>Multiple active stories. Pick one:</ask>
62
+ <action>Print numbered list, resolve user's pick.</action>
63
+ </check>
64
+ </check>
65
+
66
+ <check if="{{story.kind}} == 'offline'">
67
+ <output>🌍 {{story.id}} — {{story.title}} is offline. Use `/flow-sprint done {{story.id}} --note "..."` when complete.</output>
68
+ <action>End turn.</action>
69
+ </check>
70
+
71
+ <!-- Story file resolution + auto-scaffold ────────────────────────────── -->
72
+ <action>Determine `{{story_file}}`:
73
+ - If `{{story.file}}` set in sprint.yaml → that path.
74
+ - Else look for `docs/flow/stories/{{story.id}}-*.md` (glob).
75
+ - Else look for `docs/_bmad-output/implementation-artifacts/{{story.bmad_key}}.md` (legacy BMad spec).
76
+ - Else `{{story_file}}` is null → trigger auto-scaffold below.
77
+ </action>
78
+
79
+ <check if="{{story_file}} is null">
80
+ <action>**Auto-scaffold a minimal stub.** No interactive Q&A — derive everything from sprint.yaml + conventions.
81
+ 1. Find the most recent done story in this epic → `{{sibling}}`. This sets the "house pattern" reference.
82
+ 2. Look for design refs by convention: scan `docs/design/` for files whose name contains a slugified token of `{{story.title}}` (case-insensitive). Collect matches → `{{design_refs}}`.
83
+ 3. Look for content refs: scan `docs/_bmad-output/planning-artifacts/` for `content-*` files mentioning the story's keyword. Collect → `{{content_refs}}`.
84
+ 4. Generate the stub file at `docs/flow/stories/{{story.id}}-{{slug(story.title)}}.md` with this exact shape (5–10 lines, no boilerplate):
85
+
86
+ ```markdown
87
+ # {{story.id}} — {{story.title}}
88
+
89
+ **Epic:** {{story.epic}} — {{epic.title}}
90
+ **Tags:** {{story.tags or "(none)"}}
91
+ **Status:** {{story.status}}
92
+
93
+ **Refs:**
94
+ {{design_refs lines or "(no design ref auto-detected — check docs/design/)" }}
95
+ {{content_refs lines or "" }}
96
+ **Sibling pattern:** {{sibling.id}} — {{sibling.title}} ({{sibling.file or "see archive"}})
97
+
98
+ ## Plan
99
+
100
+ <!-- /plan will populate this. -->
101
+ ```
102
+
103
+ 5. Append `file: docs/flow/stories/{{story.id}}-{{slug(story.title)}}.md` to the sprint.yaml entry for this story. Write sprint.yaml. Set `{{story_file}}` to the new path.
104
+ </action>
105
+ <output>📄 Auto-created stub: `{{story_file}}` (sibling: {{sibling.id}}, refs: {{design_refs_count}} design + {{content_refs_count}} content)</output>
106
+ </check>
107
+
108
+ <action>Load `{{story_file_content}}` from `{{story_file}}`.</action>
109
+ </step>
110
+
111
+ <step n="2" goal="Detect current phase">
112
+ <action>Read in parallel:
113
+ - `{{branch}}` = `git rev-parse --abbrev-ref HEAD`
114
+ - `{{commits_ahead}}` = `git rev-list --count origin/main..HEAD` (fall back to `main` if no `origin/main`)
115
+ - `{{has_uncommitted}}` = `git status --porcelain` non-empty (modified, untracked, or staged-but-uncommitted)
116
+ - `{{has_changes}}` = `{{commits_ahead}} > 0` OR `{{has_uncommitted}}` (work exists to review/verify/commit, regardless of whether it's been committed yet)
117
+ - `{{has_plan_section}}` = does `{{story_file_content}}` contain a line matching the anchored regex `^## Plan\s*$` (exact heading — `## Plan B`, `## Planning`, `## Plan (revised)` all do NOT count) followed by non-comment content
118
+ - `{{review_done}}` = does `{{story_file_content}}` contain a line matching `^## Review Notes\s*$` (anchored, exact)
119
+ - `{{verify_passed}}` = does `{{story_file_content}}` contain a line matching `^## Verified\s*$` (anchored, exact)
120
+ - `{{e2e_passed}}` = does `{{story_file_content}}` contain a line matching `^## E2E\s*$` (anchored) with `passed: true` somewhere in its block
121
+ - `{{pr_number}}` + `{{pr_state}}` = if pr_adapter != none, query GH for PR on this branch
122
+ </action>
123
+
124
+ <action>Phase decision (first match wins). **Important:** review and verify run on uncommitted work; we don't require a commit before they're eligible. commit-pr happens AFTER they pass, bundling everything into a single commit.
125
+ - `{{story.status}} == 'done'` → `archived`
126
+ - `{{story.status}} == 'review'` AND `{{pr_state}} == 'merged'` → `merge-done`
127
+ - `{{story.status}} == 'review'` → `awaiting-merge`
128
+ - `{{has_changes}}` AND `{{verify_passed}}` AND (e2e ok or n/a) AND `{{review_done}}` AND NOT `{{pr_number}}` → `commit-pr`
129
+ - `{{has_changes}}` AND `{{review_done}}` AND NOT `{{verify_passed}}` → `verify`
130
+ - `{{has_changes}}` AND NOT `{{review_done}}` → `review`
131
+ - `{{branch}}` starts `flow/` AND NOT `{{has_changes}}` AND (`{{has_plan_section}}` OR `--skip-plan`) → `implement`
132
+ - `{{branch}}` starts `flow/` AND NOT `{{has_changes}}` AND NOT `{{has_plan_section}}` AND NOT `--skip-plan` → `plan`
133
+ - `{{story.status}} == 'doing'` AND `{{branch}} == 'main'` → `resume-branch`
134
+ - else → `unknown`
135
+ </action>
136
+
137
+ <output>📋 Story {{story.id}} — phase: {{phase}} {{ (advise-only) if --advise-only }}</output>
138
+ </step>
139
+
140
+ <step n="3" goal="Execute the detected phase + chain to next">
141
+
142
+ <!-- ADVISE-ONLY ESCAPE: emit command for current phase only, end turn -->
143
+ <check if="--advise-only">
144
+ <action>Print the command that WOULD execute for `{{phase}}`. Do not invoke. End turn.</action>
145
+ </check>
146
+
147
+ <!-- ────────────────────── PLAN ────────────────────── -->
148
+ <check if="phase == 'plan'">
149
+ <!-- Plan-phase decision tree. First match wins. The branches are mutually
150
+ exclusive and ordered from most-specific (flag-driven) to default.
151
+ (--advise-only is handled at the top of step 3; never reaches here.) -->
152
+
153
+ <!-- 1. --skip-plan: trivial / clone-of-sibling. Placeholder only. -->
154
+ <check if="--skip-plan">
155
+ <output>📐 plan → placeholder (--skip-plan). prp-implement plans inline.</output>
156
+ <action>Append a `## Plan` section to {{story_file}}:
157
+ ```
158
+ ## Plan
159
+
160
+ Skipped (--skip-plan). prp-implement reads ACs + Files block + sibling code → diff.
161
+ ```
162
+ </action>
163
+ <action>Re-detect phase. Continue. Do NOT end turn.</action>
164
+ </check>
165
+
166
+ <!-- 2. --strict-plan: high-risk story; force ECC /plan with CONFIRM gate. -->
167
+ <check if="--strict-plan">
168
+ <output>📐 plan → /plan (strict, CONFIRM gate enforced)…</output>
169
+ <action>Invoke `plan` skill via Skill tool with argument `@{{story_file}}`. Wait for /plan's CONFIRM gate. After /plan returns successfully: re-detect, continue. Do NOT end turn.</action>
170
+ </check>
171
+
172
+ <!-- 3. Caveman installed → cavecrew is the default planner.
173
+ Covers bare /flow-story and --auto. No CONFIRM gate, Caveman-shape output. -->
174
+ <check if="Caveman installed AND `caveman:cavecrew` skill registered">
175
+ <output>📐 plan → cavecrew (Caveman default, no CONFIRM gate)…</output>
176
+ <action>Spawn `caveman:cavecrew` (or fall back to `cavecrew` flat name) as Agent with `run_in_background: true`. Prompt: "Emit Plan section for {{story.id}} — {{story.title}}. Inputs: story file at {{story_file}}, sibling pattern {{sibling.id}} ({{sibling.file}}), refs above. Output only the Plan markdown body (no preamble). Caveman style: fragments OK, drop filler." When agent returns, append its output as the `## Plan` section in {{story_file}}.</action>
177
+ <action>Re-detect phase. Continue. Do NOT end turn.</action>
178
+ </check>
179
+
180
+ <!-- 4. Caveman not installed. Two sub-cases:
181
+ 4a. --auto → placeholder (consistent with cavecrew's no-gate behavior)
182
+ 4b. bare /flow-story → ECC /plan with CONFIRM gate -->
183
+ <check if="--auto">
184
+ <output>📐 plan → placeholder (--auto, no Caveman). prp-implement plans inline.</output>
185
+ <action>Append a `## Plan` section to {{story_file}} as in branch 1 (placeholder). Re-detect, continue.</action>
186
+ </check>
187
+
188
+ <output>📐 plan → /plan with CONFIRM gate (pass `--auto` to skip, or install Caveman for cavecrew default)…</output>
189
+ <action>Invoke the `plan` skill via the Skill tool with argument `@{{story_file}}`. The plan skill will (a) read the story, (b) propose an implementation strategy, (c) ASK the user to CONFIRM before continuing. That confirmation gate is plan's own, not flow-story's — flow-story waits for it to return.</action>
190
+ <action>After /plan returns successfully (story file now has a populated `## Plan` section): re-detect phase from Step 2 and continue. Do NOT end turn.</action>
191
+ </check>
192
+
193
+ <!-- ────────────────────── IMPLEMENT ────────────────────── -->
194
+ <check if="phase == 'implement'">
195
+ <output>🛠 implement → invoking `prp-implement` skill on {{story_file}}…</output>
196
+ <action>Invoke the `prp-implement` skill via the Skill tool with argument `@{{story_file}}`. It reads the plan + ACs + Files block and implements with internal validation loops. May commit incrementally; flow-story tolerates that.</action>
197
+ <action>After /prp-implement returns: re-detect phase (should advance to `review` once commits are on the branch). Continue. Do NOT end turn.</action>
198
+ </check>
199
+
200
+ <!-- ────────────────────── REVIEW ────────────────────── -->
201
+ <check if="phase == 'review'">
202
+ <check if="--no-review">
203
+ <output>🔍 review → skipped (--no-review). Appending placeholder Review Notes…</output>
204
+ <action>Append `## Review Notes` to {{story_file}} with `(skipped via --no-review, {{timestamp}})`. Re-detect phase. Continue.</action>
205
+ </check>
206
+
207
+ <action>Compose reviewer set:
208
+ - Always: `code-review` (generic reviewer)
209
+ - Stack-specific: from `config.review.language_reviewer` if set (e.g. `typescript-reviewer`)
210
+ - Security: `security-review` if `(config.review.auto_hard_review_tags ∩ story.tags) ≠ ∅` OR `--hard-review`
211
+ - Adversarial: `bmad-review-edge-case-hunter` if `--hard-review` or tags include `auth|payments|migration|pii`
212
+ </action>
213
+
214
+ <output>🔍 review → spawning {{reviewer_count}} reviewer(s) in parallel…</output>
215
+
216
+ <action>**Always spawn reviewers as Agents with `run_in_background: true`.** This keeps each reviewer's intermediate tool calls (greps, file reads, sub-shells) isolated in its own context — only the final findings summary returns to flow-story. Without background mode, a single review pass can consume 50+ tool uses in the main thread.
217
+
218
+ Implementation:
219
+ - One Agent tool call per reviewer, in a single message (so they run concurrently).
220
+ - `subagent_type` = `<reviewer-id>` (e.g. `code-reviewer`, `typescript-reviewer`, `security-reviewer`).
221
+ - `run_in_background: true` on every call.
222
+ - `description` = short label (e.g. "Review E2-S11 — TypeScript").
223
+ - `prompt` = self-contained: target diff (refer to "uncommitted changes" or "current branch vs main"), story acceptance criteria, severity rubric (CRITICAL/HIGH/MEDIUM/LOW), output format ("return a markdown table of findings with file:line, severity, finding").
224
+ - If `config.review.use_separate_model == true`, also set `model: "sonnet"` (or whatever model is configured) for at least one reviewer to get a fresh perspective.
225
+
226
+ After spawning: do NOT block waiting. Note the background task IDs as `{{review_task_ids}}`. Continue to verify phase. Background agents will return findings via notifications — flow-story aggregates them at the commit-pr barrier.
227
+ </action>
228
+
229
+ <action>Mark `{{review_spawned}} = true` in state (in-memory for this run; not persisted to story file yet — that happens after aggregation). Re-detect phase. Continue to verify. Do NOT end turn.</action>
230
+ <action>Re-detect phase. Continue. Do NOT end turn.</action>
231
+ </check>
232
+
233
+ <!-- ────────────────────── VERIFY ────────────────────── -->
234
+ <check if="phase == 'verify'">
235
+ <check if="--no-verify">
236
+ <output>🧪 verify → skipped (--no-verify / --no-tests). Appending placeholder Verified marker…</output>
237
+ <action>Append `## Verified` to {{story_file}} with `(skipped via --no-verify, {{timestamp}})`. Re-detect phase. Continue.</action>
238
+ </check>
239
+
240
+ <action>Load verify adapter: `~/.claude/skills/flow-story/adapters/verify/{{config.adapters.verify}}.md`. Get its `verify_cmd`.</action>
241
+
242
+ <output>🧪 verify → $ {{verify_cmd}}</output>
243
+ <action>Execute `{{verify_cmd}}` via the Bash tool. Stream output. Capture exit code.</action>
244
+
245
+ <check if="exit != 0">
246
+ <output>✗ Verify failed (exit {{code}}). Fix and re-invoke. Common helpers:
247
+
248
+ `/build-fix` — ECC build-error resolver
249
+ Read the output above for the actual error.
250
+ </output>
251
+ <action>End turn. (Verify failure always pauses, even in --auto mode.)</action>
252
+ </check>
253
+
254
+ <action>Append `## Verified` block to {{story_file}} with timestamp + command + exit_code: 0.</action>
255
+ <action>Re-detect phase. Continue. Do NOT end turn.</action>
256
+ </check>
257
+
258
+ <!-- ────────────────────── E2E ────────────────────── -->
259
+ <check if="(story.tags ∩ config.implement.e2e_auto_trigger_tags) ≠ ∅ AND config.adapters.e2e != 'none' AND NOT {{e2e_passed}} AND NOT --no-e2e">
260
+ <action>Load e2e adapter: `~/.claude/skills/flow-story/adapters/e2e/{{config.adapters.e2e}}.md`.</action>
261
+ <action>Read `## E2E Journey` block from {{story_file}}. If missing, skip e2e and emit advisory "story tagged for E2E but has no Journey block — add one for stronger coverage". Do NOT halt.</action>
262
+
263
+ <output>🎭 e2e → running journey via {{config.adapters.e2e}}…</output>
264
+ <action>Execute the adapter's `run_journey` operation (typically the Playwright MCP tools per step). Stream output. Save artifacts to `docs/flow/artifacts/{{story.id}}/`.</action>
265
+
266
+ <check if="run_journey failed">
267
+ <output>✗ E2E journey failed. Artifacts: docs/flow/artifacts/{{story.id}}/
268
+
269
+ {{summary of failed steps}}
270
+
271
+ Re-run after fixing. (E2E failure always pauses, even in --auto mode.)
272
+ </output>
273
+ <action>End turn.</action>
274
+ </check>
275
+
276
+ <action>Append `## E2E` block to {{story_file}} with `passed: true` + artifact paths.</action>
277
+ <action>Re-detect phase. Continue. Do NOT end turn.</action>
278
+ </check>
279
+
280
+ <!-- ────────────────────── DOCS ────────────────────── -->
281
+ <check if="config.mode in [standard, team] AND {{verify_passed}} AND NOT story has '## Docs' marker">
282
+ <output>📚 docs → invoking `update-docs` skill…</output>
283
+ <action>Invoke `update-docs` skill via Skill tool. Wait for completion.</action>
284
+ <action>If `config.mode == team`, also invoke `update-codemaps`.</action>
285
+ <action>Append `## Docs` marker to {{story_file}}. Re-detect. Continue. Do NOT end turn.</action>
286
+ </check>
287
+
288
+ <!-- ────────────────────── REVIEW BARRIER (before commit-pr) ────────────────────── -->
289
+ <!-- Background reviewers spawned earlier may not have completed yet. Wait for
290
+ their notifications and aggregate findings before committing. -->
291
+ <check if="phase == 'commit-pr' AND {{review_spawned}} AND NOT {{review_done}}">
292
+ <output>🔍 review barrier — waiting on {{N}} background reviewer(s) before commit…</output>
293
+ <action>Record `{{barrier_start}} = now()`. Hard cap: **15 minutes** wall-clock from `{{barrier_start}}`. Configurable via `config.review.barrier_timeout_seconds` (default 900).</action>
294
+ <action>For each background task in `{{review_task_ids}}` (or recently-spawned reviewer agents):
295
+ - If completed, parse its findings.
296
+ - If still running, await its completion notification (do not actively poll — the harness notifies on task completion).
297
+ - If `now() - {{barrier_start}} >= {{barrier_timeout}}`, mark the task as `timed_out`, stop waiting on it (do NOT block further), and record `{{timed_out_reviewers}}` += reviewer-id.
298
+ </action>
299
+
300
+ <check if="{{timed_out_reviewers}} is non-empty">
301
+ <output>⏱ Review barrier timed out after {{barrier_timeout}}s. Reviewer(s) still running: {{timed_out_reviewers}}.
302
+
303
+ Options:
304
+ 1. Re-invoke `/flow-story` later (background reviewer may have finished by then — findings will be picked up).
305
+ 2. Re-spawn the reviewer with `--no-review` to skip (NOT recommended for tagged-risky stories).
306
+ 3. Bump `config.review.barrier_timeout_seconds` if your reviewers genuinely need more time.
307
+
308
+ Committing now without these reviewers' findings is **unsafe** — halting.
309
+ </output>
310
+ <action>End turn. Do NOT commit. Do NOT advance phase.</action>
311
+ </check>
312
+
313
+ <action>Aggregate findings by severity. Compute **raw counts** from the un-compressed reviewer output and store them as `{{severity_counts}} = { critical: N, high: M, medium: P, low: Q }`. These are the **source of truth** for the safety gate — do NOT recompute from compressed output.</action>
314
+
315
+ <action>**Severity extraction fallback** (issue #6 defense): a reviewer that emits findings without explicit `CRITICAL` / `HIGH` / `MEDIUM` / `LOW` labels (e.g., emoji-only like 🚨 / ⚠) is parsed via the table below before counting. Anything still ambiguous is bucketed as `MEDIUM`.
316
+ - 🚨 / `critical` / `must-fix` / `blocker` → CRITICAL
317
+ - ⚠ / `high` / `should-fix` / `severe` → HIGH
318
+ - 🟡 / `medium` / `consider` / `nice-to-have` → MEDIUM
319
+ - 🟢 / `low` / `nit` / `style` / `optional` → LOW
320
+ </action>
321
+
322
+ <check if="{{severity_counts.critical}} > 0 OR {{severity_counts.high}} > 0">
323
+ <output>🚨 Review halted — {{severity_counts.critical}} CRITICAL, {{severity_counts.high}} HIGH finding(s):
324
+
325
+ {{render findings with file:line, severity, reviewer name}}
326
+
327
+ Fix these, then re-invoke `/flow-story`. (CRITICAL/HIGH always pauses, even in --auto / --auto-merge.)
328
+ </output>
329
+ <action>End turn.</action>
330
+ </check>
331
+
332
+ <check if="Caveman installed AND `caveman:caveman-review` skill registered">
333
+ <output>🪨 caveman-review → compressing findings to one-line-per-issue…</output>
334
+ <action>Invoke `caveman:caveman-review` skill via Skill tool with the aggregated findings as input. It returns compressed one-line-per-issue format. Store as `{{compressed_notes}}`.</action>
335
+ <action>**Severity-label preservation guard** (issue #6): scan `{{compressed_notes}}` for at least one occurrence of `CRITICAL` / `HIGH` / `MEDIUM` / `LOW` literal tokens (case-insensitive) when `{{severity_counts.critical + high + medium + low}} > 0`. If the compressed output stripped all severity tokens, prepend this header to `{{compressed_notes}}`:
336
+
337
+ ```
338
+ Findings: {{severity_counts.critical}} critical · {{severity_counts.high}} high · {{severity_counts.medium}} medium · {{severity_counts.low}} low
339
+ ```
340
+
341
+ This keeps future readers and `/flow-doctor`'s bug-probe able to detect severity even when caveman-review compresses aggressively.
342
+ </action>
343
+ </check>
344
+
345
+ <action>Append `## Review Notes` to {{story_file}} with LOW/MEDIUM findings (Caveman-compressed if available, with severity header guard applied), reviewer names, timestamps. Mark `{{review_done}} = true`. Continue to commit-pr.</action>
346
+ </check>
347
+
348
+ <!-- ────────────────────── COMMIT-PR ────────────────────── -->
349
+ <check if="phase == 'commit-pr'">
350
+ <check if="Caveman installed AND `caveman:caveman-commit` skill registered">
351
+ <output>🪨 caveman-commit → composing message…</output>
352
+ <action>Invoke `caveman:caveman-commit` skill via Skill tool. Pass it **raw, un-compressed inputs** (issue #16 — never feed caveman-compressed text into a Caveman skill or you get double-compression):
353
+ - `story.id`, `story.title`, `story.tags`
354
+ - `changed_files` = list from `git status --porcelain`
355
+ - `severity_summary` = `{{severity_counts.critical}} crit · {{severity_counts.high}} high · {{severity_counts.medium}} med · {{severity_counts.low}} low` (from the raw counts captured in the review barrier — NOT a re-read of the compressed `## Review Notes` block)
356
+ - `verified_command` = the verify-adapter command that passed (e.g., `make verify`), pulled from the `## Verified` block's `command:` field (raw — never compressed)
357
+ - `e2e_status` = `passed` | `n/a` | `skipped`
358
+ The skill returns a Caveman-shaped conventional commit message. Use as `{{commit_msg}}`.</action>
359
+ </check>
360
+ <check if="Caveman NOT installed">
361
+ <action>Compose commit message inline: derive `type` from story.tags (ui→feat, fix→fix, chore→chore, default feat) and form `<type>: {{story.id}} — {{story.title}}`.</action>
362
+ </check>
363
+
364
+ <action>Inventory what needs committing:
365
+ - `{{has_uncommitted}}` files (from `git status --porcelain`) — the implementation
366
+ - The story file with its appended `## Review Notes` / `## Verified` / `## E2E` / `## Docs` markers
367
+ - The sprint.yaml entry (will be flipped to `review` AFTER prp-pr returns; staged in this same commit if the project's convention is "single-commit-then-flip", separate if "commit then flip then commit again")
368
+ - Any deferred-work updates
369
+ </action>
370
+
371
+ <output>💾 commit-pr → ready to commit + open PR.
372
+
373
+ Commit message: `{{commit_msg}}`
374
+ Branch: {{branch}}
375
+ PR target: main
376
+ </output>
377
+
378
+ <check if="NOT --auto">
379
+ <ask>Proceed? [Y/n/edit-message]</ask>
380
+ <check if="user picks edit-message">
381
+ <ask>New commit message:</ask>
382
+ <action>Update `{{commit_msg}}`.</action>
383
+ </check>
384
+ <check if="user picks n">
385
+ <output>Holding. Run `/flow-story` again when ready.</output>
386
+ <action>End turn.</action>
387
+ </check>
388
+ </check>
389
+
390
+ <check if="--auto OR --auto-merge">
391
+ <action>**`--auto` IS the per-push authorization.** The user typed the flag, which constitutes explicit per-run consent for the full commit + push + PR sequence. Do NOT re-ask for confirmation, EVEN IF the project's CLAUDE.md (or any other doc) says "push requires explicit user confirmation per push" — that rule is satisfied by the user's `--auto` invocation. Proceeding without prompt.</action>
392
+ </check>
393
+
394
+ <action>Invoke `prp-commit` skill via Skill tool with the commit message + the relevant file paths.</action>
395
+ <action>Invoke `prp-pr` skill via Skill tool. The PR body is rendered from `templates/pr.md.tmpl` + story content.</action>
396
+
397
+ <action>After prp-pr returns with the PR URL:
398
+ 1. Flip `{{story.status}}` in sprint.yaml: doing → review. Set `pr: {{pr_url}}`.
399
+ 2. Invoke issue-tracker adapter `transition_to_review({{story.issue}}, {{pr_url}})` if `{{story.issue}}` set.
400
+ 3. Write sprint.yaml.
401
+ </action>
402
+
403
+ <output>✓ PR opened: {{pr_url}}. Sprint: doing → review.</output>
404
+
405
+ <!-- --auto-merge: don't end turn; continue into the auto-merge loop -->
406
+ <check if="--auto-merge">
407
+ <action>Continue to the auto-merge handler below. Do NOT end turn.</action>
408
+ </check>
409
+
410
+ <output>Next: review the PR, merge it, then `/flow-sprint done {{story.id}}` (or re-invoke `/flow-story` for auto-close).</output>
411
+ <action>End turn.</action>
412
+ </check>
413
+
414
+ <!-- ────────────────────── AUTO-MERGE (--auto-merge only) ────────────────────── -->
415
+ <!-- v0.7 redesign (issue #13): replaced 30s×15min poll loop with a single
416
+ short-wait + end-turn-if-not-yet handoff. The previous loop consumed
417
+ ~30 `gh pr view` calls + main-context churn per story. Now: one sleep
418
+ (60–90s), two `gh pr view` calls, then either continue to merge-done
419
+ or end turn with a clear note that the next /flow-story invocation
420
+ will pick it up. -->
421
+ <check if="--auto-merge AND ({{pr_state}} == 'open' OR phase == 'awaiting-merge')">
422
+ <output>🚀 auto-merge → enabling GitHub auto-merge on PR {{pr_number}}…</output>
423
+
424
+ <action>Run `gh pr merge {{pr_number}} --auto --squash --delete-branch`. If it errors with "auto-merge is not allowed for this repository", surface that and HALT — the user needs to enable it in repo settings.</action>
425
+
426
+ <output>✓ Auto-merge queued.</output>
427
+
428
+ <action>**Single bounded wait** (90s — configurable via `config.pr.auto_merge_wait_seconds`). Then check once:
429
+ 1. `sleep 90`
430
+ 2. `gh pr view {{pr_number}} --json state,mergedAt,statusCheckRollup --jq .` → `{{pr_state_now}}`
431
+ 3. If `mergedAt` is non-null → set `{{phase}} = merge-done`, continue to merge-done handler. Do NOT end turn.
432
+ 4. If state is `CLOSED` without mergedAt → HALT with "PR closed without merge: {{reason}}".
433
+ 5. If `statusCheckRollup` shows any FAILURE → HALT with "CI failed: {{failing_check_name}}. Auto-merge cancelled. Inspect and re-run."
434
+ 6. Otherwise (still queued, CI still running) → emit the handoff output below and end turn.
435
+ </action>
436
+
437
+ <output>⏳ Auto-merge still pending after 90s wait.
438
+
439
+ PR: {{pr_url}}
440
+ State: {{pr_state_now.state}}
441
+ CI: {{summary of statusCheckRollup — pass/pending/fail counts}}
442
+ Strategy: queued for merge once required checks pass
443
+
444
+ **Not polling further** (issue #13 — polling 30 calls × 15 min eats context). The next time you run `/flow-story` (or `/flow-sprint done {{story.id}}`), Flow will re-check and close out if the PR has merged by then. You can also leave the terminal idle — `gh pr merge --auto` runs server-side on GitHub.
445
+ </output>
446
+ <action>End turn.</action>
447
+ </check>
448
+
449
+ <!-- ────────────────────── AWAITING MERGE (manual path) ────────────────────── -->
450
+ <check if="phase == 'awaiting-merge' AND NOT --auto-merge">
451
+ <output>⏳ awaiting-merge — PR {{pr_url}} ({{pr_state}}, reviewDecision: {{reviewDecision}})
452
+
453
+ Merge via GitHub UI or `gh pr merge {{pr_number}} --squash --delete-branch`. Then re-invoke `/flow-story` (auto-closes) or `/flow-sprint done {{story.id}}` directly.
454
+
455
+ Pass `--auto-merge` next time to skip this halt (requires CI + branch protection).
456
+ </output>
457
+ <action>End turn.</action>
458
+ </check>
459
+
460
+ <!-- ────────────────────── MERGE-DONE ────────────────────── -->
461
+ <check if="phase == 'merge-done'">
462
+ <output>✓ PR merged. Closing out via /flow-sprint done…</output>
463
+ <action>Invoke `flow-sprint` skill with arg `done {{story.id}}`. That handles branch cleanup, status flip to done, archive, issue close.</action>
464
+ <action>End turn.</action>
465
+ </check>
466
+
467
+ <!-- ────────────────────── RESUME BRANCH ────────────────────── -->
468
+ <check if="phase == 'resume-branch'">
469
+ <output>⚠ Story {{story.id}} is `doing` but you're on `main`. Expected branch: `flow/{{story.id}}-*`.
470
+
471
+ Options:
472
+ - `git checkout flow/{{story.id}}-...` if the branch still exists locally
473
+ - `/flow-sprint next` to recreate
474
+ </output>
475
+ <action>End turn.</action>
476
+ </check>
477
+
478
+ <!-- ────────────────────── UNKNOWN ────────────────────── -->
479
+ <check if="phase == 'unknown'">
480
+ <output>🚨 Drift detected. State is inconsistent:
481
+
482
+ Branch: {{branch}}
483
+ Commits: {{commits_ahead}}
484
+ Plan: {{has_plan_section}}
485
+ Reviewed: {{review_done}}
486
+ Verified: {{verify_passed}}
487
+ PR: {{pr_number}} ({{pr_state}})
488
+ Story: {{story.status}}
489
+
490
+ Inspect with: `/flow-sprint status` + `git status`. Re-invoke with `--advise-only` to see what each phase would do without acting.
491
+ </output>
492
+ <action>End turn.</action>
493
+ </check>
494
+
495
+ </step>
496
+
497
+ </workflow>
498
+
499
+ ---
500
+
501
+ ## Phase summary
502
+
503
+ | Phase | Trigger | Action in execute mode | Pauses on |
504
+ |---|---|---|---|
505
+ | auto-stub | sprint.yaml entry, no story file | scaffold 5–7 line stub from conventions | — |
506
+ | plan | no `## Plan` populated | invoke `plan` skill (CONFIRM gate) — or auto-write placeholder under `--auto`/`--skip-plan` | plan's CONFIRM (skipped under `--auto`) |
507
+ | implement | branch + plan present, no commits | invoke `prp-implement` | — |
508
+ | review | changes exist (committed or uncommitted), no Review Notes | spawn reviewer(s) parallel on `git diff`; auto-append clean findings | CRITICAL/HIGH (always); skipped under `--no-review` |
509
+ | verify | reviewed, no Verified marker | run verify adapter's cmd | non-zero exit (always); skipped under `--no-verify` / `--no-tests` |
510
+ | e2e | story tags trigger, e2e adapter active | run journey via adapter | journey failure (always); skipped under `--no-e2e` / `--no-tests` |
511
+ | commit-pr | reviewed AND verified AND e2e-ok | bundle uncommitted + staged + story markers into one commit, open PR | pre-commit Y/n (skipped under `--auto`) |
512
+ | auto-merge | `--auto-merge` flag set, PR open | `gh pr merge --auto --squash --delete-branch`, single 90s wait then handoff | CI failure (halts); PR closed without merge (halts); not-yet-merged (ends turn — next /flow-story tick checks again) |
513
+ | docs | mode ≥ standard, verified, no Docs marker | invoke `update-docs` (and `update-codemaps` in team) | — |
514
+ | commit-pr | verified, no PR | propose commit; ask Y/n; invoke `prp-commit` + `prp-pr` | user n (skipped if --auto) |
515
+ | awaiting-merge | PR open | print PR link + wait | always (need human merge) |
516
+ | merge-done | PR merged | invoke `flow-sprint done` | — |
517
+
518
+ **Hard halt boundaries** (never bypassed, even in `--auto`):
519
+ - CRITICAL or HIGH review findings
520
+ - Verify non-zero exit
521
+ - E2E journey failure
522
+ - PR open (awaits human merge — Flow does not auto-merge in v0)
523
+
524
+ **Bypassed under `--auto`:**
525
+ - Plan skill's CONFIRM gate (Flow writes a placeholder Plan instead of invoking `/plan`)
526
+ - Pre-commit Y/n confirmation
527
+
528
+ **Soft halt boundaries** (skipped in `--auto`):
529
+ - Pre-commit confirmation prompt
530
+
531
+ **Never silent:** every phase prints what it's doing (`📐 plan → ...`, `🛠 implement → ...`, etc.) so the user can see chaining happen and Ctrl-C if needed.
@@ -0,0 +1,55 @@
1
+ <!-- flow-managed:begin v{{flow_version}} — do not edit between markers; regenerated by /flow-init --update -->
2
+
3
+ ## Flow Workflow
4
+
5
+ This project uses [Flow](https://github.com/mhd-ghaith-abtah/flow) — token-light per-story workflow built on BMad + ECC.
6
+
7
+ **Mode:** `{{mode}}` · **Sprint:** `docs/flow/sprint.yaml` · **Stories:** `docs/flow/stories/`
8
+
9
+ ### Per-story loop
10
+
11
+ ```bash
12
+ /flow-sprint next # mark next backlog story doing, create branch
13
+ /flow-story # advance the active story through phases
14
+ /flow-sprint done <id> # close out after PR merge
15
+ ```
16
+
17
+ ### Add a new story
18
+
19
+ ```bash
20
+ /flow-sprint add "<title>" --epic E1 --tags <tag1,tag2> [--why "..."]
21
+ ```
22
+
23
+ ### Phases driven by /flow-story
24
+
25
+ | Phase | Triggers | Delegates to |
26
+ |---|---|---|
27
+ | plan | no `## Plan` section in story file | `/plan @<story>` |
28
+ | implement | branch checked out, no commits | `/prp-implement @<story>` |
29
+ | review | commits ahead, no Review Notes | `/code-review` (+ language reviewer) |
30
+ | verify | reviewed, no Verified marker | `{{verify_cmd}}` |
31
+ | e2e | story tags trigger + e2e adapter active | `{{e2e_adapter}}` |
32
+ | docs | mode ≥ standard | `/update-docs`, `/update-codemaps` |
33
+ | commit + PR| verified | `/prp-commit`, `/prp-pr` |
34
+ | merge-done | PR merged | `/flow-sprint done` |
35
+
36
+ ### Active adapters
37
+
38
+ - **Issue tracker:** `{{adapters.issue_tracker}}`
39
+ - **PR platform:** `{{adapters.pr}}`
40
+ - **E2E:** `{{adapters.e2e}}`
41
+ - **Verify:** `{{adapters.verify}}`
42
+
43
+ Swap any via: `flow adapter swap <category> <new-adapter>`
44
+
45
+ ### Reference docs (kept from prior planning)
46
+
47
+ {{#each reference_docs}}
48
+ - `{{this}}`
49
+ {{/each}}
50
+
51
+ ### House rules (preserved from project context)
52
+
53
+ <!-- Project-specific rules live BELOW this marker, outside the flow-managed block. -->
54
+
55
+ <!-- flow-managed:end -->
@@ -0,0 +1,34 @@
1
+ # `docs/flow/` — Flow workspace
2
+
3
+ Single source of truth for sprint state in this project.
4
+
5
+ ## Files
6
+
7
+ | Path | What |
8
+ |---|---|
9
+ | `sprint.yaml` | Story tracker — backlog, doing, review, done |
10
+ | `stories/` | One ~30-line markdown file per story |
11
+ | `archive/` | Completed stories (moved here on `done`) |
12
+ | `journeys/` | Reusable E2E journey specs |
13
+ | `retros/` | Per-epic retrospective documents |
14
+ | `artifacts/` | E2E screenshots, traces, logs |
15
+ | `deferred.md` | "Real but not for this story" findings — flat backlog |
16
+
17
+ ## Don't edit by hand (usually)
18
+
19
+ - `sprint.yaml` — edit via `/flow-sprint` subcommands
20
+ - `stories/*.md` — write the body by hand, let Flow append status/audit blocks
21
+
22
+ ## Hand-editing is fine for
23
+
24
+ - Story Why, Acceptance, Files, Notes
25
+ - Retros (Flow drafts; you refine)
26
+ - `deferred.md` — append rows, mark resolved with `[x]`
27
+
28
+ ## Generated by
29
+
30
+ `/flow-init` (Flow v{{flow_version}}, {{date}})
31
+
32
+ Slash commands inside Claude Code: `/flow-init`, `/flow-sprint <subcmd>`, `/flow-story`.
33
+ Terminal: `flow help`, `flow plan`, `flow status`, `flow doctor`.
34
+ See [the Flow README](https://github.com/mhd-ghaith-abtah/flow) for the full reference.