@snipcodeit/mgw 0.1.2 → 0.1.3
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/commands/issue.md +9 -10
- package/commands/milestone.md +49 -7
- package/commands/run.md +34 -9
- package/commands/sync.md +77 -0
- package/commands/workflows/github.md +19 -4
- package/dist/bin/mgw.cjs +1 -1
- package/package.json +1 -1
package/commands/issue.md
CHANGED
|
@@ -287,16 +287,15 @@ MISSING_FIELDS_LIST = gate_result.missing_fields formatted as "- ${field}" list
|
|
|
287
287
|
```
|
|
288
288
|
|
|
289
289
|
Use the "Gate Blocked Comment" template from @~/.claude/commands/mgw/workflows/github.md.
|
|
290
|
-
Post comment and apply label:
|
|
290
|
+
Post comment and apply label using the highest-severity blocker (security > detail > validity):
|
|
291
291
|
```bash
|
|
292
|
-
#
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
# For security failures:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
#
|
|
299
|
-
# If multiple blockers, use the highest-severity label (security > detail > validity)
|
|
292
|
+
# For validity or detail failures:
|
|
293
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "mgw:needs-info"
|
|
294
|
+
|
|
295
|
+
# For security failures (highest severity — takes precedence over needs-info):
|
|
296
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "mgw:needs-security-review"
|
|
297
|
+
|
|
298
|
+
# If multiple blockers, apply security label if security gate failed; otherwise needs-info.
|
|
300
299
|
```
|
|
301
300
|
|
|
302
301
|
**If gates passed (gate_result.status == "passed"):**
|
|
@@ -316,7 +315,7 @@ ROUTE_REASONING = triage reasoning
|
|
|
316
315
|
|
|
317
316
|
Post comment and apply label:
|
|
318
317
|
```bash
|
|
319
|
-
|
|
318
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "mgw:triaged"
|
|
320
319
|
```
|
|
321
320
|
</step>
|
|
322
321
|
|
package/commands/milestone.md
CHANGED
|
@@ -417,6 +417,7 @@ FAILED_ISSUES=()
|
|
|
417
417
|
FAILED_ISSUES_WITH_CLASS=() # Entries: "issue_number:failure_class" for results display
|
|
418
418
|
BLOCKED_ISSUES=()
|
|
419
419
|
SKIPPED_ISSUES=()
|
|
420
|
+
LABEL_DRIFT_ISSUES=() # Issues where label reconciliation detected drift
|
|
420
421
|
ISSUES_RUN=0
|
|
421
422
|
```
|
|
422
423
|
|
|
@@ -562,6 +563,43 @@ COMMENTEOF
|
|
|
562
563
|
description="Run pipeline for #${ISSUE_NUMBER}"
|
|
563
564
|
)
|
|
564
565
|
|
|
566
|
+
# ── POST-WORK: Post-subagent label verification ──
|
|
567
|
+
# Read the pipeline_stage from the issue's active state file after Task() returns
|
|
568
|
+
ISSUE_STAGE=$(node -e "
|
|
569
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
570
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
571
|
+
console.log((state && state.pipeline_stage) ? state.pipeline_stage : 'unknown');
|
|
572
|
+
" 2>/dev/null || echo "unknown")
|
|
573
|
+
|
|
574
|
+
# Determine the expected MGW label for this pipeline stage
|
|
575
|
+
EXPECTED_LABEL=$(python3 -c "
|
|
576
|
+
stage_to_label = {
|
|
577
|
+
'done': '',
|
|
578
|
+
'pr-created': '',
|
|
579
|
+
'verifying': 'mgw:in-progress',
|
|
580
|
+
'executing': 'mgw:in-progress',
|
|
581
|
+
'planning': 'mgw:in-progress',
|
|
582
|
+
'blocked': 'mgw:blocked',
|
|
583
|
+
'failed': '',
|
|
584
|
+
}
|
|
585
|
+
print(stage_to_label.get('${ISSUE_STAGE}', ''))
|
|
586
|
+
")
|
|
587
|
+
|
|
588
|
+
# Compare expected label against live GitHub labels
|
|
589
|
+
LIVE_LABELS=$(gh issue view ${ISSUE_NUMBER} --json labels --jq '[.labels[].name] | join(",")' 2>/dev/null || echo "")
|
|
590
|
+
|
|
591
|
+
if [ -n "$EXPECTED_LABEL" ] && ! echo "$LIVE_LABELS" | grep -q "$EXPECTED_LABEL"; then
|
|
592
|
+
echo "MGW WARNING: label drift on #${ISSUE_NUMBER} — expected $EXPECTED_LABEL, live: $LIVE_LABELS" >&2
|
|
593
|
+
LABEL_DRIFT="drift"
|
|
594
|
+
else
|
|
595
|
+
LABEL_DRIFT="ok"
|
|
596
|
+
fi
|
|
597
|
+
|
|
598
|
+
# Track drifted issues for milestone summary
|
|
599
|
+
if [ "$LABEL_DRIFT" = "drift" ]; then
|
|
600
|
+
LABEL_DRIFT_ISSUES+=("$ISSUE_NUMBER")
|
|
601
|
+
fi
|
|
602
|
+
|
|
565
603
|
# ── POST-WORK: Detect result and post completion comment ──
|
|
566
604
|
# Check if PR was created by looking for state file or PR
|
|
567
605
|
PR_NUMBER=$(gh pr list --head "issue/${ISSUE_NUMBER}-*" --json number -q '.[0].number' 2>/dev/null || echo "")
|
|
@@ -712,18 +750,19 @@ Every comment posted during milestone orchestration includes:
|
|
|
712
750
|
<details>
|
|
713
751
|
<summary>Milestone Progress ({done}/{total} complete)</summary>
|
|
714
752
|
|
|
715
|
-
| # | Issue | Status | PR | Failure Class |
|
|
716
|
-
|
|
717
|
-
| N | title | ✓ Done | #PR | — |
|
|
718
|
-
| M | title | ✗ Failed | — | `permanent` |
|
|
719
|
-
| K | title | ○ Pending | — | — |
|
|
720
|
-
| J | title | ◆ Running | — | — |
|
|
721
|
-
| L | title | ⊘ Blocked | — | — |
|
|
753
|
+
| # | Issue | Status | PR | Failure Class | Label Drift |
|
|
754
|
+
|---|-------|--------|----|---------------|-------------|
|
|
755
|
+
| N | title | ✓ Done | #PR | — | ok |
|
|
756
|
+
| M | title | ✗ Failed | — | `permanent` | — |
|
|
757
|
+
| K | title | ○ Pending | — | — | — |
|
|
758
|
+
| J | title | ◆ Running | — | — | ok |
|
|
759
|
+
| L | title | ⊘ Blocked | — | — | — |
|
|
722
760
|
|
|
723
761
|
</details>
|
|
724
762
|
```
|
|
725
763
|
|
|
726
764
|
The **Failure Class** column surfaces `last_failure_class` from the active issue state file.
|
|
765
|
+
The **Label Drift** column shows the result of post-subagent label reconciliation: `ok` (labels matched expected), `drift` (label mismatch detected — MGW WARNING logged), or `—` (not checked / issue not run).
|
|
727
766
|
Values: `transient` (retried and exhausted), `permanent` (unrecoverable), `needs-info` (ambiguous issue), `unknown` (no state file or pre-retry issue), `—` (not failed).
|
|
728
767
|
</step>
|
|
729
768
|
|
|
@@ -1067,6 +1106,9 @@ gh issue comment ${FIRST_ISSUE_NUMBER} --body "$FINAL_RESULTS_COMMENT"
|
|
|
1067
1106
|
- [ ] Retry option calls resetRetryState() then re-invokes /mgw:run --retry for failed issues
|
|
1068
1107
|
- [ ] FAILED_ISSUES_WITH_CLASS tracks "number:class" for display in results table
|
|
1069
1108
|
- [ ] Progress table in every GitHub comment
|
|
1109
|
+
- [ ] Post-subagent label reconciliation run per issue after Task() returns
|
|
1110
|
+
- [ ] LABEL_DRIFT tracked per issue (ok/drift) and shown in progress table Label Drift column
|
|
1111
|
+
- [ ] Label drift issues logged as MGW WARNING to stderr
|
|
1070
1112
|
- [ ] Milestone close + draft release on full completion
|
|
1071
1113
|
- [ ] current_milestone pointer advanced on completion
|
|
1072
1114
|
- [ ] --interactive flag pauses between issues
|
package/commands/run.md
CHANGED
|
@@ -418,9 +418,7 @@ Add cross-ref (at `${REPO_ROOT}/.mgw/cross-refs.json`): issue → branch.
|
|
|
418
418
|
|
|
419
419
|
**Apply in-progress label:**
|
|
420
420
|
```bash
|
|
421
|
-
|
|
422
|
-
gh issue edit ${ISSUE_NUMBER} --remove-label "mgw:triaged" 2>/dev/null
|
|
423
|
-
gh issue edit ${ISSUE_NUMBER} --add-label "mgw:in-progress" 2>/dev/null
|
|
421
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "mgw:in-progress"
|
|
424
422
|
```
|
|
425
423
|
|
|
426
424
|
**PATH CONVENTION for remaining steps:**
|
|
@@ -531,8 +529,7 @@ fi
|
|
|
531
529
|
|
|
532
530
|
**When blocking comment detected — apply label:**
|
|
533
531
|
```bash
|
|
534
|
-
|
|
535
|
-
gh issue edit ${ISSUE_NUMBER} --add-label "mgw:blocked" 2>/dev/null
|
|
532
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "mgw:blocked"
|
|
536
533
|
```
|
|
537
534
|
|
|
538
535
|
If no new comments detected, continue normally.
|
|
@@ -949,8 +946,7 @@ PHASE_COUNT="TBD (determined by roadmapper)"
|
|
|
949
946
|
|
|
950
947
|
Set pipeline_stage to "discussing" and apply "mgw:discussing" label:
|
|
951
948
|
```bash
|
|
952
|
-
|
|
953
|
-
gh issue edit ${ISSUE_NUMBER} --add-label "mgw:discussing" 2>/dev/null
|
|
949
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "mgw:discussing"
|
|
954
950
|
```
|
|
955
951
|
|
|
956
952
|
Present to user:
|
|
@@ -1461,9 +1457,38 @@ rmdir "${REPO_ROOT}/.worktrees/issue" 2>/dev/null
|
|
|
1461
1457
|
rmdir "${REPO_ROOT}/.worktrees" 2>/dev/null
|
|
1462
1458
|
```
|
|
1463
1459
|
|
|
1464
|
-
|
|
1460
|
+
Clear MGW labels at completion:
|
|
1465
1461
|
```bash
|
|
1466
|
-
|
|
1462
|
+
# Pass empty string — removes all mgw: labels without applying a new one
|
|
1463
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} ""
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
Post-completion label reconciliation:
|
|
1467
|
+
```bash
|
|
1468
|
+
# Post-completion label reconciliation — verify no stray MGW labels remain
|
|
1469
|
+
LIVE_LABELS=$(gh issue view ${ISSUE_NUMBER} --json labels --jq '[.labels[].name]' 2>/dev/null || echo "[]")
|
|
1470
|
+
STRAY_MGW=$(echo "$LIVE_LABELS" | python3 -c "
|
|
1471
|
+
import json, sys
|
|
1472
|
+
labels = json.load(sys.stdin)
|
|
1473
|
+
stray = [l for l in labels if l.startswith('mgw:')]
|
|
1474
|
+
print('\n'.join(stray))
|
|
1475
|
+
" 2>/dev/null || echo "")
|
|
1476
|
+
|
|
1477
|
+
if [ -n "$STRAY_MGW" ]; then
|
|
1478
|
+
echo "MGW WARNING: unexpected MGW labels still on issue after completion: $STRAY_MGW" >&2
|
|
1479
|
+
fi
|
|
1480
|
+
|
|
1481
|
+
# Sync live labels back to .mgw/active state file
|
|
1482
|
+
LIVE_LABELS_LIST=$(gh issue view ${ISSUE_NUMBER} --json labels --jq '[.labels[].name]' 2>/dev/null || echo "[]")
|
|
1483
|
+
# Update labels field in ${REPO_ROOT}/.mgw/active/${STATE_FILE} using python3 json patch:
|
|
1484
|
+
python3 -c "
|
|
1485
|
+
import json, sys
|
|
1486
|
+
path = sys.argv[1]
|
|
1487
|
+
live = json.loads(sys.argv[2])
|
|
1488
|
+
with open(path) as f: state = json.load(f)
|
|
1489
|
+
state['labels'] = live
|
|
1490
|
+
with open(path, 'w') as f: json.dump(state, f, indent=2)
|
|
1491
|
+
" "${REPO_ROOT}/.mgw/active/${STATE_FILE}" "$LIVE_LABELS_LIST" 2>/dev/null || true
|
|
1467
1492
|
```
|
|
1468
1493
|
|
|
1469
1494
|
Extract one-liner summary for concise comment:
|
package/commands/sync.md
CHANGED
|
@@ -464,6 +464,81 @@ Classify each issue into:
|
|
|
464
464
|
- **Unreviewed comments:** COMMENT_DELTA > 0 — new comments posted since triage that haven't been classified
|
|
465
465
|
</step>
|
|
466
466
|
|
|
467
|
+
<step name="label_drift_detection">
|
|
468
|
+
**Label Drift Detection:**
|
|
469
|
+
|
|
470
|
+
For each active issue state file in `.mgw/active/`, compare the stored `labels` field
|
|
471
|
+
against the live GitHub labels to detect MGW pipeline label drift:
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
label_drift = []
|
|
475
|
+
|
|
476
|
+
for each state_file in .mgw/active/*.json:
|
|
477
|
+
stored_labels = state['labels'] # list stored at last pipeline write
|
|
478
|
+
live_labels_json = subprocess.run(
|
|
479
|
+
['gh', 'issue', 'view', str(issue_number), '--json', 'labels', '--jq', '[.labels[].name]'],
|
|
480
|
+
capture_output=True, text=True
|
|
481
|
+
).stdout.strip()
|
|
482
|
+
live_labels = json.loads(live_labels_json or '[]')
|
|
483
|
+
|
|
484
|
+
# Find MGW labels in stored vs live
|
|
485
|
+
stored_mgw = [l for l in stored_labels if l.startswith('mgw:')]
|
|
486
|
+
live_mgw = [l for l in live_labels if l.startswith('mgw:')]
|
|
487
|
+
|
|
488
|
+
if set(stored_mgw) != set(live_mgw):
|
|
489
|
+
label_drift.append({
|
|
490
|
+
'issue': issue_number,
|
|
491
|
+
'stored': stored_mgw,
|
|
492
|
+
'live': live_mgw,
|
|
493
|
+
'pipeline_stage': state['pipeline_stage']
|
|
494
|
+
})
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
If label drift detected, include in the drift report output:
|
|
498
|
+
|
|
499
|
+
```
|
|
500
|
+
Label drift detected for ${COUNT} issue(s):
|
|
501
|
+
#N: stored=[mgw:in-progress], live=[] — pipeline_stage=${STAGE}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Offer repair:**
|
|
505
|
+
```
|
|
506
|
+
AskUserQuestion(
|
|
507
|
+
header: "Label Drift Detected",
|
|
508
|
+
question: "Live GitHub labels don't match .mgw state for ${COUNT} issue(s). Repair?",
|
|
509
|
+
options: [
|
|
510
|
+
{ label: "Repair all", description: "Re-apply correct label for each issue's pipeline_stage via remove_mgw_labels_and_apply" },
|
|
511
|
+
{ label: "Skip", description: "Log drift only — no changes" }
|
|
512
|
+
]
|
|
513
|
+
)
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
If "Repair all":
|
|
517
|
+
```bash
|
|
518
|
+
# For each drifted issue, determine correct label from pipeline_stage and re-apply
|
|
519
|
+
CORRECT_LABEL=$(python3 -c "
|
|
520
|
+
stage_to_label = {
|
|
521
|
+
'triaged': 'mgw:triaged',
|
|
522
|
+
'needs-info': 'mgw:needs-info',
|
|
523
|
+
'needs-security-review': 'mgw:needs-security-review',
|
|
524
|
+
'discussing': 'mgw:discussing',
|
|
525
|
+
'approved': 'mgw:approved',
|
|
526
|
+
'planning': 'mgw:in-progress',
|
|
527
|
+
'executing': 'mgw:in-progress',
|
|
528
|
+
'verifying': 'mgw:in-progress',
|
|
529
|
+
'blocked': 'mgw:blocked',
|
|
530
|
+
'done': '',
|
|
531
|
+
'pr-created': '',
|
|
532
|
+
'failed': '',
|
|
533
|
+
}
|
|
534
|
+
print(stage_to_label.get('${PIPELINE_STAGE}', ''))
|
|
535
|
+
")
|
|
536
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "${CORRECT_LABEL}"
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Log repair actions: "Repaired label for #N: applied ${CORRECT_LABEL} (pipeline_stage=${STAGE})"
|
|
540
|
+
</step>
|
|
541
|
+
|
|
467
542
|
<step name="health_check">
|
|
468
543
|
**GSD health check (if .planning/ exists):**
|
|
469
544
|
|
|
@@ -570,5 +645,7 @@ ${gsd_milestone_consistency ? 'GSD Milestone Links:\n' + gsd_milestone_consisten
|
|
|
570
645
|
- [ ] Lingering worktrees cleaned up for completed items
|
|
571
646
|
- [ ] Branch deletion offered for completed items
|
|
572
647
|
- [ ] Stale/orphaned/drift items flagged (including comment drift and milestone inconsistencies)
|
|
648
|
+
- [ ] Label drift detected between .mgw/active labels field and live GitHub labels
|
|
649
|
+
- [ ] Repair offered for drifted issues via remove_mgw_labels_and_apply
|
|
573
650
|
- [ ] Summary presented
|
|
574
651
|
</success_criteria>
|
|
@@ -95,12 +95,15 @@ Seven labels for pipeline stage tracking. Created by init.md, managed by issue.m
|
|
|
95
95
|
| `mgw:blocked` | `b60205` | Pipeline blocked by stakeholder comment |
|
|
96
96
|
|
|
97
97
|
### Remove MGW Labels and Apply New
|
|
98
|
-
Used when transitioning pipeline stages. Removes all `mgw:*` pipeline labels, then applies the target label.
|
|
98
|
+
Used when transitioning pipeline stages. Removes all `mgw:*` pipeline labels, then applies the target label. Returns non-zero if any gh operation failed.
|
|
99
99
|
```bash
|
|
100
|
-
# Remove all mgw: pipeline labels from issue, then apply new one
|
|
100
|
+
# Remove all mgw: pipeline labels from issue, then apply new one.
|
|
101
|
+
# Pass empty string as NEW_LABEL to remove all MGW labels without applying a new one.
|
|
102
|
+
# Returns non-zero if any label operation failed.
|
|
101
103
|
remove_mgw_labels_and_apply() {
|
|
102
104
|
local ISSUE_NUMBER="$1"
|
|
103
105
|
local NEW_LABEL="$2"
|
|
106
|
+
local LABEL_FAILED=0
|
|
104
107
|
|
|
105
108
|
# Get current labels
|
|
106
109
|
CURRENT_LABELS=$(gh issue view "$ISSUE_NUMBER" --json labels --jq '.labels[].name' 2>/dev/null)
|
|
@@ -109,15 +112,27 @@ remove_mgw_labels_and_apply() {
|
|
|
109
112
|
for LABEL in $CURRENT_LABELS; do
|
|
110
113
|
case "$LABEL" in
|
|
111
114
|
mgw:triaged|mgw:needs-info|mgw:needs-security-review|mgw:discussing|mgw:approved|mgw:in-progress|mgw:blocked)
|
|
112
|
-
gh issue edit "$ISSUE_NUMBER" --remove-label "$LABEL"
|
|
115
|
+
gh issue edit "$ISSUE_NUMBER" --remove-label "$LABEL"
|
|
116
|
+
RC=$?
|
|
117
|
+
if [ $RC -ne 0 ]; then
|
|
118
|
+
echo "MGW WARNING: failed to remove label $LABEL from issue $ISSUE_NUMBER (exit $RC)" >&2
|
|
119
|
+
LABEL_FAILED=1
|
|
120
|
+
fi
|
|
113
121
|
;;
|
|
114
122
|
esac
|
|
115
123
|
done
|
|
116
124
|
|
|
117
125
|
# Apply new label
|
|
118
126
|
if [ -n "$NEW_LABEL" ]; then
|
|
119
|
-
gh issue edit "$ISSUE_NUMBER" --add-label "$NEW_LABEL"
|
|
127
|
+
gh issue edit "$ISSUE_NUMBER" --add-label "$NEW_LABEL"
|
|
128
|
+
RC=$?
|
|
129
|
+
if [ $RC -ne 0 ]; then
|
|
130
|
+
echo "MGW WARNING: failed to add label $NEW_LABEL to issue $ISSUE_NUMBER (exit $RC)" >&2
|
|
131
|
+
LABEL_FAILED=1
|
|
132
|
+
fi
|
|
120
133
|
fi
|
|
134
|
+
|
|
135
|
+
return $LABEL_FAILED
|
|
121
136
|
}
|
|
122
137
|
```
|
|
123
138
|
|
package/dist/bin/mgw.cjs
CHANGED