@llm-dev-ops/agentics-cli 1.5.9 → 1.5.11
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/README.md +478 -148
- package/dist/bundled-agents/manifest.json +1 -0
- package/dist/commands/phase4.d.ts.map +1 -1
- package/dist/commands/phase4.js +4 -2
- package/dist/commands/phase4.js.map +1 -1
- package/dist/commands/phase6.d.ts.map +1 -1
- package/dist/commands/phase6.js +4 -2
- package/dist/commands/phase6.js.map +1 -1
- package/dist/mcp/mcp-server.js +11 -10
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/pipeline/auto-chain.d.ts +5 -0
- package/dist/pipeline/auto-chain.d.ts.map +1 -1
- package/dist/pipeline/auto-chain.js +131 -47
- package/dist/pipeline/auto-chain.js.map +1 -1
- package/dist/pipeline/enterprise/artifact-assembler.d.ts +64 -0
- package/dist/pipeline/enterprise/artifact-assembler.d.ts.map +1 -0
- package/dist/pipeline/enterprise/artifact-assembler.js +542 -0
- package/dist/pipeline/enterprise/artifact-assembler.js.map +1 -0
- package/dist/pipeline/enterprise/artifact-renderers.d.ts +42 -0
- package/dist/pipeline/enterprise/artifact-renderers.d.ts.map +1 -0
- package/dist/pipeline/enterprise/artifact-renderers.js +513 -0
- package/dist/pipeline/enterprise/artifact-renderers.js.map +1 -0
- package/dist/pipeline/enterprise/code-resolver.d.ts +43 -0
- package/dist/pipeline/enterprise/code-resolver.d.ts.map +1 -0
- package/dist/pipeline/enterprise/code-resolver.js +219 -0
- package/dist/pipeline/enterprise/code-resolver.js.map +1 -0
- package/dist/pipeline/enterprise/decision-graph-client.d.ts +171 -0
- package/dist/pipeline/enterprise/decision-graph-client.d.ts.map +1 -0
- package/dist/pipeline/enterprise/decision-graph-client.js +222 -0
- package/dist/pipeline/enterprise/decision-graph-client.js.map +1 -0
- package/dist/pipeline/enterprise/decision-graph-memory.d.ts +104 -0
- package/dist/pipeline/enterprise/decision-graph-memory.d.ts.map +1 -0
- package/dist/pipeline/enterprise/decision-graph-memory.js +292 -0
- package/dist/pipeline/enterprise/decision-graph-memory.js.map +1 -0
- package/dist/pipeline/enterprise/decision-graph.d.ts +237 -0
- package/dist/pipeline/enterprise/decision-graph.d.ts.map +1 -0
- package/dist/pipeline/enterprise/decision-graph.js +654 -0
- package/dist/pipeline/enterprise/decision-graph.js.map +1 -0
- package/dist/pipeline/enterprise/index.d.ts +40 -0
- package/dist/pipeline/enterprise/index.d.ts.map +1 -0
- package/dist/pipeline/enterprise/index.js +43 -0
- package/dist/pipeline/enterprise/index.js.map +1 -0
- package/dist/pipeline/enterprise/pass-executor.d.ts +33 -0
- package/dist/pipeline/enterprise/pass-executor.d.ts.map +1 -0
- package/dist/pipeline/enterprise/pass-executor.js +459 -0
- package/dist/pipeline/enterprise/pass-executor.js.map +1 -0
- package/dist/pipeline/enterprise/pass-registry.d.ts +19 -0
- package/dist/pipeline/enterprise/pass-registry.d.ts.map +1 -0
- package/dist/pipeline/enterprise/pass-registry.js +243 -0
- package/dist/pipeline/enterprise/pass-registry.js.map +1 -0
- package/dist/pipeline/enterprise/pass2-simulation.d.ts +130 -0
- package/dist/pipeline/enterprise/pass2-simulation.d.ts.map +1 -0
- package/dist/pipeline/enterprise/pass2-simulation.js +691 -0
- package/dist/pipeline/enterprise/pass2-simulation.js.map +1 -0
- package/dist/pipeline/enterprise/pass4-governance.d.ts +195 -0
- package/dist/pipeline/enterprise/pass4-governance.d.ts.map +1 -0
- package/dist/pipeline/enterprise/pass4-governance.js +748 -0
- package/dist/pipeline/enterprise/pass4-governance.js.map +1 -0
- package/dist/pipeline/enterprise/pass5-decision.d.ts +90 -0
- package/dist/pipeline/enterprise/pass5-decision.d.ts.map +1 -0
- package/dist/pipeline/enterprise/pass5-decision.js +487 -0
- package/dist/pipeline/enterprise/pass5-decision.js.map +1 -0
- package/dist/pipeline/enterprise/pass7-observability.d.ts +198 -0
- package/dist/pipeline/enterprise/pass7-observability.d.ts.map +1 -0
- package/dist/pipeline/enterprise/pass7-observability.js +636 -0
- package/dist/pipeline/enterprise/pass7-observability.js.map +1 -0
- package/dist/pipeline/enterprise/pipeline-orchestrator.d.ts +29 -0
- package/dist/pipeline/enterprise/pipeline-orchestrator.d.ts.map +1 -0
- package/dist/pipeline/enterprise/pipeline-orchestrator.js +283 -0
- package/dist/pipeline/enterprise/pipeline-orchestrator.js.map +1 -0
- package/dist/pipeline/enterprise/provenance-tracker.d.ts +135 -0
- package/dist/pipeline/enterprise/provenance-tracker.d.ts.map +1 -0
- package/dist/pipeline/enterprise/provenance-tracker.js +437 -0
- package/dist/pipeline/enterprise/provenance-tracker.js.map +1 -0
- package/dist/pipeline/enterprise/trace-middleware.d.ts +37 -0
- package/dist/pipeline/enterprise/trace-middleware.d.ts.map +1 -0
- package/dist/pipeline/enterprise/trace-middleware.js +188 -0
- package/dist/pipeline/enterprise/trace-middleware.js.map +1 -0
- package/dist/pipeline/enterprise/types.d.ts +199 -0
- package/dist/pipeline/enterprise/types.d.ts.map +1 -0
- package/dist/pipeline/enterprise/types.js +30 -0
- package/dist/pipeline/enterprise/types.js.map +1 -0
- package/dist/pipeline/phase2/phases/adr-generator.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/adr-generator.js +56 -8
- package/dist/pipeline/phase2/phases/adr-generator.js.map +1 -1
- package/dist/pipeline/phase3/phases/test-generator.d.ts.map +1 -1
- package/dist/pipeline/phase3/phases/test-generator.js +53 -0
- package/dist/pipeline/phase3/phases/test-generator.js.map +1 -1
- package/dist/pipeline/phase4/phases/deployment-generator.d.ts.map +1 -1
- package/dist/pipeline/phase4/phases/deployment-generator.js +147 -0
- package/dist/pipeline/phase4/phases/deployment-generator.js.map +1 -1
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.d.ts.map +1 -1
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.js +52 -1
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.js.map +1 -1
- package/dist/pipeline/phase6/phases/deployment-finalizer.d.ts.map +1 -1
- package/dist/pipeline/phase6/phases/deployment-finalizer.js +226 -0
- package/dist/pipeline/phase6/phases/deployment-finalizer.js.map +1 -1
- package/dist/pipeline/phase6/phases/service-registrar.d.ts +1 -1
- package/dist/pipeline/phase6/phases/service-registrar.d.ts.map +1 -1
- package/dist/pipeline/phase6/phases/service-registrar.js +47 -7
- package/dist/pipeline/phase6/phases/service-registrar.js.map +1 -1
- package/dist/pipeline/swarm-orchestrator.d.ts.map +1 -1
- package/dist/pipeline/swarm-orchestrator.js +47 -19
- package/dist/pipeline/swarm-orchestrator.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass 4 — Governance, Compliance & CostOps (ADR-036)
|
|
3
|
+
*
|
|
4
|
+
* Executes four concurrent workstreams that converge into a unified
|
|
5
|
+
* Governance Convergence Report stored in the DecisionGraph:
|
|
6
|
+
*
|
|
7
|
+
* Workstream A: Security & Compliance Analysis (LLM-Shield)
|
|
8
|
+
* Workstream B: Policy Enforcement (LLM-Policy-Engine)
|
|
9
|
+
* Workstream C: Financial Modeling (LLM-CostOps)
|
|
10
|
+
* Workstream D: Governance Visibility (LLM-Governance-Dashboard)
|
|
11
|
+
*
|
|
12
|
+
* Key design: simulation data (Pass 2) feeds directly into cost models.
|
|
13
|
+
* Security findings (Workstream A) inform policy analysis (Workstream B).
|
|
14
|
+
*/
|
|
15
|
+
export const PASS4_WORKSTREAM_CONFIGS = [
|
|
16
|
+
{
|
|
17
|
+
id: 'security-compliance',
|
|
18
|
+
label: 'Security & Compliance Analysis (LLM-Shield)',
|
|
19
|
+
agents: [
|
|
20
|
+
{ domain: 'shield', agent: 'prompt-injection', role: 'Prompt injection detection' },
|
|
21
|
+
{ domain: 'shield', agent: 'pii', role: 'PII detection' },
|
|
22
|
+
{ domain: 'shield', agent: 'redaction', role: 'Data redaction' },
|
|
23
|
+
{ domain: 'shield', agent: 'secrets', role: 'Secrets leakage detection' },
|
|
24
|
+
{ domain: 'shield', agent: 'toxicity', role: 'Toxicity detection' },
|
|
25
|
+
{ domain: 'shield', agent: 'safety-boundary', role: 'Safety boundary enforcement' },
|
|
26
|
+
{ domain: 'shield', agent: 'moderation', role: 'Content moderation' },
|
|
27
|
+
{ domain: 'shield', agent: 'abuse', role: 'Model abuse detection' },
|
|
28
|
+
{ domain: 'shield', agent: 'credential-exposure', role: 'Credential exposure detection' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'policy-enforcement',
|
|
33
|
+
label: 'Policy Enforcement (LLM-Policy-Engine)',
|
|
34
|
+
agents: [
|
|
35
|
+
{ domain: 'policy-engine', agent: 'enforce', role: 'Policy enforcement' },
|
|
36
|
+
{ domain: 'policy-engine', agent: 'constraints', role: 'Constraint solver' },
|
|
37
|
+
{ domain: 'policy-engine', agent: 'approval', role: 'Approval routing' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'financial-modeling',
|
|
42
|
+
label: 'Financial Modeling (LLM-CostOps)',
|
|
43
|
+
agents: [
|
|
44
|
+
{ domain: 'costops', agent: 'attribution', role: 'Cost attribution' },
|
|
45
|
+
{ domain: 'costops', agent: 'forecast', role: 'Cost forecasting' },
|
|
46
|
+
{ domain: 'costops', agent: 'budget', role: 'Budget enforcement' },
|
|
47
|
+
{ domain: 'costops', agent: 'roi', role: 'ROI estimation' },
|
|
48
|
+
{ domain: 'costops', agent: 'tradeoff', role: 'Cost-performance tradeoff' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'governance-visibility',
|
|
53
|
+
label: 'Governance Visibility (LLM-Governance-Dashboard)',
|
|
54
|
+
agents: [
|
|
55
|
+
{ domain: 'governance-dashboard', agent: 'audit', role: 'Governance audit' },
|
|
56
|
+
{ domain: 'governance-dashboard', agent: 'impact', role: 'Change impact' },
|
|
57
|
+
{ domain: 'governance-dashboard', agent: 'oversight', role: 'Usage oversight' },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Pass 4 Executor
|
|
63
|
+
// ============================================================================
|
|
64
|
+
/**
|
|
65
|
+
* Execute Pass 4: Governance, Compliance & CostOps.
|
|
66
|
+
*
|
|
67
|
+
* Dispatches four workstreams concurrently, processes results into
|
|
68
|
+
* typed domain objects, converges into a GovernanceConvergence report,
|
|
69
|
+
* and writes all outputs to the DecisionGraph.
|
|
70
|
+
*/
|
|
71
|
+
export async function executePass4(params) {
|
|
72
|
+
const { prompt, graph, tracker, timeoutMs, verbose } = params;
|
|
73
|
+
const pass = 4;
|
|
74
|
+
// Build context from Passes 1-3
|
|
75
|
+
const priorContext = buildPriorContext(graph);
|
|
76
|
+
const payload = {
|
|
77
|
+
prompt,
|
|
78
|
+
pass: 4,
|
|
79
|
+
passName: 'Governance, Compliance & CostOps',
|
|
80
|
+
context: priorContext,
|
|
81
|
+
};
|
|
82
|
+
const invoke = params.invokeAgents ?? defaultInvokeAgents;
|
|
83
|
+
if (verbose) {
|
|
84
|
+
console.error(` [Pass 4] Starting 4 concurrent workstreams`);
|
|
85
|
+
}
|
|
86
|
+
// ── Execute all 4 workstreams concurrently ──
|
|
87
|
+
const workstreamPromises = PASS4_WORKSTREAM_CONFIGS.map(async (ws) => {
|
|
88
|
+
const wsStart = Date.now();
|
|
89
|
+
if (verbose) {
|
|
90
|
+
console.error(` [Pass 4] Workstream: ${ws.label} (${ws.agents.length} agents)`);
|
|
91
|
+
}
|
|
92
|
+
const results = await invoke(ws.agents, payload, pass, timeoutMs);
|
|
93
|
+
const successCount = results.filter(r => r.status >= 200 && r.status < 300).length;
|
|
94
|
+
return {
|
|
95
|
+
workstream: ws.id,
|
|
96
|
+
label: ws.label,
|
|
97
|
+
agentResults: results,
|
|
98
|
+
successCount,
|
|
99
|
+
totalCount: results.length,
|
|
100
|
+
durationMs: Date.now() - wsStart,
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
const workstreamResults = await Promise.all(workstreamPromises);
|
|
104
|
+
const allResults = [];
|
|
105
|
+
for (const ws of workstreamResults) {
|
|
106
|
+
for (const r of ws.agentResults) {
|
|
107
|
+
allResults.push(r);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// ── Extract typed domain objects per workstream ──
|
|
111
|
+
const wsA = workstreamResults.find(w => w.workstream === 'security-compliance');
|
|
112
|
+
const wsB = workstreamResults.find(w => w.workstream === 'policy-enforcement');
|
|
113
|
+
const wsC = workstreamResults.find(w => w.workstream === 'financial-modeling');
|
|
114
|
+
const wsD = workstreamResults.find(w => w.workstream === 'governance-visibility');
|
|
115
|
+
const securityAnalysis = extractSecurityAnalysis(wsA);
|
|
116
|
+
const policyAnalysis = extractPolicyAnalysis(wsB, priorContext);
|
|
117
|
+
const costModel = extractCostModel(wsC, graph);
|
|
118
|
+
const governanceReport = extractGovernanceReport(wsD, graph);
|
|
119
|
+
// ── Converge ──
|
|
120
|
+
const convergence = buildConvergence(securityAnalysis, policyAnalysis, costModel, governanceReport);
|
|
121
|
+
// ── Write to DecisionGraph ──
|
|
122
|
+
const graphNodeIds = writeToGraph(graph, securityAnalysis, policyAnalysis, costModel, governanceReport, convergence, tracker, pass);
|
|
123
|
+
if (verbose) {
|
|
124
|
+
console.error(` [Pass 4] Complete: security=${securityAnalysis.overallRiskLevel}, compliance=${policyAnalysis.overallComplianceStatus}, governance=${governanceReport.governanceReadiness.overallScore}`);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
workstreamResults,
|
|
128
|
+
securityAnalysis,
|
|
129
|
+
policyAnalysis,
|
|
130
|
+
costModel,
|
|
131
|
+
governanceReport,
|
|
132
|
+
convergence,
|
|
133
|
+
graphNodeIds,
|
|
134
|
+
totalAgentResults: allResults,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Prior Context Builder
|
|
139
|
+
// ============================================================================
|
|
140
|
+
function buildPriorContext(graph) {
|
|
141
|
+
const ctx = {};
|
|
142
|
+
// Pass 1
|
|
143
|
+
for (const n of graph.getNodesByPass(1)) {
|
|
144
|
+
if (n.type === 'business_context')
|
|
145
|
+
ctx['businessContext'] = { summary: n.summary, ...n.content };
|
|
146
|
+
if (n.type === 'problem_definition')
|
|
147
|
+
ctx['problemDefinition'] = { summary: n.summary, ...n.content };
|
|
148
|
+
if (n.type === 'technical_scope')
|
|
149
|
+
ctx['technicalScope'] = { summary: n.summary, ...n.content };
|
|
150
|
+
}
|
|
151
|
+
// Pass 2 — scenarios & risk signals feed into CostOps
|
|
152
|
+
const scenarios = [];
|
|
153
|
+
const riskSignals = [];
|
|
154
|
+
for (const n of graph.getNodesByPass(2)) {
|
|
155
|
+
if (n.type === 'scenario')
|
|
156
|
+
scenarios.push({ summary: n.summary, ...n.content });
|
|
157
|
+
if (n.type === 'risk_signal')
|
|
158
|
+
riskSignals.push({ summary: n.summary, ...n.content });
|
|
159
|
+
if (n.type === 'reliability_report')
|
|
160
|
+
ctx['reliabilityReport'] = { summary: n.summary, ...n.content };
|
|
161
|
+
}
|
|
162
|
+
ctx['scenarios'] = scenarios;
|
|
163
|
+
ctx['riskSignals'] = riskSignals;
|
|
164
|
+
// Pass 3 — architecture
|
|
165
|
+
for (const n of graph.getNodesByPass(3)) {
|
|
166
|
+
if (n.type === 'system_architecture')
|
|
167
|
+
ctx['architecture'] = { summary: n.summary, ...n.content };
|
|
168
|
+
if (n.type === 'integration_map')
|
|
169
|
+
ctx['integrationMap'] = { summary: n.summary, ...n.content };
|
|
170
|
+
if (n.type === 'prototype')
|
|
171
|
+
ctx['prototype'] = { summary: n.summary, ...n.content };
|
|
172
|
+
}
|
|
173
|
+
return ctx;
|
|
174
|
+
}
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// Workstream A: Security Analysis Extraction
|
|
177
|
+
// ============================================================================
|
|
178
|
+
function extractSecurityAnalysis(ws) {
|
|
179
|
+
const defaults = {
|
|
180
|
+
overallRiskLevel: 'medium',
|
|
181
|
+
dataExposureMap: [],
|
|
182
|
+
safetyAssessment: { injectionVectors: [], contentRisks: [], boundaryViolations: [] },
|
|
183
|
+
credentialAnalysis: { authFlows: [], storageLocations: [], abuseVectors: [] },
|
|
184
|
+
remediationPriorities: [],
|
|
185
|
+
};
|
|
186
|
+
if (!ws || ws.successCount === 0)
|
|
187
|
+
return defaults;
|
|
188
|
+
const exposures = [];
|
|
189
|
+
const injVectors = [];
|
|
190
|
+
const contentRisks = [];
|
|
191
|
+
const boundaryViolations = [];
|
|
192
|
+
const authFlows = [];
|
|
193
|
+
const storageLocations = [];
|
|
194
|
+
const abuseVectors = [];
|
|
195
|
+
const remediations = [];
|
|
196
|
+
let worstRisk = 'low';
|
|
197
|
+
for (const result of ws.agentResults) {
|
|
198
|
+
if (result.status < 200 || result.status >= 300 || !result.response)
|
|
199
|
+
continue;
|
|
200
|
+
const resp = result.response;
|
|
201
|
+
const agentName = result.agent.agent;
|
|
202
|
+
// Data protection agents
|
|
203
|
+
if (['pii', 'redaction', 'secrets'].includes(agentName)) {
|
|
204
|
+
const items = extractArray(resp, 'exposures', 'findings', 'data');
|
|
205
|
+
for (const item of items) {
|
|
206
|
+
if (typeof item === 'object' && item !== null) {
|
|
207
|
+
const r = item;
|
|
208
|
+
exposures.push({
|
|
209
|
+
dataType: String(r['dataType'] ?? r['type'] ?? agentName),
|
|
210
|
+
location: String(r['location'] ?? ''),
|
|
211
|
+
risk: String(r['risk'] ?? ''),
|
|
212
|
+
remediation: String(r['remediation'] ?? ''),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Safety agents
|
|
218
|
+
if (['prompt-injection', 'toxicity', 'safety-boundary', 'moderation'].includes(agentName)) {
|
|
219
|
+
const vectors = extractStringArray(resp, 'vectors', 'injectionVectors', 'findings');
|
|
220
|
+
const risks = extractStringArray(resp, 'risks', 'contentRisks');
|
|
221
|
+
const violations = extractStringArray(resp, 'violations', 'boundaryViolations');
|
|
222
|
+
injVectors.push(...vectors);
|
|
223
|
+
contentRisks.push(...risks);
|
|
224
|
+
boundaryViolations.push(...violations);
|
|
225
|
+
}
|
|
226
|
+
// Credential agents
|
|
227
|
+
if (['credential-exposure', 'abuse'].includes(agentName)) {
|
|
228
|
+
authFlows.push(...extractStringArray(resp, 'authFlows', 'flows'));
|
|
229
|
+
storageLocations.push(...extractStringArray(resp, 'storageLocations', 'locations'));
|
|
230
|
+
abuseVectors.push(...extractStringArray(resp, 'abuseVectors', 'vectors'));
|
|
231
|
+
}
|
|
232
|
+
// Remediation from any agent
|
|
233
|
+
const rems = extractArray(resp, 'remediations', 'remediationPriorities', 'recommendations');
|
|
234
|
+
for (const item of rems) {
|
|
235
|
+
if (typeof item === 'object' && item !== null) {
|
|
236
|
+
const r = item;
|
|
237
|
+
remediations.push({
|
|
238
|
+
finding: String(r['finding'] ?? r['description'] ?? ''),
|
|
239
|
+
severity: normalizeRiskLevel(r['severity']),
|
|
240
|
+
effort: normalizeEffort(r['effort']),
|
|
241
|
+
recommendation: String(r['recommendation'] ?? ''),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const level = normalizeRiskLevel(resp['riskLevel'] ?? resp['overallRiskLevel'] ?? resp['severity']);
|
|
246
|
+
worstRisk = worstOf(worstRisk, level);
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
overallRiskLevel: worstRisk,
|
|
250
|
+
dataExposureMap: exposures,
|
|
251
|
+
safetyAssessment: { injectionVectors: injVectors, contentRisks, boundaryViolations },
|
|
252
|
+
credentialAnalysis: { authFlows, storageLocations, abuseVectors },
|
|
253
|
+
remediationPriorities: remediations,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// ============================================================================
|
|
257
|
+
// Workstream B: Policy Analysis Extraction
|
|
258
|
+
// ============================================================================
|
|
259
|
+
function extractPolicyAnalysis(ws, _context) {
|
|
260
|
+
const defaults = {
|
|
261
|
+
applicableFrameworks: [],
|
|
262
|
+
aiGovernance: { explainabilityScore: 'partial', biasAssessment: '', humanOversightRequired: true, requirements: [] },
|
|
263
|
+
approvalRouting: [],
|
|
264
|
+
overallComplianceStatus: 'gaps_identified',
|
|
265
|
+
};
|
|
266
|
+
if (!ws || ws.successCount === 0)
|
|
267
|
+
return defaults;
|
|
268
|
+
const frameworks = [];
|
|
269
|
+
const approvals = [];
|
|
270
|
+
let aiGov = defaults.aiGovernance;
|
|
271
|
+
let complianceStatus = 'compliant';
|
|
272
|
+
for (const result of ws.agentResults) {
|
|
273
|
+
if (result.status < 200 || result.status >= 300 || !result.response)
|
|
274
|
+
continue;
|
|
275
|
+
const resp = result.response;
|
|
276
|
+
// Frameworks
|
|
277
|
+
const fws = extractArray(resp, 'frameworks', 'applicableFrameworks', 'regulations');
|
|
278
|
+
for (const item of fws) {
|
|
279
|
+
if (typeof item === 'object' && item !== null) {
|
|
280
|
+
const r = item;
|
|
281
|
+
frameworks.push({
|
|
282
|
+
framework: String(r['framework'] ?? r['name'] ?? ''),
|
|
283
|
+
applicability: String(r['applicability'] ?? r['reason'] ?? ''),
|
|
284
|
+
complianceGaps: extractStringArray(r, 'complianceGaps', 'gaps'),
|
|
285
|
+
remediationSteps: extractStringArray(r, 'remediationSteps', 'remediation', 'steps'),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
else if (typeof item === 'string') {
|
|
289
|
+
frameworks.push({ framework: item, applicability: 'Detected from context', complianceGaps: [], remediationSteps: [] });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// AI Governance
|
|
293
|
+
if (resp['aiGovernance'] && typeof resp['aiGovernance'] === 'object') {
|
|
294
|
+
const ag = resp['aiGovernance'];
|
|
295
|
+
aiGov = {
|
|
296
|
+
explainabilityScore: normalizeExplainability(ag['explainabilityScore']),
|
|
297
|
+
biasAssessment: String(ag['biasAssessment'] ?? ''),
|
|
298
|
+
humanOversightRequired: ag['humanOversightRequired'] !== false,
|
|
299
|
+
requirements: extractStringArray(ag, 'requirements'),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
// Approval routing
|
|
303
|
+
const apprs = extractArray(resp, 'approvalRouting', 'approvals');
|
|
304
|
+
for (const item of apprs) {
|
|
305
|
+
if (typeof item === 'object' && item !== null) {
|
|
306
|
+
const r = item;
|
|
307
|
+
approvals.push({
|
|
308
|
+
approver: String(r['approver'] ?? r['role'] ?? ''),
|
|
309
|
+
reason: String(r['reason'] ?? ''),
|
|
310
|
+
threshold: String(r['threshold'] ?? ''),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// Compliance status
|
|
315
|
+
const status = normalizeComplianceStatus(resp['complianceStatus'] ?? resp['overallComplianceStatus']);
|
|
316
|
+
if (status === 'non_compliant')
|
|
317
|
+
complianceStatus = 'non_compliant';
|
|
318
|
+
else if (status === 'gaps_identified' && complianceStatus !== 'non_compliant')
|
|
319
|
+
complianceStatus = 'gaps_identified';
|
|
320
|
+
}
|
|
321
|
+
// If no frameworks detected but gaps exist, mark as gaps_identified
|
|
322
|
+
if (frameworks.length === 0)
|
|
323
|
+
complianceStatus = 'gaps_identified';
|
|
324
|
+
return { applicableFrameworks: frameworks, aiGovernance: aiGov, approvalRouting: approvals, overallComplianceStatus: complianceStatus };
|
|
325
|
+
}
|
|
326
|
+
// ============================================================================
|
|
327
|
+
// Workstream C: Cost Model Extraction
|
|
328
|
+
// ============================================================================
|
|
329
|
+
function extractCostModel(ws, graph) {
|
|
330
|
+
const defaults = {
|
|
331
|
+
infrastructure: [],
|
|
332
|
+
integrationCosts: [],
|
|
333
|
+
operationalCosts: [],
|
|
334
|
+
tco: { year1: 0, year3: 0, year5: 0 },
|
|
335
|
+
roi: { expectedReturn: 0, paybackPeriodMonths: 0, confidenceLevel: 'low' },
|
|
336
|
+
scenarioVariants: [],
|
|
337
|
+
budgetRisk: { contingencyRequired: 0, budgetExceedanceProbability: 0, riskFactors: [] },
|
|
338
|
+
};
|
|
339
|
+
if (!ws || ws.successCount === 0)
|
|
340
|
+
return defaults;
|
|
341
|
+
const infra = [];
|
|
342
|
+
const integ = [];
|
|
343
|
+
const ops = [];
|
|
344
|
+
let tco = defaults.tco;
|
|
345
|
+
let roi = defaults.roi;
|
|
346
|
+
const variants = [];
|
|
347
|
+
let budgetRisk = defaults.budgetRisk;
|
|
348
|
+
for (const result of ws.agentResults) {
|
|
349
|
+
if (result.status < 200 || result.status >= 300 || !result.response)
|
|
350
|
+
continue;
|
|
351
|
+
const resp = result.response;
|
|
352
|
+
// Infrastructure costs
|
|
353
|
+
for (const item of extractArray(resp, 'infrastructure', 'infrastructureCosts')) {
|
|
354
|
+
if (typeof item === 'object' && item !== null) {
|
|
355
|
+
const r = item;
|
|
356
|
+
infra.push({
|
|
357
|
+
category: String(r['category'] ?? ''),
|
|
358
|
+
provider: String(r['provider'] ?? ''),
|
|
359
|
+
monthlyCost: Number(r['monthlyCost'] ?? r['monthly_cost'] ?? 0),
|
|
360
|
+
annualCost: Number(r['annualCost'] ?? r['annual_cost'] ?? 0),
|
|
361
|
+
assumptions: extractStringArray(r, 'assumptions'),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Integration costs
|
|
366
|
+
for (const item of extractArray(resp, 'integrationCosts', 'integrations')) {
|
|
367
|
+
if (typeof item === 'object' && item !== null) {
|
|
368
|
+
const r = item;
|
|
369
|
+
integ.push({
|
|
370
|
+
system: String(r['system'] ?? r['name'] ?? ''),
|
|
371
|
+
setupCost: Number(r['setupCost'] ?? r['setup_cost'] ?? 0),
|
|
372
|
+
ongoingMonthlyCost: Number(r['ongoingMonthlyCost'] ?? r['monthly_cost'] ?? 0),
|
|
373
|
+
assumptions: extractStringArray(r, 'assumptions'),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// Operational costs
|
|
378
|
+
for (const item of extractArray(resp, 'operationalCosts', 'operational')) {
|
|
379
|
+
if (typeof item === 'object' && item !== null) {
|
|
380
|
+
const r = item;
|
|
381
|
+
ops.push({
|
|
382
|
+
category: String(r['category'] ?? ''),
|
|
383
|
+
annualCost: Number(r['annualCost'] ?? r['annual_cost'] ?? 0),
|
|
384
|
+
fteRequired: Number(r['fteRequired'] ?? r['fte'] ?? 0),
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// TCO
|
|
389
|
+
if (resp['tco'] && typeof resp['tco'] === 'object') {
|
|
390
|
+
const t = resp['tco'];
|
|
391
|
+
tco = {
|
|
392
|
+
year1: Number(t['year1'] ?? 0),
|
|
393
|
+
year3: Number(t['year3'] ?? 0),
|
|
394
|
+
year5: Number(t['year5'] ?? 0),
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
// ROI
|
|
398
|
+
if (resp['roi'] && typeof resp['roi'] === 'object') {
|
|
399
|
+
const r = resp['roi'];
|
|
400
|
+
roi = {
|
|
401
|
+
expectedReturn: Number(r['expectedReturn'] ?? r['expected_return'] ?? 0),
|
|
402
|
+
paybackPeriodMonths: Number(r['paybackPeriodMonths'] ?? r['payback_months'] ?? 0),
|
|
403
|
+
confidenceLevel: normalizeConfidence(r['confidenceLevel'] ?? r['confidence']),
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
// Budget risk
|
|
407
|
+
if (resp['budgetRisk'] && typeof resp['budgetRisk'] === 'object') {
|
|
408
|
+
const b = resp['budgetRisk'];
|
|
409
|
+
budgetRisk = {
|
|
410
|
+
contingencyRequired: Number(b['contingencyRequired'] ?? 0),
|
|
411
|
+
budgetExceedanceProbability: Number(b['budgetExceedanceProbability'] ?? 0),
|
|
412
|
+
riskFactors: extractStringArray(b, 'riskFactors', 'factors'),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// Build scenario variants from Pass 2 scenarios
|
|
417
|
+
const scenarioNodes = graph.getNodesByType('scenario');
|
|
418
|
+
for (const sn of scenarioNodes) {
|
|
419
|
+
if (sn.pass !== 2)
|
|
420
|
+
continue;
|
|
421
|
+
const sc = sn.content;
|
|
422
|
+
variants.push({
|
|
423
|
+
scenarioId: sn.id,
|
|
424
|
+
scenarioName: String(sc['name'] ?? sn.name),
|
|
425
|
+
costDelta: Number(sc['costDelta'] ?? 0),
|
|
426
|
+
explanation: String(sc['mitigationStrategy'] ?? sc['description'] ?? ''),
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return { infrastructure: infra, integrationCosts: integ, operationalCosts: ops, tco, roi, scenarioVariants: variants, budgetRisk };
|
|
430
|
+
}
|
|
431
|
+
// ============================================================================
|
|
432
|
+
// Workstream D: Governance Report Extraction
|
|
433
|
+
// ============================================================================
|
|
434
|
+
function extractGovernanceReport(ws, graph) {
|
|
435
|
+
const defaults = {
|
|
436
|
+
auditTrail: [],
|
|
437
|
+
changeImpact: [],
|
|
438
|
+
governanceReadiness: { overallScore: 'not_ready', conditions: [], strengths: [], gaps: [] },
|
|
439
|
+
};
|
|
440
|
+
const auditTrail = [];
|
|
441
|
+
const changes = [];
|
|
442
|
+
let readiness = defaults.governanceReadiness;
|
|
443
|
+
if (!ws || ws.successCount === 0) {
|
|
444
|
+
// Fall through to auto-audit logic below
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
for (const result of ws.agentResults) {
|
|
448
|
+
if (result.status < 200 || result.status >= 300 || !result.response)
|
|
449
|
+
continue;
|
|
450
|
+
const resp = result.response;
|
|
451
|
+
// Audit trail
|
|
452
|
+
for (const item of extractArray(resp, 'auditTrail', 'audit', 'decisions')) {
|
|
453
|
+
if (typeof item === 'object' && item !== null) {
|
|
454
|
+
const r = item;
|
|
455
|
+
auditTrail.push({
|
|
456
|
+
decision: String(r['decision'] ?? ''),
|
|
457
|
+
madeBy: String(r['madeBy'] ?? r['agent'] ?? ''),
|
|
458
|
+
timestamp: String(r['timestamp'] ?? new Date().toISOString()),
|
|
459
|
+
rationale: String(r['rationale'] ?? ''),
|
|
460
|
+
dataInputs: extractStringArray(r, 'dataInputs', 'inputs'),
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Change impact
|
|
465
|
+
for (const item of extractArray(resp, 'changeImpact', 'changes', 'impacts')) {
|
|
466
|
+
if (typeof item === 'object' && item !== null) {
|
|
467
|
+
const r = item;
|
|
468
|
+
changes.push({
|
|
469
|
+
affectedSystem: String(r['affectedSystem'] ?? r['system'] ?? ''),
|
|
470
|
+
changeType: String(r['changeType'] ?? r['type'] ?? ''),
|
|
471
|
+
impactLevel: normalizeRiskLevel(r['impactLevel'] ?? r['impact']),
|
|
472
|
+
stakeholders: extractStringArray(r, 'stakeholders'),
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// Governance readiness
|
|
477
|
+
if (resp['governanceReadiness'] && typeof resp['governanceReadiness'] === 'object') {
|
|
478
|
+
const g = resp['governanceReadiness'];
|
|
479
|
+
readiness = {
|
|
480
|
+
overallScore: normalizeReadiness(g['overallScore'] ?? g['score']),
|
|
481
|
+
conditions: extractStringArray(g, 'conditions'),
|
|
482
|
+
strengths: extractStringArray(g, 'strengths'),
|
|
483
|
+
gaps: extractStringArray(g, 'gaps'),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} // end else (ws has successful agents)
|
|
488
|
+
// Auto-generate audit trail from prior graph nodes if none from agents
|
|
489
|
+
if (auditTrail.length === 0) {
|
|
490
|
+
for (const n of graph.getAllNodes()) {
|
|
491
|
+
if (n.pass <= 3) {
|
|
492
|
+
auditTrail.push({
|
|
493
|
+
decision: `${n.type}: ${n.name}`,
|
|
494
|
+
madeBy: `${n.producedBy.domain}/${n.producedBy.agent}`,
|
|
495
|
+
timestamp: n.timestamp,
|
|
496
|
+
rationale: n.summary,
|
|
497
|
+
dataInputs: [...n.derivedFrom].slice(0, 5),
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return { auditTrail, changeImpact: changes, governanceReadiness: readiness };
|
|
503
|
+
}
|
|
504
|
+
// ============================================================================
|
|
505
|
+
// Convergence
|
|
506
|
+
// ============================================================================
|
|
507
|
+
function buildConvergence(security, policy, cost, governance) {
|
|
508
|
+
const financialViability = cost.roi.expectedReturn > 0 && cost.tco.year1 > 0 ? cost.roi.confidenceLevel
|
|
509
|
+
: cost.tco.year1 > 0 ? 'medium'
|
|
510
|
+
: 'low';
|
|
511
|
+
const parts = [];
|
|
512
|
+
parts.push(`Security: ${security.overallRiskLevel}`);
|
|
513
|
+
parts.push(`Compliance: ${policy.overallComplianceStatus}`);
|
|
514
|
+
parts.push(`Financial: ${financialViability}`);
|
|
515
|
+
parts.push(`Governance: ${governance.governanceReadiness.overallScore}`);
|
|
516
|
+
return {
|
|
517
|
+
securityRiskLevel: security.overallRiskLevel,
|
|
518
|
+
complianceStatus: policy.overallComplianceStatus,
|
|
519
|
+
financialViability,
|
|
520
|
+
governanceReadiness: governance.governanceReadiness.overallScore,
|
|
521
|
+
overallAssessment: parts.join(' | '),
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
// ============================================================================
|
|
525
|
+
// DecisionGraph Write
|
|
526
|
+
// ============================================================================
|
|
527
|
+
function writeToGraph(graph, security, policy, cost, governance, convergence, tracker, pass) {
|
|
528
|
+
const nodeIds = [];
|
|
529
|
+
const priorIds = [...graph.getNodesByPass(1), ...graph.getNodesByPass(2), ...graph.getNodesByPass(3)]
|
|
530
|
+
.map(n => n.id).slice(0, 5);
|
|
531
|
+
// Security Analysis
|
|
532
|
+
const secNode = graph.createNode({
|
|
533
|
+
type: 'security_analysis',
|
|
534
|
+
name: 'Security & Compliance Analysis',
|
|
535
|
+
content: security,
|
|
536
|
+
summary: `Security risk: ${security.overallRiskLevel}. ${security.dataExposureMap.length} data exposure(s), ${security.remediationPriorities.length} remediation(s).`,
|
|
537
|
+
producedBy: { domain: 'shield', agent: 'security-analysis', role: 'Security Analyst' },
|
|
538
|
+
pass,
|
|
539
|
+
derivedFrom: priorIds,
|
|
540
|
+
confidence: riskLevelToConfidence(security.overallRiskLevel),
|
|
541
|
+
tags: [`security:${security.overallRiskLevel}`],
|
|
542
|
+
});
|
|
543
|
+
nodeIds.push(secNode.id);
|
|
544
|
+
// Policy Analysis
|
|
545
|
+
const polNode = graph.createNode({
|
|
546
|
+
type: 'policy_analysis',
|
|
547
|
+
name: 'Policy Enforcement Analysis',
|
|
548
|
+
content: policy,
|
|
549
|
+
summary: `Compliance: ${policy.overallComplianceStatus}. ${policy.applicableFrameworks.length} framework(s), ${policy.approvalRouting.length} approval(s).`,
|
|
550
|
+
producedBy: { domain: 'policy-engine', agent: 'enforce', role: 'Policy Analyst' },
|
|
551
|
+
pass,
|
|
552
|
+
derivedFrom: [secNode.id, ...priorIds.slice(0, 4)],
|
|
553
|
+
confidence: complianceToConfidence(policy.overallComplianceStatus),
|
|
554
|
+
tags: [`compliance:${policy.overallComplianceStatus}`],
|
|
555
|
+
});
|
|
556
|
+
nodeIds.push(polNode.id);
|
|
557
|
+
// Compliance Report (distinct from policy analysis per ADR-036)
|
|
558
|
+
const compNode = graph.createNode({
|
|
559
|
+
type: 'compliance_report',
|
|
560
|
+
name: 'Regulatory Compliance Report',
|
|
561
|
+
content: {
|
|
562
|
+
frameworks: policy.applicableFrameworks,
|
|
563
|
+
aiGovernance: policy.aiGovernance,
|
|
564
|
+
status: policy.overallComplianceStatus,
|
|
565
|
+
},
|
|
566
|
+
summary: `${policy.applicableFrameworks.length} regulatory framework(s) assessed. Status: ${policy.overallComplianceStatus}.`,
|
|
567
|
+
producedBy: { domain: 'policy-engine', agent: 'constraints', role: 'Compliance Analyst' },
|
|
568
|
+
pass,
|
|
569
|
+
derivedFrom: [polNode.id],
|
|
570
|
+
confidence: complianceToConfidence(policy.overallComplianceStatus),
|
|
571
|
+
tags: [`compliance:${policy.overallComplianceStatus}`],
|
|
572
|
+
});
|
|
573
|
+
nodeIds.push(compNode.id);
|
|
574
|
+
// Cost Model
|
|
575
|
+
const costNode = graph.createNode({
|
|
576
|
+
type: 'cost_model',
|
|
577
|
+
name: 'CostOps Financial Model',
|
|
578
|
+
content: cost,
|
|
579
|
+
summary: `TCO Year 1: ${cost.tco.year1}, ROI: ${cost.roi.expectedReturn}, Payback: ${cost.roi.paybackPeriodMonths}mo. ${cost.scenarioVariants.length} scenario variant(s).`,
|
|
580
|
+
producedBy: { domain: 'costops', agent: 'forecast', role: 'Financial Modeler' },
|
|
581
|
+
pass,
|
|
582
|
+
derivedFrom: priorIds,
|
|
583
|
+
confidence: confidenceToNumber(cost.roi.confidenceLevel),
|
|
584
|
+
tags: [`roi-confidence:${cost.roi.confidenceLevel}`],
|
|
585
|
+
});
|
|
586
|
+
nodeIds.push(costNode.id);
|
|
587
|
+
// Governance Report
|
|
588
|
+
const govNode = graph.createNode({
|
|
589
|
+
type: 'governance_report',
|
|
590
|
+
name: 'Governance Readiness Report',
|
|
591
|
+
content: governance,
|
|
592
|
+
summary: `Governance readiness: ${governance.governanceReadiness.overallScore}. ${governance.auditTrail.length} audit entries, ${governance.changeImpact.length} impact(s).`,
|
|
593
|
+
producedBy: { domain: 'governance-dashboard', agent: 'audit', role: 'Governance Assessor' },
|
|
594
|
+
pass,
|
|
595
|
+
derivedFrom: [...nodeIds],
|
|
596
|
+
confidence: readinessToConfidence(governance.governanceReadiness.overallScore),
|
|
597
|
+
tags: [`readiness:${governance.governanceReadiness.overallScore}`],
|
|
598
|
+
});
|
|
599
|
+
nodeIds.push(govNode.id);
|
|
600
|
+
// Convergence node
|
|
601
|
+
graph.createNode({
|
|
602
|
+
type: 'governance_report',
|
|
603
|
+
name: 'Governance Convergence Report',
|
|
604
|
+
content: convergence,
|
|
605
|
+
summary: convergence.overallAssessment,
|
|
606
|
+
producedBy: { domain: 'pipeline', agent: 'pass4-convergence', role: 'Governance Convergence' },
|
|
607
|
+
pass,
|
|
608
|
+
derivedFrom: nodeIds.slice(0, 5),
|
|
609
|
+
confidence: 0.8,
|
|
610
|
+
tags: ['convergence'],
|
|
611
|
+
});
|
|
612
|
+
// Record provenance
|
|
613
|
+
tracker.recordInvocation({
|
|
614
|
+
agent: { domain: 'pipeline', agent: 'pass4-convergence', role: 'Governance Convergence' },
|
|
615
|
+
pass,
|
|
616
|
+
passName: 'Governance, Compliance & CostOps',
|
|
617
|
+
workstream: 'convergence',
|
|
618
|
+
startedAt: new Date().toISOString(),
|
|
619
|
+
completedAt: new Date().toISOString(),
|
|
620
|
+
durationMs: 0,
|
|
621
|
+
inputNodeIds: priorIds,
|
|
622
|
+
outputNodeIds: nodeIds,
|
|
623
|
+
artifactNumbers: [5, 6, 7],
|
|
624
|
+
status: 'success',
|
|
625
|
+
confidence: 0.8,
|
|
626
|
+
});
|
|
627
|
+
return nodeIds;
|
|
628
|
+
}
|
|
629
|
+
// ============================================================================
|
|
630
|
+
// Default Agent Invoker
|
|
631
|
+
// ============================================================================
|
|
632
|
+
async function defaultInvokeAgents(agents, payload, pass, _timeoutMs) {
|
|
633
|
+
const promises = agents.map(async (agent) => {
|
|
634
|
+
const start = Date.now();
|
|
635
|
+
const timestamp = new Date().toISOString();
|
|
636
|
+
try {
|
|
637
|
+
const { executeAgentsInvokeCommand } = await import('../../commands/agents.js');
|
|
638
|
+
const result = await executeAgentsInvokeCommand(agent.domain, agent.agent, JSON.stringify(payload), { format: 'json' });
|
|
639
|
+
return { agent, status: result.status, response: result.response, durationMs: Date.now() - start, pass, timestamp };
|
|
640
|
+
}
|
|
641
|
+
catch (err) {
|
|
642
|
+
return { agent, status: 500, response: { error: err instanceof Error ? err.message : String(err) }, durationMs: Date.now() - start, pass, timestamp };
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
const settled = await Promise.allSettled(promises);
|
|
646
|
+
return settled.map((outcome, i) => outcome.status === 'fulfilled'
|
|
647
|
+
? outcome.value
|
|
648
|
+
: { agent: agents[i], status: 500, response: { error: String(outcome.reason) }, durationMs: 0, pass, timestamp: new Date().toISOString() });
|
|
649
|
+
}
|
|
650
|
+
// ============================================================================
|
|
651
|
+
// Normalization Helpers
|
|
652
|
+
// ============================================================================
|
|
653
|
+
function normalizeRiskLevel(raw) {
|
|
654
|
+
const s = String(raw ?? '').toLowerCase();
|
|
655
|
+
if (s === 'critical')
|
|
656
|
+
return 'critical';
|
|
657
|
+
if (s === 'high')
|
|
658
|
+
return 'high';
|
|
659
|
+
if (s === 'low')
|
|
660
|
+
return 'low';
|
|
661
|
+
return 'medium';
|
|
662
|
+
}
|
|
663
|
+
function normalizeEffort(raw) {
|
|
664
|
+
const s = String(raw ?? '').toLowerCase();
|
|
665
|
+
if (s === 'high')
|
|
666
|
+
return 'high';
|
|
667
|
+
if (s === 'low')
|
|
668
|
+
return 'low';
|
|
669
|
+
return 'medium';
|
|
670
|
+
}
|
|
671
|
+
function normalizeComplianceStatus(raw) {
|
|
672
|
+
const s = String(raw ?? '').toLowerCase().replace(/[\s-]/g, '_');
|
|
673
|
+
if (s === 'compliant')
|
|
674
|
+
return 'compliant';
|
|
675
|
+
if (s === 'non_compliant')
|
|
676
|
+
return 'non_compliant';
|
|
677
|
+
return 'gaps_identified';
|
|
678
|
+
}
|
|
679
|
+
function normalizeExplainability(raw) {
|
|
680
|
+
const s = String(raw ?? '').toLowerCase();
|
|
681
|
+
if (s === 'full')
|
|
682
|
+
return 'full';
|
|
683
|
+
if (s === 'opaque')
|
|
684
|
+
return 'opaque';
|
|
685
|
+
return 'partial';
|
|
686
|
+
}
|
|
687
|
+
function normalizeReadiness(raw) {
|
|
688
|
+
const s = String(raw ?? '').toLowerCase().replace(/[\s-]/g, '_');
|
|
689
|
+
if (s === 'ready')
|
|
690
|
+
return 'ready';
|
|
691
|
+
if (s === 'conditionally_ready')
|
|
692
|
+
return 'conditionally_ready';
|
|
693
|
+
return 'not_ready';
|
|
694
|
+
}
|
|
695
|
+
function normalizeConfidence(raw) {
|
|
696
|
+
const s = String(raw ?? '').toLowerCase();
|
|
697
|
+
if (s === 'high')
|
|
698
|
+
return 'high';
|
|
699
|
+
if (s === 'low')
|
|
700
|
+
return 'low';
|
|
701
|
+
return 'medium';
|
|
702
|
+
}
|
|
703
|
+
function worstOf(a, b) {
|
|
704
|
+
const order = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
705
|
+
return order[a] >= order[b] ? a : b;
|
|
706
|
+
}
|
|
707
|
+
function riskLevelToConfidence(level) {
|
|
708
|
+
switch (level) {
|
|
709
|
+
case 'critical': return 0.95;
|
|
710
|
+
case 'high': return 0.85;
|
|
711
|
+
case 'medium': return 0.7;
|
|
712
|
+
default: return 0.5;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function complianceToConfidence(status) {
|
|
716
|
+
switch (status) {
|
|
717
|
+
case 'compliant': return 0.95;
|
|
718
|
+
case 'gaps_identified': return 0.7;
|
|
719
|
+
default: return 0.4;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function confidenceToNumber(level) {
|
|
723
|
+
switch (level) {
|
|
724
|
+
case 'high': return 0.9;
|
|
725
|
+
case 'medium': return 0.7;
|
|
726
|
+
default: return 0.5;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function readinessToConfidence(score) {
|
|
730
|
+
switch (score) {
|
|
731
|
+
case 'ready': return 0.95;
|
|
732
|
+
case 'conditionally_ready': return 0.7;
|
|
733
|
+
default: return 0.4;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function extractArray(obj, ...keys) {
|
|
737
|
+
for (const k of keys) {
|
|
738
|
+
const v = obj[k];
|
|
739
|
+
if (Array.isArray(v))
|
|
740
|
+
return v;
|
|
741
|
+
}
|
|
742
|
+
return [];
|
|
743
|
+
}
|
|
744
|
+
function extractStringArray(obj, ...keys) {
|
|
745
|
+
const arr = extractArray(obj, ...keys);
|
|
746
|
+
return arr.map(String);
|
|
747
|
+
}
|
|
748
|
+
//# sourceMappingURL=pass4-governance.js.map
|