musubi-sdd 3.10.0 → 5.1.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.md +24 -19
- package/package.json +1 -1
- package/src/agents/agent-loop.js +532 -0
- package/src/agents/agentic/code-generator.js +767 -0
- package/src/agents/agentic/code-reviewer.js +698 -0
- package/src/agents/agentic/index.js +43 -0
- package/src/agents/function-tool.js +432 -0
- package/src/agents/index.js +45 -0
- package/src/agents/schema-generator.js +514 -0
- package/src/analyzers/ast-extractor.js +870 -0
- package/src/analyzers/context-optimizer.js +681 -0
- package/src/analyzers/repository-map.js +692 -0
- package/src/integrations/index.js +7 -1
- package/src/integrations/mcp/index.js +175 -0
- package/src/integrations/mcp/mcp-context-provider.js +472 -0
- package/src/integrations/mcp/mcp-discovery.js +436 -0
- package/src/integrations/mcp/mcp-tool-registry.js +467 -0
- package/src/integrations/mcp-connector.js +818 -0
- package/src/integrations/tool-discovery.js +589 -0
- package/src/managers/index.js +7 -0
- package/src/managers/skill-tools.js +565 -0
- package/src/monitoring/cost-tracker.js +7 -0
- package/src/monitoring/incident-manager.js +10 -0
- package/src/monitoring/observability.js +10 -0
- package/src/monitoring/quality-dashboard.js +491 -0
- package/src/monitoring/release-manager.js +10 -0
- package/src/orchestration/agent-skill-binding.js +655 -0
- package/src/orchestration/error-handler.js +827 -0
- package/src/orchestration/index.js +235 -1
- package/src/orchestration/mcp-tool-adapters.js +896 -0
- package/src/orchestration/reasoning/index.js +58 -0
- package/src/orchestration/reasoning/planning-engine.js +831 -0
- package/src/orchestration/reasoning/reasoning-engine.js +710 -0
- package/src/orchestration/reasoning/self-correction.js +751 -0
- package/src/orchestration/skill-executor.js +665 -0
- package/src/orchestration/skill-registry.js +650 -0
- package/src/orchestration/workflow-examples.js +1072 -0
- package/src/orchestration/workflow-executor.js +779 -0
- package/src/phase4-integration.js +248 -0
- package/src/phase5-integration.js +402 -0
- package/src/steering/steering-auto-update.js +572 -0
- package/src/steering/steering-validator.js +547 -0
- package/src/templates/template-constraints.js +646 -0
- package/src/validators/advanced-validation.js +580 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced Validation Engine
|
|
3
|
+
* クロスアーティファクト一貫性検証と仕様ギャップ検出
|
|
4
|
+
*
|
|
5
|
+
* @module validators/advanced-validation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require('events');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validation types
|
|
12
|
+
*/
|
|
13
|
+
const VALIDATION_TYPE = {
|
|
14
|
+
CROSS_ARTIFACT: 'cross-artifact',
|
|
15
|
+
GAP_DETECTION: 'gap-detection',
|
|
16
|
+
TRACEABILITY: 'traceability',
|
|
17
|
+
CONSISTENCY: 'consistency',
|
|
18
|
+
COMPLETENESS: 'completeness'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Artifact types
|
|
23
|
+
*/
|
|
24
|
+
const ARTIFACT_TYPE = {
|
|
25
|
+
REQUIREMENT: 'requirement',
|
|
26
|
+
DESIGN: 'design',
|
|
27
|
+
IMPLEMENTATION: 'implementation',
|
|
28
|
+
TEST: 'test',
|
|
29
|
+
STEERING: 'steering',
|
|
30
|
+
DOCUMENTATION: 'documentation'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Gap severity levels
|
|
35
|
+
*/
|
|
36
|
+
const GAP_SEVERITY = {
|
|
37
|
+
CRITICAL: 'critical',
|
|
38
|
+
MAJOR: 'major',
|
|
39
|
+
MINOR: 'minor',
|
|
40
|
+
INFO: 'info'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Advanced Validation Engine
|
|
45
|
+
*/
|
|
46
|
+
class AdvancedValidation extends EventEmitter {
|
|
47
|
+
/**
|
|
48
|
+
* @param {Object} options
|
|
49
|
+
* @param {boolean} options.strict - Strict mode
|
|
50
|
+
* @param {Object} options.rules - Custom validation rules
|
|
51
|
+
*/
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
super();
|
|
54
|
+
|
|
55
|
+
this.strict = options.strict ?? false;
|
|
56
|
+
this.customRules = new Map();
|
|
57
|
+
this.artifacts = new Map();
|
|
58
|
+
this.validationHistory = [];
|
|
59
|
+
this.traceabilityMatrix = new Map();
|
|
60
|
+
|
|
61
|
+
// Load custom rules
|
|
62
|
+
if (options.rules) {
|
|
63
|
+
for (const [name, rule] of Object.entries(options.rules)) {
|
|
64
|
+
this.addRule(name, rule);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Register an artifact for validation
|
|
71
|
+
* @param {string} id - Artifact identifier
|
|
72
|
+
* @param {Object} artifact - Artifact data
|
|
73
|
+
*/
|
|
74
|
+
registerArtifact(id, artifact) {
|
|
75
|
+
if (!artifact.type || !Object.values(ARTIFACT_TYPE).includes(artifact.type)) {
|
|
76
|
+
throw new Error(`Invalid artifact type: ${artifact.type}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.artifacts.set(id, {
|
|
80
|
+
id,
|
|
81
|
+
...artifact,
|
|
82
|
+
registeredAt: new Date().toISOString()
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
this.emit('artifact-registered', { id, artifact });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Unregister an artifact
|
|
90
|
+
* @param {string} id
|
|
91
|
+
*/
|
|
92
|
+
unregisterArtifact(id) {
|
|
93
|
+
this.artifacts.delete(id);
|
|
94
|
+
this.traceabilityMatrix.delete(id);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Add a traceability link
|
|
99
|
+
* @param {string} sourceId - Source artifact ID
|
|
100
|
+
* @param {string} targetId - Target artifact ID
|
|
101
|
+
* @param {string} linkType - Type of link
|
|
102
|
+
*/
|
|
103
|
+
addTraceLink(sourceId, targetId, linkType = 'implements') {
|
|
104
|
+
if (!this.artifacts.has(sourceId)) {
|
|
105
|
+
throw new Error(`Source artifact not found: ${sourceId}`);
|
|
106
|
+
}
|
|
107
|
+
if (!this.artifacts.has(targetId)) {
|
|
108
|
+
throw new Error(`Target artifact not found: ${targetId}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!this.traceabilityMatrix.has(sourceId)) {
|
|
112
|
+
this.traceabilityMatrix.set(sourceId, []);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.traceabilityMatrix.get(sourceId).push({
|
|
116
|
+
target: targetId,
|
|
117
|
+
type: linkType,
|
|
118
|
+
createdAt: new Date().toISOString()
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Validate cross-artifact consistency
|
|
124
|
+
* @param {Object} options
|
|
125
|
+
* @returns {Object}
|
|
126
|
+
*/
|
|
127
|
+
validateCrossArtifact(options = {}) {
|
|
128
|
+
const issues = [];
|
|
129
|
+
const validated = [];
|
|
130
|
+
|
|
131
|
+
// Check for orphaned artifacts (no trace links)
|
|
132
|
+
for (const [id, artifact] of this.artifacts) {
|
|
133
|
+
const hasIncoming = this.hasIncomingLinks(id);
|
|
134
|
+
const hasOutgoing = this.traceabilityMatrix.has(id);
|
|
135
|
+
|
|
136
|
+
if (!hasIncoming && !hasOutgoing && artifact.type !== ARTIFACT_TYPE.REQUIREMENT) {
|
|
137
|
+
issues.push({
|
|
138
|
+
type: 'orphaned',
|
|
139
|
+
artifactId: id,
|
|
140
|
+
artifactType: artifact.type,
|
|
141
|
+
severity: GAP_SEVERITY.MAJOR,
|
|
142
|
+
message: `Artifact "${id}" has no traceability links`
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
validated.push(id);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check for broken links
|
|
150
|
+
for (const [sourceId, links] of this.traceabilityMatrix) {
|
|
151
|
+
for (const link of links) {
|
|
152
|
+
if (!this.artifacts.has(link.target)) {
|
|
153
|
+
issues.push({
|
|
154
|
+
type: 'broken-link',
|
|
155
|
+
sourceId,
|
|
156
|
+
targetId: link.target,
|
|
157
|
+
severity: GAP_SEVERITY.CRITICAL,
|
|
158
|
+
message: `Broken link: ${sourceId} -> ${link.target}`
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check artifact type consistency
|
|
165
|
+
for (const [sourceId, links] of this.traceabilityMatrix) {
|
|
166
|
+
const sourceArtifact = this.artifacts.get(sourceId);
|
|
167
|
+
for (const link of links) {
|
|
168
|
+
const targetArtifact = this.artifacts.get(link.target);
|
|
169
|
+
if (targetArtifact) {
|
|
170
|
+
const valid = this.validateLinkTypes(sourceArtifact.type, targetArtifact.type, link.type);
|
|
171
|
+
if (!valid) {
|
|
172
|
+
issues.push({
|
|
173
|
+
type: 'invalid-link-type',
|
|
174
|
+
sourceId,
|
|
175
|
+
targetId: link.target,
|
|
176
|
+
linkType: link.type,
|
|
177
|
+
severity: GAP_SEVERITY.MINOR,
|
|
178
|
+
message: `Unusual link: ${sourceArtifact.type} -[${link.type}]-> ${targetArtifact.type}`
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const result = {
|
|
186
|
+
type: VALIDATION_TYPE.CROSS_ARTIFACT,
|
|
187
|
+
valid: issues.filter(i => i.severity === GAP_SEVERITY.CRITICAL).length === 0,
|
|
188
|
+
issues,
|
|
189
|
+
validated,
|
|
190
|
+
timestamp: new Date().toISOString()
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
this.validationHistory.push(result);
|
|
194
|
+
this.emit('validated', result);
|
|
195
|
+
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Check if artifact has incoming links
|
|
201
|
+
*/
|
|
202
|
+
hasIncomingLinks(targetId) {
|
|
203
|
+
for (const [, links] of this.traceabilityMatrix) {
|
|
204
|
+
if (links.some(l => l.target === targetId)) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Validate link type combinations
|
|
213
|
+
*/
|
|
214
|
+
validateLinkTypes(sourceType, targetType, linkType) {
|
|
215
|
+
const validCombinations = {
|
|
216
|
+
'requirement-design': ['implements', 'addresses', 'derives'],
|
|
217
|
+
'design-implementation': ['implements', 'realizes'],
|
|
218
|
+
'implementation-test': ['tests', 'verifies'],
|
|
219
|
+
'requirement-test': ['verifies', 'validates']
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const key = `${sourceType}-${targetType}`;
|
|
223
|
+
const reverseKey = `${targetType}-${sourceType}`;
|
|
224
|
+
|
|
225
|
+
const valid = validCombinations[key] || validCombinations[reverseKey];
|
|
226
|
+
return !valid || valid.includes(linkType);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Detect specification gaps
|
|
231
|
+
* @param {Object} options
|
|
232
|
+
* @returns {Object}
|
|
233
|
+
*/
|
|
234
|
+
detectGaps(options = {}) {
|
|
235
|
+
const gaps = [];
|
|
236
|
+
|
|
237
|
+
// Requirements without design
|
|
238
|
+
const requirements = this.getArtifactsByType(ARTIFACT_TYPE.REQUIREMENT);
|
|
239
|
+
const designs = this.getArtifactsByType(ARTIFACT_TYPE.DESIGN);
|
|
240
|
+
const implementations = this.getArtifactsByType(ARTIFACT_TYPE.IMPLEMENTATION);
|
|
241
|
+
const tests = this.getArtifactsByType(ARTIFACT_TYPE.TEST);
|
|
242
|
+
|
|
243
|
+
for (const req of requirements) {
|
|
244
|
+
const hasDesign = this.hasLinkToType(req.id, ARTIFACT_TYPE.DESIGN);
|
|
245
|
+
if (!hasDesign) {
|
|
246
|
+
gaps.push({
|
|
247
|
+
type: 'missing-design',
|
|
248
|
+
artifactId: req.id,
|
|
249
|
+
severity: GAP_SEVERITY.MAJOR,
|
|
250
|
+
message: `Requirement "${req.id}" has no associated design`
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Designs without implementation
|
|
256
|
+
for (const design of designs) {
|
|
257
|
+
const hasImpl = this.hasLinkToType(design.id, ARTIFACT_TYPE.IMPLEMENTATION);
|
|
258
|
+
if (!hasImpl) {
|
|
259
|
+
gaps.push({
|
|
260
|
+
type: 'missing-implementation',
|
|
261
|
+
artifactId: design.id,
|
|
262
|
+
severity: GAP_SEVERITY.MAJOR,
|
|
263
|
+
message: `Design "${design.id}" has no associated implementation`
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Implementations without tests
|
|
269
|
+
for (const impl of implementations) {
|
|
270
|
+
const hasTest = this.hasLinkToType(impl.id, ARTIFACT_TYPE.TEST);
|
|
271
|
+
if (!hasTest) {
|
|
272
|
+
gaps.push({
|
|
273
|
+
type: 'missing-test',
|
|
274
|
+
artifactId: impl.id,
|
|
275
|
+
severity: GAP_SEVERITY.MINOR,
|
|
276
|
+
message: `Implementation "${impl.id}" has no associated tests`
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check completeness
|
|
282
|
+
const completeness = {
|
|
283
|
+
requirements: requirements.length,
|
|
284
|
+
designs: designs.length,
|
|
285
|
+
implementations: implementations.length,
|
|
286
|
+
tests: tests.length,
|
|
287
|
+
coverage: this.calculateCoverage()
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const result = {
|
|
291
|
+
type: VALIDATION_TYPE.GAP_DETECTION,
|
|
292
|
+
gaps,
|
|
293
|
+
completeness,
|
|
294
|
+
gapCount: gaps.length,
|
|
295
|
+
criticalGaps: gaps.filter(g => g.severity === GAP_SEVERITY.CRITICAL).length,
|
|
296
|
+
timestamp: new Date().toISOString()
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
this.validationHistory.push(result);
|
|
300
|
+
this.emit('gaps-detected', result);
|
|
301
|
+
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get artifacts by type
|
|
307
|
+
*/
|
|
308
|
+
getArtifactsByType(type) {
|
|
309
|
+
return Array.from(this.artifacts.values()).filter(a => a.type === type);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Check if artifact has link to specific type
|
|
314
|
+
*/
|
|
315
|
+
hasLinkToType(artifactId, targetType) {
|
|
316
|
+
const links = this.traceabilityMatrix.get(artifactId) || [];
|
|
317
|
+
for (const link of links) {
|
|
318
|
+
const target = this.artifacts.get(link.target);
|
|
319
|
+
if (target && target.type === targetType) {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Calculate traceability coverage
|
|
328
|
+
*/
|
|
329
|
+
calculateCoverage() {
|
|
330
|
+
const total = this.artifacts.size;
|
|
331
|
+
if (total === 0) return 100;
|
|
332
|
+
|
|
333
|
+
let linked = 0;
|
|
334
|
+
for (const [id] of this.artifacts) {
|
|
335
|
+
if (this.traceabilityMatrix.has(id) || this.hasIncomingLinks(id)) {
|
|
336
|
+
linked++;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return Math.round((linked / total) * 100);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Validate traceability
|
|
345
|
+
* @returns {Object}
|
|
346
|
+
*/
|
|
347
|
+
validateTraceability() {
|
|
348
|
+
const issues = [];
|
|
349
|
+
const matrix = {};
|
|
350
|
+
|
|
351
|
+
// Build traceability matrix view
|
|
352
|
+
for (const [sourceId, links] of this.traceabilityMatrix) {
|
|
353
|
+
const source = this.artifacts.get(sourceId);
|
|
354
|
+
if (!matrix[source?.type]) {
|
|
355
|
+
matrix[source?.type] = {};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
for (const link of links) {
|
|
359
|
+
const target = this.artifacts.get(link.target);
|
|
360
|
+
if (!matrix[source?.type][target?.type]) {
|
|
361
|
+
matrix[source?.type][target?.type] = 0;
|
|
362
|
+
}
|
|
363
|
+
matrix[source?.type][target?.type]++;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Check for bidirectional traceability
|
|
368
|
+
for (const [sourceId, links] of this.traceabilityMatrix) {
|
|
369
|
+
for (const link of links) {
|
|
370
|
+
const reverseLinks = this.traceabilityMatrix.get(link.target) || [];
|
|
371
|
+
const hasReverse = reverseLinks.some(l => l.target === sourceId);
|
|
372
|
+
if (!hasReverse && this.strict) {
|
|
373
|
+
issues.push({
|
|
374
|
+
type: 'unidirectional',
|
|
375
|
+
sourceId,
|
|
376
|
+
targetId: link.target,
|
|
377
|
+
severity: GAP_SEVERITY.INFO,
|
|
378
|
+
message: `Unidirectional link: ${sourceId} -> ${link.target}`
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const coverage = this.calculateCoverage();
|
|
385
|
+
|
|
386
|
+
const result = {
|
|
387
|
+
type: VALIDATION_TYPE.TRACEABILITY,
|
|
388
|
+
valid: issues.length === 0,
|
|
389
|
+
issues,
|
|
390
|
+
matrix,
|
|
391
|
+
coverage,
|
|
392
|
+
artifactCount: this.artifacts.size,
|
|
393
|
+
linkCount: this.countLinks(),
|
|
394
|
+
timestamp: new Date().toISOString()
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
this.validationHistory.push(result);
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Count total links
|
|
403
|
+
*/
|
|
404
|
+
countLinks() {
|
|
405
|
+
let count = 0;
|
|
406
|
+
for (const [, links] of this.traceabilityMatrix) {
|
|
407
|
+
count += links.length;
|
|
408
|
+
}
|
|
409
|
+
return count;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Add a custom validation rule
|
|
414
|
+
* @param {string} name
|
|
415
|
+
* @param {Object} rule
|
|
416
|
+
*/
|
|
417
|
+
addRule(name, rule) {
|
|
418
|
+
if (!rule.validate || typeof rule.validate !== 'function') {
|
|
419
|
+
throw new Error('Rule must have a validate function');
|
|
420
|
+
}
|
|
421
|
+
this.customRules.set(name, {
|
|
422
|
+
severity: GAP_SEVERITY.MINOR,
|
|
423
|
+
...rule
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Run all validations
|
|
429
|
+
* @returns {Object}
|
|
430
|
+
*/
|
|
431
|
+
runAllValidations() {
|
|
432
|
+
const crossArtifact = this.validateCrossArtifact();
|
|
433
|
+
const gaps = this.detectGaps();
|
|
434
|
+
const traceability = this.validateTraceability();
|
|
435
|
+
|
|
436
|
+
// Run custom rules
|
|
437
|
+
const customResults = [];
|
|
438
|
+
for (const [name, rule] of this.customRules) {
|
|
439
|
+
try {
|
|
440
|
+
const result = rule.validate(this.artifacts, this.traceabilityMatrix);
|
|
441
|
+
customResults.push({
|
|
442
|
+
name,
|
|
443
|
+
...result
|
|
444
|
+
});
|
|
445
|
+
} catch (error) {
|
|
446
|
+
customResults.push({
|
|
447
|
+
name,
|
|
448
|
+
error: error.message
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const allIssues = [
|
|
454
|
+
...crossArtifact.issues,
|
|
455
|
+
...gaps.gaps,
|
|
456
|
+
...traceability.issues
|
|
457
|
+
];
|
|
458
|
+
|
|
459
|
+
const overallValid = allIssues.filter(i =>
|
|
460
|
+
i.severity === GAP_SEVERITY.CRITICAL
|
|
461
|
+
).length === 0;
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
valid: overallValid,
|
|
465
|
+
summary: {
|
|
466
|
+
crossArtifact: crossArtifact.valid,
|
|
467
|
+
gaps: gaps.gapCount === 0,
|
|
468
|
+
traceability: traceability.valid
|
|
469
|
+
},
|
|
470
|
+
crossArtifact,
|
|
471
|
+
gaps,
|
|
472
|
+
traceability,
|
|
473
|
+
customRules: customResults,
|
|
474
|
+
totalIssues: allIssues.length,
|
|
475
|
+
criticalIssues: allIssues.filter(i => i.severity === GAP_SEVERITY.CRITICAL).length,
|
|
476
|
+
timestamp: new Date().toISOString()
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Get validation history
|
|
482
|
+
* @param {Object} filter
|
|
483
|
+
* @returns {Array}
|
|
484
|
+
*/
|
|
485
|
+
getHistory(filter = {}) {
|
|
486
|
+
let history = [...this.validationHistory];
|
|
487
|
+
|
|
488
|
+
if (filter.type) {
|
|
489
|
+
history = history.filter(h => h.type === filter.type);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (filter.valid !== undefined) {
|
|
493
|
+
history = history.filter(h => h.valid === filter.valid);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (filter.limit) {
|
|
497
|
+
history = history.slice(-filter.limit);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return history;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Export traceability matrix as markdown
|
|
505
|
+
* @returns {string}
|
|
506
|
+
*/
|
|
507
|
+
exportMatrix() {
|
|
508
|
+
let md = `# Traceability Matrix\n\n`;
|
|
509
|
+
md += `Generated: ${new Date().toISOString()}\n\n`;
|
|
510
|
+
|
|
511
|
+
// Artifact summary
|
|
512
|
+
md += `## Artifacts\n\n`;
|
|
513
|
+
md += `| Type | Count |\n`;
|
|
514
|
+
md += `|------|-------|\n`;
|
|
515
|
+
|
|
516
|
+
const typeCounts = {};
|
|
517
|
+
for (const [, artifact] of this.artifacts) {
|
|
518
|
+
typeCounts[artifact.type] = (typeCounts[artifact.type] || 0) + 1;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
for (const [type, count] of Object.entries(typeCounts)) {
|
|
522
|
+
md += `| ${type} | ${count} |\n`;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
md += `\n## Links\n\n`;
|
|
526
|
+
md += `| Source | Link Type | Target |\n`;
|
|
527
|
+
md += `|--------|-----------|--------|\n`;
|
|
528
|
+
|
|
529
|
+
for (const [sourceId, links] of this.traceabilityMatrix) {
|
|
530
|
+
for (const link of links) {
|
|
531
|
+
md += `| ${sourceId} | ${link.type} | ${link.target} |\n`;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
md += `\n---\n`;
|
|
536
|
+
md += `Coverage: ${this.calculateCoverage()}%\n`;
|
|
537
|
+
md += `Total Links: ${this.countLinks()}\n`;
|
|
538
|
+
|
|
539
|
+
return md;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Get stats
|
|
544
|
+
* @returns {Object}
|
|
545
|
+
*/
|
|
546
|
+
getStats() {
|
|
547
|
+
return {
|
|
548
|
+
artifactCount: this.artifacts.size,
|
|
549
|
+
linkCount: this.countLinks(),
|
|
550
|
+
ruleCount: this.customRules.size,
|
|
551
|
+
historyCount: this.validationHistory.length,
|
|
552
|
+
coverage: this.calculateCoverage()
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Clear all data
|
|
558
|
+
*/
|
|
559
|
+
clear() {
|
|
560
|
+
this.artifacts.clear();
|
|
561
|
+
this.traceabilityMatrix.clear();
|
|
562
|
+
this.validationHistory = [];
|
|
563
|
+
this.emit('cleared');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Factory function
|
|
569
|
+
*/
|
|
570
|
+
function createAdvancedValidation(options = {}) {
|
|
571
|
+
return new AdvancedValidation(options);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
module.exports = {
|
|
575
|
+
AdvancedValidation,
|
|
576
|
+
createAdvancedValidation,
|
|
577
|
+
VALIDATION_TYPE,
|
|
578
|
+
ARTIFACT_TYPE,
|
|
579
|
+
GAP_SEVERITY
|
|
580
|
+
};
|