sequant 1.13.5 → 1.14.1
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/.claude-plugin/plugin.json +1 -1
- package/dist/bin/cli.js +1 -0
- package/dist/src/commands/run.d.ts +21 -0
- package/dist/src/commands/run.js +51 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +2 -0
- package/dist/src/lib/cli-ui.d.ts +1 -1
- package/dist/src/lib/cli-ui.js +12 -14
- package/dist/src/lib/scope/analyzer.d.ts +91 -0
- package/dist/src/lib/scope/analyzer.js +310 -0
- package/dist/src/lib/scope/formatter.d.ts +52 -0
- package/dist/src/lib/scope/formatter.js +169 -0
- package/dist/src/lib/scope/index.d.ts +36 -0
- package/dist/src/lib/scope/index.js +73 -0
- package/dist/src/lib/scope/types.d.ts +198 -0
- package/dist/src/lib/scope/types.js +60 -0
- package/dist/src/lib/scope/verdict.d.ts +80 -0
- package/dist/src/lib/scope/verdict.js +173 -0
- package/dist/src/lib/settings.d.ts +33 -0
- package/dist/src/lib/settings.js +21 -0
- package/dist/src/lib/workflow/phase-detection.d.ts +114 -0
- package/dist/src/lib/workflow/phase-detection.js +215 -0
- package/dist/src/lib/workflow/state-manager.d.ts +11 -0
- package/dist/src/lib/workflow/state-manager.js +26 -0
- package/dist/src/lib/workflow/state-schema.d.ts +103 -0
- package/dist/src/lib/workflow/state-schema.js +20 -0
- package/package.json +1 -1
- package/templates/skills/exec/SKILL.md +50 -1
- package/templates/skills/fullsolve/SKILL.md +47 -1
- package/templates/skills/qa/SKILL.md +47 -1
- package/templates/skills/security-review/SKILL.md +0 -1
- package/templates/skills/spec/SKILL.md +39 -1
- package/templates/skills/testgen/SKILL.md +1 -0
|
@@ -60,6 +60,35 @@ export declare const PhaseSchema: z.ZodEnum<{
|
|
|
60
60
|
merger: "merger";
|
|
61
61
|
}>;
|
|
62
62
|
export type Phase = z.infer<typeof PhaseSchema>;
|
|
63
|
+
/**
|
|
64
|
+
* Phase marker stored in GitHub issue comments for cross-session detection.
|
|
65
|
+
*
|
|
66
|
+
* Embedded as HTML comments: `<!-- SEQUANT_PHASE: {...} -->`
|
|
67
|
+
*/
|
|
68
|
+
export declare const PhaseMarkerSchema: z.ZodObject<{
|
|
69
|
+
phase: z.ZodEnum<{
|
|
70
|
+
loop: "loop";
|
|
71
|
+
verify: "verify";
|
|
72
|
+
spec: "spec";
|
|
73
|
+
exec: "exec";
|
|
74
|
+
qa: "qa";
|
|
75
|
+
"security-review": "security-review";
|
|
76
|
+
testgen: "testgen";
|
|
77
|
+
test: "test";
|
|
78
|
+
merger: "merger";
|
|
79
|
+
}>;
|
|
80
|
+
status: z.ZodEnum<{
|
|
81
|
+
pending: "pending";
|
|
82
|
+
skipped: "skipped";
|
|
83
|
+
completed: "completed";
|
|
84
|
+
in_progress: "in_progress";
|
|
85
|
+
failed: "failed";
|
|
86
|
+
}>;
|
|
87
|
+
timestamp: z.ZodString;
|
|
88
|
+
pr: z.ZodOptional<z.ZodNumber>;
|
|
89
|
+
error: z.ZodOptional<z.ZodString>;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
export type PhaseMarker = z.infer<typeof PhaseMarkerSchema>;
|
|
63
92
|
/**
|
|
64
93
|
* Individual phase state within an issue
|
|
65
94
|
*/
|
|
@@ -246,6 +275,43 @@ export declare const IssueStateSchema: z.ZodObject<{
|
|
|
246
275
|
blocked: z.ZodNumber;
|
|
247
276
|
}, z.core.$strip>;
|
|
248
277
|
}, z.core.$strip>>;
|
|
278
|
+
scopeAssessment: z.ZodOptional<z.ZodObject<{
|
|
279
|
+
assessedAt: z.ZodString;
|
|
280
|
+
skipped: z.ZodBoolean;
|
|
281
|
+
skipReason: z.ZodOptional<z.ZodString>;
|
|
282
|
+
verdict: z.ZodEnum<{
|
|
283
|
+
SCOPE_OK: "SCOPE_OK";
|
|
284
|
+
SCOPE_WARNING: "SCOPE_WARNING";
|
|
285
|
+
SCOPE_SPLIT_RECOMMENDED: "SCOPE_SPLIT_RECOMMENDED";
|
|
286
|
+
}>;
|
|
287
|
+
metrics: z.ZodArray<z.ZodObject<{
|
|
288
|
+
name: z.ZodString;
|
|
289
|
+
value: z.ZodNumber;
|
|
290
|
+
status: z.ZodEnum<{
|
|
291
|
+
red: "red";
|
|
292
|
+
green: "green";
|
|
293
|
+
yellow: "yellow";
|
|
294
|
+
}>;
|
|
295
|
+
}, z.core.$strip>>;
|
|
296
|
+
featureDetection: z.ZodObject<{
|
|
297
|
+
featureCount: z.ZodNumber;
|
|
298
|
+
clusters: z.ZodArray<z.ZodObject<{
|
|
299
|
+
keyword: z.ZodString;
|
|
300
|
+
acIds: z.ZodArray<z.ZodString>;
|
|
301
|
+
count: z.ZodNumber;
|
|
302
|
+
}, z.core.$strip>>;
|
|
303
|
+
multipleVerbs: z.ZodBoolean;
|
|
304
|
+
titleVerbs: z.ZodArray<z.ZodString>;
|
|
305
|
+
directorySpread: z.ZodNumber;
|
|
306
|
+
directories: z.ZodArray<z.ZodString>;
|
|
307
|
+
}, z.core.$strip>;
|
|
308
|
+
nonGoals: z.ZodObject<{
|
|
309
|
+
items: z.ZodArray<z.ZodString>;
|
|
310
|
+
found: z.ZodBoolean;
|
|
311
|
+
warning: z.ZodOptional<z.ZodString>;
|
|
312
|
+
}, z.core.$strip>;
|
|
313
|
+
recommendation: z.ZodString;
|
|
314
|
+
}, z.core.$strip>>;
|
|
249
315
|
sessionId: z.ZodOptional<z.ZodString>;
|
|
250
316
|
lastActivity: z.ZodString;
|
|
251
317
|
createdAt: z.ZodString;
|
|
@@ -334,6 +400,43 @@ export declare const WorkflowStateSchema: z.ZodObject<{
|
|
|
334
400
|
blocked: z.ZodNumber;
|
|
335
401
|
}, z.core.$strip>;
|
|
336
402
|
}, z.core.$strip>>;
|
|
403
|
+
scopeAssessment: z.ZodOptional<z.ZodObject<{
|
|
404
|
+
assessedAt: z.ZodString;
|
|
405
|
+
skipped: z.ZodBoolean;
|
|
406
|
+
skipReason: z.ZodOptional<z.ZodString>;
|
|
407
|
+
verdict: z.ZodEnum<{
|
|
408
|
+
SCOPE_OK: "SCOPE_OK";
|
|
409
|
+
SCOPE_WARNING: "SCOPE_WARNING";
|
|
410
|
+
SCOPE_SPLIT_RECOMMENDED: "SCOPE_SPLIT_RECOMMENDED";
|
|
411
|
+
}>;
|
|
412
|
+
metrics: z.ZodArray<z.ZodObject<{
|
|
413
|
+
name: z.ZodString;
|
|
414
|
+
value: z.ZodNumber;
|
|
415
|
+
status: z.ZodEnum<{
|
|
416
|
+
red: "red";
|
|
417
|
+
green: "green";
|
|
418
|
+
yellow: "yellow";
|
|
419
|
+
}>;
|
|
420
|
+
}, z.core.$strip>>;
|
|
421
|
+
featureDetection: z.ZodObject<{
|
|
422
|
+
featureCount: z.ZodNumber;
|
|
423
|
+
clusters: z.ZodArray<z.ZodObject<{
|
|
424
|
+
keyword: z.ZodString;
|
|
425
|
+
acIds: z.ZodArray<z.ZodString>;
|
|
426
|
+
count: z.ZodNumber;
|
|
427
|
+
}, z.core.$strip>>;
|
|
428
|
+
multipleVerbs: z.ZodBoolean;
|
|
429
|
+
titleVerbs: z.ZodArray<z.ZodString>;
|
|
430
|
+
directorySpread: z.ZodNumber;
|
|
431
|
+
directories: z.ZodArray<z.ZodString>;
|
|
432
|
+
}, z.core.$strip>;
|
|
433
|
+
nonGoals: z.ZodObject<{
|
|
434
|
+
items: z.ZodArray<z.ZodString>;
|
|
435
|
+
found: z.ZodBoolean;
|
|
436
|
+
warning: z.ZodOptional<z.ZodString>;
|
|
437
|
+
}, z.core.$strip>;
|
|
438
|
+
recommendation: z.ZodString;
|
|
439
|
+
}, z.core.$strip>>;
|
|
337
440
|
sessionId: z.ZodOptional<z.ZodString>;
|
|
338
441
|
lastActivity: z.ZodString;
|
|
339
442
|
createdAt: z.ZodString;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
import { z } from "zod";
|
|
20
|
+
import { ScopeAssessmentSchema } from "../scope/types.js";
|
|
20
21
|
/**
|
|
21
22
|
* Workflow phases in order of execution
|
|
22
23
|
*/
|
|
@@ -67,6 +68,23 @@ export const PhaseSchema = z.enum([
|
|
|
67
68
|
"loop",
|
|
68
69
|
"merger",
|
|
69
70
|
]);
|
|
71
|
+
/**
|
|
72
|
+
* Phase marker stored in GitHub issue comments for cross-session detection.
|
|
73
|
+
*
|
|
74
|
+
* Embedded as HTML comments: `<!-- SEQUANT_PHASE: {...} -->`
|
|
75
|
+
*/
|
|
76
|
+
export const PhaseMarkerSchema = z.object({
|
|
77
|
+
/** The workflow phase */
|
|
78
|
+
phase: PhaseSchema,
|
|
79
|
+
/** Phase completion status */
|
|
80
|
+
status: PhaseStatusSchema,
|
|
81
|
+
/** ISO 8601 timestamp */
|
|
82
|
+
timestamp: z.string().datetime(),
|
|
83
|
+
/** PR number if created during this phase */
|
|
84
|
+
pr: z.number().int().positive().optional(),
|
|
85
|
+
/** Error message if phase failed */
|
|
86
|
+
error: z.string().optional(),
|
|
87
|
+
});
|
|
70
88
|
/**
|
|
71
89
|
* Individual phase state within an issue
|
|
72
90
|
*/
|
|
@@ -178,6 +196,8 @@ export const IssueStateSchema = z.object({
|
|
|
178
196
|
loop: LoopStateSchema.optional(),
|
|
179
197
|
/** Acceptance criteria tracking (if extracted by /spec) */
|
|
180
198
|
acceptanceCriteria: AcceptanceCriteriaSchema.optional(),
|
|
199
|
+
/** Scope assessment result (if performed by /spec) */
|
|
200
|
+
scopeAssessment: ScopeAssessmentSchema.optional(),
|
|
181
201
|
/** Claude session ID (for resume) */
|
|
182
202
|
sessionId: z.string().optional(),
|
|
183
203
|
/** Most recent activity timestamp */
|
package/package.json
CHANGED
|
@@ -37,7 +37,7 @@ allowed-tools:
|
|
|
37
37
|
- mcp__context7__* # Library documentation lookup - falls back to web search if unavailable
|
|
38
38
|
- mcp__sequential-thinking__* # Complex reasoning - falls back to standard analysis if unavailable
|
|
39
39
|
# Task management
|
|
40
|
-
- Task
|
|
40
|
+
- Task(general-purpose)
|
|
41
41
|
- TodoWrite
|
|
42
42
|
---
|
|
43
43
|
|
|
@@ -56,6 +56,55 @@ When invoked as `/exec`, your job is to:
|
|
|
56
56
|
5. Iterate until the AC appear satisfied or clear blockers are reached.
|
|
57
57
|
6. Draft a progress update for the GitHub issue.
|
|
58
58
|
|
|
59
|
+
## Phase Detection (Smart Resumption)
|
|
60
|
+
|
|
61
|
+
**Before executing**, check if this phase has already been completed or if prerequisites are met:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Check for existing phase markers
|
|
65
|
+
phase_data=$(gh issue view <issue-number> --json comments --jq '[.comments[].body]' | \
|
|
66
|
+
grep -o '{[^}]*}' | grep '"phase"' | tail -1)
|
|
67
|
+
|
|
68
|
+
if [[ -n "$phase_data" ]]; then
|
|
69
|
+
phase=$(echo "$phase_data" | jq -r '.phase')
|
|
70
|
+
status=$(echo "$phase_data" | jq -r '.status')
|
|
71
|
+
|
|
72
|
+
# Skip if exec is already completed
|
|
73
|
+
if [[ "$phase" == "exec" && "$status" == "completed" ]]; then
|
|
74
|
+
echo "⏭️ Exec phase already completed. Skipping."
|
|
75
|
+
# Exit early — no work needed
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Resume if exec previously failed
|
|
79
|
+
if [[ "$phase" == "exec" && "$status" == "failed" ]]; then
|
|
80
|
+
echo "🔄 Exec phase previously failed. Resuming from failure point."
|
|
81
|
+
# Continue execution — will retry the implementation
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Behavior:**
|
|
87
|
+
- If `exec:completed` → Skip with message
|
|
88
|
+
- If `exec:failed` → Resume (retry implementation)
|
|
89
|
+
- If `spec:completed` (no exec marker) → Normal execution
|
|
90
|
+
- If no markers found → Normal execution (fresh start)
|
|
91
|
+
- If detection fails (API error) → Fall through to normal execution
|
|
92
|
+
|
|
93
|
+
**Phase Marker Emission:**
|
|
94
|
+
|
|
95
|
+
When posting the progress update comment to GitHub, append a phase marker at the end:
|
|
96
|
+
|
|
97
|
+
```markdown
|
|
98
|
+
<!-- SEQUANT_PHASE: {"phase":"exec","status":"completed","timestamp":"<ISO-8601>","pr":<PR_NUMBER>} -->
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
If exec fails, emit a failure marker:
|
|
102
|
+
```markdown
|
|
103
|
+
<!-- SEQUANT_PHASE: {"phase":"exec","status":"failed","timestamp":"<ISO-8601>","error":"<error message>"} -->
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Include this marker in every `gh issue comment` that represents phase completion or failure.
|
|
107
|
+
|
|
59
108
|
## Behavior
|
|
60
109
|
|
|
61
110
|
Invocation:
|
|
@@ -13,7 +13,7 @@ allowed-tools:
|
|
|
13
13
|
- Grep
|
|
14
14
|
- Bash
|
|
15
15
|
- TodoWrite
|
|
16
|
-
-
|
|
16
|
+
- Skill # For invoking child skills (/spec, /exec, /test, /qa)
|
|
17
17
|
# Optional MCP tools (enhanced functionality if available)
|
|
18
18
|
- mcp__chrome-devtools__* # Browser testing - falls back to manual checklist if unavailable
|
|
19
19
|
- mcp__sequential-thinking__* # Complex reasoning - falls back to standard analysis if unavailable
|
|
@@ -90,6 +90,52 @@ When invoked as `/fullsolve <issue-number>`, execute the complete issue resoluti
|
|
|
90
90
|
/fullsolve 218 --max-iterations 5 # Override max fix iterations
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
## Phase Detection (Smart Resumption)
|
|
94
|
+
|
|
95
|
+
**Before starting any phase**, detect the current workflow state from GitHub issue comments to enable smart resumption:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Get all phase markers from issue comments
|
|
99
|
+
comments_json=$(gh issue view <issue-number> --json comments --jq '[.comments[].body]')
|
|
100
|
+
markers=$(echo "$comments_json" | grep -o '{[^}]*}' | grep '"phase"')
|
|
101
|
+
|
|
102
|
+
if [[ -n "$markers" ]]; then
|
|
103
|
+
echo "Phase markers detected:"
|
|
104
|
+
echo "$markers" | jq -r '" \(.phase): \(.status)"'
|
|
105
|
+
|
|
106
|
+
# Determine resume point
|
|
107
|
+
latest_completed=$(echo "$markers" | jq -r 'select(.status == "completed") | .phase' | tail -1)
|
|
108
|
+
latest_failed=$(echo "$markers" | jq -r 'select(.status == "failed") | .phase' | tail -1)
|
|
109
|
+
|
|
110
|
+
echo "Latest completed: ${latest_completed:-none}"
|
|
111
|
+
echo "Latest failed: ${latest_failed:-none}"
|
|
112
|
+
fi
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Resume Logic:**
|
|
116
|
+
|
|
117
|
+
| Detected State | Action |
|
|
118
|
+
|---------------|--------|
|
|
119
|
+
| No markers | Start from Phase 1 (spec) — fresh start |
|
|
120
|
+
| `spec:completed` | Skip to Phase 2 (exec) |
|
|
121
|
+
| `exec:completed` | Skip to Phase 3 (test) or Phase 4 (qa) |
|
|
122
|
+
| `exec:failed` | Resume at Phase 2 (exec) — retry |
|
|
123
|
+
| `test:completed` | Skip to Phase 4 (qa) |
|
|
124
|
+
| `qa:completed` | Skip to Phase 5 (PR) |
|
|
125
|
+
| `qa:failed` | Resume at Phase 4 (qa) — retry with /loop |
|
|
126
|
+
| All completed | Skip to PR creation (if no PR exists) |
|
|
127
|
+
|
|
128
|
+
**Backward Compatibility:**
|
|
129
|
+
- Issues without markers → treat as fresh start (no phase detection)
|
|
130
|
+
- If detection fails (API error) → fall through to standard Phase 0 checks
|
|
131
|
+
|
|
132
|
+
**Phase Marker Emission:**
|
|
133
|
+
|
|
134
|
+
When posting progress comments after each phase, append the appropriate marker:
|
|
135
|
+
```markdown
|
|
136
|
+
<!-- SEQUANT_PHASE: {"phase":"<phase>","status":"<completed|failed>","timestamp":"<ISO-8601>"} -->
|
|
137
|
+
```
|
|
138
|
+
|
|
93
139
|
## Phase 0: Pre-flight Checks
|
|
94
140
|
|
|
95
141
|
**CRITICAL after context restoration:** Before starting any work, verify the current git state to avoid duplicate work.
|
|
@@ -19,7 +19,7 @@ allowed-tools:
|
|
|
19
19
|
- Bash(semgrep:*)
|
|
20
20
|
- Bash(npx semgrep:*)
|
|
21
21
|
- Bash(npx tsx scripts/semgrep-scan.ts:*)
|
|
22
|
-
- Task
|
|
22
|
+
- Task(general-purpose)
|
|
23
23
|
- AgentOutputTool
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -37,6 +37,52 @@ When invoked as `/qa`, your job is to:
|
|
|
37
37
|
4. Assess whether the change is "A+ status" or needs more work.
|
|
38
38
|
5. Draft a GitHub review/QA comment summarizing findings and recommendations.
|
|
39
39
|
|
|
40
|
+
## Phase Detection (Smart Resumption)
|
|
41
|
+
|
|
42
|
+
**Before executing**, check if the exec phase has been completed (prerequisite for QA):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Check for existing phase markers
|
|
46
|
+
comments_json=$(gh issue view <issue-number> --json comments --jq '[.comments[].body]')
|
|
47
|
+
exec_completed=$(echo "$comments_json" | \
|
|
48
|
+
grep -o '{[^}]*}' | grep '"phase"' | \
|
|
49
|
+
jq -r 'select(.phase == "exec" and .status == "completed")' 2>/dev/null)
|
|
50
|
+
|
|
51
|
+
if [[ -z "$exec_completed" ]]; then
|
|
52
|
+
# Check if any exec marker exists at all
|
|
53
|
+
exec_any=$(echo "$comments_json" | \
|
|
54
|
+
grep -o '{[^}]*}' | grep '"phase"' | \
|
|
55
|
+
jq -r 'select(.phase == "exec")' 2>/dev/null)
|
|
56
|
+
|
|
57
|
+
if [[ -n "$exec_any" ]]; then
|
|
58
|
+
echo "⚠️ Exec phase not completed (status: $(echo "$exec_any" | jq -r '.status')). Run /exec first."
|
|
59
|
+
else
|
|
60
|
+
echo "ℹ️ No phase markers found — proceeding with QA (may be a fresh issue or legacy workflow)."
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Behavior:**
|
|
66
|
+
- If `exec:completed` marker found → Normal QA execution
|
|
67
|
+
- If `exec:failed` or `exec:in_progress` → Warn "Exec not complete, run /exec first" (but don't block — QA may still be useful for partial review)
|
|
68
|
+
- If no markers found → Normal execution (backward compatible)
|
|
69
|
+
- If detection fails (API error) → Fall through to normal execution
|
|
70
|
+
|
|
71
|
+
**Phase Marker Emission:**
|
|
72
|
+
|
|
73
|
+
When posting the QA review comment to GitHub, append a phase marker at the end:
|
|
74
|
+
|
|
75
|
+
```markdown
|
|
76
|
+
<!-- SEQUANT_PHASE: {"phase":"qa","status":"completed","timestamp":"<ISO-8601>"} -->
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
If QA determines AC_NOT_MET, emit:
|
|
80
|
+
```markdown
|
|
81
|
+
<!-- SEQUANT_PHASE: {"phase":"qa","status":"failed","timestamp":"<ISO-8601>","error":"AC_NOT_MET"} -->
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Include this marker in every `gh issue comment` that represents QA completion.
|
|
85
|
+
|
|
40
86
|
## Behavior
|
|
41
87
|
|
|
42
88
|
Invocation:
|
|
@@ -13,7 +13,7 @@ allowed-tools:
|
|
|
13
13
|
- Bash(gh label:*)
|
|
14
14
|
- Bash(git worktree:*)
|
|
15
15
|
- Bash(git -C:*)
|
|
16
|
-
- Task
|
|
16
|
+
- Task(Explore)
|
|
17
17
|
- AgentOutputTool
|
|
18
18
|
---
|
|
19
19
|
|
|
@@ -30,6 +30,44 @@ When invoked as `/spec`, your job is to:
|
|
|
30
30
|
3. Identify ambiguities, gaps, or risks.
|
|
31
31
|
4. Draft a GitHub issue comment summarizing AC + the agreed plan.
|
|
32
32
|
|
|
33
|
+
## Phase Detection (Smart Resumption)
|
|
34
|
+
|
|
35
|
+
**Before executing**, check if this phase has already been completed by reading phase markers from issue comments:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Check for existing phase markers
|
|
39
|
+
phase_data=$(gh issue view <issue-number> --json comments --jq '[.comments[].body]' | \
|
|
40
|
+
grep -o '{[^}]*}' | grep '"phase"' | tail -1)
|
|
41
|
+
|
|
42
|
+
if [[ -n "$phase_data" ]]; then
|
|
43
|
+
phase=$(echo "$phase_data" | jq -r '.phase')
|
|
44
|
+
status=$(echo "$phase_data" | jq -r '.status')
|
|
45
|
+
|
|
46
|
+
# Skip if spec is already completed or a later phase is completed
|
|
47
|
+
if [[ "$phase" == "spec" && "$status" == "completed" ]] || \
|
|
48
|
+
[[ "$phase" == "exec" || "$phase" == "test" || "$phase" == "qa" ]]; then
|
|
49
|
+
echo "⏭️ Spec phase already completed (detected: $phase:$status). Skipping."
|
|
50
|
+
# Exit early — no work needed
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Behavior:**
|
|
56
|
+
- If `spec:completed` or a later phase is detected → Skip with message
|
|
57
|
+
- If `spec:failed` → Re-run spec (retry)
|
|
58
|
+
- If no markers found → Normal execution (fresh start)
|
|
59
|
+
- If detection fails (API error) → Fall through to normal execution
|
|
60
|
+
|
|
61
|
+
**Phase Marker Emission:**
|
|
62
|
+
|
|
63
|
+
When posting the spec plan comment to GitHub, append a phase marker at the end:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
<!-- SEQUANT_PHASE: {"phase":"spec","status":"completed","timestamp":"<ISO-8601>"} -->
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Include this marker in every `gh issue comment` that represents phase completion.
|
|
70
|
+
|
|
33
71
|
## Behavior
|
|
34
72
|
|
|
35
73
|
When called like `/spec 123`:
|