@unrdf/project-engine 5.0.1 → 26.4.2
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/package.json +16 -15
- package/src/golden-structure.mjs +2 -2
- package/src/materialize-apply.mjs +2 -2
- package/README.md +0 -53
- package/src/api-contract-validator.mjs +0 -711
- package/src/auto-test-generator.mjs +0 -444
- package/src/autonomic-mapek.mjs +0 -511
- package/src/capabilities-manifest.mjs +0 -125
- package/src/code-complexity-js.mjs +0 -368
- package/src/dependency-graph.mjs +0 -276
- package/src/doc-drift-checker.mjs +0 -172
- package/src/doc-generator.mjs +0 -229
- package/src/domain-infer.mjs +0 -966
- package/src/drift-snapshot.mjs +0 -775
- package/src/file-roles.mjs +0 -94
- package/src/fs-scan.mjs +0 -305
- package/src/gap-finder.mjs +0 -376
- package/src/hotspot-analyzer.mjs +0 -412
- package/src/index.mjs +0 -151
- package/src/initialize.mjs +0 -957
- package/src/lens/project-structure.mjs +0 -74
- package/src/mapek-orchestration.mjs +0 -665
- package/src/materialize-plan.mjs +0 -422
- package/src/materialize.mjs +0 -137
- package/src/policy-derivation.mjs +0 -869
- package/src/project-config.mjs +0 -142
- package/src/project-diff.mjs +0 -28
- package/src/project-engine/build-utils.mjs +0 -237
- package/src/project-engine/code-analyzer.mjs +0 -248
- package/src/project-engine/doc-generator.mjs +0 -407
- package/src/project-engine/infrastructure.mjs +0 -213
- package/src/project-engine/metrics.mjs +0 -146
- package/src/project-model.mjs +0 -111
- package/src/project-report.mjs +0 -348
- package/src/refactoring-guide.mjs +0 -242
- package/src/stack-detect.mjs +0 -102
- package/src/stack-linter.mjs +0 -213
- package/src/template-infer.mjs +0 -674
- package/src/type-auditor.mjs +0 -609
|
@@ -1,665 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file MAPEK Orchestration - Unified execution with all innovations
|
|
3
|
-
* @module project-engine/mapek-orchestration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
import { UnrdfDataFactory as DataFactory } from '@unrdf/core/rdf/n3-justified-only';
|
|
8
|
-
import { findMissingRoles } from './gap-finder.mjs';
|
|
9
|
-
import { auditTypeConsistency } from './type-auditor.mjs';
|
|
10
|
-
import { analyzeHotspots } from './hotspot-analyzer.mjs';
|
|
11
|
-
import { generateTestSuggestions } from './auto-test-generator.mjs';
|
|
12
|
-
import { checkDocDrift } from './doc-drift-checker.mjs';
|
|
13
|
-
import { buildDependencyGraph } from './dependency-graph.mjs';
|
|
14
|
-
import { generateAllAPISchemas, validateAPIFiles } from './api-contract-validator.mjs';
|
|
15
|
-
import { lintStack } from './stack-linter.mjs';
|
|
16
|
-
import { generateRefactoringGuide } from './refactoring-guide.mjs';
|
|
17
|
-
import { generateDocSuggestions } from './doc-generator.mjs';
|
|
18
|
-
import { promises as fs } from 'fs';
|
|
19
|
-
import path from 'path';
|
|
20
|
-
|
|
21
|
-
const { namedNode } = DataFactory;
|
|
22
|
-
|
|
23
|
-
const NS = {
|
|
24
|
-
fs: 'http://example.org/unrdf/filesystem#',
|
|
25
|
-
proj: 'http://example.org/unrdf/project#',
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Scoring configuration constants
|
|
29
|
-
const SCORE_MULTIPLIERS = {
|
|
30
|
-
GAP: 10,
|
|
31
|
-
TYPE: 15,
|
|
32
|
-
HOTSPOT: 20,
|
|
33
|
-
DEPENDENCY: 30,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const SCORE_WEIGHTS = {
|
|
37
|
-
GAP: 0.15,
|
|
38
|
-
TYPE: 0.15,
|
|
39
|
-
HOTSPOT: 0.1,
|
|
40
|
-
TEST: 0.15,
|
|
41
|
-
DOC_DRIFT: 0.05,
|
|
42
|
-
DEPENDENCY: 0.1,
|
|
43
|
-
API_CONTRACT: 0.1,
|
|
44
|
-
STACK_LINT: 0.05,
|
|
45
|
-
REFACTORING: 0.1,
|
|
46
|
-
DOC_COVERAGE: 0.05,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const SCORE_THRESHOLDS = {
|
|
50
|
-
MAX_SCORE: 100,
|
|
51
|
-
TYPE_ISSUE: 50,
|
|
52
|
-
TYPE_CRITICAL: 80,
|
|
53
|
-
TEST_ISSUE: 30,
|
|
54
|
-
TEST_HIGH: 60,
|
|
55
|
-
DOC_ISSUE: 30,
|
|
56
|
-
API_CONTRACT_ISSUE: 40,
|
|
57
|
-
API_CONTRACT_CRITICAL: 70,
|
|
58
|
-
REFACTORING_ISSUE: 40,
|
|
59
|
-
GAP_CRITICAL: 80,
|
|
60
|
-
HEALTH_REPEAT_THRESHOLD: 70,
|
|
61
|
-
COMMON_ISSUE_THRESHOLD: 50,
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Kaizen improvement: Extract magic number to named constant
|
|
65
|
-
const PRIORITIZED_ISSUES_LIMIT = 20;
|
|
66
|
-
|
|
67
|
-
// Kaizen improvement: Extract severity order mapping to constant
|
|
68
|
-
const SEVERITY_ORDER = {
|
|
69
|
-
critical: 0,
|
|
70
|
-
high: 1,
|
|
71
|
-
medium: 2,
|
|
72
|
-
low: 3,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Kaizen improvement: Extract issue type strings to constants
|
|
76
|
-
const ISSUE_TYPES = {
|
|
77
|
-
GAP: 'gap',
|
|
78
|
-
TYPE_MISMATCH: 'type-mismatch',
|
|
79
|
-
DEPENDENCY: 'dependency',
|
|
80
|
-
API_CONTRACT: 'api-contract',
|
|
81
|
-
LINT: 'lint',
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// Kaizen improvement: Add type safety for severity values
|
|
85
|
-
const SEVERITY_LEVELS = {
|
|
86
|
-
CRITICAL: 'critical',
|
|
87
|
-
HIGH: 'high',
|
|
88
|
-
MEDIUM: 'medium',
|
|
89
|
-
LOW: 'low',
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Calculate risk score from issue count and multiplier
|
|
94
|
-
* Converts issue counts to risk scores (0-100)
|
|
95
|
-
* @param {number|undefined} count - Number of issues
|
|
96
|
-
* @param {number} multiplier - Score multiplier per issue
|
|
97
|
-
* @returns {number} Risk score (0-100), capped at MAX_SCORE
|
|
98
|
-
*/
|
|
99
|
-
function calculateRiskScore(count, multiplier) {
|
|
100
|
-
if (!count || count === 0) return 0;
|
|
101
|
-
return Math.min(SCORE_THRESHOLDS.MAX_SCORE, count * multiplier);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Calculate coverage-based score from a value
|
|
106
|
-
* Converts coverage/health values (0-100) to risk scores (0-100)
|
|
107
|
-
* Higher coverage = lower risk score
|
|
108
|
-
* @param {number|undefined} value - Coverage or health score value (0-100)
|
|
109
|
-
* @returns {number} Risk score (0-100), where 0 = perfect, 100 = worst
|
|
110
|
-
*/
|
|
111
|
-
function calculateCoverageScore(value) {
|
|
112
|
-
return SCORE_THRESHOLDS.MAX_SCORE - (value || SCORE_THRESHOLDS.MAX_SCORE);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Map lint severity to issue severity
|
|
117
|
-
* Converts ESLint severity levels to standardized issue severity levels
|
|
118
|
-
* @param {string} lintSeverity - ESLint severity ("error", "warning", "info")
|
|
119
|
-
* @returns {string} Issue severity (SEVERITY_LEVELS.HIGH, MEDIUM, or LOW)
|
|
120
|
-
*/
|
|
121
|
-
function mapLintSeverity(lintSeverity) {
|
|
122
|
-
if (lintSeverity === 'error') return SEVERITY_LEVELS.HIGH;
|
|
123
|
-
if (lintSeverity === 'warning') return SEVERITY_LEVELS.MEDIUM;
|
|
124
|
-
return SEVERITY_LEVELS.LOW;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Extract issues from results and add to allIssues array
|
|
129
|
-
* Reduces repetitive issue aggregation code
|
|
130
|
-
* @param {Array} items - Array of items to convert to issues
|
|
131
|
-
* @param {string} issueType - Type of issue (from ISSUE_TYPES)
|
|
132
|
-
* @param {Function} [severityMapper] - Optional function to map item severity
|
|
133
|
-
* @returns {Array} Array of issue objects
|
|
134
|
-
*/
|
|
135
|
-
function extractIssues(items, issueType, severityMapper = item => item.severity) {
|
|
136
|
-
if (!items || items.length === 0) return [];
|
|
137
|
-
return items.map(item => ({
|
|
138
|
-
type: issueType,
|
|
139
|
-
severity: severityMapper(item),
|
|
140
|
-
item,
|
|
141
|
-
}));
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const OrchestrationOptionsSchema = z.object({
|
|
145
|
-
projectStore: z.custom(val => val && typeof val.getQuads === 'function', {
|
|
146
|
-
message: 'projectStore must be an RDF store with getQuads method',
|
|
147
|
-
}),
|
|
148
|
-
domainStore: z
|
|
149
|
-
.custom(val => val && typeof val.getQuads === 'function', {
|
|
150
|
-
message: 'domainStore must be an RDF store with getQuads method',
|
|
151
|
-
})
|
|
152
|
-
.optional(),
|
|
153
|
-
projectRoot: z.string().optional(),
|
|
154
|
-
stackProfile: z.object({}).passthrough().optional(),
|
|
155
|
-
innovations: z.array(z.string()).optional(),
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
const ALL_INNOVATIONS = [
|
|
159
|
-
'gap-finder',
|
|
160
|
-
'type-auditor',
|
|
161
|
-
'hotspot-analyzer',
|
|
162
|
-
'auto-test-generator',
|
|
163
|
-
'doc-drift-checker',
|
|
164
|
-
'dependency-graph',
|
|
165
|
-
'api-contract-validator',
|
|
166
|
-
'stack-linter',
|
|
167
|
-
'refactoring-guide',
|
|
168
|
-
'doc-generator',
|
|
169
|
-
];
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Innovation execution strategies - replaces large switch statement
|
|
173
|
-
* Maps innovation names to their execution functions
|
|
174
|
-
*/
|
|
175
|
-
const INNOVATION_STRATEGIES = {
|
|
176
|
-
'gap-finder': ({ domainStore, projectStore, stackProfile }) =>
|
|
177
|
-
findMissingRoles({ domainStore, projectStore, stackProfile }),
|
|
178
|
-
'type-auditor': async ({ domainStore, projectStore, stackProfile, projectRoot }) =>
|
|
179
|
-
await auditTypeConsistency({ domainStore, fsStore: projectStore, stackProfile, projectRoot }),
|
|
180
|
-
'hotspot-analyzer': ({ projectStore, domainStore, stackProfile }) =>
|
|
181
|
-
analyzeHotspots({ projectStore, domainStore, stackProfile }),
|
|
182
|
-
'auto-test-generator': ({ projectStore, domainStore, stackProfile }) =>
|
|
183
|
-
generateTestSuggestions({ projectStore, domainStore, stackProfile }),
|
|
184
|
-
'doc-drift-checker': ({ projectStore, projectRoot }) =>
|
|
185
|
-
checkDocDrift({ projectStore, projectRoot }),
|
|
186
|
-
'dependency-graph': ({ projectStore, projectRoot }) =>
|
|
187
|
-
buildDependencyGraph({ projectStore, projectRoot }),
|
|
188
|
-
'api-contract-validator': async ({ projectStore, domainStore, projectRoot }) =>
|
|
189
|
-
await validateApiContracts({ projectStore, domainStore, projectRoot }),
|
|
190
|
-
'stack-linter': ({ projectStore, stackProfile, projectRoot }) =>
|
|
191
|
-
lintStack({ projectStore, stackProfile, projectRoot }),
|
|
192
|
-
'refactoring-guide': ({ projectStore, domainStore, stackProfile }) =>
|
|
193
|
-
generateRefactoringGuide({ projectStore, domainStore, stackProfile }),
|
|
194
|
-
'doc-generator': ({ projectStore, domainStore, stackProfile, projectRoot }) =>
|
|
195
|
-
generateDocSuggestions({ projectStore, domainStore, stackProfile, projectRoot }),
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Validate API contracts by matching API files to domain schemas
|
|
200
|
-
* @param {Object} options
|
|
201
|
-
* @param {Store} options.projectStore - RDF project structure
|
|
202
|
-
* @param {Store} options.domainStore - RDF domain model
|
|
203
|
-
* @param {string} [options.projectRoot] - File system root
|
|
204
|
-
* @returns {Promise<Object>} Validation results
|
|
205
|
-
*/
|
|
206
|
-
async function validateApiContracts({ projectStore, domainStore, projectRoot }) {
|
|
207
|
-
if (!domainStore) {
|
|
208
|
-
return {
|
|
209
|
-
violations: [],
|
|
210
|
-
coverage: 0,
|
|
211
|
-
breaking: false,
|
|
212
|
-
summary: 'No domain store provided',
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Generate all schemas from domain store
|
|
217
|
-
const schemas = generateAllAPISchemas(domainStore);
|
|
218
|
-
if (schemas.length === 0) {
|
|
219
|
-
return {
|
|
220
|
-
violations: [],
|
|
221
|
-
coverage: 0,
|
|
222
|
-
breaking: false,
|
|
223
|
-
summary: 'No domain entities found',
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Extract API files from project store
|
|
228
|
-
const apiFileQuads = projectStore
|
|
229
|
-
.getQuads(null, namedNode(`${NS.proj}roleString`), null)
|
|
230
|
-
.filter(quad => {
|
|
231
|
-
const role = quad.object.value;
|
|
232
|
-
return role === 'Api' || role === 'Route';
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const apiFiles = [];
|
|
236
|
-
for (const roleQuad of apiFileQuads) {
|
|
237
|
-
const fileIri = roleQuad.subject;
|
|
238
|
-
const pathQuads = projectStore.getQuads(fileIri, namedNode(`${NS.fs}relativePath`), null);
|
|
239
|
-
if (pathQuads.length > 0) {
|
|
240
|
-
const filePath = pathQuads[0].object.value;
|
|
241
|
-
let content = '';
|
|
242
|
-
if (projectRoot) {
|
|
243
|
-
try {
|
|
244
|
-
const fullPath = path.join(projectRoot, filePath);
|
|
245
|
-
content = await fs.readFile(fullPath, 'utf-8');
|
|
246
|
-
} catch (err) {
|
|
247
|
-
// File doesn't exist or can't be read, skip
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
apiFiles.push({ path: filePath, content });
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (apiFiles.length === 0) {
|
|
256
|
-
return {
|
|
257
|
-
violations: [],
|
|
258
|
-
coverage: 0,
|
|
259
|
-
breaking: false,
|
|
260
|
-
summary: 'No API files found in project',
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Determine framework from stack profile or infer from file paths
|
|
265
|
-
const framework = apiFiles.some(f => f.path.includes('app/api') || f.path.includes('route.ts'))
|
|
266
|
-
? 'nextjs'
|
|
267
|
-
: apiFiles.some(f => f.path.includes('routes') || f.path.includes('router'))
|
|
268
|
-
? 'express'
|
|
269
|
-
: undefined;
|
|
270
|
-
|
|
271
|
-
// Validate each schema against matching API files
|
|
272
|
-
const allViolations = [];
|
|
273
|
-
let totalCoverage = 0;
|
|
274
|
-
let hasBreaking = false;
|
|
275
|
-
|
|
276
|
-
for (const schema of schemas) {
|
|
277
|
-
// Find API files that might match this entity (by name pattern)
|
|
278
|
-
const entityName = schema.entityName.toLowerCase();
|
|
279
|
-
const matchingFiles = apiFiles.filter(file => {
|
|
280
|
-
const fileName = path.basename(file.path, path.extname(file.path)).toLowerCase();
|
|
281
|
-
const dirName = path.dirname(file.path).toLowerCase();
|
|
282
|
-
return (
|
|
283
|
-
fileName.includes(entityName) ||
|
|
284
|
-
dirName.includes(entityName) ||
|
|
285
|
-
file.path.toLowerCase().includes(`/${entityName}`)
|
|
286
|
-
);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
if (matchingFiles.length > 0) {
|
|
290
|
-
const result = validateAPIFiles(matchingFiles, schema, { framework });
|
|
291
|
-
allViolations.push(...result.violations);
|
|
292
|
-
totalCoverage += result.coverage;
|
|
293
|
-
if (result.breaking) {
|
|
294
|
-
hasBreaking = true;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const avgCoverage = schemas.length > 0 ? Math.round(totalCoverage / schemas.length) : 0;
|
|
300
|
-
const criticalCount = allViolations.filter(v => v.severity === SEVERITY_LEVELS.CRITICAL).length;
|
|
301
|
-
const highCount = allViolations.filter(v => v.severity === SEVERITY_LEVELS.HIGH).length;
|
|
302
|
-
|
|
303
|
-
let summary = `${allViolations.length} violations found across ${schemas.length} entities`;
|
|
304
|
-
if (criticalCount > 0) {
|
|
305
|
-
summary += ` (${criticalCount} critical)`;
|
|
306
|
-
} else if (highCount > 0) {
|
|
307
|
-
summary += ` (${highCount} high severity)`;
|
|
308
|
-
}
|
|
309
|
-
if (allViolations.length === 0) {
|
|
310
|
-
summary = 'API contracts are valid';
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
violations: allViolations,
|
|
315
|
-
coverage: avgCoverage,
|
|
316
|
-
breaking: hasBreaking,
|
|
317
|
-
summary,
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Run all innovation analyzers in parallel
|
|
323
|
-
* @param {Object} options
|
|
324
|
-
* @returns {Promise<Object>} Combined findings from all innovations
|
|
325
|
-
*/
|
|
326
|
-
export async function runInnovationsParallel(options) {
|
|
327
|
-
const validated = OrchestrationOptionsSchema.parse(options);
|
|
328
|
-
const { projectStore, domainStore, stackProfile, projectRoot, innovations } = validated;
|
|
329
|
-
|
|
330
|
-
const enabledInnovations = innovations || ALL_INNOVATIONS;
|
|
331
|
-
const results = {};
|
|
332
|
-
const errors = [];
|
|
333
|
-
|
|
334
|
-
// Kaizen improvement: Add error handling for missing innovation results
|
|
335
|
-
if (!projectStore) {
|
|
336
|
-
errors.push({ innovation: 'all', error: 'projectStore is required' });
|
|
337
|
-
return { results, errors, innovationsRun: 0 };
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Strategy map replaces large switch statement - reduces complexity
|
|
341
|
-
const resultKeys = {
|
|
342
|
-
'gap-finder': 'gaps',
|
|
343
|
-
'type-auditor': 'typeIssues',
|
|
344
|
-
'hotspot-analyzer': 'hotspots',
|
|
345
|
-
'auto-test-generator': 'testSuggestions',
|
|
346
|
-
'doc-drift-checker': 'docDrift',
|
|
347
|
-
'dependency-graph': 'dependencyGraph',
|
|
348
|
-
'api-contract-validator': 'apiContracts',
|
|
349
|
-
'stack-linter': 'stackLint',
|
|
350
|
-
'refactoring-guide': 'refactoring',
|
|
351
|
-
'doc-generator': 'docSuggestions',
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
const tasks = enabledInnovations.map(async name => {
|
|
355
|
-
try {
|
|
356
|
-
const strategy = INNOVATION_STRATEGIES[name];
|
|
357
|
-
if (!strategy) {
|
|
358
|
-
errors.push({ innovation: name, error: 'Unknown innovation' });
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const context = { domainStore, projectStore, stackProfile, projectRoot };
|
|
363
|
-
const result = await strategy(context);
|
|
364
|
-
const resultKey = resultKeys[name];
|
|
365
|
-
if (resultKey) {
|
|
366
|
-
results[resultKey] = result;
|
|
367
|
-
}
|
|
368
|
-
} catch (err) {
|
|
369
|
-
errors.push({ innovation: name, error: err.message });
|
|
370
|
-
}
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
await Promise.all(tasks);
|
|
374
|
-
|
|
375
|
-
return { results, errors, innovationsRun: enabledInnovations.length };
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Aggregate findings from all innovations into unified metrics
|
|
380
|
-
*
|
|
381
|
-
* @description
|
|
382
|
-
* Calculates unified health scores from all innovation findings using weighted averages.
|
|
383
|
-
* Score formula: score = min(MAX_SCORE, issueCount * multiplier) for risk scores,
|
|
384
|
-
* or score = MAX_SCORE - coverage for coverage-based scores.
|
|
385
|
-
* Overall health = MAX_SCORE - weighted sum of all risk scores.
|
|
386
|
-
*
|
|
387
|
-
* @param {Object} innovationResults - Results from runInnovationsParallel
|
|
388
|
-
* @returns {Object} Aggregated findings with priorities
|
|
389
|
-
*/
|
|
390
|
-
export function aggregateInnovationFindings(innovationResults) {
|
|
391
|
-
const { results } = innovationResults;
|
|
392
|
-
|
|
393
|
-
// Calculate unified scores
|
|
394
|
-
const scores = {
|
|
395
|
-
gapScore: calculateRiskScore(results.gaps?.gaps?.length, SCORE_MULTIPLIERS.GAP),
|
|
396
|
-
typeScore: calculateRiskScore(results.typeIssues?.mismatches?.length, SCORE_MULTIPLIERS.TYPE),
|
|
397
|
-
hotspotScore: calculateRiskScore(
|
|
398
|
-
results.hotspots?.hotspots?.filter(h => h.risk === 'HIGH').length,
|
|
399
|
-
SCORE_MULTIPLIERS.HOTSPOT
|
|
400
|
-
),
|
|
401
|
-
testScore: calculateCoverageScore(results.testSuggestions?.coverage),
|
|
402
|
-
docDriftScore: calculateCoverageScore(results.docDrift?.healthScore),
|
|
403
|
-
dependencyScore: calculateRiskScore(
|
|
404
|
-
results.dependencyGraph?.issues?.filter(i => i.severity === SEVERITY_LEVELS.CRITICAL).length,
|
|
405
|
-
SCORE_MULTIPLIERS.DEPENDENCY
|
|
406
|
-
),
|
|
407
|
-
apiContractScore: calculateCoverageScore(results.apiContracts?.coverage),
|
|
408
|
-
stackLintScore: calculateCoverageScore(results.stackLint?.score),
|
|
409
|
-
refactoringScore: results.refactoring?.technicalDebt || 0,
|
|
410
|
-
docCoverageScore: calculateCoverageScore(results.docSuggestions?.coverage),
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
// Calculate overall health (weighted average)
|
|
414
|
-
const weights = {
|
|
415
|
-
gapScore: SCORE_WEIGHTS.GAP,
|
|
416
|
-
typeScore: SCORE_WEIGHTS.TYPE,
|
|
417
|
-
hotspotScore: SCORE_WEIGHTS.HOTSPOT,
|
|
418
|
-
testScore: SCORE_WEIGHTS.TEST,
|
|
419
|
-
docDriftScore: SCORE_WEIGHTS.DOC_DRIFT,
|
|
420
|
-
dependencyScore: SCORE_WEIGHTS.DEPENDENCY,
|
|
421
|
-
apiContractScore: SCORE_WEIGHTS.API_CONTRACT,
|
|
422
|
-
stackLintScore: SCORE_WEIGHTS.STACK_LINT,
|
|
423
|
-
refactoringScore: SCORE_WEIGHTS.REFACTORING,
|
|
424
|
-
docCoverageScore: SCORE_WEIGHTS.DOC_COVERAGE,
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
// Kaizen improvement: Validate score weights sum to 1.0
|
|
428
|
-
const weightSum = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
|
|
429
|
-
if (Math.abs(weightSum - 1.0) > 0.001) {
|
|
430
|
-
console.warn(`Warning: SCORE_WEIGHTS sum to ${weightSum}, expected 1.0`);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const overallRisk = Object.entries(scores).reduce((total, [key, score]) => {
|
|
434
|
-
return total + score * (weights[key] || 0);
|
|
435
|
-
}, 0);
|
|
436
|
-
|
|
437
|
-
const overallHealth = Math.round(SCORE_THRESHOLDS.MAX_SCORE - overallRisk);
|
|
438
|
-
|
|
439
|
-
// Prioritize issues - extract repetitive pattern
|
|
440
|
-
const allIssues = [
|
|
441
|
-
...extractIssues(results.gaps?.gaps, ISSUE_TYPES.GAP, g =>
|
|
442
|
-
g.score > SCORE_THRESHOLDS.GAP_CRITICAL ? SEVERITY_LEVELS.CRITICAL : SEVERITY_LEVELS.MEDIUM
|
|
443
|
-
),
|
|
444
|
-
...extractIssues(results.typeIssues?.mismatches, ISSUE_TYPES.TYPE_MISMATCH),
|
|
445
|
-
...extractIssues(results.dependencyGraph?.issues, ISSUE_TYPES.DEPENDENCY),
|
|
446
|
-
...extractIssues(results.apiContracts?.violations, ISSUE_TYPES.API_CONTRACT),
|
|
447
|
-
...extractIssues(results.stackLint?.issues, ISSUE_TYPES.LINT, i => mapLintSeverity(i.severity)),
|
|
448
|
-
];
|
|
449
|
-
|
|
450
|
-
// Sort by severity
|
|
451
|
-
allIssues.sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]);
|
|
452
|
-
|
|
453
|
-
return {
|
|
454
|
-
scores,
|
|
455
|
-
overallHealth,
|
|
456
|
-
overallRisk: Math.round(overallRisk),
|
|
457
|
-
totalIssues: allIssues.length,
|
|
458
|
-
criticalIssues: allIssues.filter(i => i.severity === SEVERITY_LEVELS.CRITICAL).length,
|
|
459
|
-
highIssues: allIssues.filter(i => i.severity === SEVERITY_LEVELS.HIGH).length,
|
|
460
|
-
prioritizedIssues: allIssues.slice(0, PRIORITIZED_ISSUES_LIMIT),
|
|
461
|
-
summaries: {
|
|
462
|
-
gaps: results.gaps?.summary || 'N/A',
|
|
463
|
-
types: results.typeIssues?.summary || 'N/A',
|
|
464
|
-
hotspots: results.hotspots?.summary || 'N/A',
|
|
465
|
-
tests: results.testSuggestions?.summary || 'N/A',
|
|
466
|
-
docDrift: results.docDrift?.summary || 'N/A',
|
|
467
|
-
dependencies: results.dependencyGraph?.summary || 'N/A',
|
|
468
|
-
apiContracts: results.apiContracts?.summary || 'N/A',
|
|
469
|
-
stackLint: results.stackLint?.summary || 'N/A',
|
|
470
|
-
refactoring: results.refactoring?.summary || 'N/A',
|
|
471
|
-
docSuggestions: results.docSuggestions?.summary || 'N/A',
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Create a decision object - reduces repetitive decision generation code
|
|
478
|
-
* @param {string} issue - Issue identifier
|
|
479
|
-
* @param {string} severity - Severity level
|
|
480
|
-
* @param {string} action - Action to take
|
|
481
|
-
* @param {boolean} autoFixable - Whether action can be auto-fixed
|
|
482
|
-
* @param {string} description - Description of the decision
|
|
483
|
-
* @returns {Object} Decision object
|
|
484
|
-
*/
|
|
485
|
-
function createDecision(issue, severity, action, autoFixable, description) {
|
|
486
|
-
return { issue, severity, action, autoFixable, description };
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Kaizen improvement: Extract decision generation logic to helper function
|
|
491
|
-
* Generates decisions based on aggregated findings and innovation results
|
|
492
|
-
* @param {Object} aggregated - Aggregated findings from aggregateInnovationFindings
|
|
493
|
-
* @param {Object} innovationResults - Results from runInnovationsParallel
|
|
494
|
-
* @returns {Array<Object>} Array of decision objects
|
|
495
|
-
*/
|
|
496
|
-
function generateDecisions(aggregated, innovationResults) {
|
|
497
|
-
const decisions = [];
|
|
498
|
-
|
|
499
|
-
// Decision: Fix type mismatches
|
|
500
|
-
if (aggregated.scores.typeScore > SCORE_THRESHOLDS.TYPE_ISSUE) {
|
|
501
|
-
decisions.push(
|
|
502
|
-
createDecision(
|
|
503
|
-
'type-mismatch',
|
|
504
|
-
aggregated.scores.typeScore > SCORE_THRESHOLDS.TYPE_CRITICAL
|
|
505
|
-
? SEVERITY_LEVELS.CRITICAL
|
|
506
|
-
: SEVERITY_LEVELS.HIGH,
|
|
507
|
-
'sync-zod-ts-types',
|
|
508
|
-
true,
|
|
509
|
-
'Sync Zod schemas with TypeScript types'
|
|
510
|
-
)
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// Decision: Generate missing tests
|
|
515
|
-
if (aggregated.scores.testScore > SCORE_THRESHOLDS.TEST_ISSUE) {
|
|
516
|
-
decisions.push(
|
|
517
|
-
createDecision(
|
|
518
|
-
'low-test-coverage',
|
|
519
|
-
aggregated.scores.testScore > SCORE_THRESHOLDS.TEST_HIGH
|
|
520
|
-
? SEVERITY_LEVELS.HIGH
|
|
521
|
-
: SEVERITY_LEVELS.MEDIUM,
|
|
522
|
-
'generate-tests',
|
|
523
|
-
true,
|
|
524
|
-
'Generate tests for uncovered files'
|
|
525
|
-
)
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Decision: Update documentation
|
|
530
|
-
if (
|
|
531
|
-
aggregated.scores.docDriftScore > SCORE_THRESHOLDS.DOC_ISSUE ||
|
|
532
|
-
aggregated.scores.docCoverageScore > SCORE_THRESHOLDS.DOC_ISSUE
|
|
533
|
-
) {
|
|
534
|
-
decisions.push(
|
|
535
|
-
createDecision(
|
|
536
|
-
'documentation-drift',
|
|
537
|
-
SEVERITY_LEVELS.MEDIUM,
|
|
538
|
-
'update-docs',
|
|
539
|
-
false,
|
|
540
|
-
'Update out-of-sync documentation'
|
|
541
|
-
)
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Decision: Fix circular dependencies
|
|
546
|
-
if (innovationResults.results.dependencyGraph?.metrics?.circularCount > 0) {
|
|
547
|
-
decisions.push(
|
|
548
|
-
createDecision(
|
|
549
|
-
'circular-dependency',
|
|
550
|
-
SEVERITY_LEVELS.CRITICAL,
|
|
551
|
-
'refactor-dependencies',
|
|
552
|
-
false,
|
|
553
|
-
'Break circular dependency chains'
|
|
554
|
-
)
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// Decision: Fix API contract violations
|
|
559
|
-
if (aggregated.scores.apiContractScore > SCORE_THRESHOLDS.API_CONTRACT_ISSUE) {
|
|
560
|
-
decisions.push(
|
|
561
|
-
createDecision(
|
|
562
|
-
'api-contract-violation',
|
|
563
|
-
aggregated.scores.apiContractScore > SCORE_THRESHOLDS.API_CONTRACT_CRITICAL
|
|
564
|
-
? SEVERITY_LEVELS.CRITICAL
|
|
565
|
-
: SEVERITY_LEVELS.HIGH,
|
|
566
|
-
'fix-api-contracts',
|
|
567
|
-
true,
|
|
568
|
-
'Add missing API validation and documentation'
|
|
569
|
-
)
|
|
570
|
-
);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Decision: Address stack lint errors
|
|
574
|
-
const lintErrors =
|
|
575
|
-
innovationResults.results.stackLint?.issues?.filter(i => i.severity === 'error').length || 0;
|
|
576
|
-
if (lintErrors > 0) {
|
|
577
|
-
decisions.push(
|
|
578
|
-
createDecision(
|
|
579
|
-
'lint-errors',
|
|
580
|
-
SEVERITY_LEVELS.HIGH,
|
|
581
|
-
'fix-lint-errors',
|
|
582
|
-
true,
|
|
583
|
-
`${lintErrors} lint errors require fixing`
|
|
584
|
-
)
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// Decision: Refactoring
|
|
589
|
-
if (aggregated.scores.refactoringScore > SCORE_THRESHOLDS.REFACTORING_ISSUE) {
|
|
590
|
-
decisions.push(
|
|
591
|
-
createDecision(
|
|
592
|
-
'technical-debt',
|
|
593
|
-
SEVERITY_LEVELS.MEDIUM,
|
|
594
|
-
'refactor-code',
|
|
595
|
-
false,
|
|
596
|
-
'Address high technical debt areas'
|
|
597
|
-
)
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
return decisions;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
/**
|
|
605
|
-
* Run full MAPEK cycle with all 10 innovations
|
|
606
|
-
* @param {Object} options
|
|
607
|
-
* @returns {Promise<Object>} Complete MAPEK result with all findings and decisions
|
|
608
|
-
*/
|
|
609
|
-
export async function runFullMapekWithAllInnovations(options) {
|
|
610
|
-
const _validated = OrchestrationOptionsSchema.parse(options);
|
|
611
|
-
const startTime = Date.now();
|
|
612
|
-
|
|
613
|
-
// Phase 1: Monitor - Run all innovations in parallel
|
|
614
|
-
const innovationResults = await runInnovationsParallel(options);
|
|
615
|
-
|
|
616
|
-
// Phase 2: Analyze - Aggregate findings
|
|
617
|
-
const aggregated = aggregateInnovationFindings(innovationResults);
|
|
618
|
-
|
|
619
|
-
// Phase 3: Plan - Generate decisions based on findings
|
|
620
|
-
const decisions = generateDecisions(aggregated, innovationResults);
|
|
621
|
-
|
|
622
|
-
// Phase 4: Execute - Plan actions
|
|
623
|
-
const actions = decisions
|
|
624
|
-
.filter(d => d.autoFixable)
|
|
625
|
-
.map(d => ({
|
|
626
|
-
type: d.action,
|
|
627
|
-
status: 'planned',
|
|
628
|
-
timestamp: new Date().toISOString(),
|
|
629
|
-
}));
|
|
630
|
-
|
|
631
|
-
// Phase 5: Knowledge - Extract learnings
|
|
632
|
-
const learnings = {
|
|
633
|
-
timestamp: new Date().toISOString(),
|
|
634
|
-
patterns: {
|
|
635
|
-
highRiskAreas: aggregated.prioritizedIssues.slice(0, 5).map(i => i.type),
|
|
636
|
-
commonIssueTypes: Object.entries(aggregated.scores)
|
|
637
|
-
.filter(([_k, v]) => v > SCORE_THRESHOLDS.COMMON_ISSUE_THRESHOLD)
|
|
638
|
-
.map(([k]) => k.replace('Score', '')),
|
|
639
|
-
},
|
|
640
|
-
thresholds: {
|
|
641
|
-
criticalTypeScore: SCORE_THRESHOLDS.TYPE_CRITICAL,
|
|
642
|
-
criticalGapScore: SCORE_THRESHOLDS.GAP_CRITICAL,
|
|
643
|
-
criticalApiScore: SCORE_THRESHOLDS.API_CONTRACT_CRITICAL,
|
|
644
|
-
},
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
const duration = Date.now() - startTime;
|
|
648
|
-
|
|
649
|
-
return {
|
|
650
|
-
phase: 'knowledge',
|
|
651
|
-
timestamp: new Date().toISOString(),
|
|
652
|
-
duration,
|
|
653
|
-
overallHealth: aggregated.overallHealth,
|
|
654
|
-
allFindings: innovationResults.results,
|
|
655
|
-
aggregated,
|
|
656
|
-
decisions,
|
|
657
|
-
actions,
|
|
658
|
-
learnings,
|
|
659
|
-
shouldRepeat:
|
|
660
|
-
aggregated.overallHealth < SCORE_THRESHOLDS.HEALTH_REPEAT_THRESHOLD && actions.length > 0,
|
|
661
|
-
errors: innovationResults.errors,
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
export { ALL_INNOVATIONS };
|