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.
Files changed (55) hide show
  1. package/README.ja.md +3 -3
  2. package/README.md +3 -3
  3. package/bin/musubi-dashboard.js +22 -13
  4. package/bin/musubi-design.js +3 -3
  5. package/bin/musubi-gaps.js +9 -9
  6. package/bin/musubi-init.js +14 -1310
  7. package/bin/musubi-requirements.js +1 -1
  8. package/bin/musubi-tasks.js +5 -5
  9. package/bin/musubi-trace.js +23 -23
  10. package/bin/musubi-upgrade.js +7 -2
  11. package/bin/musubi.js +1 -1
  12. package/package.json +2 -2
  13. package/src/analyzers/gap-detector.js +3 -3
  14. package/src/analyzers/traceability.js +17 -17
  15. package/src/cli/dashboard-cli.js +54 -60
  16. package/src/cli/init-generators.js +464 -0
  17. package/src/cli/init-helpers.js +884 -0
  18. package/src/constitutional/checker.js +67 -65
  19. package/src/constitutional/ci-reporter.js +50 -43
  20. package/src/constitutional/index.js +2 -2
  21. package/src/constitutional/phase-minus-one.js +22 -25
  22. package/src/constitutional/steering-sync.js +28 -39
  23. package/src/dashboard/index.js +2 -2
  24. package/src/dashboard/sprint-planner.js +17 -19
  25. package/src/dashboard/sprint-reporter.js +46 -37
  26. package/src/dashboard/transition-recorder.js +12 -18
  27. package/src/dashboard/workflow-dashboard.js +27 -38
  28. package/src/enterprise/error-recovery.js +109 -49
  29. package/src/enterprise/experiment-report.js +62 -36
  30. package/src/enterprise/index.js +5 -5
  31. package/src/enterprise/rollback-manager.js +28 -29
  32. package/src/enterprise/tech-article.js +41 -35
  33. package/src/generators/design.js +3 -3
  34. package/src/generators/requirements.js +5 -3
  35. package/src/generators/tasks.js +2 -2
  36. package/src/integrations/platforms.js +1 -1
  37. package/src/templates/agents/claude-code/CLAUDE.md +1 -1
  38. package/src/templates/agents/claude-code/skills/design-reviewer/SKILL.md +132 -113
  39. package/src/templates/agents/claude-code/skills/requirements-reviewer/SKILL.md +85 -56
  40. package/src/templates/agents/codex/AGENTS.md +2 -2
  41. package/src/templates/agents/cursor/AGENTS.md +2 -2
  42. package/src/templates/agents/gemini-cli/GEMINI.md +2 -2
  43. package/src/templates/agents/github-copilot/AGENTS.md +2 -2
  44. package/src/templates/agents/github-copilot/commands/sdd-requirements.prompt.md +23 -4
  45. package/src/templates/agents/qwen-code/QWEN.md +2 -2
  46. package/src/templates/agents/shared/AGENTS.md +1 -1
  47. package/src/templates/agents/windsurf/AGENTS.md +2 -2
  48. package/src/templates/skills/browser-agent.md +1 -1
  49. package/src/traceability/extractor.js +21 -20
  50. package/src/traceability/gap-detector.js +19 -17
  51. package/src/traceability/index.js +2 -2
  52. package/src/traceability/matrix-storage.js +20 -22
  53. package/src/validators/constitution.js +5 -2
  54. package/src/validators/critic-system.js +6 -6
  55. package/src/validators/traceability-validator.js +3 -3
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Error Recovery Handler
3
- *
3
+ *
4
4
  * Provides recovery guidance for workflow failures.
5
- *
5
+ *
6
6
  * Requirement: IMP-6.2-008-01
7
- *
7
+ *
8
8
  * @module enterprise/error-recovery
9
9
  */
10
10
 
@@ -23,7 +23,7 @@ const ERROR_CATEGORY = {
23
23
  DEPENDENCY_ERROR: 'dependency-error',
24
24
  CONFIGURATION_ERROR: 'configuration-error',
25
25
  RUNTIME_ERROR: 'runtime-error',
26
- UNKNOWN: 'unknown'
26
+ UNKNOWN: 'unknown',
27
27
  };
28
28
 
29
29
  /**
@@ -36,7 +36,7 @@ const RECOVERY_ACTION = {
36
36
  UPDATE_CONFIG: 'update-config',
37
37
  ROLLBACK: 'rollback',
38
38
  MANUAL_REVIEW: 'manual-review',
39
- RETRY: 'retry'
39
+ RETRY: 'retry',
40
40
  };
41
41
 
42
42
  /**
@@ -52,7 +52,7 @@ class ErrorRecoveryHandler {
52
52
  storageDir: config.storageDir || 'storage/errors',
53
53
  maxHistorySize: config.maxHistorySize || 100,
54
54
  enableAutoAnalysis: config.enableAutoAnalysis !== false,
55
- ...config
55
+ ...config,
56
56
  };
57
57
 
58
58
  this.errorHistory = [];
@@ -67,68 +67,128 @@ class ErrorRecoveryHandler {
67
67
  return {
68
68
  [ERROR_CATEGORY.TEST_FAILURE]: {
69
69
  patterns: [
70
- { match: /expect.*toEqual/i, cause: 'Assertion mismatch', action: RECOVERY_ACTION.FIX_CODE },
70
+ {
71
+ match: /expect.*toEqual/i,
72
+ cause: 'Assertion mismatch',
73
+ action: RECOVERY_ACTION.FIX_CODE,
74
+ },
71
75
  { match: /undefined is not/i, cause: 'Null reference', action: RECOVERY_ACTION.FIX_CODE },
72
76
  { match: /timeout/i, cause: 'Test timeout', action: RECOVERY_ACTION.UPDATE_TEST },
73
- { match: /cannot find module/i, cause: 'Missing import', action: RECOVERY_ACTION.INSTALL_DEPS }
77
+ {
78
+ match: /cannot find module/i,
79
+ cause: 'Missing import',
80
+ action: RECOVERY_ACTION.INSTALL_DEPS,
81
+ },
74
82
  ],
75
- defaultAction: RECOVERY_ACTION.MANUAL_REVIEW
83
+ defaultAction: RECOVERY_ACTION.MANUAL_REVIEW,
76
84
  },
77
85
  [ERROR_CATEGORY.VALIDATION_ERROR]: {
78
86
  patterns: [
79
- { match: /ears.*format/i, cause: 'EARS format violation', action: RECOVERY_ACTION.FIX_CODE },
80
- { match: /traceability/i, cause: 'Missing traceability', action: RECOVERY_ACTION.FIX_CODE },
81
- { match: /constitutional/i, cause: 'Constitutional violation', action: RECOVERY_ACTION.MANUAL_REVIEW }
87
+ {
88
+ match: /ears.*format/i,
89
+ cause: 'EARS format violation',
90
+ action: RECOVERY_ACTION.FIX_CODE,
91
+ },
92
+ {
93
+ match: /traceability/i,
94
+ cause: 'Missing traceability',
95
+ action: RECOVERY_ACTION.FIX_CODE,
96
+ },
97
+ {
98
+ match: /constitutional/i,
99
+ cause: 'Constitutional violation',
100
+ action: RECOVERY_ACTION.MANUAL_REVIEW,
101
+ },
82
102
  ],
83
- defaultAction: RECOVERY_ACTION.FIX_CODE
103
+ defaultAction: RECOVERY_ACTION.FIX_CODE,
84
104
  },
85
105
  [ERROR_CATEGORY.BUILD_ERROR]: {
86
106
  patterns: [
87
107
  { match: /syntax.*error/i, cause: 'Syntax error', action: RECOVERY_ACTION.FIX_CODE },
88
- { match: /cannot resolve/i, cause: 'Module resolution failed', action: RECOVERY_ACTION.INSTALL_DEPS },
89
- { match: /out of memory/i, cause: 'Memory limit exceeded', action: RECOVERY_ACTION.UPDATE_CONFIG }
108
+ {
109
+ match: /cannot resolve/i,
110
+ cause: 'Module resolution failed',
111
+ action: RECOVERY_ACTION.INSTALL_DEPS,
112
+ },
113
+ {
114
+ match: /out of memory/i,
115
+ cause: 'Memory limit exceeded',
116
+ action: RECOVERY_ACTION.UPDATE_CONFIG,
117
+ },
90
118
  ],
91
- defaultAction: RECOVERY_ACTION.FIX_CODE
119
+ defaultAction: RECOVERY_ACTION.FIX_CODE,
92
120
  },
93
121
  [ERROR_CATEGORY.LINT_ERROR]: {
94
122
  patterns: [
95
123
  { match: /parsing error/i, cause: 'Parse error', action: RECOVERY_ACTION.FIX_CODE },
96
124
  { match: /no-unused/i, cause: 'Unused code', action: RECOVERY_ACTION.FIX_CODE },
97
- { match: /prefer-const/i, cause: 'Style violation', action: RECOVERY_ACTION.FIX_CODE }
125
+ { match: /prefer-const/i, cause: 'Style violation', action: RECOVERY_ACTION.FIX_CODE },
98
126
  ],
99
- defaultAction: RECOVERY_ACTION.FIX_CODE
127
+ defaultAction: RECOVERY_ACTION.FIX_CODE,
100
128
  },
101
129
  [ERROR_CATEGORY.TYPE_ERROR]: {
102
130
  patterns: [
103
- { match: /type.*not assignable/i, cause: 'Type mismatch', action: RECOVERY_ACTION.FIX_CODE },
104
- { match: /property.*does not exist/i, cause: 'Missing property', action: RECOVERY_ACTION.FIX_CODE },
105
- { match: /cannot find name/i, cause: 'Undefined identifier', action: RECOVERY_ACTION.FIX_CODE }
131
+ {
132
+ match: /type.*not assignable/i,
133
+ cause: 'Type mismatch',
134
+ action: RECOVERY_ACTION.FIX_CODE,
135
+ },
136
+ {
137
+ match: /property.*does not exist/i,
138
+ cause: 'Missing property',
139
+ action: RECOVERY_ACTION.FIX_CODE,
140
+ },
141
+ {
142
+ match: /cannot find name/i,
143
+ cause: 'Undefined identifier',
144
+ action: RECOVERY_ACTION.FIX_CODE,
145
+ },
106
146
  ],
107
- defaultAction: RECOVERY_ACTION.FIX_CODE
147
+ defaultAction: RECOVERY_ACTION.FIX_CODE,
108
148
  },
109
149
  [ERROR_CATEGORY.DEPENDENCY_ERROR]: {
110
150
  patterns: [
111
- { match: /peer dep/i, cause: 'Peer dependency conflict', action: RECOVERY_ACTION.INSTALL_DEPS },
112
- { match: /not found in npm registry/i, cause: 'Package not found', action: RECOVERY_ACTION.UPDATE_CONFIG },
113
- { match: /version.*incompatible/i, cause: 'Version conflict', action: RECOVERY_ACTION.INSTALL_DEPS }
151
+ {
152
+ match: /peer dep/i,
153
+ cause: 'Peer dependency conflict',
154
+ action: RECOVERY_ACTION.INSTALL_DEPS,
155
+ },
156
+ {
157
+ match: /not found in npm registry/i,
158
+ cause: 'Package not found',
159
+ action: RECOVERY_ACTION.UPDATE_CONFIG,
160
+ },
161
+ {
162
+ match: /version.*incompatible/i,
163
+ cause: 'Version conflict',
164
+ action: RECOVERY_ACTION.INSTALL_DEPS,
165
+ },
114
166
  ],
115
- defaultAction: RECOVERY_ACTION.INSTALL_DEPS
167
+ defaultAction: RECOVERY_ACTION.INSTALL_DEPS,
116
168
  },
117
169
  [ERROR_CATEGORY.CONFIGURATION_ERROR]: {
118
170
  patterns: [
119
- { match: /invalid.*config/i, cause: 'Invalid configuration', action: RECOVERY_ACTION.UPDATE_CONFIG },
120
- { match: /missing.*required/i, cause: 'Missing required field', action: RECOVERY_ACTION.UPDATE_CONFIG }
171
+ {
172
+ match: /invalid.*config/i,
173
+ cause: 'Invalid configuration',
174
+ action: RECOVERY_ACTION.UPDATE_CONFIG,
175
+ },
176
+ {
177
+ match: /missing.*required/i,
178
+ cause: 'Missing required field',
179
+ action: RECOVERY_ACTION.UPDATE_CONFIG,
180
+ },
121
181
  ],
122
- defaultAction: RECOVERY_ACTION.UPDATE_CONFIG
182
+ defaultAction: RECOVERY_ACTION.UPDATE_CONFIG,
123
183
  },
124
184
  [ERROR_CATEGORY.RUNTIME_ERROR]: {
125
185
  patterns: [
126
186
  { match: /enoent/i, cause: 'File not found', action: RECOVERY_ACTION.FIX_CODE },
127
187
  { match: /eacces/i, cause: 'Permission denied', action: RECOVERY_ACTION.UPDATE_CONFIG },
128
- { match: /econnrefused/i, cause: 'Connection refused', action: RECOVERY_ACTION.RETRY }
188
+ { match: /econnrefused/i, cause: 'Connection refused', action: RECOVERY_ACTION.RETRY },
129
189
  ],
130
- defaultAction: RECOVERY_ACTION.MANUAL_REVIEW
131
- }
190
+ defaultAction: RECOVERY_ACTION.MANUAL_REVIEW,
191
+ },
132
192
  };
133
193
  }
134
194
 
@@ -152,7 +212,7 @@ class ErrorRecoveryHandler {
152
212
  rootCause,
153
213
  remediation,
154
214
  context,
155
- confidence: this.calculateConfidence(rootCause)
215
+ confidence: this.calculateConfidence(rootCause),
156
216
  };
157
217
 
158
218
  // Record in history
@@ -172,14 +232,14 @@ class ErrorRecoveryHandler {
172
232
  message: error.message,
173
233
  name: error.name,
174
234
  stack: error.stack,
175
- code: error.code
235
+ code: error.code,
176
236
  };
177
237
  }
178
238
  return {
179
239
  message: error.message || String(error),
180
240
  name: error.name || 'Error',
181
241
  stack: error.stack || '',
182
- code: error.code || ''
242
+ code: error.code || '',
183
243
  };
184
244
  }
185
245
 
@@ -221,24 +281,24 @@ class ErrorRecoveryHandler {
221
281
  */
222
282
  identifyRootCause(errorInfo, category) {
223
283
  const patterns = this.recoveryPatterns[category];
224
-
284
+
225
285
  if (!patterns) {
226
286
  return {
227
287
  cause: 'Unknown error',
228
288
  action: RECOVERY_ACTION.MANUAL_REVIEW,
229
- matched: false
289
+ matched: false,
230
290
  };
231
291
  }
232
292
 
233
293
  const message = errorInfo.message;
234
-
294
+
235
295
  for (const pattern of patterns.patterns) {
236
296
  if (pattern.match.test(message)) {
237
297
  return {
238
298
  cause: pattern.cause,
239
299
  action: pattern.action,
240
300
  matched: true,
241
- pattern: pattern.match.toString()
301
+ pattern: pattern.match.toString(),
242
302
  };
243
303
  }
244
304
  }
@@ -246,7 +306,7 @@ class ErrorRecoveryHandler {
246
306
  return {
247
307
  cause: 'Unrecognized error pattern',
248
308
  action: patterns.defaultAction,
249
- matched: false
309
+ matched: false,
250
310
  };
251
311
  }
252
312
 
@@ -319,7 +379,7 @@ class ErrorRecoveryHandler {
319
379
  steps,
320
380
  commands,
321
381
  estimatedTime: this.estimateRecoveryTime(rootCause.action),
322
- priority: this.determinePriority(category, context)
382
+ priority: this.determinePriority(category, context),
323
383
  };
324
384
  }
325
385
 
@@ -336,7 +396,7 @@ class ErrorRecoveryHandler {
336
396
  [RECOVERY_ACTION.UPDATE_CONFIG]: '5-15 minutes',
337
397
  [RECOVERY_ACTION.ROLLBACK]: '10-30 minutes',
338
398
  [RECOVERY_ACTION.RETRY]: '1-5 minutes',
339
- [RECOVERY_ACTION.MANUAL_REVIEW]: '30-60 minutes'
399
+ [RECOVERY_ACTION.MANUAL_REVIEW]: '30-60 minutes',
340
400
  };
341
401
  return estimates[action] || 'Unknown';
342
402
  }
@@ -349,10 +409,10 @@ class ErrorRecoveryHandler {
349
409
  */
350
410
  determinePriority(category, context) {
351
411
  if (context.blocking) return 'critical';
352
-
412
+
353
413
  const highPriority = [ERROR_CATEGORY.BUILD_ERROR, ERROR_CATEGORY.TEST_FAILURE];
354
414
  const mediumPriority = [ERROR_CATEGORY.TYPE_ERROR, ERROR_CATEGORY.LINT_ERROR];
355
-
415
+
356
416
  if (highPriority.includes(category)) return 'high';
357
417
  if (mediumPriority.includes(category)) return 'medium';
358
418
  return 'low';
@@ -374,7 +434,7 @@ class ErrorRecoveryHandler {
374
434
  */
375
435
  recordError(analysis) {
376
436
  this.errorHistory.unshift(analysis);
377
-
437
+
378
438
  // Trim history
379
439
  if (this.errorHistory.length > this.config.maxHistorySize) {
380
440
  this.errorHistory = this.errorHistory.slice(0, this.config.maxHistorySize);
@@ -410,10 +470,10 @@ class ErrorRecoveryHandler {
410
470
  */
411
471
  async saveAnalysis(analysis) {
412
472
  await this.ensureStorageDir();
413
-
473
+
414
474
  const fileName = `error-${analysis.id}.json`;
415
475
  const filePath = path.join(this.config.storageDir, fileName);
416
-
476
+
417
477
  await fs.writeFile(filePath, JSON.stringify(analysis, null, 2), 'utf-8');
418
478
  return filePath;
419
479
  }
@@ -426,7 +486,7 @@ class ErrorRecoveryHandler {
426
486
  async loadAnalysis(id) {
427
487
  const fileName = `error-${id}.json`;
428
488
  const filePath = path.join(this.config.storageDir, fileName);
429
-
489
+
430
490
  try {
431
491
  const content = await fs.readFile(filePath, 'utf-8');
432
492
  return JSON.parse(content);
@@ -520,5 +580,5 @@ module.exports = {
520
580
  ErrorRecoveryHandler,
521
581
  createErrorRecoveryHandler,
522
582
  ERROR_CATEGORY,
523
- RECOVERY_ACTION
583
+ RECOVERY_ACTION,
524
584
  };
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Experiment Report Generator
3
- *
3
+ *
4
4
  * Automatically generates experiment reports from test results.
5
- *
5
+ *
6
6
  * Requirement: IMP-6.2-006-01
7
- *
7
+ *
8
8
  * @module enterprise/experiment-report
9
9
  */
10
10
 
@@ -17,7 +17,7 @@ const path = require('path');
17
17
  const REPORT_FORMAT = {
18
18
  MARKDOWN: 'markdown',
19
19
  HTML: 'html',
20
- JSON: 'json'
20
+ JSON: 'json',
21
21
  };
22
22
 
23
23
  /**
@@ -27,7 +27,7 @@ const TEST_STATUS = {
27
27
  PASSED: 'passed',
28
28
  FAILED: 'failed',
29
29
  SKIPPED: 'skipped',
30
- PENDING: 'pending'
30
+ PENDING: 'pending',
31
31
  };
32
32
 
33
33
  /**
@@ -45,7 +45,7 @@ class ExperimentReportGenerator {
45
45
  includeMetrics: config.includeMetrics !== false,
46
46
  includeObservations: config.includeObservations !== false,
47
47
  includeCodeSnippets: config.includeCodeSnippets !== false,
48
- ...config
48
+ ...config,
49
49
  };
50
50
  }
51
51
 
@@ -66,23 +66,27 @@ class ExperimentReportGenerator {
66
66
  version: options.version || '1.0.0',
67
67
  generatedAt: new Date().toISOString(),
68
68
  author: options.author || 'MUSUBI SDD',
69
- environment: this.getEnvironment()
69
+ environment: this.getEnvironment(),
70
70
  },
71
71
  summary: reportData.summary,
72
72
  testResults: reportData.tests,
73
73
  metrics,
74
74
  observations,
75
- conclusions: options.conclusions || []
75
+ conclusions: options.conclusions || [],
76
76
  };
77
77
 
78
78
  const formatted = this.formatReport(report, options.format || this.config.format);
79
- const filePath = await this.saveReport(formatted, report.metadata, options.format || this.config.format);
79
+ const filePath = await this.saveReport(
80
+ formatted,
81
+ report.metadata,
82
+ options.format || this.config.format
83
+ );
80
84
 
81
85
  return {
82
86
  report,
83
87
  formatted,
84
88
  filePath,
85
- format: options.format || this.config.format
89
+ format: options.format || this.config.format,
86
90
  };
87
91
  }
88
92
 
@@ -108,7 +112,7 @@ class ExperimentReportGenerator {
108
112
  suite: suite.name || path.basename(suite.testFilePath || ''),
109
113
  status,
110
114
  duration: test.duration || 0,
111
- failureMessages: test.failureMessages || []
115
+ failureMessages: test.failureMessages || [],
112
116
  });
113
117
 
114
118
  if (status === TEST_STATUS.PASSED) passed++;
@@ -128,7 +132,7 @@ class ExperimentReportGenerator {
128
132
  suite: test.suite || 'Default',
129
133
  status,
130
134
  duration: test.duration || 0,
131
- failureMessages: test.errors || []
135
+ failureMessages: test.errors || [],
132
136
  });
133
137
 
134
138
  if (status === TEST_STATUS.PASSED) passed++;
@@ -146,7 +150,7 @@ class ExperimentReportGenerator {
146
150
  skipped,
147
151
  pending,
148
152
  passRate: tests.length > 0 ? ((passed / tests.length) * 100).toFixed(2) + '%' : '0%',
149
- duration: testResults.duration || tests.reduce((sum, t) => sum + t.duration, 0)
153
+ duration: testResults.duration || tests.reduce((sum, t) => sum + t.duration, 0),
150
154
  };
151
155
 
152
156
  return { tests, summary };
@@ -175,7 +179,7 @@ class ExperimentReportGenerator {
175
179
  const metrics = {
176
180
  performance: {},
177
181
  coverage: {},
178
- quality: {}
182
+ quality: {},
179
183
  };
180
184
 
181
185
  // Performance metrics
@@ -190,7 +194,9 @@ class ExperimentReportGenerator {
190
194
  .filter(d => d > 0);
191
195
 
192
196
  if (durations.length > 0) {
193
- metrics.performance.avgTestDuration = (durations.reduce((a, b) => a + b, 0) / durations.length).toFixed(2);
197
+ metrics.performance.avgTestDuration = (
198
+ durations.reduce((a, b) => a + b, 0) / durations.length
199
+ ).toFixed(2);
194
200
  metrics.performance.maxTestDuration = Math.max(...durations);
195
201
  metrics.performance.minTestDuration = Math.min(...durations);
196
202
  }
@@ -203,16 +209,15 @@ class ExperimentReportGenerator {
203
209
  lines: coverage.lines || coverage.line || 'N/A',
204
210
  branches: coverage.branches || coverage.branch || 'N/A',
205
211
  functions: coverage.functions || coverage.function || 'N/A',
206
- statements: coverage.statements || coverage.statement || 'N/A'
212
+ statements: coverage.statements || coverage.statement || 'N/A',
207
213
  };
208
214
  }
209
215
 
210
216
  // Quality metrics
211
217
  const summary = this.parseTestResults(testResults).summary;
212
218
  metrics.quality.passRate = summary.passRate;
213
- metrics.quality.failureRate = summary.total > 0
214
- ? ((summary.failed / summary.total) * 100).toFixed(2) + '%'
215
- : '0%';
219
+ metrics.quality.failureRate =
220
+ summary.total > 0 ? ((summary.failed / summary.total) * 100).toFixed(2) + '%' : '0%';
216
221
 
217
222
  return metrics;
218
223
  }
@@ -226,7 +231,7 @@ class ExperimentReportGenerator {
226
231
  node: process.version,
227
232
  platform: process.platform,
228
233
  arch: process.arch,
229
- cwd: process.cwd()
234
+ cwd: process.cwd(),
230
235
  };
231
236
  }
232
237
 
@@ -433,34 +438,50 @@ class ExperimentReportGenerator {
433
438
  <tr><td>Duration</td><td>${this.formatDuration(summary.duration)}</td></tr>
434
439
  </table>
435
440
 
436
- ${testResults.length > 0 ? `
441
+ ${
442
+ testResults.length > 0
443
+ ? `
437
444
  <h2>Test Results</h2>
438
445
  <table>
439
446
  <tr><th>Suite</th><th>Test</th><th>Status</th><th>Duration</th></tr>
440
- ${testResults.map(t => `
447
+ ${testResults
448
+ .map(
449
+ t => `
441
450
  <tr>
442
451
  <td>${t.suite}</td>
443
452
  <td>${t.name}</td>
444
453
  <td class="${t.status}">${t.status}</td>
445
454
  <td>${t.duration}ms</td>
446
455
  </tr>
447
- `).join('')}
456
+ `
457
+ )
458
+ .join('')}
448
459
  </table>
449
- ` : ''}
460
+ `
461
+ : ''
462
+ }
450
463
 
451
- ${observations.length > 0 ? `
464
+ ${
465
+ observations.length > 0
466
+ ? `
452
467
  <h2>Observations</h2>
453
468
  <ul>
454
469
  ${observations.map(o => `<li>${o}</li>`).join('')}
455
470
  </ul>
456
- ` : ''}
471
+ `
472
+ : ''
473
+ }
457
474
 
458
- ${conclusions.length > 0 ? `
475
+ ${
476
+ conclusions.length > 0
477
+ ? `
459
478
  <h2>Conclusions</h2>
460
479
  <ul>
461
480
  ${conclusions.map(c => `<li>${c}</li>`).join('')}
462
481
  </ul>
463
- ` : ''}
482
+ `
483
+ : ''
484
+ }
464
485
 
465
486
  <hr>
466
487
  <p class="meta"><em>Generated by MUSUBI SDD Experiment Report Generator</em></p>
@@ -479,8 +500,8 @@ class ExperimentReportGenerator {
479
500
  await this.ensureOutputDir();
480
501
 
481
502
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
482
- const extension = format === REPORT_FORMAT.HTML ? 'html' :
483
- format === REPORT_FORMAT.JSON ? 'json' : 'md';
503
+ const extension =
504
+ format === REPORT_FORMAT.HTML ? 'html' : format === REPORT_FORMAT.JSON ? 'json' : 'md';
484
505
  const fileName = `experiment-${timestamp}.${extension}`;
485
506
  const filePath = path.join(this.config.outputDir, fileName);
486
507
 
@@ -503,11 +524,16 @@ class ExperimentReportGenerator {
503
524
  */
504
525
  getStatusIcon(status) {
505
526
  switch (status) {
506
- case TEST_STATUS.PASSED: return '✅';
507
- case TEST_STATUS.FAILED: return '';
508
- case TEST_STATUS.SKIPPED: return '⏭️';
509
- case TEST_STATUS.PENDING: return '';
510
- default: return '❓';
527
+ case TEST_STATUS.PASSED:
528
+ return '';
529
+ case TEST_STATUS.FAILED:
530
+ return '';
531
+ case TEST_STATUS.SKIPPED:
532
+ return '⏭️';
533
+ case TEST_STATUS.PENDING:
534
+ return '⏳';
535
+ default:
536
+ return '❓';
511
537
  }
512
538
  }
513
539
 
@@ -569,5 +595,5 @@ module.exports = {
569
595
  ExperimentReportGenerator,
570
596
  createExperimentReportGenerator,
571
597
  REPORT_FORMAT,
572
- TEST_STATUS
598
+ TEST_STATUS,
573
599
  };
@@ -26,21 +26,21 @@ const {
26
26
  ExperimentReportGenerator,
27
27
  createExperimentReportGenerator,
28
28
  REPORT_FORMAT,
29
- TEST_STATUS
29
+ TEST_STATUS,
30
30
  } = require('./experiment-report');
31
31
 
32
32
  const {
33
33
  TechArticleGenerator,
34
34
  createTechArticleGenerator,
35
35
  PLATFORM,
36
- ARTICLE_TYPE
36
+ ARTICLE_TYPE,
37
37
  } = require('./tech-article');
38
38
 
39
39
  const {
40
40
  ErrorRecoveryHandler,
41
41
  createErrorRecoveryHandler,
42
42
  ERROR_CATEGORY,
43
- RECOVERY_ACTION
43
+ RECOVERY_ACTION,
44
44
  } = require('./error-recovery');
45
45
 
46
46
  const {
@@ -48,7 +48,7 @@ const {
48
48
  createRollbackManager,
49
49
  ROLLBACK_LEVEL,
50
50
  ROLLBACK_STATUS,
51
- WORKFLOW_STAGE
51
+ WORKFLOW_STAGE,
52
52
  } = require('./rollback-manager');
53
53
 
54
54
  module.exports = {
@@ -89,5 +89,5 @@ module.exports = {
89
89
  createRollbackManager,
90
90
  ROLLBACK_LEVEL,
91
91
  ROLLBACK_STATUS,
92
- WORKFLOW_STAGE
92
+ WORKFLOW_STAGE,
93
93
  };