UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

338 lines 13.6 kB
export class PrioritizationEngine { async prioritizeFeatures(features, criteria) { let results = []; switch (criteria.method) { case 'rice': results = await this.riceScoring(features); break; case 'value-effort': results = await this.valueEffortMatrix(features); break; case 'moscow': results = await this.moscowMethod(features); break; case 'kano': results = await this.kanoModel(features); break; case 'custom': results = await this.customWeightedScoring(features, criteria.weights); break; default: throw new Error(`Unknown prioritization method: ${criteria.method}`); } // Sort by score descending and assign ranks results.sort((a, b) => b.score - a.score); results.forEach((result, index) => { result.rank = index + 1; }); return results; } async riceScoring(features) { return features.map(feature => { // Calculate RICE components based on feature properties const reach = this.estimateReach(feature); const impact = this.estimateImpact(feature); const confidence = this.estimateConfidence(feature); const effort = this.estimateEffort(feature); const score = (reach * impact * confidence) / effort; const riceBreakdown = { reach, impact, confidence, effort, score }; return { featureId: feature.id, method: 'rice', score, rank: 0, // Will be set later rationale: `RICE Score: R=${reach}, I=${impact}, C=${confidence}, E=${effort}`, breakdown: riceBreakdown }; }); } async valueEffortMatrix(features) { return features.map(feature => { const value = feature.businessValue.score; const effort = this.estimateEffort(feature); // Normalize to 0-100 scale const normalizedEffort = Math.min(100, effort * 10); // Score calculation: high value + low effort = high score const score = value * (100 - normalizedEffort) / 100; const category = this.getValueEffortCategory(value, normalizedEffort); return { featureId: feature.id, method: 'value-effort', score, rank: 0, rationale: `Value: ${value}/100, Effort: ${normalizedEffort}/100, Category: ${category}`, breakdown: { value, effort: normalizedEffort, category } }; }); } async moscowMethod(features) { return features.map(feature => { const category = this.getMoscowCategory(feature); // Assign scores based on MoSCoW categories const categoryScores = { 'must-have': 100, 'should-have': 75, 'could-have': 50, 'wont-have': 25 }; const score = categoryScores[category]; return { featureId: feature.id, method: 'moscow', score, rank: 0, rationale: `MoSCoW Category: ${category}`, breakdown: { category } }; }); } async kanoModel(features) { return features.map(feature => { const category = this.getKanoCategory(feature); // Assign scores based on Kano categories const categoryScores = { 'basic': 90, // Must-be quality 'performance': 70, // One-dimensional quality 'excitement': 100, // Attractive quality 'indifferent': 30, // Indifferent quality 'reverse': 10 // Reverse quality }; const score = categoryScores[category]; return { featureId: feature.id, method: 'kano', score, rank: 0, rationale: `Kano Category: ${category}`, breakdown: { category } }; }); } async customWeightedScoring(features, weights) { return features.map(feature => { // Calculate individual scores (0-100 scale) const businessValueScore = feature.businessValue.score; const userImpactScore = this.calculateUserImpactScore(feature); const strategicAlignmentScore = this.calculateStrategicAlignmentScore(feature); const technicalFeasibilityScore = this.calculateTechnicalFeasibilityScore(feature); const riskScore = 100 - this.calculateRiskScore(feature); // Invert so lower risk = higher score // Apply weights const weightedScore = (businessValueScore * weights.businessValue + userImpactScore * weights.userImpact + strategicAlignmentScore * weights.strategicAlignment + technicalFeasibilityScore * weights.technicalFeasibility + riskScore * weights.risk) / (weights.businessValue + weights.userImpact + weights.strategicAlignment + weights.technicalFeasibility + weights.risk); return { featureId: feature.id, method: 'custom', score: weightedScore, rank: 0, rationale: `Weighted Score: BV=${businessValueScore}, UI=${userImpactScore}, SA=${strategicAlignmentScore}, TF=${technicalFeasibilityScore}, R=${riskScore}`, breakdown: { businessValue: businessValueScore, userImpact: userImpactScore, strategicAlignment: strategicAlignmentScore, technicalFeasibility: technicalFeasibilityScore, risk: riskScore, weights } }; }); } // Helper methods for RICE scoring estimateReach(feature) { // Estimate based on business value and user impact const impactLevel = feature.businessValue.score; if (impactLevel > 80) return 10000; // High reach if (impactLevel > 60) return 5000; // Medium reach if (impactLevel > 40) return 1000; // Low reach return 500; // Minimal reach } estimateImpact(feature) { // 3 = massive impact, 2 = high, 1 = medium, 0.5 = low, 0.25 = minimal const score = feature.businessValue.score; if (score > 80) return 3; if (score > 60) return 2; if (score > 40) return 1; if (score > 20) return 0.5; return 0.25; } estimateConfidence(feature) { // 100% = high confidence, 80% = medium, 50% = low // Based on feature status and complexity if (feature.status === 'approved') return 1.0; if (feature.status === 'proposed') return 0.8; if (feature.technicalComplexity === 'very-high') return 0.5; if (feature.technicalComplexity === 'high') return 0.7; return 0.9; } estimateEffort(feature) { // Estimate person-months based on complexity const complexityEffort = { 'low': 0.5, 'medium': 2, 'high': 4, 'very-high': 8 }; return complexityEffort[feature.technicalComplexity]; } // Helper methods for Value-Effort Matrix getValueEffortCategory(value, effort) { if (value > 60 && effort < 40) return 'Quick Wins'; if (value > 60 && effort >= 40) return 'Major Projects'; if (value <= 60 && effort < 40) return 'Fill-ins'; return 'Time Sinks'; } // Helper methods for MoSCoW getMoscowCategory(feature) { const score = feature.businessValue.score; const complexity = feature.technicalComplexity; // High value, manageable complexity = must have if (score > 70 && complexity !== 'very-high') return 'must-have'; // High value but complex, or medium value with low complexity = should have if ((score > 70 && complexity === 'very-high') || (score > 50 && complexity === 'low')) return 'should-have'; // Medium value = could have if (score > 30) return 'could-have'; // Low value = won't have this time return 'wont-have'; } // Helper methods for Kano Model getKanoCategory(feature) { // This is a simplified categorization - in practice, you'd survey users const value = feature.businessValue.score; const description = feature.description.toLowerCase(); // Basic features (must-haves) if (description.includes('security') || description.includes('performance') || description.includes('reliability')) { return 'basic'; } // Excitement features (delighters) if (description.includes('ai') || description.includes('innovative') || description.includes('unique')) { return 'excitement'; } // Performance features (more is better) if (value > 60) return 'performance'; // Indifferent features if (value < 30) return 'indifferent'; return 'performance'; // Default } // Helper methods for custom scoring calculateUserImpactScore(feature) { // Simplified calculation - could be enhanced with actual user data return Math.min(100, feature.businessValue.score * 1.2); } calculateStrategicAlignmentScore(feature) { // Check if feature aligns with strategic themes // In a real implementation, this would check against company strategy return feature.businessValue.score; // Simplified } calculateTechnicalFeasibilityScore(feature) { const feasibilityScores = { 'low': 90, 'medium': 70, 'high': 50, 'very-high': 30 }; return feasibilityScores[feature.technicalComplexity]; } calculateRiskScore(feature) { // Higher complexity = higher risk const riskScores = { 'low': 20, 'medium': 40, 'high': 60, 'very-high': 80 }; return riskScores[feature.technicalComplexity]; } // Initiative prioritization async prioritizeInitiatives(initiatives, criteria) { // Convert initiatives to a format similar to features for prioritization const results = initiatives.map(initiative => { let score = 0; // Calculate score based on value metrics and effort const valueScore = this.calculateInitiativeValueScore(initiative.value); const effortScore = this.calculateInitiativeEffortScore(initiative.effort); // Simple scoring: value / effort score = valueScore / Math.max(1, effortScore); return { featureId: initiative.id, // Using featureId field for compatibility method: criteria.method, score, rank: 0, rationale: `Initiative Value: ${valueScore}, Effort: ${effortScore}`, breakdown: { value: valueScore, effort: effortScore, valueMetrics: initiative.value, effortEstimate: initiative.effort } }; }); // Sort and rank results.sort((a, b) => b.score - a.score); results.forEach((result, index) => { result.rank = index + 1; }); return results; } calculateInitiativeValueScore(value) { const impactScores = { low: 25, medium: 50, high: 75, critical: 100 }; const userImpactScore = impactScores[value.userImpact]; // Normalize financial metrics (assuming millions) const revenueScore = Math.min(100, (value.revenueImpact / 1000000) * 20); const savingsScore = Math.min(100, (value.costSavings / 1000000) * 30); // Strategic value is already 1-10, scale to 100 const strategicScore = value.strategicValue * 10; // Customer satisfaction impact (NPS scale -100 to 100, normalize to 0-100) const satisfactionScore = (value.customerSatisfaction + 100) / 2; // Weighted average return (userImpactScore * 0.3 + revenueScore * 0.2 + savingsScore * 0.2 + strategicScore * 0.2 + satisfactionScore * 0.1); } calculateInitiativeEffortScore(effort) { const totalWeeks = effort.developmentWeeks + effort.designWeeks + effort.qaWeeks; // Adjust for confidence const confidenceMultiplier = { low: 1.5, // Low confidence = multiply effort by 1.5 medium: 1.0, high: 0.9 // High confidence = slight reduction }; return totalWeeks * confidenceMultiplier[effort.confidence]; } } //# sourceMappingURL=prioritization-engine.js.map