Skip to content

Commit 21a86e0

Browse files
authored
Merge pull request #40 from snipcodeit/issue/36-feat-comment-aware-pipeline
feat: comment-aware pipeline for MGW
2 parents b9bd213 + 680a964 commit 21a86e0

12 files changed

Lines changed: 863 additions & 9 deletions

File tree

.claude/commands/mgw/issue.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,27 @@ gh issue view $ISSUE_NUMBER --json number,title,body,labels,assignees,state,comm
6666
If issue not found → error: "Issue #$ISSUE_NUMBER not found in this repo."
6767

6868
Store as $ISSUE_DATA.
69+
70+
**Capture comment tracking snapshot:**
71+
72+
Record the current comment count and timestamp of the most recent comment. These
73+
are stored in the triage state and used by run.md's pre-flight comment check to
74+
detect new comments posted between triage and execution.
75+
76+
```bash
77+
COMMENT_COUNT=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('comments', [])))")
78+
LAST_COMMENT_AT=$(echo "$ISSUE_DATA" | python3 -c "
79+
import json,sys
80+
d = json.load(sys.stdin)
81+
comments = d.get('comments', [])
82+
if comments:
83+
print(comments[-1].get('createdAt', ''))
84+
else:
85+
print('')
86+
" 2>/dev/null || echo "")
87+
```
88+
89+
Store $COMMENT_COUNT and $LAST_COMMENT_AT for use in the write_state step.
6990
</step>
7091

7192
<step name="assign_self">
@@ -223,6 +244,8 @@ Write to `.mgw/active/${ISSUE_NUMBER}-${slug}.json` using the schema from state.
223244
Populate:
224245
- issue: from $ISSUE_DATA
225246
- triage: from analysis report
247+
- triage.last_comment_count: from $COMMENT_COUNT (captured in fetch_issue step)
248+
- triage.last_comment_at: from $LAST_COMMENT_AT (captured in fetch_issue step, null if no comments)
226249
- gsd_route: confirmed or overridden route
227250
- pipeline_stage: "triaged"
228251
- All other fields: defaults (empty arrays, null)
@@ -257,11 +280,12 @@ Consider closing or commenting on the issue with your reasoning.
257280

258281
<success_criteria>
259282
- [ ] Issue fetched from GitHub via gh CLI
283+
- [ ] Comment tracking snapshot captured (count + last timestamp)
260284
- [ ] Self-assigned if not already
261285
- [ ] Analysis agent spawned and returned structured report
262286
- [ ] Scope, validity, security, conflicts all assessed
263287
- [ ] GSD route recommended with reasoning
264288
- [ ] User confirms, overrides, or rejects
265-
- [ ] State file written to .mgw/active/ (if accepted)
289+
- [ ] State file written to .mgw/active/ (if accepted) with comment tracking fields
266290
- [ ] Next steps offered
267291
</success_criteria>

.claude/commands/mgw/review.md

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
---
2+
name: mgw:review
3+
description: Review and classify new comments on a GitHub issue since last triage
4+
argument-hint: "<issue-number>"
5+
allowed-tools:
6+
- Bash
7+
- Read
8+
- Write
9+
- Edit
10+
- Task
11+
- AskUserQuestion
12+
---
13+
14+
<objective>
15+
Standalone comment review for a triaged issue. Fetches new comments posted since
16+
triage, classifies them (material/informational/blocking), and updates the state
17+
file accordingly.
18+
19+
Use this when you want to check for stakeholder feedback before running the pipeline,
20+
or to review comments on a blocked issue before unblocking it.
21+
</objective>
22+
23+
<execution_context>
24+
@~/.claude/commands/mgw/workflows/state.md
25+
@~/.claude/commands/mgw/workflows/github.md
26+
@~/.claude/commands/mgw/workflows/gsd.md
27+
@~/.claude/commands/mgw/workflows/validation.md
28+
</execution_context>
29+
30+
<context>
31+
Issue number: $ARGUMENTS
32+
33+
State: .mgw/active/ (must exist — issue must be triaged first)
34+
</context>
35+
36+
<process>
37+
38+
<step name="validate_and_load">
39+
**Validate input and load state:**
40+
41+
```bash
42+
REPO_ROOT=$(git rev-parse --show-toplevel)
43+
```
44+
45+
Parse $ARGUMENTS for issue number. If missing:
46+
```
47+
AskUserQuestion(
48+
header: "Issue Number Required",
49+
question: "Which issue number do you want to review comments for?",
50+
followUp: "Enter the GitHub issue number (e.g., 42)"
51+
)
52+
```
53+
54+
Load state file: `${REPO_ROOT}/.mgw/active/${ISSUE_NUMBER}-*.json`
55+
56+
If no state file exists:
57+
```
58+
Issue #${ISSUE_NUMBER} hasn't been triaged yet.
59+
Run /mgw:issue ${ISSUE_NUMBER} first, then review comments.
60+
```
61+
</step>
62+
63+
<step name="fetch_comments">
64+
**Fetch current comment state from GitHub:**
65+
66+
```bash
67+
CURRENT_COMMENTS=$(gh issue view $ISSUE_NUMBER --json comments --jq '.comments | length' 2>/dev/null || echo "0")
68+
STORED_COMMENTS="${triage.last_comment_count}"
69+
70+
if [ -z "$STORED_COMMENTS" ] || [ "$STORED_COMMENTS" = "null" ]; then
71+
STORED_COMMENTS=0
72+
fi
73+
74+
NEW_COUNT=$(($CURRENT_COMMENTS - $STORED_COMMENTS))
75+
```
76+
77+
If no new comments (`NEW_COUNT <= 0`):
78+
```
79+
No new comments on #${ISSUE_NUMBER} since triage (${STORED_COMMENTS} comments at triage, ${CURRENT_COMMENTS} now).
80+
```
81+
Stop.
82+
83+
If new comments exist, fetch them:
84+
```bash
85+
NEW_COMMENTS=$(gh issue view $ISSUE_NUMBER --json comments \
86+
--jq "[.comments[-${NEW_COUNT}:]] | .[] | {author: .author.login, body: .body, createdAt: .createdAt}" 2>/dev/null)
87+
```
88+
</step>
89+
90+
<step name="classify_comments">
91+
**Spawn classification agent:**
92+
93+
```
94+
Task(
95+
prompt="
96+
<files_to_read>
97+
- ./CLAUDE.md (Project instructions — if exists, follow all guidelines)
98+
</files_to_read>
99+
100+
Classify new comments on GitHub issue #${ISSUE_NUMBER}.
101+
102+
<issue_context>
103+
Title: ${issue_title}
104+
Current pipeline stage: ${pipeline_stage}
105+
GSD Route: ${gsd_route}
106+
Triage scope: ${triage.scope}
107+
</issue_context>
108+
109+
<new_comments>
110+
${NEW_COMMENTS}
111+
</new_comments>
112+
113+
<classification_rules>
114+
Classify each comment (and the overall batch) into ONE of:
115+
116+
- **material** — Comment changes scope, requirements, acceptance criteria, or design.
117+
Examples: 'Actually we also need to handle X', 'Changed the requirement to Y',
118+
'Don't forget about edge case Z'.
119+
120+
- **informational** — Status update, acknowledgment, question that doesn't change scope, +1.
121+
Examples: 'Looks good', 'Thanks for picking this up', 'What's the ETA?', '+1'.
122+
123+
- **blocking** — Explicit instruction to stop or wait. Must contain clear hold language.
124+
Examples: 'Don't work on this yet', 'Hold off', 'Blocked by external dependency',
125+
'Wait for design review'.
126+
127+
If ANY comment in the batch is blocking, overall classification is blocking.
128+
If ANY comment is material (and none blocking), overall classification is material.
129+
Otherwise, informational.
130+
</classification_rules>
131+
132+
<output_format>
133+
Return ONLY valid JSON:
134+
{
135+
\"classification\": \"material|informational|blocking\",
136+
\"reasoning\": \"Brief explanation of why this classification was chosen\",
137+
\"per_comment\": [
138+
{
139+
\"author\": \"username\",
140+
\"snippet\": \"first 100 chars of comment\",
141+
\"classification\": \"material|informational|blocking\"
142+
}
143+
],
144+
\"new_requirements\": [\"list of new requirements if material, empty array otherwise\"],
145+
\"blocking_reason\": \"reason if blocking, empty string otherwise\"
146+
}
147+
</output_format>
148+
",
149+
subagent_type="general-purpose",
150+
description="Classify comments on #${ISSUE_NUMBER}"
151+
)
152+
```
153+
</step>
154+
155+
<step name="present_and_act">
156+
**Present classification and offer actions:**
157+
158+
Display the classification result:
159+
160+
```
161+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
162+
MGW ► COMMENT REVIEW — #${ISSUE_NUMBER}
163+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
164+
165+
New comments: ${NEW_COUNT} since triage
166+
Classification: ${classification}
167+
Reasoning: ${reasoning}
168+
169+
${per_comment_table}
170+
171+
${if material: 'New requirements detected:\n' + new_requirements}
172+
${if blocking: 'Blocking reason: ' + blocking_reason}
173+
```
174+
175+
Offer actions based on classification:
176+
177+
**If informational:**
178+
```
179+
AskUserQuestion(
180+
header: "Informational Comments",
181+
question: "Mark comments as reviewed and update state?",
182+
options: [
183+
{ label: "Yes", description: "Update last_comment_count, continue" },
184+
{ label: "No", description: "Keep current state, don't update count" }
185+
]
186+
)
187+
```
188+
If yes: update `triage.last_comment_count` to $CURRENT_COMMENTS in state file.
189+
190+
**If material:**
191+
```
192+
AskUserQuestion(
193+
header: "Material Comments Detected",
194+
question: "How should MGW handle the scope change?",
195+
options: [
196+
{ label: "Acknowledge and continue", description: "Update state with new requirements, keep current route" },
197+
{ label: "Re-triage", description: "Run /mgw:issue to re-analyze with new context" },
198+
{ label: "Ignore", description: "Don't update state" }
199+
]
200+
)
201+
```
202+
If acknowledge: update `triage.last_comment_count` and store new_requirements in state.
203+
If re-triage: suggest running `/mgw:issue ${ISSUE_NUMBER}` to re-triage.
204+
205+
**If blocking:**
206+
```
207+
AskUserQuestion(
208+
header: "Blocking Comment Detected",
209+
question: "Block the pipeline for this issue?",
210+
options: [
211+
{ label: "Block", description: "Set pipeline_stage to 'blocked'" },
212+
{ label: "Override", description: "Ignore blocker, keep current stage" },
213+
{ label: "Review", description: "I'll review the comments manually" }
214+
]
215+
)
216+
```
217+
If block: update `pipeline_stage = "blocked"` and `triage.last_comment_count` in state.
218+
If override: update `triage.last_comment_count` only, keep pipeline_stage.
219+
</step>
220+
221+
</process>
222+
223+
<success_criteria>
224+
- [ ] Issue state loaded from .mgw/active/
225+
- [ ] Current comment count fetched from GitHub
226+
- [ ] New comments identified (delta from stored count)
227+
- [ ] Classification agent spawned and returned structured result
228+
- [ ] Classification presented to user with per-comment breakdown
229+
- [ ] User chose action (acknowledge/re-triage/block/ignore)
230+
- [ ] State file updated according to user choice
231+
</success_criteria>

.claude/commands/mgw/run.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ If no state file exists → issue not triaged yet. Run triage inline:
7676
If state file exists → load it. Check pipeline_stage:
7777
- "triaged" → proceed to GSD execution
7878
- "planning" / "executing" → resume from where we left off
79+
- "blocked" → "Pipeline for #${ISSUE_NUMBER} is blocked by a stakeholder comment. Review the issue comments, resolve the blocker, then re-run."
7980
- "pr-created" / "done" → "Pipeline already completed for #${ISSUE_NUMBER}. Run /mgw:sync to reconcile."
8081
</step>
8182

@@ -124,6 +125,99 @@ Add cross-ref (at `${REPO_ROOT}/.mgw/cross-refs.json`): issue → branch.
124125
(`.mgw/` is gitignored — it only exists in the main repo, not the worktree)
125126
</step>
126127

128+
<step name="preflight_comment_check">
129+
**Pre-flight comment check — detect new comments since triage:**
130+
131+
Before GSD execution begins, check if new comments have been posted on the issue
132+
since triage. This prevents executing against a stale plan when stakeholders have
133+
posted material changes, blockers, or scope updates.
134+
135+
```bash
136+
# Fetch current comment count from GitHub
137+
CURRENT_COMMENTS=$(gh issue view $ISSUE_NUMBER --json comments --jq '.comments | length' 2>/dev/null || echo "0")
138+
STORED_COMMENTS="${triage.last_comment_count}" # From state file
139+
140+
# If stored count is missing (pre-comment-tracking state), skip check
141+
if [ -z "$STORED_COMMENTS" ] || [ "$STORED_COMMENTS" = "null" ] || [ "$STORED_COMMENTS" = "0" ]; then
142+
STORED_COMMENTS=0
143+
fi
144+
```
145+
146+
If new comments detected (`CURRENT_COMMENTS > STORED_COMMENTS`):
147+
148+
1. **Fetch new comment bodies:**
149+
```bash
150+
NEW_COUNT=$(($CURRENT_COMMENTS - $STORED_COMMENTS))
151+
NEW_COMMENTS=$(gh issue view $ISSUE_NUMBER --json comments \
152+
--jq "[.comments[-${NEW_COUNT}:]] | .[] | {author: .author.login, body: .body, createdAt: .createdAt}" 2>/dev/null)
153+
```
154+
155+
2. **Spawn classification agent:**
156+
```
157+
Task(
158+
prompt="
159+
<files_to_read>
160+
- ./CLAUDE.md (Project instructions — if exists, follow all guidelines)
161+
</files_to_read>
162+
163+
Classify new comments on GitHub issue #${ISSUE_NUMBER}.
164+
165+
<issue_context>
166+
Title: ${issue_title}
167+
Current pipeline stage: ${pipeline_stage}
168+
GSD Route: ${gsd_route}
169+
Triage scope: ${triage.scope}
170+
</issue_context>
171+
172+
<new_comments>
173+
${NEW_COMMENTS}
174+
</new_comments>
175+
176+
<classification_rules>
177+
Classify each comment (and the overall batch) into ONE of:
178+
179+
- **material** — Comment changes scope, requirements, acceptance criteria, or design.
180+
Examples: 'Actually we also need to handle X', 'Changed the requirement to Y',
181+
'Don't forget about edge case Z'.
182+
183+
- **informational** — Status update, acknowledgment, question that doesn't change scope, +1.
184+
Examples: 'Looks good', 'Thanks for picking this up', 'What's the ETA?', '+1'.
185+
186+
- **blocking** — Explicit instruction to stop or wait. Must contain clear hold language.
187+
Examples: 'Don't work on this yet', 'Hold off', 'Blocked by external dependency',
188+
'Wait for design review'.
189+
190+
If ANY comment in the batch is blocking, overall classification is blocking.
191+
If ANY comment is material (and none blocking), overall classification is material.
192+
Otherwise, informational.
193+
</classification_rules>
194+
195+
<output_format>
196+
Return ONLY valid JSON:
197+
{
198+
\"classification\": \"material|informational|blocking\",
199+
\"reasoning\": \"Brief explanation of why this classification was chosen\",
200+
\"new_requirements\": [\"list of new requirements if material, empty array otherwise\"],
201+
\"blocking_reason\": \"reason if blocking, empty string otherwise\"
202+
}
203+
</output_format>
204+
",
205+
subagent_type="general-purpose",
206+
description="Classify comments on #${ISSUE_NUMBER}"
207+
)
208+
```
209+
210+
3. **React based on classification:**
211+
212+
| Classification | Action |
213+
|---------------|--------|
214+
| **informational** | Log: "MGW: ${NEW_COUNT} new comment(s) reviewed — informational, continuing." Update `triage.last_comment_count` in state file. Continue pipeline. |
215+
| **material** | Log: "MGW: Material comment(s) detected — scope may have changed." Update state: add new_requirements to triage context. Update `triage.last_comment_count`. Re-read issue body for updated requirements. Continue with enriched context (pass new_requirements to planner). |
216+
| **blocking** | Log: "MGW: Blocking comment detected — pipeline paused." Update state: `pipeline_stage = "blocked"`. Post comment on issue: `> **MGW** . \`pipeline-blocked\` . Blocked by stakeholder comment. Reason: ${blocking_reason}`. Stop pipeline execution. |
217+
218+
If no new comments detected, continue normally.
219+
</step>
220+
127221
<step name="post_triage_update">
128222
**Post structured triage comment on issue:**
129223

@@ -753,6 +847,7 @@ Next:
753847
<success_criteria>
754848
- [ ] Issue number validated and state loaded (or triage run first)
755849
- [ ] Isolated worktree created (.worktrees/ gitignored)
850+
- [ ] Pre-flight comment check performed (new comments classified before execution)
756851
- [ ] Structured triage comment posted on issue (scope, route, files, security)
757852
- [ ] GSD pipeline executed in worktree (quick or milestone route)
758853
- [ ] Execution-complete comment posted on issue (commits, changes, test status)

0 commit comments

Comments
 (0)