musubi-sdd 6.2.0 → 6.2.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/README.ja.md +60 -1
- package/README.md +60 -1
- package/bin/musubi-dashboard.js +340 -0
- package/package.json +3 -2
- package/src/cli/dashboard-cli.js +536 -0
- package/src/constitutional/checker.js +633 -0
- package/src/constitutional/ci-reporter.js +336 -0
- package/src/constitutional/index.js +22 -0
- package/src/constitutional/phase-minus-one.js +404 -0
- package/src/constitutional/steering-sync.js +473 -0
- package/src/dashboard/index.js +20 -0
- package/src/dashboard/sprint-planner.js +361 -0
- package/src/dashboard/sprint-reporter.js +378 -0
- package/src/dashboard/transition-recorder.js +209 -0
- package/src/dashboard/workflow-dashboard.js +434 -0
- package/src/enterprise/error-recovery.js +524 -0
- package/src/enterprise/experiment-report.js +573 -0
- package/src/enterprise/index.js +57 -4
- package/src/enterprise/rollback-manager.js +584 -0
- package/src/enterprise/tech-article.js +509 -0
- package/src/traceability/extractor.js +294 -0
- package/src/traceability/gap-detector.js +230 -0
- package/src/traceability/index.js +15 -0
- package/src/traceability/matrix-storage.js +368 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI Reporter
|
|
3
|
+
*
|
|
4
|
+
* Generates CI-friendly reports for Constitutional checks.
|
|
5
|
+
*
|
|
6
|
+
* Requirement: IMP-6.2-005-03
|
|
7
|
+
* Design: Section 5.4
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { ConstitutionalChecker, SEVERITY } = require('./checker');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Output formats
|
|
14
|
+
*/
|
|
15
|
+
const OUTPUT_FORMAT = {
|
|
16
|
+
TEXT: 'text',
|
|
17
|
+
JSON: 'json',
|
|
18
|
+
GITHUB: 'github',
|
|
19
|
+
JUNIT: 'junit'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Exit codes
|
|
24
|
+
*/
|
|
25
|
+
const EXIT_CODE = {
|
|
26
|
+
SUCCESS: 0,
|
|
27
|
+
WARNINGS: 0,
|
|
28
|
+
FAILURES: 1,
|
|
29
|
+
ERROR: 2
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* CIReporter
|
|
34
|
+
*
|
|
35
|
+
* Reports Constitutional check results for CI/CD systems.
|
|
36
|
+
*/
|
|
37
|
+
class CIReporter {
|
|
38
|
+
/**
|
|
39
|
+
* @param {Object} config - Configuration
|
|
40
|
+
*/
|
|
41
|
+
constructor(config = {}) {
|
|
42
|
+
this.config = {
|
|
43
|
+
format: OUTPUT_FORMAT.TEXT,
|
|
44
|
+
failOnWarning: false,
|
|
45
|
+
...config
|
|
46
|
+
};
|
|
47
|
+
this.checker = new ConstitutionalChecker(config.checkerConfig);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Run check and report
|
|
52
|
+
* @param {Array} filePaths - Files to check
|
|
53
|
+
* @param {Object} options - Options
|
|
54
|
+
* @returns {Promise<Object>} Report result
|
|
55
|
+
*/
|
|
56
|
+
async runAndReport(filePaths, options = {}) {
|
|
57
|
+
const format = options.format || this.config.format;
|
|
58
|
+
|
|
59
|
+
// Run checks
|
|
60
|
+
const results = await this.checker.checkFiles(filePaths);
|
|
61
|
+
const blockDecision = this.checker.shouldBlockMerge(results);
|
|
62
|
+
|
|
63
|
+
// Generate report
|
|
64
|
+
let report;
|
|
65
|
+
switch (format) {
|
|
66
|
+
case OUTPUT_FORMAT.JSON:
|
|
67
|
+
report = this.formatJSON(results, blockDecision);
|
|
68
|
+
break;
|
|
69
|
+
case OUTPUT_FORMAT.GITHUB:
|
|
70
|
+
report = this.formatGitHub(results, blockDecision);
|
|
71
|
+
break;
|
|
72
|
+
case OUTPUT_FORMAT.JUNIT:
|
|
73
|
+
report = this.formatJUnit(results, blockDecision);
|
|
74
|
+
break;
|
|
75
|
+
default:
|
|
76
|
+
report = this.formatText(results, blockDecision);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Determine exit code
|
|
80
|
+
const exitCode = this.determineExitCode(results, blockDecision);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
report,
|
|
84
|
+
exitCode,
|
|
85
|
+
summary: results.summary,
|
|
86
|
+
blockDecision,
|
|
87
|
+
format
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Format as plain text
|
|
93
|
+
* @param {Object} results - Check results
|
|
94
|
+
* @param {Object} blockDecision - Block decision
|
|
95
|
+
* @returns {string} Text report
|
|
96
|
+
*/
|
|
97
|
+
formatText(results, blockDecision) {
|
|
98
|
+
const lines = [];
|
|
99
|
+
|
|
100
|
+
lines.push('═══════════════════════════════════════════════════════════');
|
|
101
|
+
lines.push(' MUSUBI Constitutional Compliance Report ');
|
|
102
|
+
lines.push('═══════════════════════════════════════════════════════════');
|
|
103
|
+
lines.push('');
|
|
104
|
+
|
|
105
|
+
// Summary
|
|
106
|
+
lines.push(`Files Checked: ${results.summary.filesChecked}`);
|
|
107
|
+
lines.push(`Files Passed: ${results.summary.filesPassed}`);
|
|
108
|
+
lines.push(`Files Failed: ${results.summary.filesFailed}`);
|
|
109
|
+
lines.push(`Violations: ${results.summary.totalViolations}`);
|
|
110
|
+
lines.push('');
|
|
111
|
+
|
|
112
|
+
// Status
|
|
113
|
+
if (blockDecision.shouldBlock) {
|
|
114
|
+
lines.push('❌ BLOCKED - Constitutional violations detected');
|
|
115
|
+
if (blockDecision.requiresPhaseMinusOne) {
|
|
116
|
+
lines.push(' Phase -1 Gate review required');
|
|
117
|
+
}
|
|
118
|
+
} else if (results.summary.totalViolations > 0) {
|
|
119
|
+
lines.push('⚠️ PASSED WITH WARNINGS');
|
|
120
|
+
} else {
|
|
121
|
+
lines.push('✅ PASSED - No violations');
|
|
122
|
+
}
|
|
123
|
+
lines.push('');
|
|
124
|
+
|
|
125
|
+
// Violations
|
|
126
|
+
if (results.summary.totalViolations > 0) {
|
|
127
|
+
lines.push('───────────────────────────────────────────────────────────');
|
|
128
|
+
lines.push('Violations:');
|
|
129
|
+
lines.push('───────────────────────────────────────────────────────────');
|
|
130
|
+
|
|
131
|
+
for (const result of results.results) {
|
|
132
|
+
if (result.violations.length > 0) {
|
|
133
|
+
lines.push('');
|
|
134
|
+
lines.push(`📁 ${result.filePath}`);
|
|
135
|
+
for (const v of result.violations) {
|
|
136
|
+
const icon = this.getSeverityIcon(v.severity);
|
|
137
|
+
lines.push(` ${icon} [Article ${v.article}] ${v.message}`);
|
|
138
|
+
if (v.line) {
|
|
139
|
+
lines.push(` Line: ${v.line}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
lines.push('');
|
|
147
|
+
lines.push('═══════════════════════════════════════════════════════════');
|
|
148
|
+
|
|
149
|
+
return lines.join('\n');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Format as JSON
|
|
154
|
+
* @param {Object} results - Check results
|
|
155
|
+
* @param {Object} blockDecision - Block decision
|
|
156
|
+
* @returns {string} JSON report
|
|
157
|
+
*/
|
|
158
|
+
formatJSON(results, blockDecision) {
|
|
159
|
+
return JSON.stringify({
|
|
160
|
+
version: '1.0.0',
|
|
161
|
+
timestamp: new Date().toISOString(),
|
|
162
|
+
summary: {
|
|
163
|
+
filesChecked: results.summary.filesChecked,
|
|
164
|
+
filesPassed: results.summary.filesPassed,
|
|
165
|
+
filesFailed: results.summary.filesFailed,
|
|
166
|
+
totalViolations: results.summary.totalViolations,
|
|
167
|
+
violationsByArticle: results.summary.violationsByArticle
|
|
168
|
+
},
|
|
169
|
+
status: {
|
|
170
|
+
blocked: blockDecision.shouldBlock,
|
|
171
|
+
requiresPhaseMinusOne: blockDecision.requiresPhaseMinusOne,
|
|
172
|
+
reason: blockDecision.reason
|
|
173
|
+
},
|
|
174
|
+
violations: results.results.flatMap(r =>
|
|
175
|
+
r.violations.map(v => ({
|
|
176
|
+
file: r.filePath,
|
|
177
|
+
...v
|
|
178
|
+
}))
|
|
179
|
+
),
|
|
180
|
+
exitCode: this.determineExitCode(results, blockDecision)
|
|
181
|
+
}, null, 2);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Format for GitHub Actions
|
|
186
|
+
* @param {Object} results - Check results
|
|
187
|
+
* @param {Object} blockDecision - Block decision
|
|
188
|
+
* @returns {string} GitHub Actions output
|
|
189
|
+
*/
|
|
190
|
+
formatGitHub(results, blockDecision) {
|
|
191
|
+
const lines = [];
|
|
192
|
+
|
|
193
|
+
// Summary as workflow command
|
|
194
|
+
lines.push(`::group::Constitutional Compliance Summary`);
|
|
195
|
+
lines.push(`Files Checked: ${results.summary.filesChecked}`);
|
|
196
|
+
lines.push(`Violations: ${results.summary.totalViolations}`);
|
|
197
|
+
|
|
198
|
+
if (blockDecision.shouldBlock) {
|
|
199
|
+
lines.push(`Status: BLOCKED`);
|
|
200
|
+
} else {
|
|
201
|
+
lines.push(`Status: PASSED`);
|
|
202
|
+
}
|
|
203
|
+
lines.push('::endgroup::');
|
|
204
|
+
|
|
205
|
+
// Violations as annotations
|
|
206
|
+
for (const result of results.results) {
|
|
207
|
+
for (const v of result.violations) {
|
|
208
|
+
const command = v.severity === SEVERITY.CRITICAL || v.severity === SEVERITY.HIGH
|
|
209
|
+
? 'error'
|
|
210
|
+
: 'warning';
|
|
211
|
+
|
|
212
|
+
const line = v.line || 1;
|
|
213
|
+
const file = result.filePath;
|
|
214
|
+
const title = `Article ${v.article}: ${v.articleName}`;
|
|
215
|
+
const message = v.message;
|
|
216
|
+
|
|
217
|
+
lines.push(`::${command} file=${file},line=${line},title=${title}::${message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Set output
|
|
222
|
+
lines.push('');
|
|
223
|
+
lines.push(`::set-output name=violations::${results.summary.totalViolations}`);
|
|
224
|
+
lines.push(`::set-output name=blocked::${blockDecision.shouldBlock}`);
|
|
225
|
+
lines.push(`::set-output name=phase_minus_one::${blockDecision.requiresPhaseMinusOne}`);
|
|
226
|
+
|
|
227
|
+
return lines.join('\n');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Format as JUnit XML
|
|
232
|
+
* @param {Object} results - Check results
|
|
233
|
+
* @param {Object} blockDecision - Block decision
|
|
234
|
+
* @returns {string} JUnit XML
|
|
235
|
+
*/
|
|
236
|
+
formatJUnit(results, blockDecision) {
|
|
237
|
+
const lines = [];
|
|
238
|
+
|
|
239
|
+
lines.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
240
|
+
lines.push(`<testsuites name="Constitutional Compliance" tests="${results.summary.filesChecked}" failures="${results.summary.filesFailed}" errors="0">`);
|
|
241
|
+
|
|
242
|
+
for (const result of results.results) {
|
|
243
|
+
const testName = result.filePath.replace(/[<>&'"]/g, '_');
|
|
244
|
+
|
|
245
|
+
lines.push(` <testsuite name="${testName}" tests="1" failures="${result.violations.length > 0 ? 1 : 0}" errors="0">`);
|
|
246
|
+
lines.push(` <testcase name="constitutional-check" classname="${testName}">`);
|
|
247
|
+
|
|
248
|
+
if (result.violations.length > 0) {
|
|
249
|
+
for (const v of result.violations) {
|
|
250
|
+
const type = `Article${v.article}Violation`;
|
|
251
|
+
const message = this.escapeXml(v.message);
|
|
252
|
+
const details = this.escapeXml(`${v.articleName}: ${v.suggestion}`);
|
|
253
|
+
|
|
254
|
+
lines.push(` <failure type="${type}" message="${message}">`);
|
|
255
|
+
lines.push(` ${details}`);
|
|
256
|
+
if (v.line) {
|
|
257
|
+
lines.push(` Line: ${v.line}`);
|
|
258
|
+
}
|
|
259
|
+
lines.push(` </failure>`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
lines.push(` </testcase>`);
|
|
264
|
+
lines.push(` </testsuite>`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
lines.push('</testsuites>');
|
|
268
|
+
|
|
269
|
+
return lines.join('\n');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Determine exit code
|
|
274
|
+
* @param {Object} results - Results
|
|
275
|
+
* @param {Object} blockDecision - Block decision
|
|
276
|
+
* @returns {number} Exit code
|
|
277
|
+
*/
|
|
278
|
+
determineExitCode(results, blockDecision) {
|
|
279
|
+
if (blockDecision.shouldBlock) {
|
|
280
|
+
return EXIT_CODE.FAILURES;
|
|
281
|
+
}
|
|
282
|
+
if (this.config.failOnWarning && results.summary.totalViolations > 0) {
|
|
283
|
+
return EXIT_CODE.WARNINGS;
|
|
284
|
+
}
|
|
285
|
+
return EXIT_CODE.SUCCESS;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get severity icon
|
|
290
|
+
* @param {string} severity - Severity
|
|
291
|
+
* @returns {string} Icon
|
|
292
|
+
*/
|
|
293
|
+
getSeverityIcon(severity) {
|
|
294
|
+
switch (severity) {
|
|
295
|
+
case SEVERITY.CRITICAL:
|
|
296
|
+
return '🔴';
|
|
297
|
+
case SEVERITY.HIGH:
|
|
298
|
+
return '🟠';
|
|
299
|
+
case SEVERITY.MEDIUM:
|
|
300
|
+
return '🟡';
|
|
301
|
+
case SEVERITY.LOW:
|
|
302
|
+
return '🟢';
|
|
303
|
+
default:
|
|
304
|
+
return '⚪';
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Escape XML special characters
|
|
310
|
+
* @param {string} str - String to escape
|
|
311
|
+
* @returns {string} Escaped string
|
|
312
|
+
*/
|
|
313
|
+
escapeXml(str) {
|
|
314
|
+
if (!str) return '';
|
|
315
|
+
return str
|
|
316
|
+
.replace(/&/g, '&')
|
|
317
|
+
.replace(/</g, '<')
|
|
318
|
+
.replace(/>/g, '>')
|
|
319
|
+
.replace(/"/g, '"')
|
|
320
|
+
.replace(/'/g, ''');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Print to stdout
|
|
325
|
+
* @param {string} report - Report string
|
|
326
|
+
*/
|
|
327
|
+
print(report) {
|
|
328
|
+
console.log(report);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
module.exports = {
|
|
333
|
+
CIReporter,
|
|
334
|
+
OUTPUT_FORMAT,
|
|
335
|
+
EXIT_CODE
|
|
336
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constitutional Module
|
|
3
|
+
*
|
|
4
|
+
* Requirement: IMP-6.2-005, IMP-6.2-007
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { ConstitutionalChecker, ARTICLES, SEVERITY } = require('./checker');
|
|
8
|
+
const { PhaseMinusOneGate, GATE_STATUS } = require('./phase-minus-one');
|
|
9
|
+
const { SteeringSync } = require('./steering-sync');
|
|
10
|
+
const { CIReporter, OUTPUT_FORMAT, EXIT_CODE } = require('./ci-reporter');
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
ConstitutionalChecker,
|
|
14
|
+
PhaseMinusOneGate,
|
|
15
|
+
SteeringSync,
|
|
16
|
+
CIReporter,
|
|
17
|
+
ARTICLES,
|
|
18
|
+
SEVERITY,
|
|
19
|
+
GATE_STATUS,
|
|
20
|
+
OUTPUT_FORMAT,
|
|
21
|
+
EXIT_CODE
|
|
22
|
+
};
|