/home/techb158/balavpn.abdallabala.com/src/domain
Edit: /home/techb158/balavpn.abdallabala.com/src/domain/risk-engine.js (13443B)
const DIMENSIONS = ["Organizational", "Technical", "Human", "Governance", "Legal", "Ethical", "Operational"];
const LEVELS = [
{ name: "Low", min: 0, max: 24 },
{ name: "Moderate", min: 25, max: 49 },
{ name: "High", min: 50, max: 74 },
{ name: "Critical", min: 75, max: 100 }
];
function clamp(value, min, max) {
return Math.max(min, Math.min(max, Number(value) || 0));
}
function normalizeStatus(status) {
return String(status || "OPEN").replace(/_/g, " ").toLowerCase();
}
function normalizeApprovalStatus(status) {
return String(status || "PENDING").replace(/_/g, " ").toLowerCase();
}
function getLevel(score) {
const bounded = clamp(score, 0, 100);
const level = LEVELS.find(item => bounded >= item.min && bounded <= item.max);
return level ? level.name : "Unknown";
}
function scoreRisk(risk) {
const probability = clamp(risk.probability, 1, 5);
const impact = clamp(risk.impact, 1, 5);
const detectability = clamp(risk.detectability, 1, 5);
const mitigationProgress = clamp(risk.mitigationProgress ?? risk.mitigation_progress ?? risk.progressPercent ?? 0, 0, 100);
const mitigationEffectiveness = clamp(risk.mitigationEffectiveness ?? risk.mitigation_effectiveness ?? risk.effectivenessPercent ?? 0, 0, 100);
const rawScore = probability * impact * detectability;
const normalizedScore = Math.round((rawScore / 125) * 100);
const reduction = (mitigationProgress / 100) * (mitigationEffectiveness / 100);
const residualScore = normalizeStatus(risk.status) === "closed"
? Math.round(normalizedScore * 0.1)
: Math.round(normalizedScore * (1 - reduction));
return Object.assign({}, risk, {
probability,
impact,
detectability,
mitigationProgress,
mitigationEffectiveness,
rawScore,
normalizedScore,
residualScore: clamp(residualScore, 0, 100),
severity: getLevel(normalizedScore),
residualSeverity: getLevel(residualScore)
});
}
function average(values) {
const clean = values.filter(value => Number.isFinite(Number(value)));
if (!clean.length) return 0;
return Math.round(clean.reduce((sum, value) => sum + Number(value), 0) / clean.length);
}
function selectedExperiment(experiments) {
return experiments.find(item => item.selected) || experiments[0] || null;
}
function riskMitigationRollup(risk) {
const mitigations = risk.mitigations || [];
if (!mitigations.length) return { mitigationProgress: 0, mitigationEffectiveness: 0 };
return {
mitigationProgress: average(mitigations.map(item => item.progressPercent)),
mitigationEffectiveness: average(mitigations.map(item => item.effectivenessPercent))
};
}
function scoreProjectRisks(risks) {
return risks.map(risk => scoreRisk(Object.assign({}, risk, riskMitigationRollup(risk))));
}
function calculateOverallScore(scoredRisks) {
const active = scoredRisks.filter(risk => normalizeStatus(risk.status) !== "closed");
if (!active.length) return 0;
const topRisks = active.slice().sort((a, b) => b.normalizedScore - a.normalizedScore).slice(0, 8);
const avgTopNormalized = average(topRisks.map(risk => risk.normalizedScore));
const avgTopResidual = average(topRisks.map(risk => risk.residualScore));
const criticalOpen = active.filter(risk => risk.normalizedScore >= 75 && !["accepted", "approved"].includes(normalizeApprovalStatus(risk.approvalStatus))).length;
const highUnapproved = active.filter(risk => risk.normalizedScore >= 50 && !["accepted", "approved"].includes(normalizeApprovalStatus(risk.approvalStatus))).length;
const approvalPenalty = Math.min(12, highUnapproved * 2) + Math.min(15, criticalOpen * 4);
const exposure = Math.round((avgTopNormalized * 0.7) + (avgTopResidual * 0.3));
return clamp(exposure + approvalPenalty, 0, 100);
}
function dimensionScores(scoredRisks) {
return DIMENSIONS.map(dimension => {
const risks = scoredRisks.filter(risk => risk.dimension === dimension && normalizeStatus(risk.status) !== "closed");
return { name: dimension, score: average(risks.map(risk => risk.residualScore)) };
});
}
function lifecycleScores(lifecyclePhases, scoredRisks) {
return lifecyclePhases.map(phase => {
const phaseRisks = scoredRisks.filter(risk => risk.lifecyclePhase === phase.name && normalizeStatus(risk.status) !== "closed");
const riskScore = average(phaseRisks.map(risk => risk.residualScore));
return Object.assign({}, phase, {
openRisks: phaseRisks.length,
riskScore,
riskLevel: getLevel(riskScore)
});
});
}
function mitigationCompletion(scoredRisks) {
const active = scoredRisks.filter(risk => normalizeStatus(risk.status) !== "closed");
return average(active.map(risk => risk.mitigationProgress));
}
function evaluateGate(project, scoredRisks, experiments, overallScore, mitigationPct) {
const thresholds = project.thresholds || {};
const reviews = project.reviews || {};
const selected = selectedExperiment(experiments || []);
const active = scoredRisks.filter(risk => normalizeStatus(risk.status) !== "closed");
const criticalOpen = active.filter(risk => risk.normalizedScore >= 75 && !["accepted", "approved"].includes(normalizeApprovalStatus(risk.approvalStatus))).length;
const unapprovedHigh = active.filter(risk => risk.normalizedScore >= 50 && !["accepted", "approved"].includes(normalizeApprovalStatus(risk.approvalStatus))).length;
const selectedF1 = selected && selected.metrics ? selected.metrics.find(metric => metric.metricName === "f1") : null;
const selectedStability = selected && selected.metrics ? selected.metrics.find(metric => metric.metricName === "stability") : null;
const criteria = [
{
name: "Overall risk score",
required: `<= ${thresholds.overallRiskMax ?? 50}`,
actual: overallScore,
status: overallScore <= (thresholds.overallRiskMax ?? 50) ? "Pass" : "Blocked",
blocking: overallScore > (thresholds.overallRiskMax ?? 50),
evidence: "Calculated from residual active risks and approval penalty."
},
{
name: "Open critical risks",
required: `<= ${thresholds.criticalOpenMax ?? 0}`,
actual: criticalOpen,
status: criticalOpen <= (thresholds.criticalOpenMax ?? 0) ? "Pass" : "Blocked",
blocking: criticalOpen > (thresholds.criticalOpenMax ?? 0),
evidence: "Critical risks must be closed, accepted, or approved."
},
{
name: "Mitigation completeness",
required: `>= ${thresholds.mitigationCompletionMin ?? 60}%`,
actual: `${mitigationPct}%`,
status: mitigationPct >= (thresholds.mitigationCompletionMin ?? 60) ? "Pass" : "Blocked",
blocking: mitigationPct < (thresholds.mitigationCompletionMin ?? 60),
evidence: "Average mitigation progress across active risks."
},
{
name: "Data readiness",
required: `>= ${thresholds.dataReadinessMin ?? 70}`,
actual: reviews.dataReadiness ? reviews.dataReadiness.score : "Missing",
status: reviews.dataReadiness && reviews.dataReadiness.score >= (thresholds.dataReadinessMin ?? 70) ? "Pass" : "Blocked",
blocking: !(reviews.dataReadiness && reviews.dataReadiness.score >= (thresholds.dataReadinessMin ?? 70)),
evidence: reviews.dataReadiness ? reviews.dataReadiness.evidence : "Data readiness review missing."
},
{
name: "Selected model performance",
required: `F1 >= ${thresholds.selectedModelF1Min ?? 0.8}`,
actual: selectedF1 ? selectedF1.metricValue : "Missing",
status: selectedF1 && selectedF1.metricValue >= (thresholds.selectedModelF1Min ?? 0.8) ? "Pass" : "Blocked",
blocking: !(selectedF1 && selectedF1.metricValue >= (thresholds.selectedModelF1Min ?? 0.8)),
evidence: selected ? `${selected.name} selected for gate review.` : "No selected experiment."
},
{
name: "Selected model stability",
required: `>= ${thresholds.selectedModelStabilityMin ?? 75}`,
actual: selectedStability ? selectedStability.metricValue : "Missing",
status: selectedStability && selectedStability.metricValue >= (thresholds.selectedModelStabilityMin ?? 75) ? "Pass" : "Warning",
blocking: false,
evidence: selected ? "Stability is based on repeated evaluation variation." : "No selected experiment."
},
{
name: "Ethical review",
required: "Approved or Accepted",
actual: reviews.ethicalReview ? reviews.ethicalReview.status : "Missing",
status: reviews.ethicalReview && ["Approved", "Accepted"].includes(reviews.ethicalReview.status) ? "Pass" : "Blocked",
blocking: !(reviews.ethicalReview && ["Approved", "Accepted"].includes(reviews.ethicalReview.status)),
evidence: reviews.ethicalReview ? reviews.ethicalReview.evidence : "Ethical review missing."
},
{
name: "Legal review",
required: "Approved or Accepted",
actual: reviews.legalReview ? reviews.legalReview.status : "Missing",
status: reviews.legalReview && ["Approved", "Accepted"].includes(reviews.legalReview.status) ? "Pass" : "Blocked",
blocking: !(reviews.legalReview && ["Approved", "Accepted"].includes(reviews.legalReview.status)),
evidence: reviews.legalReview ? reviews.legalReview.evidence : "Legal review missing."
},
{
name: "High-risk approval state",
required: "No unapproved high risks",
actual: unapprovedHigh,
status: unapprovedHigh === 0 ? "Pass" : "Blocked",
blocking: unapprovedHigh !== 0,
evidence: "High risks require owner approval, acceptance, closure, or documented treatment."
}
];
const blocked = criteria.filter(item => item.status === "Blocked");
const warnings = criteria.filter(item => item.status === "Warning");
const status = blocked.length ? "BLOCKED" : warnings.length || overallScore >= (thresholds.gateWarningScore ?? 35) ? "WARNING" : "READY";
return {
status,
criteria,
blockedCount: blocked.length,
warningCount: warnings.length,
message: status === "BLOCKED"
? `Deployment gate blocked by ${blocked.length} criterion or criteria.`
: status === "WARNING"
? "Deployment gate requires reviewer attention before release."
: "Deployment gate is ready."
};
}
function calculateDashboard(project) {
const risks = scoreProjectRisks(project.risks || []);
const mitigationPct = mitigationCompletion(risks);
const overallScore = calculateOverallScore(risks);
const gate = evaluateGate(project, risks, project.experiments || [], overallScore, mitigationPct);
const active = risks.filter(risk => normalizeStatus(risk.status) !== "closed");
const lifecyclePhases = project.lifecyclePhases || [];
const lifecycleReadiness = lifecycleScores(lifecyclePhases, risks).map((phase, i) => ({
...phase,
readinessScore: Math.max(0, 100 - phase.riskScore),
code: `LC-${String(i + 1).padStart(2, '0')}`
}));
const presentDims = [...new Set(risks.map(r => r.dimension).filter(Boolean))];
const DIM_ORDER = ["Organizational", "Technical", "Human", "Governance", "Ethical", "Legal", "Operational"];
const ordered = presentDims.length > 0 ? DIM_ORDER.filter(d => presentDims.includes(d)) : DIMENSIONS;
const dimensionResidual = ordered.map(dim => {
const dimRisks = risks.filter(r => r.dimension === dim);
return {
name: dim,
score: average(dimRisks.map(r => r.residualScore)),
openRisks: active.filter(r => r.dimension === dim).length,
totalRisks: dimRisks.length
};
});
const orgScore = dimensionResidual.find(d => d.name === "Organizational")?.score || 0;
const techScore = dimensionResidual.find(d => d.name === "Technical")?.score || 0;
const humanScore = dimensionResidual.find(d => d.name === "Human")?.score || 0;
return {
project: {
id: project.id,
workspaceId: project.workspaceId,
name: project.name,
subtitle: project.subtitle,
status: project.status,
assessmentDate: project.assessmentDate,
projectType: project.projectType,
currentLifecyclePhase: project.currentLifecyclePhase
},
summary: {
overallScore,
riskLevel: getLevel(overallScore),
gateStatus: gate.status,
openRisks: active.length,
totalRisks: risks.length,
highRisks: active.filter(risk => risk.normalizedScore >= 50 && risk.normalizedScore < 75).length,
criticalRisks: active.filter(risk => risk.normalizedScore >= 75).length,
mitigationCompletion: mitigationPct
},
dimensions: dimensionScores(risks),
dimensionResidual,
governanceTriangle: {
organizational: orgScore,
technical: techScore,
human: humanScore
},
lifecycle: lifecycleScores(lifecyclePhases, risks),
lifecycleReadiness,
gate,
scoredRisks: risks,
topRisks: active.slice().sort((a, b) => b.residualScore - a.residualScore).slice(0, 8),
mitigations: risks.flatMap(risk => (risk.mitigations || []).map(mitigation => Object.assign({}, mitigation, { riskTitle: risk.title, riskResidualScore: risk.residualScore }))),
indicators: project.indicators || [],
experiments: project.experiments || [],
workspace: project.workspace ? { id: project.workspace.id, name: project.workspace.name, organizationId: project.workspace.organizationId } : null,
integrations: project.workspace?.integrations || []
};
}
module.exports = {
DIMENSIONS,
scoreRisk,
getLevel,
calculateDashboard,
evaluateGate,
calculateOverallScore,
scoreProjectRisks
};