/home/techb158/trellopowerup.abdallabala.com/src
Edit: /home/techb158/trellopowerup.abdallabala.com/src/riskEngine.js (5756B)
const DIMENSION_WEIGHTS = {
organizational: 0.34,
technical: 0.43,
human: 0.23
};
const LIFECYCLE_WEIGHTS = {
needs_analysis: 1.0,
data_preparation: 1.25,
design: 1.1,
model_development: 1.35,
testing_evaluation: 1.3,
deployment_monitoring: 1.4
};
const TYPOLOGY_WEIGHTS = {
incremental: 0.85,
disruptive: 1.25,
applied_research: 1.2,
ai_enabler: 1.05,
citizen_led: 1.15
};
const MATURITY_DISCOUNT = {
ad_hoc: 1.2,
defined: 1.0,
measured: 0.9,
controlled: 0.8
};
function clamp(value, min, max) {
const numeric = Number(value);
if (Number.isNaN(numeric)) return min;
return Math.max(min, Math.min(max, numeric));
}
function normalizeItem(item) {
const probability = clamp(item.probability ?? 3, 1, 5);
const impact = clamp(item.impact ?? 3, 1, 5);
const detectionDifficulty = clamp(item.detectionDifficulty ?? 3, 1, 5);
const mitigationCompleteness = clamp(item.mitigationCompleteness ?? 0, 0, 100);
const residualFactor = 1 - mitigationCompleteness / 100;
return {
id: item.id,
title: item.title || 'Untitled risk',
category: item.category || 'technical',
dimension: item.dimension || 'technical',
lifecyclePhase: item.lifecyclePhase || 'model_development',
probability,
impact,
detectionDifficulty,
mitigationCompleteness,
owner: item.owner || null,
dueDate: item.dueDate || null,
mitigation: item.mitigation || '',
approvalState: item.approvalState || 'draft',
rawScore: probability * impact * detectionDifficulty,
residualScore: probability * impact * detectionDifficulty * residualFactor
};
}
function statusFromScore(score) {
if (score >= 55) return 'critical';
if (score >= 35) return 'high';
if (score >= 18) return 'medium';
return 'low';
}
function approvalGateStatus({ status, approvalState, mitigationCompleteness, metrics = {} }) {
const blocking = [];
if (status === 'critical') blocking.push('Critical residual risk requires explicit approval.');
if (mitigationCompleteness < 60) blocking.push('Mitigation completeness is below 60%.');
if (!['approved', 'accepted'].includes(approvalState)) blocking.push('Risk is not approved or accepted.');
if (metrics.modelPerformanceScore !== undefined && Number(metrics.modelPerformanceScore) < 70) {
blocking.push('Model performance score is below 70%.');
}
if (metrics.dataReadinessScore !== undefined && Number(metrics.dataReadinessScore) < 70) {
blocking.push('Data readiness score is below 70%.');
}
if (metrics.ethicalReviewCompleted === false) blocking.push('Ethical review is not completed.');
if (metrics.legalReviewCompleted === false) blocking.push('Legal review is not completed.');
return {
deploymentReady: blocking.length === 0,
blockingReasons: blocking
};
}
export function scoreRiskAssessment(input = {}) {
const projectTypology = input.projectTypology || 'ai_enabler';
const lifecyclePhase = input.lifecyclePhase || 'model_development';
const maturity = input.measurementMaturity || 'defined';
const metrics = input.metrics || {};
const items = Array.isArray(input.risks) ? input.risks.map(normalizeItem) : [];
const dimensionTotals = {
organizational: 0,
technical: 0,
human: 0
};
for (const item of items) {
const dimension = dimensionTotals[item.dimension] !== undefined ? item.dimension : 'technical';
dimensionTotals[dimension] += item.residualScore;
}
const weightedDimensionScore = Object.entries(dimensionTotals).reduce((sum, [dimension, total]) => {
return sum + total * (DIMENSION_WEIGHTS[dimension] || 0.33);
}, 0);
const lifecycleMultiplier = LIFECYCLE_WEIGHTS[lifecyclePhase] || 1;
const typologyMultiplier = TYPOLOGY_WEIGHTS[projectTypology] || 1;
const maturityMultiplier = MATURITY_DISCOUNT[maturity] || 1;
const normalizedScore = Math.round(
clamp(weightedDimensionScore * lifecycleMultiplier * typologyMultiplier * maturityMultiplier, 0, 100)
);
const status = statusFromScore(normalizedScore);
const averageMitigation = items.length
? Math.round(items.reduce((sum, item) => sum + item.mitigationCompleteness, 0) / items.length)
: 0;
const highestRisk = [...items].sort((a, b) => b.residualScore - a.residualScore)[0] || null;
const approvalState = input.approvalState || 'draft';
const gate = approvalGateStatus({
status,
approvalState,
mitigationCompleteness: averageMitigation,
metrics
});
return {
projectTypology,
lifecyclePhase,
measurementMaturity: maturity,
score: normalizedScore,
status,
averageMitigationCompleteness: averageMitigation,
dimensionTotals,
highestRisk,
gate,
risks: items,
interpretation: buildInterpretation(normalizedScore, status, gate)
};
}
function buildInterpretation(score, status, gate) {
const base = `Residual AI project risk is ${status} with a normalized score of ${score}/100.`;
if (gate.deploymentReady) {
return `${base} Deployment gate can proceed if accountable stakeholders approve the record.`;
}
return `${base} Deployment gate is blocked by ${gate.blockingReasons.length} criterion or criteria.`;
}
export function getReferenceTaxonomy() {
return {
lifecyclePhases: Object.keys(LIFECYCLE_WEIGHTS),
projectTypologies: Object.keys(TYPOLOGY_WEIGHTS),
maturityLevels: Object.keys(MATURITY_DISCOUNT),
dimensions: Object.keys(DIMENSION_WEIGHTS),
categories: ['strategic_organizational', 'technical', 'legal_ethical'],
gateCriteria: [
'risk_status',
'mitigation_completeness',
'approval_state',
'model_performance_metrics',
'data_readiness',
'ethical_review',
'legal_review',
'deployment_readiness'
]
};
}