@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/commands/run.md
CHANGED
|
@@ -57,7 +57,7 @@ REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
|
57
57
|
DEFAULT=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Parse $ARGUMENTS for issue number. If missing:
|
|
60
|
+
Parse $ARGUMENTS for issue number and flags. If issue number missing:
|
|
61
61
|
```
|
|
62
62
|
AskUserQuestion(
|
|
63
63
|
header: "Issue Number Required",
|
|
@@ -66,6 +66,16 @@ AskUserQuestion(
|
|
|
66
66
|
)
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
Extract flags from $ARGUMENTS:
|
|
70
|
+
```bash
|
|
71
|
+
RETRY_FLAG=false
|
|
72
|
+
for ARG in $ARGUMENTS; do
|
|
73
|
+
case "$ARG" in
|
|
74
|
+
--retry) RETRY_FLAG=true ;;
|
|
75
|
+
esac
|
|
76
|
+
done
|
|
77
|
+
```
|
|
78
|
+
|
|
69
79
|
Check for existing state: `${REPO_ROOT}/.mgw/active/${ISSUE_NUMBER}-*.json`
|
|
70
80
|
|
|
71
81
|
If no state file exists → issue not triaged yet. Run triage inline:
|
|
@@ -73,11 +83,71 @@ If no state file exists → issue not triaged yet. Run triage inline:
|
|
|
73
83
|
- Execute the mgw:issue triage flow (steps from issue.md) inline.
|
|
74
84
|
- After triage, reload state file.
|
|
75
85
|
|
|
76
|
-
If state file exists → load it.
|
|
86
|
+
If state file exists → load it. **Run migrateProjectState() to ensure retry fields exist:**
|
|
87
|
+
```bash
|
|
88
|
+
node -e "
|
|
89
|
+
const { migrateProjectState } = require('./lib/state.cjs');
|
|
90
|
+
migrateProjectState();
|
|
91
|
+
" 2>/dev/null || true
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Check pipeline_stage:
|
|
77
95
|
- "triaged" → proceed to GSD execution
|
|
78
96
|
- "planning" / "executing" → resume from where we left off
|
|
79
97
|
- "blocked" → "Pipeline for #${ISSUE_NUMBER} is blocked by a stakeholder comment. Review the issue comments, resolve the blocker, then re-run."
|
|
80
98
|
- "pr-created" / "done" → "Pipeline already completed for #${ISSUE_NUMBER}. Run /mgw:sync to reconcile."
|
|
99
|
+
- "failed" → Check for --retry flag:
|
|
100
|
+
- If --retry NOT present:
|
|
101
|
+
```
|
|
102
|
+
Pipeline for #${ISSUE_NUMBER} has failed (failure class: ${last_failure_class || "unknown"}).
|
|
103
|
+
dead_letter: ${dead_letter}
|
|
104
|
+
|
|
105
|
+
To retry: /mgw:run ${ISSUE_NUMBER} --retry
|
|
106
|
+
To inspect: /mgw:issue ${ISSUE_NUMBER}
|
|
107
|
+
```
|
|
108
|
+
STOP.
|
|
109
|
+
- If --retry present and dead_letter === true:
|
|
110
|
+
```bash
|
|
111
|
+
# Clear dead_letter and reset retry state via resetRetryState()
|
|
112
|
+
node -e "
|
|
113
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
114
|
+
const { resetRetryState } = require('./lib/retry.cjs');
|
|
115
|
+
const fs = require('fs'), path = require('path');
|
|
116
|
+
const activeDir = path.join(process.cwd(), '.mgw', 'active');
|
|
117
|
+
const files = fs.readdirSync(activeDir);
|
|
118
|
+
const file = files.find(f => f.startsWith('${ISSUE_NUMBER}-') && f.endsWith('.json'));
|
|
119
|
+
if (!file) { console.error('No state file for #${ISSUE_NUMBER}'); process.exit(1); }
|
|
120
|
+
const filePath = path.join(activeDir, file);
|
|
121
|
+
const state = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
122
|
+
const reset = resetRetryState(state);
|
|
123
|
+
reset.pipeline_stage = 'triaged';
|
|
124
|
+
fs.writeFileSync(filePath, JSON.stringify(reset, null, 2));
|
|
125
|
+
console.log('Retry state cleared for #${ISSUE_NUMBER}');
|
|
126
|
+
"
|
|
127
|
+
# Remove pipeline-failed label
|
|
128
|
+
gh issue edit ${ISSUE_NUMBER} --remove-label "pipeline-failed" 2>/dev/null || true
|
|
129
|
+
```
|
|
130
|
+
Log: "MGW: dead_letter cleared for #${ISSUE_NUMBER} via --retry flag. Re-queuing."
|
|
131
|
+
Continue pipeline (treat as triaged).
|
|
132
|
+
- If --retry present and dead_letter !== true (manual retry of non-dead-lettered failure):
|
|
133
|
+
```bash
|
|
134
|
+
node -e "
|
|
135
|
+
const { resetRetryState } = require('./lib/retry.cjs');
|
|
136
|
+
const fs = require('fs'), path = require('path');
|
|
137
|
+
const activeDir = path.join(process.cwd(), '.mgw', 'active');
|
|
138
|
+
const files = fs.readdirSync(activeDir);
|
|
139
|
+
const file = files.find(f => f.startsWith('${ISSUE_NUMBER}-') && f.endsWith('.json'));
|
|
140
|
+
if (!file) { console.error('No state file'); process.exit(1); }
|
|
141
|
+
const filePath = path.join(activeDir, file);
|
|
142
|
+
const state = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
143
|
+
const reset = resetRetryState(state);
|
|
144
|
+
reset.pipeline_stage = 'triaged';
|
|
145
|
+
fs.writeFileSync(filePath, JSON.stringify(reset, null, 2));
|
|
146
|
+
console.log('Retry state reset for #${ISSUE_NUMBER}');
|
|
147
|
+
"
|
|
148
|
+
gh issue edit ${ISSUE_NUMBER} --remove-label "pipeline-failed" 2>/dev/null || true
|
|
149
|
+
```
|
|
150
|
+
Continue pipeline.
|
|
81
151
|
- "needs-info" → Check for --force flag in $ARGUMENTS:
|
|
82
152
|
If --force NOT present:
|
|
83
153
|
```
|
|
@@ -107,6 +177,24 @@ If state file exists → load it. Check pipeline_stage:
|
|
|
107
177
|
Update state: pipeline_stage = "triaged", add override_log entry.
|
|
108
178
|
Continue pipeline.
|
|
109
179
|
|
|
180
|
+
**Route selection via gsd-adapter (runs after loading issue state):**
|
|
181
|
+
|
|
182
|
+
Use `selectGsdRoute()` from `lib/gsd-adapter.cjs` to determine the GSD execution
|
|
183
|
+
path. This centralizes the routing decision so it is auditable and consistent
|
|
184
|
+
across all pipeline commands:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
GSD_ROUTE=$(node -e "
|
|
188
|
+
const { selectGsdRoute } = require('./lib/gsd-adapter.cjs');
|
|
189
|
+
const issue = $(cat ${REPO_ROOT}/.mgw/active/${STATE_FILE});
|
|
190
|
+
const { loadProjectState } = require('./lib/state.cjs');
|
|
191
|
+
const projectState = loadProjectState() || {};
|
|
192
|
+
const route = selectGsdRoute(issue, projectState);
|
|
193
|
+
console.log(route);
|
|
194
|
+
")
|
|
195
|
+
# GSD_ROUTE is one of: quick | plan-phase | diagnose | execute-only | verify-only
|
|
196
|
+
```
|
|
197
|
+
|
|
110
198
|
**Cross-milestone detection (runs after loading issue state):**
|
|
111
199
|
|
|
112
200
|
Check if this issue belongs to a non-active GSD milestone:
|
|
@@ -465,7 +553,7 @@ SYSTEM_LIST="${triage.scope.systems}"
|
|
|
465
553
|
FILE_LIST="${triage.scope.files}"
|
|
466
554
|
CONFLICTS="${triage.conflicts}"
|
|
467
555
|
ROUTE_REASONING="${triage.route_reasoning}"
|
|
468
|
-
TIMESTAMP=$(node
|
|
556
|
+
TIMESTAMP=$(node -e "try{process.stdout.write(require('./lib/gsd-adapter.cjs').getTimestamp())}catch(e){process.stdout.write(new Date().toISOString().replace(/\\.\\d{3}Z$/,'Z'))}")
|
|
469
557
|
|
|
470
558
|
# Load milestone/phase context from project.json if available
|
|
471
559
|
MILESTONE_CONTEXT=""
|
|
@@ -524,6 +612,24 @@ Log comment in state file (at `${REPO_ROOT}/.mgw/active/`).
|
|
|
524
612
|
|
|
525
613
|
Only run this step if gsd_route is "gsd:quick" or "gsd:quick --full".
|
|
526
614
|
|
|
615
|
+
**Retry loop initialization:**
|
|
616
|
+
```bash
|
|
617
|
+
# Load retry state from .mgw/active/ state file
|
|
618
|
+
RETRY_COUNT=$(node -e "
|
|
619
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
620
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
621
|
+
console.log((state && typeof state.retry_count === 'number') ? state.retry_count : 0);
|
|
622
|
+
" 2>/dev/null || echo "0")
|
|
623
|
+
EXECUTION_SUCCEEDED=false
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**Begin retry loop** — wraps the GSD quick execution (steps 1–11 below) with transient-failure retry:
|
|
627
|
+
|
|
628
|
+
```
|
|
629
|
+
RETRY_LOOP:
|
|
630
|
+
while canRetry(issue_state) AND NOT EXECUTION_SUCCEEDED:
|
|
631
|
+
```
|
|
632
|
+
|
|
527
633
|
Update pipeline_stage to "executing" in state file (at `${REPO_ROOT}/.mgw/active/`).
|
|
528
634
|
|
|
529
635
|
Determine flags:
|
|
@@ -725,6 +831,77 @@ node ~/.claude/get-shit-done/bin/gsd-tools.cjs commit "docs(quick-${next_num}):
|
|
|
725
831
|
```
|
|
726
832
|
|
|
727
833
|
Update state (at `${REPO_ROOT}/.mgw/active/`): gsd_artifacts.path = $QUICK_DIR, pipeline_stage = "verifying".
|
|
834
|
+
|
|
835
|
+
**Retry loop — on execution failure:**
|
|
836
|
+
|
|
837
|
+
If any step above fails (executor or verifier agent returns error, summary missing, etc.), capture the error and apply retry logic:
|
|
838
|
+
|
|
839
|
+
```bash
|
|
840
|
+
# On failure — classify and decide whether to retry
|
|
841
|
+
FAILURE_CLASS=$(node -e "
|
|
842
|
+
const { classifyFailure, canRetry, incrementRetry, getBackoffMs } = require('./lib/retry.cjs');
|
|
843
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
844
|
+
const fs = require('fs'), path = require('path');
|
|
845
|
+
|
|
846
|
+
const activeDir = path.join(process.cwd(), '.mgw', 'active');
|
|
847
|
+
const files = fs.readdirSync(activeDir);
|
|
848
|
+
const file = files.find(f => f.startsWith('${ISSUE_NUMBER}-') && f.endsWith('.json'));
|
|
849
|
+
const filePath = path.join(activeDir, file);
|
|
850
|
+
let issueState = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
851
|
+
|
|
852
|
+
// Classify the failure from the error context
|
|
853
|
+
const error = { message: '${EXECUTION_ERROR_MESSAGE}' };
|
|
854
|
+
const result = classifyFailure(error);
|
|
855
|
+
console.error('Failure classified as: ' + result.class + ' — ' + result.reason);
|
|
856
|
+
|
|
857
|
+
// Persist failure class to state
|
|
858
|
+
issueState.last_failure_class = result.class;
|
|
859
|
+
|
|
860
|
+
if (result.class === 'transient' && canRetry(issueState)) {
|
|
861
|
+
const backoff = getBackoffMs(issueState.retry_count || 0);
|
|
862
|
+
issueState = incrementRetry(issueState);
|
|
863
|
+
fs.writeFileSync(filePath, JSON.stringify(issueState, null, 2));
|
|
864
|
+
// Output: backoff ms so shell can sleep
|
|
865
|
+
console.log('retry:' + backoff + ':' + result.class);
|
|
866
|
+
} else {
|
|
867
|
+
// Permanent failure or retries exhausted — dead-letter
|
|
868
|
+
issueState.dead_letter = true;
|
|
869
|
+
fs.writeFileSync(filePath, JSON.stringify(issueState, null, 2));
|
|
870
|
+
console.log('dead_letter:' + result.class);
|
|
871
|
+
}
|
|
872
|
+
")
|
|
873
|
+
|
|
874
|
+
case "$FAILURE_CLASS" in
|
|
875
|
+
retry:*)
|
|
876
|
+
BACKOFF_MS=$(echo "$FAILURE_CLASS" | cut -d':' -f2)
|
|
877
|
+
BACKOFF_SEC=$(( (BACKOFF_MS + 999) / 1000 ))
|
|
878
|
+
echo "MGW: Transient failure detected — retrying in ${BACKOFF_SEC}s (retry ${RETRY_COUNT})..."
|
|
879
|
+
sleep "$BACKOFF_SEC"
|
|
880
|
+
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
881
|
+
# Loop back to retry
|
|
882
|
+
;;
|
|
883
|
+
dead_letter:*)
|
|
884
|
+
FAILURE_CLASS_NAME=$(echo "$FAILURE_CLASS" | cut -d':' -f2)
|
|
885
|
+
EXECUTION_SUCCEEDED=false
|
|
886
|
+
# Break out of retry loop — handled in post_execution_update
|
|
887
|
+
break
|
|
888
|
+
;;
|
|
889
|
+
esac
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
On successful execution (EXECUTION_SUCCEEDED=true): break out of retry loop, clear last_failure_class:
|
|
893
|
+
```bash
|
|
894
|
+
node -e "
|
|
895
|
+
const fs = require('fs'), path = require('path');
|
|
896
|
+
const activeDir = path.join(process.cwd(), '.mgw', 'active');
|
|
897
|
+
const files = fs.readdirSync(activeDir);
|
|
898
|
+
const file = files.find(f => f.startsWith('${ISSUE_NUMBER}-') && f.endsWith('.json'));
|
|
899
|
+
const filePath = path.join(activeDir, file);
|
|
900
|
+
const state = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
901
|
+
state.last_failure_class = null;
|
|
902
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2));
|
|
903
|
+
" 2>/dev/null || true
|
|
904
|
+
```
|
|
728
905
|
</step>
|
|
729
906
|
|
|
730
907
|
<step name="execute_gsd_milestone">
|
|
@@ -732,13 +909,25 @@ Update state (at `${REPO_ROOT}/.mgw/active/`): gsd_artifacts.path = $QUICK_DIR,
|
|
|
732
909
|
|
|
733
910
|
Only run this step if gsd_route is "gsd:new-milestone".
|
|
734
911
|
|
|
912
|
+
**Retry loop initialization** (same pattern as execute_gsd_quick):
|
|
913
|
+
```bash
|
|
914
|
+
RETRY_COUNT=$(node -e "
|
|
915
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
916
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
917
|
+
console.log((state && typeof state.retry_count === 'number') ? state.retry_count : 0);
|
|
918
|
+
" 2>/dev/null || echo "0")
|
|
919
|
+
EXECUTION_SUCCEEDED=false
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
**Begin retry loop** — wraps the phase-execution loop (steps 2b–2e below) with transient-failure retry. Step 2 (milestone roadmap creation) is NOT wrapped in the retry loop — roadmap creation failures are always treated as permanent (require human intervention).
|
|
923
|
+
|
|
735
924
|
This is the most complex path. The orchestrator needs to:
|
|
736
925
|
|
|
737
926
|
**Resolve models for milestone agents:**
|
|
738
927
|
```bash
|
|
739
|
-
PLANNER_MODEL=$(node
|
|
740
|
-
EXECUTOR_MODEL=$(node
|
|
741
|
-
VERIFIER_MODEL=$(node
|
|
928
|
+
PLANNER_MODEL=$(node -e "process.stdout.write(require('./lib/gsd-adapter.cjs').resolveModel('gsd-planner'))")
|
|
929
|
+
EXECUTOR_MODEL=$(node -e "process.stdout.write(require('./lib/gsd-adapter.cjs').resolveModel('gsd-executor'))")
|
|
930
|
+
VERIFIER_MODEL=$(node -e "process.stdout.write(require('./lib/gsd-adapter.cjs').resolveModel('gsd-verifier'))")
|
|
742
931
|
```
|
|
743
932
|
|
|
744
933
|
1. **Discussion phase trigger for large-scope issues:**
|
|
@@ -747,7 +936,7 @@ If the issue was triaged with large scope and `gsd_route == "gsd:new-milestone"`
|
|
|
747
936
|
a scope proposal comment and set the discussing stage before proceeding to phase execution:
|
|
748
937
|
|
|
749
938
|
```bash
|
|
750
|
-
DISCUSS_TIMESTAMP=$(node
|
|
939
|
+
DISCUSS_TIMESTAMP=$(node -e "try{process.stdout.write(require('./lib/gsd-adapter.cjs').getTimestamp())}catch(e){process.stdout.write(new Date().toISOString().replace(/\\.\\d{3}Z$/,'Z'))}")
|
|
751
940
|
|
|
752
941
|
# Build scope breakdown from triage data
|
|
753
942
|
SCOPE_SIZE="${triage.scope.size}"
|
|
@@ -970,19 +1159,99 @@ COMMENTEOF
|
|
|
970
1159
|
gh issue comment ${ISSUE_NUMBER} --body "$PHASE_BODY" 2>/dev/null || true
|
|
971
1160
|
```
|
|
972
1161
|
|
|
1162
|
+
**Retry loop — on phase execution failure** (apply same pattern as execute_gsd_quick):
|
|
1163
|
+
|
|
1164
|
+
If a phase's executor or verifier fails, capture the error and apply retry logic via `classifyFailure()`, `canRetry()`, `incrementRetry()`, and `getBackoffMs()` from `lib/retry.cjs`. Only the failing phase is retried (restart from step 2b for that phase). If the failure is transient and `canRetry()` is true: sleep backoff, call `incrementRetry()`, loop. If permanent or retries exhausted: set `dead_letter = true`, set `last_failure_class`, break the retry loop.
|
|
1165
|
+
|
|
1166
|
+
On successful completion of all phases: clear `last_failure_class`, set `EXECUTION_SUCCEEDED=true`.
|
|
1167
|
+
|
|
973
1168
|
After ALL phases complete → update pipeline_stage to "verifying" (at `${REPO_ROOT}/.mgw/active/`).
|
|
974
1169
|
</step>
|
|
975
1170
|
|
|
976
1171
|
<step name="post_execution_update">
|
|
977
|
-
**Post execution-complete comment on issue:**
|
|
1172
|
+
**Post execution-complete comment on issue (or failure comment if dead_letter):**
|
|
1173
|
+
|
|
1174
|
+
Read `dead_letter` and `last_failure_class` from current issue state:
|
|
1175
|
+
```bash
|
|
1176
|
+
DEAD_LETTER=$(node -e "
|
|
1177
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
1178
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
1179
|
+
console.log(state && state.dead_letter === true ? 'true' : 'false');
|
|
1180
|
+
" 2>/dev/null || echo "false")
|
|
1181
|
+
|
|
1182
|
+
LAST_FAILURE_CLASS=$(node -e "
|
|
1183
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
1184
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
1185
|
+
console.log((state && state.last_failure_class) ? state.last_failure_class : 'unknown');
|
|
1186
|
+
" 2>/dev/null || echo "unknown")
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
**If dead_letter === true — post failure comment and halt:**
|
|
1190
|
+
```bash
|
|
1191
|
+
if [ "$DEAD_LETTER" = "true" ]; then
|
|
1192
|
+
FAIL_TIMESTAMP=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs current-timestamp --raw 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
1193
|
+
RETRY_COUNT_CURRENT=$(node -e "
|
|
1194
|
+
const { loadActiveIssue } = require('./lib/state.cjs');
|
|
1195
|
+
const state = loadActiveIssue(${ISSUE_NUMBER});
|
|
1196
|
+
console.log((state && typeof state.retry_count === 'number') ? state.retry_count : 0);
|
|
1197
|
+
" 2>/dev/null || echo "0")
|
|
1198
|
+
|
|
1199
|
+
FAIL_BODY=$(cat <<COMMENTEOF
|
|
1200
|
+
> **MGW** · \`pipeline-failed\` · ${FAIL_TIMESTAMP}
|
|
1201
|
+
> ${MILESTONE_CONTEXT}
|
|
1202
|
+
|
|
1203
|
+
### Pipeline Failed
|
|
1204
|
+
|
|
1205
|
+
Issue #${ISSUE_NUMBER} — ${issue_title}
|
|
1206
|
+
|
|
1207
|
+
| | |
|
|
1208
|
+
|---|---|
|
|
1209
|
+
| **Failure class** | \`${LAST_FAILURE_CLASS}\` |
|
|
1210
|
+
| **Retries attempted** | ${RETRY_COUNT_CURRENT} of 3 |
|
|
1211
|
+
| **Status** | Dead-lettered — requires human intervention |
|
|
1212
|
+
|
|
1213
|
+
**Failure class meaning:**
|
|
1214
|
+
- \`transient\` — retry exhausted (rate limit, network, or overload)
|
|
1215
|
+
- \`permanent\` — unrecoverable (auth, missing deps, bad config)
|
|
1216
|
+
- \`needs-info\` — issue is ambiguous or incomplete
|
|
1217
|
+
|
|
1218
|
+
**To retry after resolving root cause:**
|
|
1219
|
+
\`\`\`
|
|
1220
|
+
/mgw:run ${ISSUE_NUMBER} --retry
|
|
1221
|
+
\`\`\`
|
|
1222
|
+
COMMENTEOF
|
|
1223
|
+
)
|
|
1224
|
+
|
|
1225
|
+
gh issue comment ${ISSUE_NUMBER} --body "$FAIL_BODY" 2>/dev/null || true
|
|
1226
|
+
gh issue edit ${ISSUE_NUMBER} --add-label "pipeline-failed" 2>/dev/null || true
|
|
1227
|
+
gh label create "pipeline-failed" --description "Pipeline execution failed" --color "d73a4a" --force 2>/dev/null || true
|
|
1228
|
+
|
|
1229
|
+
# Update pipeline_stage to failed
|
|
1230
|
+
node -e "
|
|
1231
|
+
const fs = require('fs'), path = require('path');
|
|
1232
|
+
const activeDir = path.join(process.cwd(), '.mgw', 'active');
|
|
1233
|
+
const files = fs.readdirSync(activeDir);
|
|
1234
|
+
const file = files.find(f => f.startsWith('${ISSUE_NUMBER}-') && f.endsWith('.json'));
|
|
1235
|
+
const filePath = path.join(activeDir, file);
|
|
1236
|
+
const state = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
1237
|
+
state.pipeline_stage = 'failed';
|
|
1238
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2));
|
|
1239
|
+
" 2>/dev/null || true
|
|
1240
|
+
|
|
1241
|
+
echo "MGW: Pipeline dead-lettered for #${ISSUE_NUMBER} (class: ${LAST_FAILURE_CLASS}). Use --retry after fixing root cause."
|
|
1242
|
+
exit 1
|
|
1243
|
+
fi
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
**Otherwise — post execution-complete comment:**
|
|
978
1247
|
|
|
979
|
-
After GSD execution completes, post a structured update before creating the PR:
|
|
1248
|
+
After GSD execution completes successfully, post a structured update before creating the PR:
|
|
980
1249
|
|
|
981
1250
|
```bash
|
|
982
1251
|
COMMIT_COUNT=$(git rev-list ${DEFAULT_BRANCH}..HEAD --count 2>/dev/null || echo "0")
|
|
983
1252
|
TEST_STATUS=$(npm test 2>&1 >/dev/null && echo "passing" || echo "failing")
|
|
984
1253
|
FILE_CHANGES=$(git diff --stat ${DEFAULT_BRANCH}..HEAD 2>/dev/null | tail -1)
|
|
985
|
-
EXEC_TIMESTAMP=$(node
|
|
1254
|
+
EXEC_TIMESTAMP=$(node -e "try{process.stdout.write(require('./lib/gsd-adapter.cjs').getTimestamp())}catch(e){process.stdout.write(new Date().toISOString().replace(/\\.\\d{3}Z$/,'Z'))}")
|
|
986
1255
|
```
|
|
987
1256
|
|
|
988
1257
|
Post the execution-complete comment directly (no sub-agent — guarantees it happens):
|
|
@@ -1205,7 +1474,7 @@ ONE_LINER=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs summary-extract "${gs
|
|
|
1205
1474
|
Post structured PR-ready comment directly (no sub-agent — guarantees it happens):
|
|
1206
1475
|
|
|
1207
1476
|
```bash
|
|
1208
|
-
DONE_TIMESTAMP=$(node
|
|
1477
|
+
DONE_TIMESTAMP=$(node -e "try{process.stdout.write(require('./lib/gsd-adapter.cjs').getTimestamp())}catch(e){process.stdout.write(new Date().toISOString().replace(/\\.\\d{3}Z$/,'Z'))}")
|
|
1209
1478
|
|
|
1210
1479
|
PR_READY_BODY=$(cat <<COMMENTEOF
|
|
1211
1480
|
> **MGW** · \`pr-ready\` · ${DONE_TIMESTAMP}
|
|
@@ -1265,12 +1534,17 @@ Next:
|
|
|
1265
1534
|
- [ ] Issue number validated and state loaded (or triage run first)
|
|
1266
1535
|
- [ ] Pipeline refuses needs-info without --force
|
|
1267
1536
|
- [ ] Pipeline refuses needs-security-review without --security-ack
|
|
1537
|
+
- [ ] --retry flag clears dead_letter state, removes pipeline-failed label, and re-queues issue
|
|
1538
|
+
- [ ] migrateProjectState() called at load time to ensure retry fields exist on active issue files
|
|
1268
1539
|
- [ ] Isolated worktree created (.worktrees/ gitignored)
|
|
1269
1540
|
- [ ] mgw:in-progress label applied during execution
|
|
1270
1541
|
- [ ] Pre-flight comment check performed (new comments classified before execution)
|
|
1271
1542
|
- [ ] mgw:blocked label applied when blocking comments detected
|
|
1272
1543
|
- [ ] Work-starting comment posted on issue (route, scope, branch)
|
|
1273
1544
|
- [ ] GSD pipeline executed in worktree (quick or milestone route)
|
|
1545
|
+
- [ ] Transient execution failures retried up to 3 times with exponential backoff
|
|
1546
|
+
- [ ] Failure comment includes failure_class from classifyFailure()
|
|
1547
|
+
- [ ] dead_letter=true set when retries exhausted or failure is permanent
|
|
1274
1548
|
- [ ] New-milestone route triggers discussion phase with mgw:discussing label
|
|
1275
1549
|
- [ ] Execution-complete comment posted on issue (commits, changes, test status)
|
|
1276
1550
|
- [ ] PR created with summary, milestone context, testing procedures, cross-refs
|