# Submit Sign-off - Record Your Approval Decision
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
Call: mcp__github__get_me()
current_user = response.login
HALT
Which document are you signing off on? Enter key:
document_key = response
if (document_type === 'prd') {
doc_path = `${docs_dir}/prd/${document_key}.md`
doc_label = `prd:${document_key}`
review_label = 'type:prd-review'
} else {
doc_path = `${docs_dir}/epics/epic-${document_key}.md`
doc_label = `epic:${document_key}`
review_label = 'type:epic-review'
}
Call: mcp__github__search_issues({
query: "repo:{{github_owner}}/{{github_repo}} label:{{review_label}} label:{{doc_label}} label:review-status:signoff is:open"
})
HALT
review_issue = response.items[0]
Read doc_path
HALT
doc_content = file_content
title = extract_title(doc_content)
version = extract_version(doc_content)
// Check if user already signed off
signoff_label_prefix = `signoff-${current_user}-`
existing_signoff = review_issue.labels.some(l =>
l.name.startsWith(signoff_label_prefix)
)
Choice:
HALT
View document? (y/n):
Decision (1-3):
decision_map = {
'1': { key: 'approved', emoji: '✅', text: 'Approved' },
'2': { key: 'approved_with_note', emoji: '✅📝', text: 'Approved with Note' },
'3': { key: 'blocked', emoji: '🚫', text: 'Blocked' }
}
decision = decision_map[response] || decision_map['1']
Enter your note (this will be visible to all stakeholders):
note = response
Enter your blocking reason:
note = response
Create feedback issue? (y/n):
create_feedback_issue = true
note = null
// Build sign-off comment
signoff_comment = `### ${decision.emoji} Sign-off from @${current_user}
**Decision:** ${decision.text}
**Date:** ${new Date().toISOString().split('T')[0]}`
if (note) {
signoff_comment += `
**Note:**
${note}`
}
Call: mcp__github__add_issue_comment({
owner: "{{github_owner}}",
repo: "{{github_repo}}",
issue_number: review_issue.number,
body: signoff_comment
})
// Get current labels
current_labels = review_issue.labels.map(l => l.name)
// Remove any existing signoff label for this user
new_labels = current_labels.filter(l =>
!l.startsWith(`signoff-${current_user}-`)
)
// Add new signoff label
decision_label = decision.key.replace(/_/g, '-')
new_labels.push(`signoff-${current_user}-${decision_label}`)
Call: mcp__github__issue_write({
method: 'update',
owner: "{{github_owner}}",
repo: "{{github_repo}}",
issue_number: review_issue.number,
labels: new_labels
})
feedback_body = `# 🚫 Blocking Concern
**Document:** \`${doc_label}\`
**Review:** #${review_issue.number}
**Type:** Blocking concern requiring resolution
---
## Concern
${note}
---
_Submitted by @${current_user} as part of sign-off for v${version}_`
Call: mcp__github__issue_write({
method: 'create',
owner: "{{github_owner}}",
repo: "{{github_repo}}",
title: "🚫 Blocking: {{title}}",
body: feedback_body,
labels: ['type:{{document_type}}-feedback', doc_label, 'feedback-type:concern', 'priority:high', 'feedback-status:new', `linked-review:${review_issue.number}`]
})
// Refresh issue to get updated labels
Call: mcp__github__issue_read({
method: 'get',
owner: github_owner,
repo: github_repo,
issue_number: review_issue.number
})
// Count sign-offs
labels = response.labels.map(l => l.name)
approved_count = labels.filter(l =>
l.includes('-approved') || l.includes('-approved-with-note')
).length
blocked_count = labels.filter(l => l.includes('-blocked')).length
// Get stakeholder count from document
stakeholder_count = extract_stakeholders(doc_content).length
Mark document as approved? (y/n):
// Update document status
updated_content = doc_content.replace(/\*\*Status:\*\* .+/, '**Status:** Approved')
Write updated_content to doc_path
// Update review issue
final_labels = labels
.filter(l => !l.startsWith('review-status:'))
.concat(['review-status:approved'])
Call: mcp__github__issue_write({
method: 'update',
owner: "{{github_owner}}",
repo: "{{github_repo}}",
issue_number: review_issue.number,
labels: final_labels,
state: 'closed',
state_reason: 'completed'
})
Call: mcp__github__add_issue_comment({
owner: "{{github_owner}}",
repo: "{{github_repo}}",
issue_number: review_issue.number,
body: `## ✅ DOCUMENT APPROVED
All stakeholders have signed off. This document is now approved and ready for implementation.
**Final Version:** v${version}
**Approved:** ${new Date().toISOString().split('T')[0]}
**Approvals:** ${approved_count} / ${stakeholder_count}`
})
## Helper Functions
```javascript
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_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('@', ''));
}
```
## Natural Language Triggers
This workflow responds to:
- "Sign off on [document]"
- "Submit my sign-off"
- "Approve the PRD"
- "I approve [document]"
- Menu trigger: `SO`