@snipcodeit/mgw 0.1.1 → 0.1.2
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/README.md +20 -9
- package/commands/board.md +75 -0
- package/commands/milestone.md +138 -15
- package/commands/project.md +55 -1651
- package/commands/run.md +285 -11
- package/commands/sync.md +332 -1
- package/dist/bin/mgw.cjs +2 -2
- package/dist/{claude-Vp9qvImH.cjs → claude-Dk1oVsaG.cjs} +156 -0
- package/dist/lib/index.cjs +237 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# MGW — My GSD Workflow
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/mgw)
|
|
3
|
+
[](https://www.npmjs.com/package/@snipcodeit/mgw)
|
|
4
|
+
[](https://www.npmjs.com/package/@snipcodeit/mgw)
|
|
4
5
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://nodejs.org)
|
|
6
|
+
[](https://nodejs.org)
|
|
6
7
|
[](https://github.com/snipcodeit/mgw)
|
|
7
8
|
|
|
8
9
|
> Issue in. PR out. No excuses.
|
|
@@ -95,6 +96,9 @@ If you're already using Claude Code and GSD for development, MGW is the missing
|
|
|
95
96
|
| `/mgw:review <n>` | Review and classify new comments on an issue since last triage |
|
|
96
97
|
| `/mgw:link <ref> <ref>` | Cross-reference issues, PRs, and branches (including milestone ↔ GSD milestone maps-to links) |
|
|
97
98
|
| `/mgw:status [n]` | Project dashboard — milestone progress, issue stages, open PRs |
|
|
99
|
+
| `/mgw:roadmap [--set-dates] [--post-discussion]` | Render project milestones as a roadmap table; optionally set GitHub due dates or post as a Discussion |
|
|
100
|
+
| `/mgw:assign <n> [user]` | Claim or reassign an issue; resolves GitHub noreply co-author tag |
|
|
101
|
+
| `/mgw:board [--sync]` | Create, configure, and sync a GitHub Projects v2 board |
|
|
98
102
|
| `/mgw:sync` | Reconcile local state with GitHub; verifies GSD milestone consistency |
|
|
99
103
|
| `/mgw:help` | Command reference |
|
|
100
104
|
|
|
@@ -184,9 +188,12 @@ MGW tracks pipeline state in a local `.mgw/` directory (gitignored, per-develope
|
|
|
184
188
|
42-fix-auth.json Issue state: triage results, pipeline stage, artifacts
|
|
185
189
|
completed/ Archived after PR merge
|
|
186
190
|
cross-refs.json Bidirectional issue/PR/branch/milestone links
|
|
187
|
-
vision-
|
|
188
|
-
vision-
|
|
191
|
+
vision-research.json (Fresh projects) Domain research from vision-researcher agent
|
|
192
|
+
vision-draft.md (Fresh projects) Rolling decisions from questioning loop
|
|
193
|
+
vision-brief.json (Fresh projects) Structured Vision Brief (MoSCoW, personas, scope)
|
|
194
|
+
vision-handoff.md (Fresh projects) Condensed brief handed off to gsd:new-project
|
|
189
195
|
alignment-report.json (GSD-Only projects) GSD state mapped for milestone backfill
|
|
196
|
+
drift-report.json (Diverged projects) Reconciliation table from drift-analyzer agent
|
|
190
197
|
```
|
|
191
198
|
|
|
192
199
|
Pipeline stages flow: `new` → `triaged` → `planning` → `executing` → `verifying` → `pr-created` → `done` (or `failed`/`blocked`). Bugs routed to `gsd:diagnose-issues` pass through `diagnosing` before `planning`.
|
|
@@ -248,9 +255,9 @@ mgw --version
|
|
|
248
255
|
|
|
249
256
|
# Slash commands (installed automatically by postinstall)
|
|
250
257
|
ls ~/.claude/commands/mgw/
|
|
251
|
-
# ask.md board.md help.md init.md issue.md issues.md link.md
|
|
252
|
-
# next.md pr.md project.md review.md
|
|
253
|
-
# update.md workflows/
|
|
258
|
+
# ask.md assign.md board.md help.md init.md issue.md issues.md link.md
|
|
259
|
+
# milestone.md next.md pr.md project.md review.md roadmap.md run.md
|
|
260
|
+
# status.md sync.md update.md workflows/
|
|
254
261
|
```
|
|
255
262
|
|
|
256
263
|
Then in Claude Code:
|
|
@@ -266,7 +273,7 @@ Not all commands work via `npx`. The CLI has two tiers:
|
|
|
266
273
|
| Tier | Commands | Requirements |
|
|
267
274
|
|------|----------|--------------|
|
|
268
275
|
| **CLI-only** (works with npx) | `issues`, `sync`, `link`, `help`, `--help`, `--version` | Node.js >= 18, `gh` CLI |
|
|
269
|
-
| **AI-powered** (requires full install) | `run`, `init`, `project`, `milestone`, `next`, `issue`, `update`, `pr`, `ask`, `review` | Node.js >= 18, `gh` CLI, Claude Code CLI, GSD |
|
|
276
|
+
| **AI-powered** (requires full install) | `run`, `init`, `project`, `milestone`, `next`, `issue`, `update`, `pr`, `ask`, `review`, `assign`, `board`, `roadmap`, `status` | Node.js >= 18, `gh` CLI, Claude Code CLI, GSD |
|
|
270
277
|
|
|
271
278
|
AI-powered commands call `claude -p` under the hood and require the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code/overview) to be installed and authenticated. The slash command `.md` files must also be deployed to `~/.claude/commands/mgw/` for the full pipeline to work — this happens automatically via `postinstall`. Use `npx @snipcodeit/mgw` to explore the CLI and verify your GitHub setup before committing to a full install.
|
|
272
279
|
|
|
@@ -336,12 +343,15 @@ lib/
|
|
|
336
343
|
claude.cjs Claude Code invocation helpers
|
|
337
344
|
github.cjs GitHub CLI wrappers (issues, PRs, milestones, Projects v2)
|
|
338
345
|
gsd.cjs GSD integration
|
|
346
|
+
gsd-adapter.cjs GSD route adapter (maps triage results to GSD spawn args)
|
|
339
347
|
state.cjs .mgw/ state management (migrateProjectState, resolveActiveMilestoneIndex)
|
|
340
348
|
output.cjs Logging and formatting
|
|
349
|
+
retry.cjs Retry logic for GitHub API calls
|
|
341
350
|
templates.cjs Template system
|
|
342
351
|
template-loader.cjs Output validation (JSON Schema) + parseRoadmap()
|
|
343
352
|
commands/ Slash command source files (deployed to ~/.claude/commands/mgw/ at install time)
|
|
344
353
|
ask.md Contextual question routing during milestone execution
|
|
354
|
+
assign.md Claim/reassign issues; resolves GitHub noreply co-author tag
|
|
345
355
|
board.md GitHub Projects v2 board management
|
|
346
356
|
help.md Command reference display
|
|
347
357
|
init.md One-time repo bootstrap (state, templates, labels)
|
|
@@ -350,6 +360,7 @@ commands/ Slash command source files (deployed to ~/.claude/com
|
|
|
350
360
|
issue.md Deep triage with agent analysis
|
|
351
361
|
next.md Next unblocked issue picker (surfaces failed issues as advisory)
|
|
352
362
|
review.md Comment review and classification since last triage
|
|
363
|
+
roadmap.md Milestone roadmap table; optional GitHub due-date setter and Discussion post
|
|
353
364
|
run.md Autonomous pipeline orchestrator (cross-milestone enforcement)
|
|
354
365
|
milestone.md Milestone execution with dependency ordering and failed-issue recovery
|
|
355
366
|
update.md Structured GitHub comment templates
|
|
@@ -491,7 +502,7 @@ If rate-limited, wait for the reset window (usually under an hour) or reduce the
|
|
|
491
502
|
|
|
492
503
|
```bash
|
|
493
504
|
# Remove CLI
|
|
494
|
-
npm
|
|
505
|
+
npm uninstall -g @snipcodeit/mgw
|
|
495
506
|
|
|
496
507
|
# Remove slash commands
|
|
497
508
|
rm -rf ~/.claude/commands/mgw/
|
package/commands/board.md
CHANGED
|
@@ -176,6 +176,81 @@ for name, data in fields.items():
|
|
|
176
176
|
fi
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
+
**Board discovery: check GitHub for an existing board before creating a new one:**
|
|
180
|
+
|
|
181
|
+
One lightweight GraphQL list call. Searches the first 20 user/org projects for a title
|
|
182
|
+
containing the project name. If found, registers it in project.json and exits — no fields
|
|
183
|
+
created, no board duplicated. Only runs when `BOARD_CONFIGURED = false`.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
echo "Checking GitHub for existing boards..."
|
|
187
|
+
DISCOVERED=$(node -e "
|
|
188
|
+
const { findExistingBoard, getProjectFields } = require('./lib/github.cjs');
|
|
189
|
+
const board = findExistingBoard('${OWNER}', '${PROJECT_NAME}');
|
|
190
|
+
if (!board) { process.stdout.write(''); process.exit(0); }
|
|
191
|
+
const fields = getProjectFields('${OWNER}', board.number) || {};
|
|
192
|
+
console.log(JSON.stringify({ ...board, fields }));
|
|
193
|
+
" 2>/dev/null || echo "")
|
|
194
|
+
|
|
195
|
+
if [ -n "$DISCOVERED" ]; then
|
|
196
|
+
DISC_NUMBER=$(echo "$DISCOVERED" | python3 -c "import json,sys; print(json.load(sys.stdin)['number'])")
|
|
197
|
+
DISC_URL=$(echo "$DISCOVERED" | python3 -c "import json,sys; print(json.load(sys.stdin)['url'])")
|
|
198
|
+
DISC_NODE_ID=$(echo "$DISCOVERED" | python3 -c "import json,sys; print(json.load(sys.stdin)['nodeId'])")
|
|
199
|
+
DISC_TITLE=$(echo "$DISCOVERED" | python3 -c "import json,sys; print(json.load(sys.stdin)['title'])")
|
|
200
|
+
DISC_FIELDS=$(echo "$DISCOVERED" | python3 -c "import json,sys; print(json.dumps(json.load(sys.stdin).get('fields', {})))")
|
|
201
|
+
|
|
202
|
+
echo " Found existing board: #${DISC_NUMBER} \"${DISC_TITLE}\" — ${DISC_URL}"
|
|
203
|
+
|
|
204
|
+
python3 << PYEOF
|
|
205
|
+
import json
|
|
206
|
+
|
|
207
|
+
with open('${MGW_DIR}/project.json') as f:
|
|
208
|
+
project = json.load(f)
|
|
209
|
+
|
|
210
|
+
fields = json.loads('''${DISC_FIELDS}''') if '${DISC_FIELDS}' not in ('', '{}') else {}
|
|
211
|
+
|
|
212
|
+
project['project']['project_board'] = {
|
|
213
|
+
'number': int('${DISC_NUMBER}'),
|
|
214
|
+
'url': '${DISC_URL}',
|
|
215
|
+
'node_id': '${DISC_NODE_ID}',
|
|
216
|
+
'fields': fields
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
with open('${MGW_DIR}/project.json', 'w') as f:
|
|
220
|
+
json.dump(project, f, indent=2)
|
|
221
|
+
|
|
222
|
+
print('project.json updated')
|
|
223
|
+
PYEOF
|
|
224
|
+
|
|
225
|
+
echo ""
|
|
226
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
227
|
+
echo " MGW ► EXISTING BOARD REGISTERED"
|
|
228
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
229
|
+
echo ""
|
|
230
|
+
echo "Board: #${DISC_NUMBER} — ${DISC_URL}"
|
|
231
|
+
echo "Node ID: ${DISC_NODE_ID}"
|
|
232
|
+
echo ""
|
|
233
|
+
if [ "$DISC_FIELDS" != "{}" ] && [ -n "$DISC_FIELDS" ]; then
|
|
234
|
+
echo "Fields registered:"
|
|
235
|
+
echo "$DISC_FIELDS" | python3 -c "
|
|
236
|
+
import json,sys
|
|
237
|
+
fields = json.load(sys.stdin)
|
|
238
|
+
for name, data in fields.items():
|
|
239
|
+
ftype = data.get('type', '?')
|
|
240
|
+
print(f' {name}: {data.get(\"field_id\",\"?\")} ({ftype})')
|
|
241
|
+
" 2>/dev/null
|
|
242
|
+
else
|
|
243
|
+
echo " (no custom fields found — run /mgw:board configure to add them)"
|
|
244
|
+
fi
|
|
245
|
+
echo ""
|
|
246
|
+
echo "To see board items: /mgw:board show"
|
|
247
|
+
exit 0
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
echo " No existing board found — creating new board..."
|
|
251
|
+
echo ""
|
|
252
|
+
```
|
|
253
|
+
|
|
179
254
|
**Get owner and repo node IDs (required for GraphQL mutations):**
|
|
180
255
|
|
|
181
256
|
```bash
|
package/commands/milestone.md
CHANGED
|
@@ -414,6 +414,7 @@ Track state for progress table:
|
|
|
414
414
|
```bash
|
|
415
415
|
COMPLETED_ISSUES=()
|
|
416
416
|
FAILED_ISSUES=()
|
|
417
|
+
FAILED_ISSUES_WITH_CLASS=() # Entries: "issue_number:failure_class" for results display
|
|
417
418
|
BLOCKED_ISSUES=()
|
|
418
419
|
SKIPPED_ISSUES=()
|
|
419
420
|
ISSUES_RUN=0
|
|
@@ -620,9 +621,30 @@ COMMENTEOF
|
|
|
620
621
|
echo " ✓ #${ISSUE_NUMBER} — PR #${PR_NUMBER} created"
|
|
621
622
|
|
|
622
623
|
else
|
|
623
|
-
# Failure — post failure comment
|
|
624
|
+
# Failure — read failure_class from active issue state, then post failure comment
|
|
624
625
|
FAILED_ISSUES+=("$ISSUE_NUMBER")
|
|
625
626
|
|
|
627
|
+
# Read failure_class and dead_letter from the active issue state file
|
|
628
|
+
ISSUE_FAILURE_CLASS=$(node -e "
|
|
629
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
630
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
631
|
+
console.log((state && state.last_failure_class) ? state.last_failure_class : 'unknown');
|
|
632
|
+
" 2>/dev/null || echo "unknown")
|
|
633
|
+
|
|
634
|
+
ISSUE_DEAD_LETTER=$(node -e "
|
|
635
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
636
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
637
|
+
console.log(state && state.dead_letter === true ? 'true' : 'false');
|
|
638
|
+
" 2>/dev/null || echo "false")
|
|
639
|
+
|
|
640
|
+
ISSUE_RETRY_COUNT=$(node -e "
|
|
641
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
642
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
643
|
+
console.log((state && typeof state.retry_count === 'number') ? state.retry_count : 0);
|
|
644
|
+
" 2>/dev/null || echo "0")
|
|
645
|
+
|
|
646
|
+
FAILED_ISSUES_WITH_CLASS+=("${ISSUE_NUMBER}:${ISSUE_FAILURE_CLASS}")
|
|
647
|
+
|
|
626
648
|
FAIL_BODY=$(cat <<COMMENTEOF
|
|
627
649
|
> **MGW** · \`pipeline-failed\` · ${DONE_TIMESTAMP}
|
|
628
650
|
> Milestone: ${MILESTONE_NAME} | Phase ${PHASE_NUM}: ${PHASE_NAME}
|
|
@@ -630,16 +652,22 @@ COMMENTEOF
|
|
|
630
652
|
### Pipeline Failed
|
|
631
653
|
|
|
632
654
|
Issue #${ISSUE_NUMBER} did not produce a PR.
|
|
633
|
-
|
|
655
|
+
|
|
656
|
+
| | |
|
|
657
|
+
|---|---|
|
|
658
|
+
| **Failure class** | \`${ISSUE_FAILURE_CLASS}\` |
|
|
659
|
+
| **Retries attempted** | ${ISSUE_RETRY_COUNT} of 3 |
|
|
660
|
+
| **Dead-lettered** | ${ISSUE_DEAD_LETTER} |
|
|
634
661
|
|
|
635
662
|
Dependents of this issue will be skipped.
|
|
663
|
+
To retry after resolving root cause: \`/mgw:run ${ISSUE_NUMBER} --retry\`
|
|
636
664
|
COMMENTEOF
|
|
637
665
|
)
|
|
638
666
|
|
|
639
667
|
gh issue comment ${ISSUE_NUMBER} --body "$FAIL_BODY" 2>/dev/null || true
|
|
640
668
|
gh issue edit ${ISSUE_NUMBER} --add-label "pipeline-failed" 2>/dev/null || true
|
|
641
669
|
gh label create "pipeline-failed" --description "Pipeline execution failed" --color "d73a4a" --force 2>/dev/null || true
|
|
642
|
-
echo " ✗ #${ISSUE_NUMBER} — Failed (no PR created)"
|
|
670
|
+
echo " ✗ #${ISSUE_NUMBER} — Failed (class: ${ISSUE_FAILURE_CLASS}, no PR created)"
|
|
643
671
|
fi
|
|
644
672
|
|
|
645
673
|
# Update project.json checkpoint (MLST-05)
|
|
@@ -684,16 +712,19 @@ Every comment posted during milestone orchestration includes:
|
|
|
684
712
|
<details>
|
|
685
713
|
<summary>Milestone Progress ({done}/{total} complete)</summary>
|
|
686
714
|
|
|
687
|
-
| # | Issue | Status | PR |
|
|
688
|
-
|
|
689
|
-
| N | title | ✓ Done | #PR |
|
|
690
|
-
| M | title | ✗ Failed | — |
|
|
691
|
-
| K | title | ○ Pending | — |
|
|
692
|
-
| J | title | ◆ Running | — |
|
|
693
|
-
| L | title | ⊘ Blocked | — |
|
|
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 | — | — |
|
|
694
722
|
|
|
695
723
|
</details>
|
|
696
724
|
```
|
|
725
|
+
|
|
726
|
+
The **Failure Class** column surfaces `last_failure_class` from the active issue state file.
|
|
727
|
+
Values: `transient` (retried and exhausted), `permanent` (unrecoverable), `needs-info` (ambiguous issue), `unknown` (no state file or pre-retry issue), `—` (not failed).
|
|
697
728
|
</step>
|
|
698
729
|
|
|
699
730
|
<step name="post_loop">
|
|
@@ -793,9 +824,18 @@ writeProjectState(state);
|
|
|
793
824
|
|
|
794
825
|
5. Milestone mapping verification:
|
|
795
826
|
|
|
796
|
-
After advancing to the next milestone, check its GSD linkage
|
|
827
|
+
After advancing to the next milestone, check its GSD linkage using `getGsdState()`
|
|
828
|
+
from `lib/gsd-adapter.cjs` to read current GSD execution state (.planning/STATE.md
|
|
829
|
+
and ROADMAP.md) alongside the project.json milestone map:
|
|
797
830
|
|
|
798
831
|
```bash
|
|
832
|
+
# Read current GSD state from .planning/ via the adapter
|
|
833
|
+
GSD_STATE=$(node -e "
|
|
834
|
+
const { getGsdState } = require('./lib/gsd-adapter.cjs');
|
|
835
|
+
const state = getGsdState();
|
|
836
|
+
console.log(JSON.stringify(state));
|
|
837
|
+
" 2>/dev/null || echo "null")
|
|
838
|
+
|
|
799
839
|
NEXT_MILESTONE_CHECK=$(node -e "
|
|
800
840
|
const { loadProjectState, resolveActiveMilestoneIndex } = require('./lib/state.cjs');
|
|
801
841
|
const state = loadProjectState();
|
|
@@ -906,6 +946,23 @@ Draft release created: ${RELEASE_TAG}
|
|
|
906
946
|
|
|
907
947
|
**If some issues failed:**
|
|
908
948
|
|
|
949
|
+
Build failure class lookup from `FAILED_ISSUES_WITH_CLASS` array for display:
|
|
950
|
+
```bash
|
|
951
|
+
# Build failure class map: { issue_number → failure_class }
|
|
952
|
+
FAILURE_CLASS_MAP=$(python3 -c "
|
|
953
|
+
import json, sys
|
|
954
|
+
|
|
955
|
+
entries = '${FAILED_ISSUES_WITH_CLASS[@]}'.split()
|
|
956
|
+
result = {}
|
|
957
|
+
for entry in entries:
|
|
958
|
+
if ':' in entry:
|
|
959
|
+
num, cls = entry.split(':', 1)
|
|
960
|
+
result[num] = cls
|
|
961
|
+
print(json.dumps(result))
|
|
962
|
+
" 2>/dev/null || echo "{}")
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
Display results table including failure_class for each failed issue:
|
|
909
966
|
```
|
|
910
967
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
911
968
|
MGW ► MILESTONE ${MILESTONE_NUM} INCOMPLETE
|
|
@@ -913,15 +970,78 @@ Draft release created: ${RELEASE_TAG}
|
|
|
913
970
|
|
|
914
971
|
${MILESTONE_NAME}
|
|
915
972
|
|
|
916
|
-
| # | Issue | Status | PR |
|
|
917
|
-
|
|
918
|
-
${
|
|
973
|
+
| # | Issue | Status | PR | Failure Class |
|
|
974
|
+
|---|-------|--------|----|---------------|
|
|
975
|
+
${results_table_with_failure_class}
|
|
919
976
|
|
|
920
977
|
Completed: ${TOTAL_DONE}/${TOTAL_ISSUES}
|
|
921
978
|
Failed: ${TOTAL_FAILED}
|
|
922
979
|
Blocked: ${TOTAL_BLOCKED}
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
For each failed issue, present recovery options:
|
|
983
|
+
```bash
|
|
984
|
+
for ENTRY in "${FAILED_ISSUES_WITH_CLASS[@]}"; do
|
|
985
|
+
FAIL_NUM=$(echo "$ENTRY" | cut -d':' -f1)
|
|
986
|
+
FAIL_CLASS=$(echo "$ENTRY" | cut -d':' -f2)
|
|
987
|
+
|
|
988
|
+
echo ""
|
|
989
|
+
echo " Failed: #${FAIL_NUM} (class: ${FAIL_CLASS})"
|
|
990
|
+
AskUserQuestion(
|
|
991
|
+
header: "Recovery — Issue #${FAIL_NUM}",
|
|
992
|
+
question: "Issue #${FAIL_NUM} failed (failure class: ${FAIL_CLASS}). What would you like to do?",
|
|
993
|
+
options: [
|
|
994
|
+
{
|
|
995
|
+
label: "Retry",
|
|
996
|
+
description: "Reset retry state via resetRetryState() and re-run /mgw:run #${FAIL_NUM} --retry"
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
label: "Skip",
|
|
1000
|
+
description: "Mark as skipped and continue to next issue (dependents will remain blocked)"
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
label: "Abort",
|
|
1004
|
+
description: "Stop milestone recovery here"
|
|
1005
|
+
}
|
|
1006
|
+
]
|
|
1007
|
+
)
|
|
923
1008
|
|
|
924
|
-
|
|
1009
|
+
case "$RECOVERY_CHOICE" in
|
|
1010
|
+
Retry)
|
|
1011
|
+
# Call resetRetryState() to clear retry_count, last_failure_class, dead_letter
|
|
1012
|
+
node -e "
|
|
1013
|
+
const { resetRetryState } = require('./lib/retry.cjs');
|
|
1014
|
+
const fs = require('fs'), path = require('path');
|
|
1015
|
+
const activeDir = path.join(process.cwd(), '.mgw', 'active');
|
|
1016
|
+
const files = fs.readdirSync(activeDir);
|
|
1017
|
+
const file = files.find(f => f.startsWith('${FAIL_NUM}-') && f.endsWith('.json'));
|
|
1018
|
+
if (!file) { console.error('No state file for #${FAIL_NUM}'); process.exit(1); }
|
|
1019
|
+
const filePath = path.join(activeDir, file);
|
|
1020
|
+
const state = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
1021
|
+
const reset = resetRetryState(state);
|
|
1022
|
+
reset.pipeline_stage = 'triaged';
|
|
1023
|
+
fs.writeFileSync(filePath, JSON.stringify(reset, null, 2));
|
|
1024
|
+
console.log('Retry state cleared for #${FAIL_NUM}');
|
|
1025
|
+
"
|
|
1026
|
+
# Remove pipeline-failed label before re-run
|
|
1027
|
+
gh issue edit ${FAIL_NUM} --remove-label "pipeline-failed" 2>/dev/null || true
|
|
1028
|
+
# Re-run the pipeline for this issue
|
|
1029
|
+
/mgw:run ${FAIL_NUM} --retry
|
|
1030
|
+
;;
|
|
1031
|
+
Skip)
|
|
1032
|
+
echo " ⊘ #${FAIL_NUM} — Skipped (will not retry)"
|
|
1033
|
+
;;
|
|
1034
|
+
Abort)
|
|
1035
|
+
echo "Milestone recovery aborted at #${FAIL_NUM}."
|
|
1036
|
+
break
|
|
1037
|
+
;;
|
|
1038
|
+
esac
|
|
1039
|
+
done
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
After recovery loop:
|
|
1043
|
+
```
|
|
1044
|
+
Milestone NOT closed. Re-run after resolving remaining failures:
|
|
925
1045
|
/mgw:milestone ${MILESTONE_NUM}
|
|
926
1046
|
```
|
|
927
1047
|
|
|
@@ -943,6 +1063,9 @@ gh issue comment ${FIRST_ISSUE_NUMBER} --body "$FINAL_RESULTS_COMMENT"
|
|
|
943
1063
|
- [ ] Sequential execution via /mgw:run Task() delegation (MLST-01)
|
|
944
1064
|
- [ ] Per-issue checkpoint to project.json after completion (MLST-05)
|
|
945
1065
|
- [ ] Failure handling: skip failed, label, comment, block dependents
|
|
1066
|
+
- [ ] failure_class surfaced in results table and failure comment for each failed issue
|
|
1067
|
+
- [ ] Retry option calls resetRetryState() then re-invokes /mgw:run --retry for failed issues
|
|
1068
|
+
- [ ] FAILED_ISSUES_WITH_CLASS tracks "number:class" for display in results table
|
|
946
1069
|
- [ ] Progress table in every GitHub comment
|
|
947
1070
|
- [ ] Milestone close + draft release on full completion
|
|
948
1071
|
- [ ] current_milestone pointer advanced on completion
|