musubi-sdd 6.2.2 → 6.3.0
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.ja.md +3 -3
- package/README.md +3 -3
- package/bin/musubi-dashboard.js +22 -13
- package/bin/musubi-design.js +3 -3
- package/bin/musubi-gaps.js +9 -9
- package/bin/musubi-init.js +14 -1310
- package/bin/musubi-requirements.js +1 -1
- package/bin/musubi-tasks.js +5 -5
- package/bin/musubi-trace.js +23 -23
- package/bin/musubi-upgrade.js +7 -2
- package/bin/musubi.js +1 -1
- package/package.json +2 -2
- package/src/analyzers/gap-detector.js +3 -3
- package/src/analyzers/traceability.js +17 -17
- package/src/cli/dashboard-cli.js +54 -60
- package/src/cli/init-generators.js +464 -0
- package/src/cli/init-helpers.js +884 -0
- package/src/constitutional/checker.js +67 -65
- package/src/constitutional/ci-reporter.js +50 -43
- package/src/constitutional/index.js +2 -2
- package/src/constitutional/phase-minus-one.js +22 -25
- package/src/constitutional/steering-sync.js +28 -39
- package/src/dashboard/index.js +2 -2
- package/src/dashboard/sprint-planner.js +17 -19
- package/src/dashboard/sprint-reporter.js +46 -37
- package/src/dashboard/transition-recorder.js +12 -18
- package/src/dashboard/workflow-dashboard.js +27 -38
- package/src/enterprise/error-recovery.js +109 -49
- package/src/enterprise/experiment-report.js +62 -36
- package/src/enterprise/index.js +5 -5
- package/src/enterprise/rollback-manager.js +28 -29
- package/src/enterprise/tech-article.js +41 -35
- package/src/generators/design.js +3 -3
- package/src/generators/requirements.js +5 -3
- package/src/generators/tasks.js +2 -2
- package/src/integrations/platforms.js +1 -1
- package/src/templates/agents/claude-code/CLAUDE.md +1 -1
- package/src/templates/agents/claude-code/skills/design-reviewer/SKILL.md +132 -113
- package/src/templates/agents/claude-code/skills/requirements-reviewer/SKILL.md +85 -56
- package/src/templates/agents/codex/AGENTS.md +2 -2
- package/src/templates/agents/cursor/AGENTS.md +2 -2
- package/src/templates/agents/gemini-cli/GEMINI.md +2 -2
- package/src/templates/agents/github-copilot/AGENTS.md +2 -2
- package/src/templates/agents/github-copilot/commands/sdd-requirements.prompt.md +23 -4
- package/src/templates/agents/qwen-code/QWEN.md +2 -2
- package/src/templates/agents/shared/AGENTS.md +1 -1
- package/src/templates/agents/windsurf/AGENTS.md +2 -2
- package/src/templates/skills/browser-agent.md +1 -1
- package/src/traceability/extractor.js +21 -20
- package/src/traceability/gap-detector.js +19 -17
- package/src/traceability/index.js +2 -2
- package/src/traceability/matrix-storage.js +20 -22
- package/src/validators/constitution.js +5 -2
- package/src/validators/critic-system.js +6 -6
- package/src/validators/traceability-validator.js +3 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SprintReporter Implementation
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Generates sprint completion reports.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Requirement: IMP-6.2-003-04
|
|
7
7
|
* Design: Section 4.4
|
|
8
8
|
*/
|
|
@@ -14,12 +14,12 @@ const path = require('path');
|
|
|
14
14
|
* Default configuration
|
|
15
15
|
*/
|
|
16
16
|
const DEFAULT_CONFIG = {
|
|
17
|
-
storageDir: 'storage/reports'
|
|
17
|
+
storageDir: 'storage/reports',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* SprintReporter
|
|
22
|
-
*
|
|
22
|
+
*
|
|
23
23
|
* Generates and manages sprint reports.
|
|
24
24
|
*/
|
|
25
25
|
class SprintReporter {
|
|
@@ -46,12 +46,12 @@ class SprintReporter {
|
|
|
46
46
|
start: sprint.startDate,
|
|
47
47
|
end: sprint.endDate,
|
|
48
48
|
startedAt: sprint.startedAt,
|
|
49
|
-
completedAt: sprint.completedAt
|
|
49
|
+
completedAt: sprint.completedAt,
|
|
50
50
|
},
|
|
51
51
|
metrics: this.calculateMetrics(sprint),
|
|
52
52
|
taskSummary: this.summarizeTasks(sprint),
|
|
53
53
|
velocityAnalysis: this.analyzeVelocity(sprint),
|
|
54
|
-
recommendations: this.generateRecommendations(sprint)
|
|
54
|
+
recommendations: this.generateRecommendations(sprint),
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
await this.saveReport(report);
|
|
@@ -81,18 +81,14 @@ class SprintReporter {
|
|
|
81
81
|
totalPoints,
|
|
82
82
|
completedPoints,
|
|
83
83
|
remainingPoints: totalPoints - completedPoints,
|
|
84
|
-
completionRate:
|
|
85
|
-
? Math.round((completedTasks.length / tasks.length) * 100)
|
|
86
|
-
|
|
87
|
-
pointsCompletionRate: totalPoints > 0
|
|
88
|
-
? Math.round((completedPoints / totalPoints) * 100)
|
|
89
|
-
: 0,
|
|
84
|
+
completionRate:
|
|
85
|
+
tasks.length > 0 ? Math.round((completedTasks.length / tasks.length) * 100) : 0,
|
|
86
|
+
pointsCompletionRate: totalPoints > 0 ? Math.round((completedPoints / totalPoints) * 100) : 0,
|
|
90
87
|
plannedVelocity,
|
|
91
88
|
actualVelocity,
|
|
92
89
|
velocityDiff,
|
|
93
|
-
velocityAccuracy:
|
|
94
|
-
? Math.round((actualVelocity / plannedVelocity) * 100)
|
|
95
|
-
: 0
|
|
90
|
+
velocityAccuracy:
|
|
91
|
+
plannedVelocity > 0 ? Math.round((actualVelocity / plannedVelocity) * 100) : 0,
|
|
96
92
|
};
|
|
97
93
|
}
|
|
98
94
|
|
|
@@ -107,34 +103,34 @@ class SprintReporter {
|
|
|
107
103
|
const byStatus = {
|
|
108
104
|
todo: tasks.filter(t => t.status === 'todo'),
|
|
109
105
|
inProgress: tasks.filter(t => t.status === 'in-progress'),
|
|
110
|
-
done: tasks.filter(t => t.status === 'done')
|
|
106
|
+
done: tasks.filter(t => t.status === 'done'),
|
|
111
107
|
};
|
|
112
108
|
|
|
113
109
|
const byPriority = {
|
|
114
110
|
critical: tasks.filter(t => t.priority === 'critical'),
|
|
115
111
|
high: tasks.filter(t => t.priority === 'high'),
|
|
116
112
|
medium: tasks.filter(t => t.priority === 'medium'),
|
|
117
|
-
low: tasks.filter(t => t.priority === 'low')
|
|
113
|
+
low: tasks.filter(t => t.priority === 'low'),
|
|
118
114
|
};
|
|
119
115
|
|
|
120
116
|
const completedByPriority = {
|
|
121
117
|
critical: byPriority.critical.filter(t => t.status === 'done').length,
|
|
122
118
|
high: byPriority.high.filter(t => t.status === 'done').length,
|
|
123
119
|
medium: byPriority.medium.filter(t => t.status === 'done').length,
|
|
124
|
-
low: byPriority.low.filter(t => t.status === 'done').length
|
|
120
|
+
low: byPriority.low.filter(t => t.status === 'done').length,
|
|
125
121
|
};
|
|
126
122
|
|
|
127
123
|
return {
|
|
128
124
|
byStatus: {
|
|
129
125
|
todo: byStatus.todo.length,
|
|
130
126
|
inProgress: byStatus.inProgress.length,
|
|
131
|
-
done: byStatus.done.length
|
|
127
|
+
done: byStatus.done.length,
|
|
132
128
|
},
|
|
133
129
|
byPriority: {
|
|
134
130
|
critical: byPriority.critical.length,
|
|
135
131
|
high: byPriority.high.length,
|
|
136
132
|
medium: byPriority.medium.length,
|
|
137
|
-
low: byPriority.low.length
|
|
133
|
+
low: byPriority.low.length,
|
|
138
134
|
},
|
|
139
135
|
completedByPriority,
|
|
140
136
|
incompleteTasks: [...byStatus.todo, ...byStatus.inProgress].map(t => ({
|
|
@@ -142,8 +138,8 @@ class SprintReporter {
|
|
|
142
138
|
title: t.title,
|
|
143
139
|
priority: t.priority,
|
|
144
140
|
storyPoints: t.storyPoints,
|
|
145
|
-
status: t.status
|
|
146
|
-
}))
|
|
141
|
+
status: t.status,
|
|
142
|
+
})),
|
|
147
143
|
};
|
|
148
144
|
}
|
|
149
145
|
|
|
@@ -154,7 +150,7 @@ class SprintReporter {
|
|
|
154
150
|
*/
|
|
155
151
|
analyzeVelocity(sprint) {
|
|
156
152
|
const metrics = this.calculateMetrics(sprint);
|
|
157
|
-
|
|
153
|
+
|
|
158
154
|
let status;
|
|
159
155
|
if (metrics.velocityAccuracy >= 90 && metrics.velocityAccuracy <= 110) {
|
|
160
156
|
status = 'on-target';
|
|
@@ -171,7 +167,7 @@ class SprintReporter {
|
|
|
171
167
|
actual: metrics.actualVelocity,
|
|
172
168
|
difference: metrics.velocityDiff,
|
|
173
169
|
accuracy: metrics.velocityAccuracy,
|
|
174
|
-
status
|
|
170
|
+
status,
|
|
175
171
|
};
|
|
176
172
|
}
|
|
177
173
|
|
|
@@ -190,23 +186,26 @@ class SprintReporter {
|
|
|
190
186
|
recommendations.push({
|
|
191
187
|
type: 'velocity',
|
|
192
188
|
severity: 'high',
|
|
193
|
-
message:
|
|
189
|
+
message:
|
|
190
|
+
'スプリントの実績ベロシティが計画の70%未満でした。次のスプリントでは計画ベロシティを下げることを検討してください。',
|
|
194
191
|
});
|
|
195
192
|
} else if (metrics.velocityAccuracy > 130) {
|
|
196
193
|
recommendations.push({
|
|
197
194
|
type: 'velocity',
|
|
198
195
|
severity: 'medium',
|
|
199
|
-
message:
|
|
196
|
+
message:
|
|
197
|
+
'計画以上のベロシティを達成しました。次のスプリントでは計画ベロシティを上げることを検討してください。',
|
|
200
198
|
});
|
|
201
199
|
}
|
|
202
200
|
|
|
203
201
|
// Incomplete critical tasks
|
|
204
|
-
const incompleteCritical =
|
|
202
|
+
const incompleteCritical =
|
|
203
|
+
taskSummary.byPriority.critical - taskSummary.completedByPriority.critical;
|
|
205
204
|
if (incompleteCritical > 0) {
|
|
206
205
|
recommendations.push({
|
|
207
206
|
type: 'priority',
|
|
208
207
|
severity: 'critical',
|
|
209
|
-
message: `${incompleteCritical}
|
|
208
|
+
message: `${incompleteCritical}件のクリティカルタスクが未完了です。次のスプリントで優先的に対応してください。`,
|
|
210
209
|
});
|
|
211
210
|
}
|
|
212
211
|
|
|
@@ -215,7 +214,8 @@ class SprintReporter {
|
|
|
215
214
|
recommendations.push({
|
|
216
215
|
type: 'planning',
|
|
217
216
|
severity: 'high',
|
|
218
|
-
message:
|
|
217
|
+
message:
|
|
218
|
+
'タスク完了率が50%未満です。タスクの見積もりや優先順位付けの改善を検討してください。',
|
|
219
219
|
});
|
|
220
220
|
}
|
|
221
221
|
|
|
@@ -224,7 +224,8 @@ class SprintReporter {
|
|
|
224
224
|
recommendations.push({
|
|
225
225
|
type: 'wip',
|
|
226
226
|
severity: 'medium',
|
|
227
|
-
message:
|
|
227
|
+
message:
|
|
228
|
+
'進行中のタスクが多すぎます。WIP制限を設けてフォーカスを高めることを検討してください。',
|
|
228
229
|
});
|
|
229
230
|
}
|
|
230
231
|
|
|
@@ -271,7 +272,7 @@ class SprintReporter {
|
|
|
271
272
|
'on-target': '✅',
|
|
272
273
|
'over-performing': '🚀',
|
|
273
274
|
'slightly-under': '⚠️',
|
|
274
|
-
'under-performing': '❌'
|
|
275
|
+
'under-performing': '❌',
|
|
275
276
|
};
|
|
276
277
|
lines.push(`Status: ${statusEmoji[va.status] || '❓'} **${va.status}**`);
|
|
277
278
|
lines.push('');
|
|
@@ -286,10 +287,18 @@ class SprintReporter {
|
|
|
286
287
|
lines.push('');
|
|
287
288
|
|
|
288
289
|
lines.push('### By Priority');
|
|
289
|
-
lines.push(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
lines.push(
|
|
290
|
+
lines.push(
|
|
291
|
+
`- 🔴 Critical: ${report.taskSummary.completedByPriority.critical}/${report.taskSummary.byPriority.critical}`
|
|
292
|
+
);
|
|
293
|
+
lines.push(
|
|
294
|
+
`- 🟠 High: ${report.taskSummary.completedByPriority.high}/${report.taskSummary.byPriority.high}`
|
|
295
|
+
);
|
|
296
|
+
lines.push(
|
|
297
|
+
`- 🟡 Medium: ${report.taskSummary.completedByPriority.medium}/${report.taskSummary.byPriority.medium}`
|
|
298
|
+
);
|
|
299
|
+
lines.push(
|
|
300
|
+
`- 🟢 Low: ${report.taskSummary.completedByPriority.low}/${report.taskSummary.byPriority.low}`
|
|
301
|
+
);
|
|
293
302
|
lines.push('');
|
|
294
303
|
|
|
295
304
|
// Incomplete Tasks
|
|
@@ -310,7 +319,7 @@ class SprintReporter {
|
|
|
310
319
|
critical: '🔴',
|
|
311
320
|
high: '🟠',
|
|
312
321
|
medium: '🟡',
|
|
313
|
-
low: '🟢'
|
|
322
|
+
low: '🟢',
|
|
314
323
|
};
|
|
315
324
|
for (const rec of report.recommendations) {
|
|
316
325
|
lines.push(`${severityEmoji[rec.severity] || '❓'} **${rec.type}**: ${rec.message}`);
|
|
@@ -327,7 +336,7 @@ class SprintReporter {
|
|
|
327
336
|
*/
|
|
328
337
|
async saveReport(report) {
|
|
329
338
|
await this.ensureStorageDir();
|
|
330
|
-
|
|
339
|
+
|
|
331
340
|
const filePath = path.join(this.config.storageDir, `${report.id}.json`);
|
|
332
341
|
await fs.writeFile(filePath, JSON.stringify(report, null, 2), 'utf-8');
|
|
333
342
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TransitionRecorder Implementation
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Records stage transitions with timestamps and reviewers.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Requirement: IMP-6.2-003-02
|
|
7
7
|
* Design: Section 4.2
|
|
8
8
|
*/
|
|
@@ -14,12 +14,12 @@ const path = require('path');
|
|
|
14
14
|
* Default configuration
|
|
15
15
|
*/
|
|
16
16
|
const DEFAULT_CONFIG = {
|
|
17
|
-
storageDir: 'storage/transitions'
|
|
17
|
+
storageDir: 'storage/transitions',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* TransitionRecorder
|
|
22
|
-
*
|
|
22
|
+
*
|
|
23
23
|
* Records and manages workflow stage transitions.
|
|
24
24
|
*/
|
|
25
25
|
class TransitionRecorder {
|
|
@@ -37,10 +37,10 @@ class TransitionRecorder {
|
|
|
37
37
|
* @returns {Promise<Object>} Created transition record
|
|
38
38
|
*/
|
|
39
39
|
async recordTransition(featureId, transition) {
|
|
40
|
-
const history = await this.getHistory(featureId) || {
|
|
40
|
+
const history = (await this.getHistory(featureId)) || {
|
|
41
41
|
featureId,
|
|
42
42
|
transitions: [],
|
|
43
|
-
createdAt: new Date().toISOString()
|
|
43
|
+
createdAt: new Date().toISOString(),
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
const record = {
|
|
@@ -52,7 +52,7 @@ class TransitionRecorder {
|
|
|
52
52
|
reviewResult: transition.reviewResult || null,
|
|
53
53
|
artifacts: transition.artifacts || [],
|
|
54
54
|
notes: transition.notes || null,
|
|
55
|
-
timestamp: new Date().toISOString()
|
|
55
|
+
timestamp: new Date().toISOString(),
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
history.transitions.push(record);
|
|
@@ -70,10 +70,7 @@ class TransitionRecorder {
|
|
|
70
70
|
*/
|
|
71
71
|
async getHistory(featureId) {
|
|
72
72
|
try {
|
|
73
|
-
const filePath = path.join(
|
|
74
|
-
this.config.storageDir,
|
|
75
|
-
`${featureId}-transitions.json`
|
|
76
|
-
);
|
|
73
|
+
const filePath = path.join(this.config.storageDir, `${featureId}-transitions.json`);
|
|
77
74
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
78
75
|
return JSON.parse(content);
|
|
79
76
|
} catch {
|
|
@@ -146,7 +143,7 @@ class TransitionRecorder {
|
|
|
146
143
|
successfulTransitions: 0,
|
|
147
144
|
failedTransitions: 0,
|
|
148
145
|
averageTime: 0,
|
|
149
|
-
stageTransitions: {}
|
|
146
|
+
stageTransitions: {},
|
|
150
147
|
};
|
|
151
148
|
}
|
|
152
149
|
|
|
@@ -174,7 +171,7 @@ class TransitionRecorder {
|
|
|
174
171
|
successfulTransitions: successful,
|
|
175
172
|
failedTransitions: failed,
|
|
176
173
|
averageTime,
|
|
177
|
-
stageTransitions
|
|
174
|
+
stageTransitions,
|
|
178
175
|
};
|
|
179
176
|
}
|
|
180
177
|
|
|
@@ -185,11 +182,8 @@ class TransitionRecorder {
|
|
|
185
182
|
*/
|
|
186
183
|
async saveHistory(featureId, history) {
|
|
187
184
|
await this.ensureStorageDir();
|
|
188
|
-
|
|
189
|
-
const filePath = path.join(
|
|
190
|
-
this.config.storageDir,
|
|
191
|
-
`${featureId}-transitions.json`
|
|
192
|
-
);
|
|
185
|
+
|
|
186
|
+
const filePath = path.join(this.config.storageDir, `${featureId}-transitions.json`);
|
|
193
187
|
|
|
194
188
|
await fs.writeFile(filePath, JSON.stringify(history, null, 2), 'utf-8');
|
|
195
189
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WorkflowDashboard Implementation
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Manages workflow state and progress visualization.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Requirement: IMP-6.2-003-01
|
|
7
7
|
* Design: Section 4.1
|
|
8
8
|
*/
|
|
@@ -13,13 +13,7 @@ const path = require('path');
|
|
|
13
13
|
/**
|
|
14
14
|
* Valid workflow stages
|
|
15
15
|
*/
|
|
16
|
-
const WORKFLOW_STAGES = [
|
|
17
|
-
'steering',
|
|
18
|
-
'requirements',
|
|
19
|
-
'design',
|
|
20
|
-
'implementation',
|
|
21
|
-
'validation'
|
|
22
|
-
];
|
|
16
|
+
const WORKFLOW_STAGES = ['steering', 'requirements', 'design', 'implementation', 'validation'];
|
|
23
17
|
|
|
24
18
|
/**
|
|
25
19
|
* Stage statuses
|
|
@@ -28,19 +22,19 @@ const STAGE_STATUS = {
|
|
|
28
22
|
NOT_STARTED: 'not-started',
|
|
29
23
|
IN_PROGRESS: 'in-progress',
|
|
30
24
|
COMPLETED: 'completed',
|
|
31
|
-
BLOCKED: 'blocked'
|
|
25
|
+
BLOCKED: 'blocked',
|
|
32
26
|
};
|
|
33
27
|
|
|
34
28
|
/**
|
|
35
29
|
* Default configuration
|
|
36
30
|
*/
|
|
37
31
|
const DEFAULT_CONFIG = {
|
|
38
|
-
storageDir: 'storage/workflows'
|
|
32
|
+
storageDir: 'storage/workflows',
|
|
39
33
|
};
|
|
40
34
|
|
|
41
35
|
/**
|
|
42
36
|
* WorkflowDashboard
|
|
43
|
-
*
|
|
37
|
+
*
|
|
44
38
|
* Manages workflow state and progress for features.
|
|
45
39
|
*/
|
|
46
40
|
class WorkflowDashboard {
|
|
@@ -60,13 +54,13 @@ class WorkflowDashboard {
|
|
|
60
54
|
*/
|
|
61
55
|
async createWorkflow(featureId, options = {}) {
|
|
62
56
|
const stages = {};
|
|
63
|
-
|
|
57
|
+
|
|
64
58
|
for (const stage of WORKFLOW_STAGES) {
|
|
65
59
|
stages[stage] = {
|
|
66
60
|
status: STAGE_STATUS.NOT_STARTED,
|
|
67
61
|
startedAt: null,
|
|
68
62
|
completedAt: null,
|
|
69
|
-
artifacts: []
|
|
63
|
+
artifacts: [],
|
|
70
64
|
};
|
|
71
65
|
}
|
|
72
66
|
|
|
@@ -83,7 +77,7 @@ class WorkflowDashboard {
|
|
|
83
77
|
currentStage: 'steering',
|
|
84
78
|
stages,
|
|
85
79
|
blockers: [],
|
|
86
|
-
metadata: options.metadata || {}
|
|
80
|
+
metadata: options.metadata || {},
|
|
87
81
|
};
|
|
88
82
|
|
|
89
83
|
this.workflows.set(featureId, workflow);
|
|
@@ -165,7 +159,7 @@ class WorkflowDashboard {
|
|
|
165
159
|
severity: blocker.severity || 'medium',
|
|
166
160
|
createdAt: new Date().toISOString(),
|
|
167
161
|
resolvedAt: null,
|
|
168
|
-
resolution: null
|
|
162
|
+
resolution: null,
|
|
169
163
|
};
|
|
170
164
|
|
|
171
165
|
workflow.blockers.push(blockerEntry);
|
|
@@ -203,9 +197,7 @@ class WorkflowDashboard {
|
|
|
203
197
|
blocker.resolution = resolution;
|
|
204
198
|
|
|
205
199
|
// Check if stage can be unblocked
|
|
206
|
-
const stageBlockers = workflow.blockers.filter(
|
|
207
|
-
b => b.stage === blocker.stage && !b.resolvedAt
|
|
208
|
-
);
|
|
200
|
+
const stageBlockers = workflow.blockers.filter(b => b.stage === blocker.stage && !b.resolvedAt);
|
|
209
201
|
|
|
210
202
|
if (stageBlockers.length === 0 && workflow.stages[blocker.stage]) {
|
|
211
203
|
workflow.stages[blocker.stage].status = STAGE_STATUS.IN_PROGRESS;
|
|
@@ -239,7 +231,7 @@ class WorkflowDashboard {
|
|
|
239
231
|
type: 'resolve-blocker',
|
|
240
232
|
priority: 'high',
|
|
241
233
|
description: `${unresolvedBlockers.length}件のブロッカーを解決してください`,
|
|
242
|
-
blockers: unresolvedBlockers
|
|
234
|
+
blockers: unresolvedBlockers,
|
|
243
235
|
});
|
|
244
236
|
}
|
|
245
237
|
|
|
@@ -250,7 +242,7 @@ class WorkflowDashboard {
|
|
|
250
242
|
type: 'create-artifact',
|
|
251
243
|
priority: 'medium',
|
|
252
244
|
description: 'プロジェクトメモリファイルを作成してください',
|
|
253
|
-
artifacts: ['structure.md', 'tech.md', 'product.md']
|
|
245
|
+
artifacts: ['structure.md', 'tech.md', 'product.md'],
|
|
254
246
|
});
|
|
255
247
|
break;
|
|
256
248
|
case 'requirements':
|
|
@@ -258,7 +250,7 @@ class WorkflowDashboard {
|
|
|
258
250
|
type: 'create-artifact',
|
|
259
251
|
priority: 'medium',
|
|
260
252
|
description: 'EARS形式の要件ドキュメントを作成してください',
|
|
261
|
-
artifacts: ['requirements.md']
|
|
253
|
+
artifacts: ['requirements.md'],
|
|
262
254
|
});
|
|
263
255
|
break;
|
|
264
256
|
case 'design':
|
|
@@ -266,21 +258,21 @@ class WorkflowDashboard {
|
|
|
266
258
|
type: 'create-artifact',
|
|
267
259
|
priority: 'medium',
|
|
268
260
|
description: 'C4設計ドキュメントとADRを作成してください',
|
|
269
|
-
artifacts: ['design.md', 'adr-*.md']
|
|
261
|
+
artifacts: ['design.md', 'adr-*.md'],
|
|
270
262
|
});
|
|
271
263
|
break;
|
|
272
264
|
case 'implementation':
|
|
273
265
|
actions.push({
|
|
274
266
|
type: 'run-review',
|
|
275
267
|
priority: 'medium',
|
|
276
|
-
description: 'レビューゲートを実行して実装を検証してください'
|
|
268
|
+
description: 'レビューゲートを実行して実装を検証してください',
|
|
277
269
|
});
|
|
278
270
|
break;
|
|
279
271
|
case 'validation':
|
|
280
272
|
actions.push({
|
|
281
273
|
type: 'complete-validation',
|
|
282
274
|
priority: 'medium',
|
|
283
|
-
description: '全てのテストが通過していることを確認してください'
|
|
275
|
+
description: '全てのテストが通過していることを確認してください',
|
|
284
276
|
});
|
|
285
277
|
break;
|
|
286
278
|
}
|
|
@@ -336,8 +328,8 @@ class WorkflowDashboard {
|
|
|
336
328
|
stages: Object.entries(workflow.stages).map(([name, data]) => ({
|
|
337
329
|
name,
|
|
338
330
|
status: data.status,
|
|
339
|
-
artifactCount: data.artifacts.length
|
|
340
|
-
}))
|
|
331
|
+
artifactCount: data.artifacts.length,
|
|
332
|
+
})),
|
|
341
333
|
};
|
|
342
334
|
}
|
|
343
335
|
|
|
@@ -362,11 +354,8 @@ class WorkflowDashboard {
|
|
|
362
354
|
*/
|
|
363
355
|
async saveWorkflow(workflow) {
|
|
364
356
|
await this.ensureStorageDir();
|
|
365
|
-
|
|
366
|
-
const filePath = path.join(
|
|
367
|
-
this.config.storageDir,
|
|
368
|
-
`${workflow.featureId}.json`
|
|
369
|
-
);
|
|
357
|
+
|
|
358
|
+
const filePath = path.join(this.config.storageDir, `${workflow.featureId}.json`);
|
|
370
359
|
|
|
371
360
|
await fs.writeFile(filePath, JSON.stringify(workflow, null, 2), 'utf-8');
|
|
372
361
|
this.workflows.set(workflow.featureId, workflow);
|
|
@@ -398,7 +387,7 @@ class WorkflowDashboard {
|
|
|
398
387
|
await this.ensureStorageDir();
|
|
399
388
|
const files = await fs.readdir(this.config.storageDir);
|
|
400
389
|
const workflows = [];
|
|
401
|
-
|
|
390
|
+
|
|
402
391
|
for (const file of files) {
|
|
403
392
|
if (file.endsWith('.json')) {
|
|
404
393
|
const featureId = file.replace('.json', '');
|
|
@@ -408,7 +397,7 @@ class WorkflowDashboard {
|
|
|
408
397
|
}
|
|
409
398
|
}
|
|
410
399
|
}
|
|
411
|
-
|
|
400
|
+
|
|
412
401
|
return workflows;
|
|
413
402
|
} catch {
|
|
414
403
|
return [];
|
|
@@ -427,8 +416,8 @@ class WorkflowDashboard {
|
|
|
427
416
|
}
|
|
428
417
|
}
|
|
429
418
|
|
|
430
|
-
module.exports = {
|
|
431
|
-
WorkflowDashboard,
|
|
432
|
-
WORKFLOW_STAGES,
|
|
433
|
-
STAGE_STATUS
|
|
419
|
+
module.exports = {
|
|
420
|
+
WorkflowDashboard,
|
|
421
|
+
WORKFLOW_STAGES,
|
|
422
|
+
STAGE_STATUS,
|
|
434
423
|
};
|