12 KiB
Open Epic Feedback - Collect Stakeholder Input on Story Breakdown
The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml You MUST have already loaded and processed: {installed_path}/workflow.yaml
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 💬 OPEN EPIC FEEDBACK ROUND ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Call: mcp__github__get_me() current_user = response.login
❌ GitHub MCP not accessible HALT Call: mcp__github__search_issues({ query: "repo:{{github_owner}}/{{github_repo}} label:type:epic-review label:review-status:draft is:open" }) <check if="response.items.length == 0">
<output>
❌ No draft epics found.
Create an epic first with: "Create epic from PRD" HALT
<action>
draft_epics = response.items.map(issue => {
const labels = issue.labels.map(l => l.name)
return {
key: labels.find(l => l.startsWith('epic:'))?.replace('epic:', ''),
title: issue.title.replace(/^Epic Review:\s*/, ''),
source_prd: labels.find(l => l.startsWith('source-prd:'))?.replace('source-prd:', ''),
issue_number: issue.number
}
}).filter(e => e.key)
</action>
<output>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 📦 DRAFT EPICS AVAILABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{{#each draft_epics}} [{{@index + 1}}] epic:{{key}} - {{title}} Source: prd:{{source_prd}} | Issue: #{{issue_number}} {{/each}}
</output>
</substep>
<ask>Select epic (1-{{draft_epics.length}}):</ask>
<action>epic_key = draft_epics[parseInt(response) - 1].key</action>
<action>review_issue_number = draft_epics[parseInt(response) - 1].issue_number</action>
📦 Selected: epic:{{epic_key}}
epic_path = `${docs_dir}/epics/epic-${epic_key}.md`
Read epic_path
❌ Epic document not found: {{epic_path}}
HALT
epic_content = file_content title = extract_title(epic_content) version = extract_version(epic_content) stakeholders = extract_stakeholders(epic_content) source_prd = extract_source_prd(epic_content) stories = extract_epic_stories(epic_content)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 📦 EPIC SUMMARY ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Title: {{title}} Version: v{{version}} Source PRD: prd:{{source_prd}} Stories: {{stories.length}} implementation stories Stakeholders: {{stakeholders.length}}
Call: mcp__github__search_issues({ query: "repo:{{github_owner}}/{{github_repo}} label:type:epic-review label:epic:{{epic_key}} is:open" })<check if="response.items.length == 0">
<output>❌ No review issue found for epic:{{epic_key}}</output>
<action>HALT</action>
</check>
<action>review_issue = response.items[0]</action>
<action>review_issue_number = review_issue.number</action>
Call: mcp__github__issue_read({
method: 'get',
owner: github_owner,
repo: github_repo,
issue_number: review_issue_number
})
review_issue = response
📋 Review Issue: #{{review_issue_number}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚙️ FEEDBACK ROUND CONFIGURATION
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Epic feedback focuses on:
-
Scope: Is the epic size right? Should it be split/merged?
-
Story Breakdown: Are stories well-defined and independent?
-
Dependencies: Are technical dependencies captured?
-
Priority: Is the story order correct?
-
Technical Risk: Are there architecture concerns?
Days until feedback deadline (default: {{feedback_days}}): feedback_days = parseInt(response) || feedback_days deadline = new Date() deadline.setDate(deadline.getDate() + feedback_days) deadline_str = deadline.toISOString().split('T')[0]
Deadline: {{deadline_str}} ({{feedback_days}} days from now)
Stakeholders to notify: {{#each stakeholders}} • @{{this}} {{/each}}
Add additional stakeholders? (comma-separated or 'none'): if (response.toLowerCase() !== 'none' && response.trim()) { additional = response.split(',').map(s => s.trim().replace('@', '')) stakeholders = [...new Set([...stakeholders, ...additional])] }
updated_content = epic_content .replace(/\*\*Status:\*\* .+/, '**Status:** Feedback') .replace(/\| Feedback Deadline \| .+ \|/, `| Feedback Deadline | ${deadline_str} |`)Write updated_content to epic_path
✅ Epic status updated to 'Feedback' // Get current labels current_labels = review_issue.labels.map(l => l.name)// Update status label
new_labels = current_labels
.filter(l => !l.startsWith('review-status:'))
.concat(['review-status:open'])
Call: mcp__github__issue_write({ method: 'update', owner: "{{github_owner}}", repo: "{{github_repo}}", issue_number: review_issue_number, labels: new_labels, assignees: stakeholders })
✅ Review issue updated with stakeholders feedback_comment = `## 💬 Feedback Round Open${stakeholders.map(s => '@' + s).join(' ')}
Epic: epic:${epic_key} Version: v${version} Deadline: ${deadline_str} Source PRD: prd:${source_prd}
📦 Story Breakdown
{stories.map((s, i) => `{i + 1}. **{s.title}** ({s.complexity || 'TBD'})\n ${s.description || ''}`).join('\n\n')}
Feedback Types for Epics
Please provide feedback on:
- 🔍 Scope: Is this epic the right size? Should it be split or merged with another?
- 📝 Story Breakdown: Are stories well-defined, independent, and testable?
- 🔗 Dependencies: Are technical dependencies correctly identified?
- ⚡ Priority: Is the story order optimal for delivery?
- ⚠️ Technical Risk: Are there architectural or technical concerns?
- ➕ Missing Stories: Should additional stories be added?
How to Submit Feedback
Reply with structured feedback or use the feedback workflow:
``` /feedback epic:${epic_key} Section: [Story Breakdown / Dependencies / Technical Risk / etc.] Type: [scope / dependency / priority / technical_risk / story_split / missing_story] Feedback: [Your detailed feedback] ```
Or simply comment with your thoughts.
Feedback requested by @{current_user} on {new Date().toISOString().split('T')[0]}
Please respond by ${deadline_str}`
Call: mcp__github__add_issue_comment({ owner: "{{github_owner}}", repo: "{{github_repo}}", issue_number: review_issue_number, body: feedback_comment })
✅ Feedback request posted ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ EPIC FEEDBACK ROUND OPENED ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Epic: epic:{{epic_key}} Title: {{title}} Review Issue: #{{review_issue_number}} Deadline: {{deadline_str}} Stakeholders: {{stakeholders.length}} notified
All stakeholders have been @mentioned and will receive GitHub notifications.
Monitor progress with:
- "View feedback for epic:{{epic_key}}"
- "Epic Dashboard" or [ED]
After collecting feedback:
- "Synthesize feedback for epic:{{epic_key}}"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Helper Functions
function extract_title(content) {
const match = content.match(/^#\s+(PRD|Epic):\s*(.+)$/m);
return match ? match[2].trim() : 'Untitled';
}
function extract_version(content) {
const match = content.match(/\*\*Version:\*\*\s*(\d+)/);
return match ? match[1] : '1';
}
function extract_source_prd(content) {
const match = content.match(/\*\*Source PRD:\*\*\s*`?prd:([^`\s]+)`?/);
return match ? match[1] : null;
}
function extract_stakeholders(content) {
const field = content.match(/\|\s*Stakeholders\s*\|\s*(.+?)\s*\|/);
if (!field) return [];
return field[1]
.split(/[,\s]+/)
.filter(s => s.startsWith('@'))
.map(s => s.replace('@', ''));
}
function extract_epic_stories(content) {
const stories = [];
// Match story sections in various formats
const storyRegex = /###\s+Story\s+\d+:\s*(.+)\n+([\s\S]*?)(?=###|---|\n##|$)/gi;
let match;
while ((match = storyRegex.exec(content)) !== null) {
const title = match[1].trim();
const body = match[2];
// Extract complexity
const complexityMatch = body.match(/\*\*Estimated Complexity:\*\*\s*(\w+)/i);
// Extract description
const descMatch = body.match(/\*\*Description:\*\*\s*(.+)/);
stories.push({
title: title,
description: descMatch ? descMatch[1].trim() : '',
complexity: complexityMatch ? complexityMatch[1] : null
});
}
return stories;
}
Epic-Specific Feedback Types
feedback_types:
scope_concern:
label: "Scope"
description: "Epic is too large/small, should be split/merged"
emoji: "🔍"
story_split:
label: "Story Breakdown"
description: "Story needs to be split, combined, or redefined"
emoji: "📝"
dependency:
label: "Dependency"
description: "Missing or incorrect dependency identification"
emoji: "🔗"
priority_question:
label: "Priority"
description: "Story order or priority should change"
emoji: "⚡"
technical_risk:
label: "Technical Risk"
description: "Architecture or technical feasibility concern"
emoji: "⚠️"
missing_story:
label: "Missing Story"
description: "An additional story should be added"
emoji: "➕"
Natural Language Triggers
This workflow responds to:
- "Open feedback for epic"
- "Start epic feedback round"
- "Get feedback on epic"
- Menu trigger:
OE