@girardmedia/bootspring 2.0.36 → 2.0.37
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/cli/plan.js +602 -2
- package/core/planning/adaptive-engine.js +958 -0
- package/core/planning/feature-decomposer.js +772 -0
- package/core/planning/index.js +49 -0
- package/core/planning/simulator.js +1328 -0
- package/core/planning/stage-planner.js +624 -0
- package/intelligence/agent-router.js +795 -0
- package/intelligence/index.js +10 -0
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planning Simulator
|
|
3
|
+
*
|
|
4
|
+
* Provides "what if" scenario planning for projects.
|
|
5
|
+
* Simulates feature additions, refactoring impacts, timeline changes,
|
|
6
|
+
* and resource allocation decisions.
|
|
7
|
+
*
|
|
8
|
+
* @package bootspring
|
|
9
|
+
* @module core/planning/simulator
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs').promises;
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Scenario types and their configurations
|
|
17
|
+
*/
|
|
18
|
+
const SCENARIO_TYPES = {
|
|
19
|
+
feature: {
|
|
20
|
+
name: 'Feature Addition',
|
|
21
|
+
description: 'Simulate adding a new feature to the project',
|
|
22
|
+
requiredInputs: ['featureName', 'complexity'],
|
|
23
|
+
optionalInputs: ['dependencies', 'timeline', 'resources'],
|
|
24
|
+
outputs: ['timeline', 'risks', 'dependencies', 'documentation', 'testing']
|
|
25
|
+
},
|
|
26
|
+
refactor: {
|
|
27
|
+
name: 'Refactoring',
|
|
28
|
+
description: 'Simulate refactoring existing code',
|
|
29
|
+
requiredInputs: ['scope', 'target'],
|
|
30
|
+
optionalInputs: ['approach', 'constraints'],
|
|
31
|
+
outputs: ['risk', 'testing', 'documentation', 'timeline']
|
|
32
|
+
},
|
|
33
|
+
timeline: {
|
|
34
|
+
name: 'Timeline Change',
|
|
35
|
+
description: 'Simulate compressing or extending project timeline',
|
|
36
|
+
requiredInputs: ['change', 'amount'],
|
|
37
|
+
optionalInputs: ['priorities', 'constraints'],
|
|
38
|
+
outputs: ['scope', 'quality', 'resources', 'risks']
|
|
39
|
+
},
|
|
40
|
+
resource: {
|
|
41
|
+
name: 'Resource Change',
|
|
42
|
+
description: 'Simulate adding or removing team resources',
|
|
43
|
+
requiredInputs: ['action', 'count'],
|
|
44
|
+
optionalInputs: ['skills', 'timing'],
|
|
45
|
+
outputs: ['velocity', 'coordination', 'timeline', 'quality']
|
|
46
|
+
},
|
|
47
|
+
dependency: {
|
|
48
|
+
name: 'Dependency Change',
|
|
49
|
+
description: 'Simulate adding, updating, or removing dependencies',
|
|
50
|
+
requiredInputs: ['action', 'package'],
|
|
51
|
+
optionalInputs: ['version', 'alternatives'],
|
|
52
|
+
outputs: ['compatibility', 'security', 'bundleSize', 'maintenance']
|
|
53
|
+
},
|
|
54
|
+
architecture: {
|
|
55
|
+
name: 'Architecture Change',
|
|
56
|
+
description: 'Simulate architectural modifications',
|
|
57
|
+
requiredInputs: ['change', 'components'],
|
|
58
|
+
optionalInputs: ['constraints', 'phases'],
|
|
59
|
+
outputs: ['complexity', 'risk', 'timeline', 'benefits']
|
|
60
|
+
},
|
|
61
|
+
migration: {
|
|
62
|
+
name: 'Technology Migration',
|
|
63
|
+
description: 'Simulate migrating to new technology',
|
|
64
|
+
requiredInputs: ['from', 'to'],
|
|
65
|
+
optionalInputs: ['scope', 'parallel'],
|
|
66
|
+
outputs: ['effort', 'risk', 'timeline', 'training']
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Impact levels
|
|
72
|
+
*/
|
|
73
|
+
const IMPACT_LEVELS = {
|
|
74
|
+
minimal: { score: 1, label: 'Minimal', color: 'green' },
|
|
75
|
+
low: { score: 2, label: 'Low', color: 'blue' },
|
|
76
|
+
medium: { score: 3, label: 'Medium', color: 'yellow' },
|
|
77
|
+
high: { score: 4, label: 'High', color: 'orange' },
|
|
78
|
+
critical: { score: 5, label: 'Critical', color: 'red' }
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Complexity multipliers
|
|
83
|
+
*/
|
|
84
|
+
const COMPLEXITY_MULTIPLIERS = {
|
|
85
|
+
trivial: 0.5,
|
|
86
|
+
simple: 1.0,
|
|
87
|
+
moderate: 1.5,
|
|
88
|
+
complex: 2.5,
|
|
89
|
+
very_complex: 4.0
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* PlanningSimulator class
|
|
94
|
+
*/
|
|
95
|
+
class PlanningSimulator {
|
|
96
|
+
/**
|
|
97
|
+
* @param {Object} options - Configuration options
|
|
98
|
+
*/
|
|
99
|
+
constructor(options = {}) {
|
|
100
|
+
this.projectRoot = options.projectRoot || process.cwd();
|
|
101
|
+
this.projectContext = null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Initialize with project context
|
|
106
|
+
* @param {Object} context - Project context
|
|
107
|
+
*/
|
|
108
|
+
async initialize(context = null) {
|
|
109
|
+
if (context) {
|
|
110
|
+
this.projectContext = context;
|
|
111
|
+
} else {
|
|
112
|
+
this.projectContext = await this.loadProjectContext();
|
|
113
|
+
}
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Load project context from codebase
|
|
119
|
+
*/
|
|
120
|
+
async loadProjectContext() {
|
|
121
|
+
const context = {
|
|
122
|
+
framework: null,
|
|
123
|
+
language: 'javascript',
|
|
124
|
+
hasTypescript: false,
|
|
125
|
+
hasTests: false,
|
|
126
|
+
hasCI: false,
|
|
127
|
+
dependencies: [],
|
|
128
|
+
devDependencies: [],
|
|
129
|
+
structure: 'unknown'
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const pkgPath = path.join(this.projectRoot, 'package.json');
|
|
134
|
+
const pkgContent = await fs.readFile(pkgPath, 'utf-8');
|
|
135
|
+
const pkg = JSON.parse(pkgContent);
|
|
136
|
+
|
|
137
|
+
context.dependencies = Object.keys(pkg.dependencies || {});
|
|
138
|
+
context.devDependencies = Object.keys(pkg.devDependencies || {});
|
|
139
|
+
|
|
140
|
+
// Detect framework
|
|
141
|
+
if (context.dependencies.includes('next')) context.framework = 'nextjs';
|
|
142
|
+
else if (context.dependencies.includes('react')) context.framework = 'react';
|
|
143
|
+
else if (context.dependencies.includes('vue')) context.framework = 'vue';
|
|
144
|
+
else if (context.dependencies.includes('express')) context.framework = 'express';
|
|
145
|
+
|
|
146
|
+
// Detect TypeScript
|
|
147
|
+
context.hasTypescript = context.devDependencies.includes('typescript');
|
|
148
|
+
|
|
149
|
+
// Detect tests
|
|
150
|
+
context.hasTests = context.devDependencies.some(d =>
|
|
151
|
+
['jest', 'vitest', 'mocha', 'cypress'].includes(d)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Detect CI
|
|
155
|
+
try {
|
|
156
|
+
await fs.access(path.join(this.projectRoot, '.github/workflows'));
|
|
157
|
+
context.hasCI = true;
|
|
158
|
+
} catch {
|
|
159
|
+
context.hasCI = false;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// No package.json
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return context;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Run a simulation scenario
|
|
170
|
+
* @param {Object} scenario - Scenario configuration
|
|
171
|
+
*/
|
|
172
|
+
async simulate(scenario) {
|
|
173
|
+
const scenarioType = SCENARIO_TYPES[scenario.type];
|
|
174
|
+
if (!scenarioType) {
|
|
175
|
+
throw new Error(`Unknown scenario type: ${scenario.type}. Valid types: ${Object.keys(SCENARIO_TYPES).join(', ')}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Validate required inputs
|
|
179
|
+
for (const input of scenarioType.requiredInputs) {
|
|
180
|
+
if (!scenario[input]) {
|
|
181
|
+
throw new Error(`Missing required input: ${input}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Run the appropriate simulation
|
|
186
|
+
const simulationMethod = `simulate${scenario.type.charAt(0).toUpperCase() + scenario.type.slice(1)}`;
|
|
187
|
+
const results = await this[simulationMethod](scenario);
|
|
188
|
+
|
|
189
|
+
// Add metadata
|
|
190
|
+
return {
|
|
191
|
+
timestamp: new Date().toISOString(),
|
|
192
|
+
scenario: {
|
|
193
|
+
type: scenario.type,
|
|
194
|
+
name: scenarioType.name,
|
|
195
|
+
inputs: scenario
|
|
196
|
+
},
|
|
197
|
+
results,
|
|
198
|
+
summary: this.generateSummary(results),
|
|
199
|
+
recommendations: this.generateRecommendations(scenario, results)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Simulate feature addition
|
|
205
|
+
* @param {Object} scenario - Feature scenario
|
|
206
|
+
*/
|
|
207
|
+
async simulateFeature(scenario) {
|
|
208
|
+
const complexity = scenario.complexity || 'moderate';
|
|
209
|
+
const multiplier = COMPLEXITY_MULTIPLIERS[complexity] || 1.5;
|
|
210
|
+
|
|
211
|
+
const results = {
|
|
212
|
+
timeline: this.estimateFeatureTimeline(scenario, multiplier),
|
|
213
|
+
risks: this.identifyFeatureRisks(scenario, complexity),
|
|
214
|
+
dependencies: this.analyzeFeatureDependencies(scenario),
|
|
215
|
+
documentation: this.assessDocumentationNeeds(scenario),
|
|
216
|
+
testing: this.assessTestingNeeds(scenario, complexity),
|
|
217
|
+
impact: this.assessFeatureImpact(scenario, complexity)
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Estimate feature timeline
|
|
225
|
+
* @param {Object} scenario - Feature scenario
|
|
226
|
+
* @param {number} multiplier - Complexity multiplier
|
|
227
|
+
*/
|
|
228
|
+
estimateFeatureTimeline(scenario, multiplier) {
|
|
229
|
+
const baseDays = {
|
|
230
|
+
trivial: 0.5,
|
|
231
|
+
simple: 2,
|
|
232
|
+
moderate: 5,
|
|
233
|
+
complex: 10,
|
|
234
|
+
very_complex: 20
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const base = baseDays[scenario.complexity] || 5;
|
|
238
|
+
const adjusted = base * multiplier;
|
|
239
|
+
|
|
240
|
+
// Factor in dependencies
|
|
241
|
+
const depFactor = (scenario.dependencies?.length || 0) * 0.5;
|
|
242
|
+
|
|
243
|
+
const totalDays = adjusted + depFactor;
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
estimatedDays: Math.round(totalDays),
|
|
247
|
+
range: {
|
|
248
|
+
optimistic: Math.round(totalDays * 0.7),
|
|
249
|
+
likely: Math.round(totalDays),
|
|
250
|
+
pessimistic: Math.round(totalDays * 1.5)
|
|
251
|
+
},
|
|
252
|
+
phases: [
|
|
253
|
+
{ name: 'Design', days: Math.round(totalDays * 0.15) },
|
|
254
|
+
{ name: 'Implementation', days: Math.round(totalDays * 0.5) },
|
|
255
|
+
{ name: 'Testing', days: Math.round(totalDays * 0.25) },
|
|
256
|
+
{ name: 'Documentation', days: Math.round(totalDays * 0.1) }
|
|
257
|
+
],
|
|
258
|
+
confidence: scenario.complexity === 'trivial' ? 'high' : 'medium'
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Identify feature risks
|
|
264
|
+
* @param {Object} scenario - Feature scenario
|
|
265
|
+
* @param {string} complexity - Complexity level
|
|
266
|
+
*/
|
|
267
|
+
identifyFeatureRisks(scenario, complexity) {
|
|
268
|
+
const risks = [];
|
|
269
|
+
|
|
270
|
+
// Complexity-based risks
|
|
271
|
+
if (complexity === 'complex' || complexity === 'very_complex') {
|
|
272
|
+
risks.push({
|
|
273
|
+
type: 'complexity',
|
|
274
|
+
severity: 'high',
|
|
275
|
+
description: 'High complexity increases implementation risk',
|
|
276
|
+
mitigation: 'Break into smaller increments, add review checkpoints'
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Dependency risks
|
|
281
|
+
if (scenario.dependencies?.length > 3) {
|
|
282
|
+
risks.push({
|
|
283
|
+
type: 'dependencies',
|
|
284
|
+
severity: 'medium',
|
|
285
|
+
description: 'Multiple new dependencies increase integration risk',
|
|
286
|
+
mitigation: 'Evaluate alternatives, lock versions, add integration tests'
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Timeline risks
|
|
291
|
+
if (scenario.timeline === 'tight') {
|
|
292
|
+
risks.push({
|
|
293
|
+
type: 'timeline',
|
|
294
|
+
severity: 'high',
|
|
295
|
+
description: 'Tight timeline may impact quality',
|
|
296
|
+
mitigation: 'Identify scope reduction options, prioritize core functionality'
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Integration risks
|
|
301
|
+
risks.push({
|
|
302
|
+
type: 'integration',
|
|
303
|
+
severity: complexity === 'trivial' ? 'low' : 'medium',
|
|
304
|
+
description: 'New feature may affect existing functionality',
|
|
305
|
+
mitigation: 'Comprehensive regression testing, feature flags'
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
return risks;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Analyze feature dependencies
|
|
313
|
+
* @param {Object} scenario - Feature scenario
|
|
314
|
+
*/
|
|
315
|
+
analyzeFeatureDependencies(scenario) {
|
|
316
|
+
const deps = scenario.dependencies || [];
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
explicit: deps,
|
|
320
|
+
count: deps.length,
|
|
321
|
+
impact: deps.length > 3 ? 'high' : deps.length > 1 ? 'medium' : 'low',
|
|
322
|
+
suggestions: deps.length > 0 ? [
|
|
323
|
+
'Lock dependency versions for stability',
|
|
324
|
+
'Add integration tests for dependency interactions',
|
|
325
|
+
'Document dependency requirements'
|
|
326
|
+
] : ['No external dependencies - good isolation']
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Assess documentation needs
|
|
332
|
+
* @param {Object} scenario - Feature scenario
|
|
333
|
+
*/
|
|
334
|
+
assessDocumentationNeeds(scenario) {
|
|
335
|
+
const complexity = scenario.complexity || 'moderate';
|
|
336
|
+
|
|
337
|
+
const docs = [];
|
|
338
|
+
|
|
339
|
+
// API docs if backend
|
|
340
|
+
if (scenario.includesApi !== false) {
|
|
341
|
+
docs.push({
|
|
342
|
+
type: 'API',
|
|
343
|
+
priority: 'high',
|
|
344
|
+
description: 'API endpoint documentation'
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// User docs
|
|
349
|
+
if (complexity !== 'trivial') {
|
|
350
|
+
docs.push({
|
|
351
|
+
type: 'User Guide',
|
|
352
|
+
priority: 'medium',
|
|
353
|
+
description: 'End-user documentation'
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Technical docs for complex features
|
|
358
|
+
if (complexity === 'complex' || complexity === 'very_complex') {
|
|
359
|
+
docs.push({
|
|
360
|
+
type: 'Technical',
|
|
361
|
+
priority: 'high',
|
|
362
|
+
description: 'Architecture and design documentation'
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// PRD update
|
|
367
|
+
docs.push({
|
|
368
|
+
type: 'PRD',
|
|
369
|
+
priority: 'high',
|
|
370
|
+
description: 'Update PRD with new feature'
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
required: docs,
|
|
375
|
+
estimatedEffort: `${docs.length * 2} hours`,
|
|
376
|
+
automatable: ['API documentation can be auto-generated from code']
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Assess testing needs
|
|
382
|
+
* @param {Object} scenario - Feature scenario
|
|
383
|
+
* @param {string} complexity - Complexity level
|
|
384
|
+
*/
|
|
385
|
+
assessTestingNeeds(scenario, complexity) {
|
|
386
|
+
const tests = [];
|
|
387
|
+
|
|
388
|
+
// Unit tests always needed
|
|
389
|
+
tests.push({
|
|
390
|
+
type: 'Unit',
|
|
391
|
+
coverage: complexity === 'trivial' ? '70%' : '80%',
|
|
392
|
+
priority: 'high'
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Integration tests for non-trivial
|
|
396
|
+
if (complexity !== 'trivial') {
|
|
397
|
+
tests.push({
|
|
398
|
+
type: 'Integration',
|
|
399
|
+
coverage: '60%',
|
|
400
|
+
priority: 'high'
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// E2E for complex features
|
|
405
|
+
if (complexity === 'complex' || complexity === 'very_complex') {
|
|
406
|
+
tests.push({
|
|
407
|
+
type: 'E2E',
|
|
408
|
+
coverage: 'Critical paths',
|
|
409
|
+
priority: 'high'
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Performance tests if needed
|
|
414
|
+
if (scenario.performanceSensitive) {
|
|
415
|
+
tests.push({
|
|
416
|
+
type: 'Performance',
|
|
417
|
+
coverage: 'Key operations',
|
|
418
|
+
priority: 'medium'
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
required: tests,
|
|
424
|
+
estimatedTestCode: this.estimateTestCode(complexity),
|
|
425
|
+
regressionRisk: complexity === 'trivial' ? 'low' : 'medium'
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Estimate test code effort
|
|
431
|
+
* @param {string} complexity - Complexity level
|
|
432
|
+
*/
|
|
433
|
+
estimateTestCode(complexity) {
|
|
434
|
+
const ratios = {
|
|
435
|
+
trivial: '0.3x implementation',
|
|
436
|
+
simple: '0.5x implementation',
|
|
437
|
+
moderate: '0.7x implementation',
|
|
438
|
+
complex: '1x implementation',
|
|
439
|
+
very_complex: '1.2x implementation'
|
|
440
|
+
};
|
|
441
|
+
return ratios[complexity] || '0.7x implementation';
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Assess feature impact
|
|
446
|
+
* @param {Object} scenario - Feature scenario
|
|
447
|
+
* @param {string} complexity - Complexity level
|
|
448
|
+
*/
|
|
449
|
+
assessFeatureImpact(scenario, complexity) {
|
|
450
|
+
const impactScore = COMPLEXITY_MULTIPLIERS[complexity] || 1.5;
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
codebase: impactScore > 2 ? 'high' : impactScore > 1 ? 'medium' : 'low',
|
|
454
|
+
users: scenario.userFacing !== false ? 'visible' : 'internal',
|
|
455
|
+
performance: scenario.performanceSensitive ? 'may impact' : 'minimal',
|
|
456
|
+
security: scenario.securitySensitive ? 'requires review' : 'standard',
|
|
457
|
+
maintenance: impactScore > 2.5 ? 'increased' : 'normal'
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Simulate refactoring
|
|
463
|
+
* @param {Object} scenario - Refactor scenario
|
|
464
|
+
*/
|
|
465
|
+
async simulateRefactor(scenario) {
|
|
466
|
+
return {
|
|
467
|
+
risk: this.assessRefactoringRisk(scenario),
|
|
468
|
+
testing: this.assessRefactoringTests(scenario),
|
|
469
|
+
timeline: this.estimateRefactoringTimeline(scenario),
|
|
470
|
+
documentation: this.assessRefactoringDocs(scenario),
|
|
471
|
+
rollback: this.planRefactoringRollback(scenario)
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Assess refactoring risk
|
|
477
|
+
* @param {Object} scenario - Refactor scenario
|
|
478
|
+
*/
|
|
479
|
+
assessRefactoringRisk(scenario) {
|
|
480
|
+
const scope = scenario.scope || 'module';
|
|
481
|
+
const risks = [];
|
|
482
|
+
|
|
483
|
+
if (scope === 'system' || scope === 'architecture') {
|
|
484
|
+
risks.push({
|
|
485
|
+
type: 'scope',
|
|
486
|
+
severity: 'high',
|
|
487
|
+
description: 'Wide-reaching changes increase regression risk',
|
|
488
|
+
mitigation: 'Incremental approach with feature flags'
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
risks.push({
|
|
493
|
+
type: 'regression',
|
|
494
|
+
severity: scope === 'file' ? 'low' : 'medium',
|
|
495
|
+
description: 'Existing functionality may break',
|
|
496
|
+
mitigation: 'Comprehensive test coverage before starting'
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (!this.projectContext?.hasTests) {
|
|
500
|
+
risks.push({
|
|
501
|
+
type: 'testing',
|
|
502
|
+
severity: 'high',
|
|
503
|
+
description: 'Lack of existing tests increases risk',
|
|
504
|
+
mitigation: 'Write characterization tests first'
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return {
|
|
509
|
+
overall: scope === 'system' ? 'high' : scope === 'module' ? 'medium' : 'low',
|
|
510
|
+
factors: risks
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Assess refactoring test requirements
|
|
516
|
+
* @param {Object} scenario - Refactor scenario
|
|
517
|
+
*/
|
|
518
|
+
assessRefactoringTests(scenario) {
|
|
519
|
+
return {
|
|
520
|
+
beforeRefactor: [
|
|
521
|
+
'Characterization tests for current behavior',
|
|
522
|
+
'Integration tests for affected modules',
|
|
523
|
+
'Performance baseline if relevant'
|
|
524
|
+
],
|
|
525
|
+
afterRefactor: [
|
|
526
|
+
'Same tests must pass',
|
|
527
|
+
'Add tests for new structure',
|
|
528
|
+
'Regression suite execution'
|
|
529
|
+
],
|
|
530
|
+
coverage: 'Ensure 80%+ coverage of refactored code'
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Estimate refactoring timeline
|
|
536
|
+
* @param {Object} scenario - Refactor scenario
|
|
537
|
+
*/
|
|
538
|
+
estimateRefactoringTimeline(scenario) {
|
|
539
|
+
const scopeMultipliers = {
|
|
540
|
+
file: 1,
|
|
541
|
+
module: 3,
|
|
542
|
+
feature: 5,
|
|
543
|
+
system: 10,
|
|
544
|
+
architecture: 20
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const baseDays = scopeMultipliers[scenario.scope] || 3;
|
|
548
|
+
|
|
549
|
+
return {
|
|
550
|
+
estimatedDays: baseDays,
|
|
551
|
+
phases: [
|
|
552
|
+
{ name: 'Analysis', days: Math.round(baseDays * 0.2) },
|
|
553
|
+
{ name: 'Test Setup', days: Math.round(baseDays * 0.2) },
|
|
554
|
+
{ name: 'Refactor', days: Math.round(baseDays * 0.4) },
|
|
555
|
+
{ name: 'Validation', days: Math.round(baseDays * 0.2) }
|
|
556
|
+
]
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Assess refactoring documentation
|
|
562
|
+
* @param {Object} scenario - Refactor scenario
|
|
563
|
+
*/
|
|
564
|
+
assessRefactoringDocs(scenario) {
|
|
565
|
+
return {
|
|
566
|
+
required: [
|
|
567
|
+
'ADR for refactoring decision',
|
|
568
|
+
'Updated architecture docs if structure changes',
|
|
569
|
+
'Migration guide if API changes'
|
|
570
|
+
],
|
|
571
|
+
updateNeeded: ['Technical spec', 'Code comments']
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Plan refactoring rollback
|
|
577
|
+
* @param {Object} scenario - Refactor scenario
|
|
578
|
+
*/
|
|
579
|
+
planRefactoringRollback(scenario) {
|
|
580
|
+
return {
|
|
581
|
+
strategy: 'git revert',
|
|
582
|
+
preparation: [
|
|
583
|
+
'Commit frequently with clear messages',
|
|
584
|
+
'Use feature branch',
|
|
585
|
+
'Tag stable points'
|
|
586
|
+
],
|
|
587
|
+
timeToRollback: scenario.scope === 'system' ? '2-4 hours' : '15-30 minutes'
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Simulate timeline change
|
|
593
|
+
* @param {Object} scenario - Timeline scenario
|
|
594
|
+
*/
|
|
595
|
+
async simulateTimeline(scenario) {
|
|
596
|
+
const change = scenario.change; // 'compress' or 'extend'
|
|
597
|
+
const amount = scenario.amount || 20; // percentage
|
|
598
|
+
|
|
599
|
+
return {
|
|
600
|
+
scope: this.assessTimelineScopeImpact(change, amount),
|
|
601
|
+
quality: this.assessTimelineQualityImpact(change, amount),
|
|
602
|
+
resources: this.assessTimelineResourceImpact(change, amount),
|
|
603
|
+
risks: this.identifyTimelineRisks(change, amount)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Assess timeline scope impact
|
|
609
|
+
* @param {string} change - Change type
|
|
610
|
+
* @param {number} amount - Percentage change
|
|
611
|
+
*/
|
|
612
|
+
assessTimelineScopeImpact(change, amount) {
|
|
613
|
+
if (change === 'compress') {
|
|
614
|
+
return {
|
|
615
|
+
impact: amount > 30 ? 'high' : 'medium',
|
|
616
|
+
recommendations: [
|
|
617
|
+
'Prioritize MVP features',
|
|
618
|
+
'Defer nice-to-have features',
|
|
619
|
+
'Consider phased release'
|
|
620
|
+
],
|
|
621
|
+
scopeReduction: `${Math.round(amount * 0.7)}%`
|
|
622
|
+
};
|
|
623
|
+
} else {
|
|
624
|
+
return {
|
|
625
|
+
impact: 'positive',
|
|
626
|
+
recommendations: [
|
|
627
|
+
'Can include more features',
|
|
628
|
+
'More time for polish',
|
|
629
|
+
'Better testing coverage'
|
|
630
|
+
],
|
|
631
|
+
additionalScope: `${Math.round(amount * 0.5)}%`
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Assess timeline quality impact
|
|
638
|
+
* @param {string} change - Change type
|
|
639
|
+
* @param {number} amount - Percentage change
|
|
640
|
+
*/
|
|
641
|
+
assessTimelineQualityImpact(change, amount) {
|
|
642
|
+
if (change === 'compress') {
|
|
643
|
+
return {
|
|
644
|
+
risk: amount > 30 ? 'high' : 'medium',
|
|
645
|
+
areas: [
|
|
646
|
+
{ name: 'Test coverage', impact: 'may decrease' },
|
|
647
|
+
{ name: 'Code reviews', impact: 'less thorough' },
|
|
648
|
+
{ name: 'Documentation', impact: 'likely reduced' }
|
|
649
|
+
],
|
|
650
|
+
mitigations: [
|
|
651
|
+
'Automated testing critical',
|
|
652
|
+
'Focus on critical path quality',
|
|
653
|
+
'Accept technical debt consciously'
|
|
654
|
+
]
|
|
655
|
+
};
|
|
656
|
+
} else {
|
|
657
|
+
return {
|
|
658
|
+
risk: 'low',
|
|
659
|
+
areas: [
|
|
660
|
+
{ name: 'Test coverage', impact: 'can increase' },
|
|
661
|
+
{ name: 'Code reviews', impact: 'more thorough' },
|
|
662
|
+
{ name: 'Documentation', impact: 'can improve' }
|
|
663
|
+
]
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Assess timeline resource impact
|
|
670
|
+
* @param {string} change - Change type
|
|
671
|
+
* @param {number} amount - Percentage change
|
|
672
|
+
*/
|
|
673
|
+
assessTimelineResourceImpact(change, amount) {
|
|
674
|
+
if (change === 'compress') {
|
|
675
|
+
return {
|
|
676
|
+
options: [
|
|
677
|
+
'Add team members (ramp-up overhead)',
|
|
678
|
+
'Increase working hours (burnout risk)',
|
|
679
|
+
'Outsource components (coordination overhead)'
|
|
680
|
+
],
|
|
681
|
+
recommendation: amount > 20 ?
|
|
682
|
+
'Consider scope reduction over resource increase' :
|
|
683
|
+
'Current team may absorb with focused priorities'
|
|
684
|
+
};
|
|
685
|
+
} else {
|
|
686
|
+
return {
|
|
687
|
+
options: [
|
|
688
|
+
'Maintain current team',
|
|
689
|
+
'Add quality-focused resources',
|
|
690
|
+
'Invest in tooling/automation'
|
|
691
|
+
],
|
|
692
|
+
recommendation: 'Use time for quality improvements, not just features'
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Identify timeline risks
|
|
699
|
+
* @param {string} change - Change type
|
|
700
|
+
* @param {number} amount - Percentage change
|
|
701
|
+
*/
|
|
702
|
+
identifyTimelineRisks(change, amount) {
|
|
703
|
+
const risks = [];
|
|
704
|
+
|
|
705
|
+
if (change === 'compress') {
|
|
706
|
+
risks.push({
|
|
707
|
+
type: 'burnout',
|
|
708
|
+
severity: amount > 30 ? 'high' : 'medium',
|
|
709
|
+
description: 'Team fatigue from accelerated pace',
|
|
710
|
+
mitigation: 'Clear priorities, sustainable pace commitment'
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
risks.push({
|
|
714
|
+
type: 'technical_debt',
|
|
715
|
+
severity: 'high',
|
|
716
|
+
description: 'Shortcuts accumulate',
|
|
717
|
+
mitigation: 'Track debt explicitly, plan remediation'
|
|
718
|
+
});
|
|
719
|
+
} else {
|
|
720
|
+
risks.push({
|
|
721
|
+
type: 'scope_creep',
|
|
722
|
+
severity: 'medium',
|
|
723
|
+
description: 'Extra time invites additional features',
|
|
724
|
+
mitigation: 'Lock scope, use time for quality'
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return risks;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Simulate resource change
|
|
733
|
+
* @param {Object} scenario - Resource scenario
|
|
734
|
+
*/
|
|
735
|
+
async simulateResource(scenario) {
|
|
736
|
+
const action = scenario.action; // 'add' or 'remove'
|
|
737
|
+
const count = scenario.count || 1;
|
|
738
|
+
|
|
739
|
+
return {
|
|
740
|
+
velocity: this.assessResourceVelocity(action, count),
|
|
741
|
+
coordination: this.assessResourceCoordination(action, count),
|
|
742
|
+
timeline: this.assessResourceTimeline(action, count),
|
|
743
|
+
quality: this.assessResourceQuality(action, count),
|
|
744
|
+
costs: this.assessResourceCosts(action, count)
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Assess resource velocity impact
|
|
750
|
+
* @param {string} action - Add or remove
|
|
751
|
+
* @param {number} count - Number of resources
|
|
752
|
+
*/
|
|
753
|
+
assessResourceVelocity(action, count) {
|
|
754
|
+
if (action === 'add') {
|
|
755
|
+
return {
|
|
756
|
+
immediate: 'Decreased (onboarding)',
|
|
757
|
+
shortTerm: `+${count * 15}% after ramp-up`,
|
|
758
|
+
longTerm: `+${count * 25}% at full productivity`,
|
|
759
|
+
rampUpTime: `${count * 2}-${count * 4} weeks`
|
|
760
|
+
};
|
|
761
|
+
} else {
|
|
762
|
+
return {
|
|
763
|
+
immediate: `-${count * 30}%`,
|
|
764
|
+
shortTerm: 'Stabilizes as work redistributes',
|
|
765
|
+
longTerm: 'New baseline established',
|
|
766
|
+
knowledgeLoss: 'Critical knowledge may be lost'
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Assess resource coordination impact
|
|
773
|
+
* @param {string} action - Add or remove
|
|
774
|
+
* @param {number} count - Number of resources
|
|
775
|
+
*/
|
|
776
|
+
assessResourceCoordination(action, count) {
|
|
777
|
+
if (action === 'add') {
|
|
778
|
+
return {
|
|
779
|
+
overhead: count > 2 ? 'high' : 'medium',
|
|
780
|
+
meetings: 'More coordination meetings needed',
|
|
781
|
+
communication: 'Communication channels expand',
|
|
782
|
+
recommendations: [
|
|
783
|
+
'Clear ownership boundaries',
|
|
784
|
+
'Regular sync meetings',
|
|
785
|
+
'Documentation emphasis'
|
|
786
|
+
]
|
|
787
|
+
};
|
|
788
|
+
} else {
|
|
789
|
+
return {
|
|
790
|
+
overhead: 'Decreased',
|
|
791
|
+
consolidation: 'Responsibilities must be reassigned',
|
|
792
|
+
recommendations: [
|
|
793
|
+
'Knowledge transfer sessions',
|
|
794
|
+
'Document tribal knowledge',
|
|
795
|
+
'Update ownership docs'
|
|
796
|
+
]
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Assess resource timeline impact
|
|
803
|
+
* @param {string} action - Add or remove
|
|
804
|
+
* @param {number} count - Number of resources
|
|
805
|
+
*/
|
|
806
|
+
assessResourceTimeline(action, count) {
|
|
807
|
+
if (action === 'add') {
|
|
808
|
+
return {
|
|
809
|
+
effect: 'Potential acceleration after ramp-up',
|
|
810
|
+
caution: 'Brooks Law: adding people to late projects makes them later',
|
|
811
|
+
netEffect: count > 2 ? 'Diminishing returns likely' : 'Moderate acceleration possible'
|
|
812
|
+
};
|
|
813
|
+
} else {
|
|
814
|
+
return {
|
|
815
|
+
effect: 'Timeline extension likely needed',
|
|
816
|
+
amount: `${count * 15}-${count * 25}% longer`,
|
|
817
|
+
alternatives: ['Reduce scope', 'Accept delays', 'Backfill']
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Assess resource quality impact
|
|
824
|
+
* @param {string} action - Add or remove
|
|
825
|
+
* @param {number} count - Number of resources
|
|
826
|
+
*/
|
|
827
|
+
assessResourceQuality(action, count) {
|
|
828
|
+
if (action === 'add') {
|
|
829
|
+
return {
|
|
830
|
+
shortTerm: 'May decrease (new contributors learning)',
|
|
831
|
+
longTerm: 'Can increase (more reviewers, diverse perspectives)',
|
|
832
|
+
risks: ['Inconsistent coding styles', 'Knowledge silos']
|
|
833
|
+
};
|
|
834
|
+
} else {
|
|
835
|
+
return {
|
|
836
|
+
shortTerm: 'May decrease (remaining team stretched)',
|
|
837
|
+
risks: ['Fewer reviewers', 'Burnout risk', 'Knowledge gaps']
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Assess resource costs
|
|
844
|
+
* @param {string} action - Add or remove
|
|
845
|
+
* @param {number} count - Number of resources
|
|
846
|
+
*/
|
|
847
|
+
assessResourceCosts(action, count) {
|
|
848
|
+
// Assuming $150k/year average fully loaded cost
|
|
849
|
+
const avgCost = 150000;
|
|
850
|
+
const monthlyImpact = Math.round((avgCost / 12) * count);
|
|
851
|
+
|
|
852
|
+
if (action === 'add') {
|
|
853
|
+
return {
|
|
854
|
+
monthly: `+$${monthlyImpact.toLocaleString()}`,
|
|
855
|
+
oneTime: `$${(count * 5000).toLocaleString()} (equipment, onboarding)`,
|
|
856
|
+
notes: 'Productivity lag during ramp-up'
|
|
857
|
+
};
|
|
858
|
+
} else {
|
|
859
|
+
return {
|
|
860
|
+
monthly: `-$${monthlyImpact.toLocaleString()}`,
|
|
861
|
+
oneTime: 'Potential severance costs',
|
|
862
|
+
notes: 'Hidden costs: knowledge loss, morale impact'
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Simulate dependency change
|
|
869
|
+
* @param {Object} scenario - Dependency scenario
|
|
870
|
+
*/
|
|
871
|
+
async simulateDependency(scenario) {
|
|
872
|
+
return {
|
|
873
|
+
compatibility: this.assessDependencyCompatibility(scenario),
|
|
874
|
+
security: this.assessDependencySecurity(scenario),
|
|
875
|
+
bundleSize: this.assessDependencyBundleSize(scenario),
|
|
876
|
+
maintenance: this.assessDependencyMaintenance(scenario)
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Assess dependency compatibility
|
|
882
|
+
* @param {Object} scenario - Dependency scenario
|
|
883
|
+
*/
|
|
884
|
+
assessDependencyCompatibility(scenario) {
|
|
885
|
+
return {
|
|
886
|
+
risk: scenario.action === 'add' ? 'medium' : 'low',
|
|
887
|
+
checks: [
|
|
888
|
+
'Verify version compatibility with existing deps',
|
|
889
|
+
'Check peer dependency requirements',
|
|
890
|
+
'Test in staging environment'
|
|
891
|
+
]
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Assess dependency security
|
|
897
|
+
* @param {Object} scenario - Dependency scenario
|
|
898
|
+
*/
|
|
899
|
+
assessDependencySecurity(scenario) {
|
|
900
|
+
return {
|
|
901
|
+
action: 'Run npm audit after change',
|
|
902
|
+
considerations: [
|
|
903
|
+
'Check for known vulnerabilities',
|
|
904
|
+
'Review package maintainer reputation',
|
|
905
|
+
'Consider supply chain risks'
|
|
906
|
+
]
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Assess dependency bundle size
|
|
912
|
+
* @param {Object} scenario - Dependency scenario
|
|
913
|
+
*/
|
|
914
|
+
assessDependencyBundleSize(scenario) {
|
|
915
|
+
if (scenario.action === 'add') {
|
|
916
|
+
return {
|
|
917
|
+
impact: 'Increased',
|
|
918
|
+
recommendations: [
|
|
919
|
+
'Check bundle size with bundlephobia.com',
|
|
920
|
+
'Consider tree-shaking support',
|
|
921
|
+
'Evaluate alternatives'
|
|
922
|
+
]
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
impact: 'Decreased',
|
|
927
|
+
recommendations: ['Verify bundle reduction']
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Assess dependency maintenance
|
|
933
|
+
* @param {Object} scenario - Dependency scenario
|
|
934
|
+
*/
|
|
935
|
+
assessDependencyMaintenance(scenario) {
|
|
936
|
+
return {
|
|
937
|
+
ongoing: [
|
|
938
|
+
'Monitor for security updates',
|
|
939
|
+
'Track breaking changes in major versions',
|
|
940
|
+
'Consider dependabot or similar'
|
|
941
|
+
]
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Simulate architecture change
|
|
947
|
+
* @param {Object} scenario - Architecture scenario
|
|
948
|
+
*/
|
|
949
|
+
async simulateArchitecture(scenario) {
|
|
950
|
+
return {
|
|
951
|
+
complexity: this.assessArchitectureComplexity(scenario),
|
|
952
|
+
risk: this.assessArchitectureRisk(scenario),
|
|
953
|
+
timeline: this.estimateArchitectureTimeline(scenario),
|
|
954
|
+
benefits: this.identifyArchitectureBenefits(scenario)
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Assess architecture complexity
|
|
960
|
+
* @param {Object} scenario - Architecture scenario
|
|
961
|
+
*/
|
|
962
|
+
assessArchitectureComplexity(scenario) {
|
|
963
|
+
const components = scenario.components?.length || 1;
|
|
964
|
+
return {
|
|
965
|
+
level: components > 5 ? 'very_high' : components > 2 ? 'high' : 'medium',
|
|
966
|
+
factors: [
|
|
967
|
+
'Cross-cutting concerns',
|
|
968
|
+
'Data migration needs',
|
|
969
|
+
'API compatibility',
|
|
970
|
+
'Testing strategy changes'
|
|
971
|
+
]
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Assess architecture risk
|
|
977
|
+
* @param {Object} scenario - Architecture scenario
|
|
978
|
+
*/
|
|
979
|
+
assessArchitectureRisk(scenario) {
|
|
980
|
+
return {
|
|
981
|
+
overall: 'high',
|
|
982
|
+
factors: [
|
|
983
|
+
{
|
|
984
|
+
type: 'scope',
|
|
985
|
+
severity: 'high',
|
|
986
|
+
description: 'Architecture changes affect many components'
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
type: 'knowledge',
|
|
990
|
+
severity: 'medium',
|
|
991
|
+
description: 'Team needs to learn new patterns'
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
type: 'rollback',
|
|
995
|
+
severity: 'high',
|
|
996
|
+
description: 'Difficult to revert once started'
|
|
997
|
+
}
|
|
998
|
+
],
|
|
999
|
+
mitigation: [
|
|
1000
|
+
'Incremental migration',
|
|
1001
|
+
'Parallel running period',
|
|
1002
|
+
'Feature flags for gradual rollout'
|
|
1003
|
+
]
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Estimate architecture timeline
|
|
1009
|
+
* @param {Object} scenario - Architecture scenario
|
|
1010
|
+
*/
|
|
1011
|
+
estimateArchitectureTimeline(scenario) {
|
|
1012
|
+
const components = scenario.components?.length || 1;
|
|
1013
|
+
const weeks = components * 2;
|
|
1014
|
+
|
|
1015
|
+
return {
|
|
1016
|
+
estimatedWeeks: weeks,
|
|
1017
|
+
phases: [
|
|
1018
|
+
{ name: 'Design', weeks: Math.ceil(weeks * 0.2) },
|
|
1019
|
+
{ name: 'Foundation', weeks: Math.ceil(weeks * 0.3) },
|
|
1020
|
+
{ name: 'Migration', weeks: Math.ceil(weeks * 0.35) },
|
|
1021
|
+
{ name: 'Validation', weeks: Math.ceil(weeks * 0.15) }
|
|
1022
|
+
],
|
|
1023
|
+
parallelWork: 'Limited - changes are often sequential'
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Identify architecture benefits
|
|
1029
|
+
* @param {Object} scenario - Architecture scenario
|
|
1030
|
+
*/
|
|
1031
|
+
identifyArchitectureBenefits(scenario) {
|
|
1032
|
+
return {
|
|
1033
|
+
shortTerm: [
|
|
1034
|
+
'Cleaner code structure',
|
|
1035
|
+
'Better separation of concerns'
|
|
1036
|
+
],
|
|
1037
|
+
longTerm: [
|
|
1038
|
+
'Easier to maintain',
|
|
1039
|
+
'Better scalability',
|
|
1040
|
+
'Improved testability',
|
|
1041
|
+
'Faster feature development'
|
|
1042
|
+
],
|
|
1043
|
+
breakEven: 'Typically 3-6 months post-migration'
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* Simulate technology migration
|
|
1049
|
+
* @param {Object} scenario - Migration scenario
|
|
1050
|
+
*/
|
|
1051
|
+
async simulateMigration(scenario) {
|
|
1052
|
+
return {
|
|
1053
|
+
effort: this.estimateMigrationEffort(scenario),
|
|
1054
|
+
risk: this.assessMigrationRisk(scenario),
|
|
1055
|
+
timeline: this.estimateMigrationTimeline(scenario),
|
|
1056
|
+
training: this.assessMigrationTraining(scenario)
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* Estimate migration effort
|
|
1062
|
+
* @param {Object} scenario - Migration scenario
|
|
1063
|
+
*/
|
|
1064
|
+
estimateMigrationEffort(scenario) {
|
|
1065
|
+
return {
|
|
1066
|
+
level: 'high',
|
|
1067
|
+
components: [
|
|
1068
|
+
'Code migration',
|
|
1069
|
+
'Configuration updates',
|
|
1070
|
+
'Testing updates',
|
|
1071
|
+
'Documentation updates',
|
|
1072
|
+
'CI/CD pipeline changes'
|
|
1073
|
+
],
|
|
1074
|
+
hiddenEffort: [
|
|
1075
|
+
'Learning curve',
|
|
1076
|
+
'Debugging new issues',
|
|
1077
|
+
'Performance tuning'
|
|
1078
|
+
]
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Assess migration risk
|
|
1084
|
+
* @param {Object} scenario - Migration scenario
|
|
1085
|
+
*/
|
|
1086
|
+
assessMigrationRisk(scenario) {
|
|
1087
|
+
return {
|
|
1088
|
+
overall: 'high',
|
|
1089
|
+
factors: [
|
|
1090
|
+
'Breaking changes likely',
|
|
1091
|
+
'Knowledge gaps in new technology',
|
|
1092
|
+
'Integration unknowns',
|
|
1093
|
+
'Performance differences'
|
|
1094
|
+
],
|
|
1095
|
+
mitigation: [
|
|
1096
|
+
'Proof of concept first',
|
|
1097
|
+
'Incremental migration',
|
|
1098
|
+
'Comprehensive testing',
|
|
1099
|
+
'Rollback plan'
|
|
1100
|
+
]
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Estimate migration timeline
|
|
1106
|
+
* @param {Object} scenario - Migration scenario
|
|
1107
|
+
*/
|
|
1108
|
+
estimateMigrationTimeline(scenario) {
|
|
1109
|
+
const scope = scenario.scope || 'full';
|
|
1110
|
+
const baseWeeks = scope === 'full' ? 8 : 4;
|
|
1111
|
+
|
|
1112
|
+
return {
|
|
1113
|
+
estimatedWeeks: baseWeeks,
|
|
1114
|
+
phases: [
|
|
1115
|
+
{ name: 'Planning & POC', weeks: 2 },
|
|
1116
|
+
{ name: 'Core Migration', weeks: Math.round(baseWeeks * 0.5) },
|
|
1117
|
+
{ name: 'Testing & Fixes', weeks: Math.round(baseWeeks * 0.25) },
|
|
1118
|
+
{ name: 'Deployment', weeks: 1 }
|
|
1119
|
+
],
|
|
1120
|
+
parallel: scenario.parallel ? 'Run both systems during transition' : 'Big bang cutover'
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Assess migration training needs
|
|
1126
|
+
* @param {Object} scenario - Migration scenario
|
|
1127
|
+
*/
|
|
1128
|
+
assessMigrationTraining(scenario) {
|
|
1129
|
+
return {
|
|
1130
|
+
needed: true,
|
|
1131
|
+
areas: [
|
|
1132
|
+
`${scenario.to} fundamentals`,
|
|
1133
|
+
'New patterns and best practices',
|
|
1134
|
+
'Tooling changes',
|
|
1135
|
+
'Debugging techniques'
|
|
1136
|
+
],
|
|
1137
|
+
estimatedHours: '20-40 hours per developer',
|
|
1138
|
+
resources: [
|
|
1139
|
+
'Official documentation',
|
|
1140
|
+
'Online courses',
|
|
1141
|
+
'Pair programming sessions',
|
|
1142
|
+
'Code reviews'
|
|
1143
|
+
]
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Generate simulation summary
|
|
1149
|
+
* @param {Object} results - Simulation results
|
|
1150
|
+
*/
|
|
1151
|
+
generateSummary(results) {
|
|
1152
|
+
const summaryPoints = [];
|
|
1153
|
+
|
|
1154
|
+
// Extract key insights from results
|
|
1155
|
+
for (const [key, value] of Object.entries(results)) {
|
|
1156
|
+
if (value.overall) {
|
|
1157
|
+
summaryPoints.push(`${key}: ${value.overall} impact`);
|
|
1158
|
+
} else if (value.estimatedDays) {
|
|
1159
|
+
summaryPoints.push(`Timeline: ${value.estimatedDays} days estimated`);
|
|
1160
|
+
} else if (value.level) {
|
|
1161
|
+
summaryPoints.push(`${key}: ${value.level}`);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return {
|
|
1166
|
+
keyPoints: summaryPoints,
|
|
1167
|
+
overallAssessment: this.determineOverallAssessment(results)
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Determine overall assessment
|
|
1173
|
+
* @param {Object} results - Simulation results
|
|
1174
|
+
*/
|
|
1175
|
+
determineOverallAssessment(results) {
|
|
1176
|
+
// Count high-risk/high-impact items
|
|
1177
|
+
let highCount = 0;
|
|
1178
|
+
const checkValue = (val) => {
|
|
1179
|
+
if (typeof val === 'object' && val !== null) {
|
|
1180
|
+
if (val.severity === 'high' || val.level === 'high' || val.impact === 'high') {
|
|
1181
|
+
highCount++;
|
|
1182
|
+
}
|
|
1183
|
+
Object.values(val).forEach(checkValue);
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
Object.values(results).forEach(checkValue);
|
|
1188
|
+
|
|
1189
|
+
if (highCount >= 3) {
|
|
1190
|
+
return 'High risk - careful planning required';
|
|
1191
|
+
} else if (highCount >= 1) {
|
|
1192
|
+
return 'Moderate risk - manageable with proper planning';
|
|
1193
|
+
} else {
|
|
1194
|
+
return 'Low risk - straightforward to execute';
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* Generate recommendations from simulation
|
|
1200
|
+
* @param {Object} scenario - Input scenario
|
|
1201
|
+
* @param {Object} results - Simulation results
|
|
1202
|
+
*/
|
|
1203
|
+
generateRecommendations(scenario, results) {
|
|
1204
|
+
const recommendations = [];
|
|
1205
|
+
|
|
1206
|
+
// Add risk-based recommendations
|
|
1207
|
+
if (results.risks) {
|
|
1208
|
+
const highRisks = results.risks.filter(r => r.severity === 'high');
|
|
1209
|
+
if (highRisks.length > 0) {
|
|
1210
|
+
recommendations.push({
|
|
1211
|
+
priority: 'high',
|
|
1212
|
+
action: 'Address high-severity risks before proceeding',
|
|
1213
|
+
details: highRisks.map(r => r.mitigation)
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// Add testing recommendations
|
|
1219
|
+
if (results.testing) {
|
|
1220
|
+
recommendations.push({
|
|
1221
|
+
priority: 'medium',
|
|
1222
|
+
action: 'Establish testing strategy',
|
|
1223
|
+
details: results.testing.required?.map(t => t.type) || []
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// Add documentation recommendations
|
|
1228
|
+
if (results.documentation) {
|
|
1229
|
+
recommendations.push({
|
|
1230
|
+
priority: 'medium',
|
|
1231
|
+
action: 'Update documentation',
|
|
1232
|
+
details: results.documentation.required?.map(d => d.type) || []
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// Add scenario-specific recommendations
|
|
1237
|
+
if (scenario.type === 'feature') {
|
|
1238
|
+
recommendations.push({
|
|
1239
|
+
priority: 'low',
|
|
1240
|
+
action: 'Consider feature decomposition',
|
|
1241
|
+
command: `bootspring plan decompose "${scenario.featureName}"`
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
return recommendations.sort((a, b) => {
|
|
1246
|
+
const order = { high: 0, medium: 1, low: 2 };
|
|
1247
|
+
return order[a.priority] - order[b.priority];
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Compare multiple scenarios
|
|
1253
|
+
* @param {Array} scenarios - Array of scenarios to compare
|
|
1254
|
+
*/
|
|
1255
|
+
async compare(scenarios) {
|
|
1256
|
+
const results = [];
|
|
1257
|
+
|
|
1258
|
+
for (const scenario of scenarios) {
|
|
1259
|
+
const result = await this.simulate(scenario);
|
|
1260
|
+
results.push({
|
|
1261
|
+
scenario: scenario,
|
|
1262
|
+
result: result
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
return {
|
|
1267
|
+
comparisons: results,
|
|
1268
|
+
recommendation: this.determinePreferredScenario(results),
|
|
1269
|
+
tradeoffs: this.identifyTradeoffs(results)
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Determine preferred scenario
|
|
1275
|
+
* @param {Array} results - Simulation results
|
|
1276
|
+
*/
|
|
1277
|
+
determinePreferredScenario(results) {
|
|
1278
|
+
// Simple scoring based on overall assessment
|
|
1279
|
+
const scores = results.map((r, i) => {
|
|
1280
|
+
let score = 100;
|
|
1281
|
+
const assessment = r.result.summary?.overallAssessment || '';
|
|
1282
|
+
|
|
1283
|
+
if (assessment.includes('High risk')) score -= 40;
|
|
1284
|
+
else if (assessment.includes('Moderate risk')) score -= 20;
|
|
1285
|
+
|
|
1286
|
+
return { index: i, scenario: r.scenario, score };
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
scores.sort((a, b) => b.score - a.score);
|
|
1290
|
+
|
|
1291
|
+
return {
|
|
1292
|
+
preferred: scores[0]?.scenario,
|
|
1293
|
+
ranking: scores.map(s => ({
|
|
1294
|
+
scenario: s.scenario.type,
|
|
1295
|
+
score: s.score
|
|
1296
|
+
}))
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Identify tradeoffs between scenarios
|
|
1302
|
+
* @param {Array} results - Simulation results
|
|
1303
|
+
*/
|
|
1304
|
+
identifyTradeoffs(results) {
|
|
1305
|
+
if (results.length < 2) return [];
|
|
1306
|
+
|
|
1307
|
+
const tradeoffs = [];
|
|
1308
|
+
|
|
1309
|
+
// Compare timeline vs risk for each pair
|
|
1310
|
+
for (let i = 0; i < results.length; i++) {
|
|
1311
|
+
for (let j = i + 1; j < results.length; j++) {
|
|
1312
|
+
tradeoffs.push({
|
|
1313
|
+
scenarios: [results[i].scenario.type, results[j].scenario.type],
|
|
1314
|
+
factors: ['timeline', 'risk', 'effort']
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
return tradeoffs;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
module.exports = {
|
|
1324
|
+
PlanningSimulator,
|
|
1325
|
+
SCENARIO_TYPES,
|
|
1326
|
+
IMPACT_LEVELS,
|
|
1327
|
+
COMPLEXITY_MULTIPLIERS
|
|
1328
|
+
};
|