@llm-dev-ops/agentics-cli 1.6.6 → 1.6.8
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/dist/pipeline/phase2/phases/adr-generator.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/adr-generator.js +5 -6
- package/dist/pipeline/phase2/phases/adr-generator.js.map +1 -1
- package/dist/pipeline/phase2/phases/tech-stack-detector.d.ts +6 -0
- package/dist/pipeline/phase2/phases/tech-stack-detector.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/tech-stack-detector.js +79 -28
- package/dist/pipeline/phase2/phases/tech-stack-detector.js.map +1 -1
- package/dist/synthesis/simulation-renderers.d.ts +19 -0
- package/dist/synthesis/simulation-renderers.d.ts.map +1 -1
- package/dist/synthesis/simulation-renderers.js +649 -306
- package/dist/synthesis/simulation-renderers.js.map +1 -1
- package/package.json +1 -1
|
@@ -133,144 +133,377 @@ function extractSuccessProbability(simData) {
|
|
|
133
133
|
// Enterprise projects always carry residual risk, and showing 100% destroys credibility.
|
|
134
134
|
return Math.min(prob, 0.88);
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* ADR-PIPELINE-026: Synthesize financial data from ALL sources:
|
|
138
|
+
* 1. Simulation engine result (simData)
|
|
139
|
+
* 2. Agent results (planner, what-if, scenario, decision, costops)
|
|
140
|
+
* 3. Query-based estimates as last resort
|
|
141
|
+
* Documents should NEVER say "To be quantified".
|
|
142
|
+
*/
|
|
143
|
+
export function synthesizeFinancials(simData, platformResults, query) {
|
|
144
|
+
const fm = (safeGet(simData, 'financial_model') ?? safeGet(simData, 'cost_model') ?? safeGet(simData, 'financials'));
|
|
145
|
+
// Collect from all possible agent sources — not just costops
|
|
146
|
+
const financialFields = {};
|
|
147
|
+
const financialKeys = [
|
|
148
|
+
['budget', 'budget', 'investment', 'total_investment', 'allocated_budget', 'total_budget', 'estimated_cost', 'total_cost', 'project_cost'],
|
|
149
|
+
['roi', 'roi', 'expected_roi', 'return_on_investment', 'projected_roi'],
|
|
150
|
+
['npv', 'npv', 'net_present_value', '5_year_npv', 'five_year_npv'],
|
|
151
|
+
['payback', 'payback_period', 'payback', 'roi_timeline', 'break_even'],
|
|
152
|
+
['revenue', 'projected_revenue', 'revenue_impact', 'annual_value', 'annual_savings', 'savings'],
|
|
153
|
+
['costSavings', 'cost_savings', 'cost_reduction', 'savings', 'efficiency_gain'],
|
|
154
|
+
];
|
|
155
|
+
// Source 1: simData direct fields
|
|
156
|
+
for (const [key, ...aliases] of financialKeys) {
|
|
157
|
+
if (!key)
|
|
158
|
+
continue;
|
|
159
|
+
for (const alias of aliases) {
|
|
160
|
+
const val = safeString(simData, alias);
|
|
161
|
+
if (val && !financialFields[key]) {
|
|
162
|
+
financialFields[key] = val;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Source 1b: nested financial_model
|
|
167
|
+
if (!financialFields[key] && fm) {
|
|
168
|
+
for (const alias of aliases) {
|
|
169
|
+
const val = safeString(fm, alias);
|
|
170
|
+
if (val && !financialFields[key]) {
|
|
171
|
+
financialFields[key] = val;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Source 2: ALL agent results — scan every successful agent for financial fields
|
|
178
|
+
for (const agent of platformResults) {
|
|
179
|
+
if (agent.status < 200 || agent.status >= 300)
|
|
180
|
+
continue;
|
|
181
|
+
const data = extractSignalPayload(agent.response).data;
|
|
182
|
+
if (!data || typeof data !== 'object')
|
|
183
|
+
continue;
|
|
184
|
+
for (const [key, ...aliases] of financialKeys) {
|
|
185
|
+
if (!key || financialFields[key])
|
|
186
|
+
continue;
|
|
187
|
+
for (const alias of aliases) {
|
|
188
|
+
const val = safeString(data, alias);
|
|
189
|
+
if (val) {
|
|
190
|
+
financialFields[key] = val;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Check nested data.financial_model, data.result, data.output
|
|
195
|
+
if (!financialFields[key]) {
|
|
196
|
+
for (const nested of ['financial_model', 'result', 'output', 'analysis', 'data']) {
|
|
197
|
+
const sub = safeGet(data, nested);
|
|
198
|
+
if (!sub)
|
|
199
|
+
continue;
|
|
200
|
+
for (const alias of aliases) {
|
|
201
|
+
const val = safeString(sub, alias);
|
|
202
|
+
if (val) {
|
|
203
|
+
financialFields[key] = val;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (financialFields[key])
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const hasRealData = !!(financialFields['budget'] || financialFields['roi'] || financialFields['npv'] || financialFields['payback'] || financialFields['revenue'] || financialFields['costSavings']);
|
|
214
|
+
// Source 3: Estimate from query context when nothing else available
|
|
215
|
+
if (!hasRealData) {
|
|
216
|
+
return estimateFinancialsFromQuery(query);
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
budget: financialFields['budget'] ?? '',
|
|
220
|
+
roi: financialFields['roi'] ?? '',
|
|
221
|
+
npv: financialFields['npv'] ?? '',
|
|
222
|
+
payback: financialFields['payback'] ?? '',
|
|
223
|
+
revenue: financialFields['revenue'] ?? '',
|
|
224
|
+
costSavings: financialFields['costSavings'] ?? '',
|
|
225
|
+
hasData: true,
|
|
226
|
+
isEstimated: false,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* ADR-PIPELINE-025 §7a: Generate financial estimates from query context.
|
|
231
|
+
* Uses employee count, industry, and scope to produce rough-order-of-magnitude numbers.
|
|
232
|
+
* All clearly labeled as estimates.
|
|
233
|
+
*/
|
|
234
|
+
function estimateFinancialsFromQuery(query) {
|
|
235
|
+
// Extract employee count — simple string search, no regex
|
|
236
|
+
let employees = 10000;
|
|
237
|
+
const lower = query.toLowerCase();
|
|
238
|
+
const empIdx = lower.indexOf('employees');
|
|
239
|
+
const staffIdx = lower.indexOf('staff');
|
|
240
|
+
const targetIdx = empIdx !== -1 ? empIdx : staffIdx;
|
|
241
|
+
if (targetIdx > 5) {
|
|
242
|
+
// Walk backward from "employees" to find the number
|
|
243
|
+
const before = query.substring(Math.max(0, targetIdx - 30), targetIdx).trim();
|
|
244
|
+
const words = before.split(/\s+/);
|
|
245
|
+
for (let i = words.length - 1; i >= 0; i--) {
|
|
246
|
+
const cleaned = (words[i] ?? '').replace(/,/g, '').replace(/approximately/i, '').trim();
|
|
247
|
+
const num = parseInt(cleaned, 10);
|
|
248
|
+
if (num > 100 && num < 10_000_000) {
|
|
249
|
+
employees = num;
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Industry affects per-employee costs
|
|
255
|
+
const isPharma = lower.includes('pharma') || lower.includes('drug') || lower.includes('gxp') || lower.includes('clinical');
|
|
256
|
+
const isFinancial = lower.includes('financial') || lower.includes('banking') || lower.includes('insurance');
|
|
257
|
+
const isRetail = lower.includes('retail') || lower.includes('grocery') || lower.includes('store');
|
|
258
|
+
const costPerEmployee = isPharma ? 35 : isFinancial ? 30 : isRetail ? 15 : 25;
|
|
259
|
+
const savingsMultiplier = isPharma ? 80 : isFinancial ? 60 : isRetail ? 40 : 50;
|
|
260
|
+
const pilotCost = Math.round(employees * costPerEmployee / 1000) * 1000;
|
|
261
|
+
const annualSavings = Math.round(employees * savingsMultiplier / 1000) * 1000;
|
|
262
|
+
const paybackMonths = Math.max(6, Math.min(24, Math.round(pilotCost / (annualSavings / 12))));
|
|
263
|
+
const npv5yr = Math.round(annualSavings * 3.5); // ~10% discount rate, 5 years
|
|
264
|
+
const roiPct = Math.round((annualSavings / pilotCost) * 100);
|
|
265
|
+
const fmt = (n) => n >= 1_000_000 ? `$${(n / 1_000_000).toFixed(1)}M` : `$${(n / 1000).toFixed(0)}K`;
|
|
266
|
+
return {
|
|
267
|
+
budget: `${fmt(pilotCost)} - ${fmt(pilotCost * 1.5)} (estimated based on ${employees.toLocaleString()} employees)`,
|
|
268
|
+
roi: `${roiPct}% projected (estimated)`,
|
|
269
|
+
npv: `${fmt(npv5yr)} 5-year NPV (estimated, 10% discount rate)`,
|
|
270
|
+
payback: `${paybackMonths}-${paybackMonths + 6} months (estimated)`,
|
|
271
|
+
revenue: `${fmt(annualSavings)} annual efficiency gains (estimated)`,
|
|
272
|
+
costSavings: '',
|
|
273
|
+
hasData: true,
|
|
274
|
+
isEstimated: true,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
// extractFinancials is now synthesizeFinancials — kept as export for external callers
|
|
278
|
+
export { synthesizeFinancials as extractFinancials };
|
|
279
|
+
/**
|
|
280
|
+
* ADR-PIPELINE-025 §5c: Extract system name from query text using string matching.
|
|
281
|
+
* Mirrors tech-stack-detector's extractSystemNameFromText but for renderer context.
|
|
282
|
+
*/
|
|
283
|
+
function extractSystemFromQuery(query) {
|
|
284
|
+
const lower = query.toLowerCase();
|
|
285
|
+
const triggers = [
|
|
286
|
+
'managed through ', 'managed by ', 'managed via ', 'managed with ',
|
|
287
|
+
'managed using ', 'managed in ', 'are managed through ',
|
|
288
|
+
'inside ', 'records inside ', 'plans inside ', 'actions inside ',
|
|
289
|
+
];
|
|
290
|
+
const stopWords = new Set(['the', 'our', 'this', 'that', 'a', 'an', 'it', 'we', 'they', 'and', 'or', 'but', 'if', 'because', 'however']);
|
|
291
|
+
for (const trigger of triggers) {
|
|
292
|
+
const idx = lower.indexOf(trigger);
|
|
293
|
+
if (idx === -1)
|
|
294
|
+
continue;
|
|
295
|
+
const after = query.substring(idx + trigger.length).trim();
|
|
296
|
+
const words = [];
|
|
297
|
+
for (const raw of after.split(/\s+/)) {
|
|
298
|
+
const word = raw.replace(/[.,;:!?)\]]+$/, '');
|
|
299
|
+
if (!word)
|
|
300
|
+
break;
|
|
301
|
+
if (['erp', 'system', 'platform', 'software', 'crm', 'wms', 'tms'].includes(word.toLowerCase())) {
|
|
302
|
+
words.push(word);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
if (stopWords.has(word.toLowerCase()) && words.length > 0)
|
|
306
|
+
break;
|
|
307
|
+
if (word[0] === word[0]?.toLowerCase() && word[0] !== word[0]?.toUpperCase() && words.length > 0)
|
|
308
|
+
break;
|
|
309
|
+
words.push(word);
|
|
310
|
+
if (words.length >= 5)
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
const name = words.join(' ').trim();
|
|
314
|
+
if (name.length >= 2 && name.length <= 50 && !stopWords.has(name.toLowerCase()))
|
|
315
|
+
return name;
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* ADR-PIPELINE-024: Generate domain-appropriate risks from query context.
|
|
321
|
+
* Every enterprise project has risks. Never return an empty list.
|
|
322
|
+
*/
|
|
323
|
+
function generateDomainRisks(query, extracted) {
|
|
324
|
+
const risks = [];
|
|
325
|
+
const q = query.toLowerCase();
|
|
326
|
+
const systems = extracted.systems;
|
|
327
|
+
const primarySystem = systems[0] ?? 'enterprise platform';
|
|
328
|
+
// Every project has these
|
|
329
|
+
risks.push({
|
|
330
|
+
risk: `Data quality and completeness in ${primarySystem} may not meet analytical requirements`,
|
|
331
|
+
category: 'Data', likelihood: 'High', impact: 'Medium', score: 6,
|
|
332
|
+
mitigation: 'Data profiling during discovery phase; validation layer with quarantine for invalid records',
|
|
333
|
+
});
|
|
334
|
+
risks.push({
|
|
335
|
+
risk: `${primarySystem} API integration complexity — rate limits, schema changes, authentication`,
|
|
336
|
+
category: 'Technical', likelihood: 'Medium', impact: 'High', score: 6,
|
|
337
|
+
mitigation: `Technical spike to validate ${primarySystem} API capabilities before full commitment`,
|
|
338
|
+
});
|
|
339
|
+
risks.push({
|
|
340
|
+
risk: 'Scope creep beyond initial prototype boundaries',
|
|
341
|
+
category: 'Project', likelihood: 'High', impact: 'Medium', score: 6,
|
|
342
|
+
mitigation: 'Fixed scope contract with explicit phase gates; changes require stakeholder re-approval',
|
|
343
|
+
});
|
|
344
|
+
// Size-dependent risks
|
|
345
|
+
if (/\b\d{2,6}\s*(?:employee|staff|people|worker|team member)/i.test(q)) {
|
|
346
|
+
risks.push({
|
|
347
|
+
risk: 'Change management across large workforce — adoption resistance and training burden',
|
|
348
|
+
category: 'Organizational', likelihood: 'Medium', impact: 'High', score: 6,
|
|
349
|
+
mitigation: 'Phased rollout starting with pilot group; champion network; training program before go-live',
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
// Multi-region risks
|
|
353
|
+
if (/(?:north america|europe|asia|global|multi.?region|across.*(?:region|countr|continent))/i.test(q)) {
|
|
354
|
+
risks.push({
|
|
355
|
+
risk: 'Regional regulatory variance — data residency, privacy, and compliance requirements differ by jurisdiction',
|
|
356
|
+
category: 'Regulatory', likelihood: 'Medium', impact: 'High', score: 6,
|
|
357
|
+
mitigation: 'Legal review per jurisdiction during discovery; configurable compliance rules per region',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
// Sustainability risks
|
|
361
|
+
if (/(?:emission|carbon|sustainab|environmental|energy|waste|esg)/i.test(q)) {
|
|
362
|
+
risks.push({
|
|
363
|
+
risk: 'Emissions factor accuracy — default factors may not match specific operational context',
|
|
364
|
+
category: 'Data', likelihood: 'Medium', impact: 'Medium', score: 4,
|
|
365
|
+
mitigation: 'Allow user-supplied factors with audit trail; flag calculations using default vs. measured values',
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
// Financial risks
|
|
369
|
+
if (/(?:investment|budget|cost|roi|revenue|profitab)/i.test(q)) {
|
|
370
|
+
risks.push({
|
|
371
|
+
risk: 'Financial assumptions may not hold — market conditions, operational costs, or adoption rates may differ from projections',
|
|
372
|
+
category: 'Financial', likelihood: 'Medium', impact: 'Medium', score: 4,
|
|
373
|
+
mitigation: 'Sensitivity analysis on key assumptions; quarterly re-validation of financial model',
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
// Always include vendor dependency
|
|
377
|
+
risks.push({
|
|
378
|
+
risk: 'Technology vendor dependency — platform availability, pricing changes, or API deprecation',
|
|
379
|
+
category: 'Technical', likelihood: 'Low', impact: 'High', score: 3,
|
|
380
|
+
mitigation: 'Ports-and-adapters architecture enables vendor swap; contractual SLA requirements',
|
|
381
|
+
});
|
|
382
|
+
return risks.sort((a, b) => b.score - a.score);
|
|
383
|
+
}
|
|
136
384
|
// ============================================================================
|
|
137
|
-
// Executive Summary Renderer
|
|
385
|
+
// Executive Summary Renderer (ADR-PIPELINE-024: Pyramid Principle)
|
|
138
386
|
// ============================================================================
|
|
139
387
|
export function renderExecutiveSummary(query, simulationResult, platformResults) {
|
|
140
388
|
const now = new Date().toISOString();
|
|
141
|
-
const execSummaryAgent = platformResults.find(r => r.agent === 'executive-summary');
|
|
142
|
-
const riskAgent = platformResults.find(r => r.agent === 'risk-score');
|
|
143
|
-
const decisionAgent = platformResults.find(r => r.agent === 'decision-memo');
|
|
144
|
-
// Extract simulation outcome
|
|
145
389
|
const simPayload = extractSignalPayload(simulationResult);
|
|
146
390
|
const simData = simPayload.data ?? {};
|
|
147
391
|
const successProb = extractSuccessProbability(simData);
|
|
148
|
-
const timeline = safeString(simData, 'timeline_estimate') || safeString(simData, 'timeline');
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
// Determine confidence level
|
|
155
|
-
let confidence = 'LOW';
|
|
156
|
-
if (successProb >= 0.9)
|
|
157
|
-
confidence = 'HIGH';
|
|
158
|
-
else if (successProb >= 0.7)
|
|
159
|
-
confidence = 'MEDIUM';
|
|
160
|
-
// Determine recommendation
|
|
392
|
+
const timeline = safeString(simData, 'timeline_estimate') || safeString(simData, 'timeline') || '12-18 weeks';
|
|
393
|
+
const problemStatement = distillProblemStatement(query);
|
|
394
|
+
const extracted = extractScenarioFromQuery(query);
|
|
395
|
+
const fin = synthesizeFinancials(simData, platformResults, query);
|
|
396
|
+
const risks = generateDomainRisks(query, extracted);
|
|
397
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'the enterprise platform';
|
|
161
398
|
let recommendation = 'DEFER';
|
|
162
|
-
|
|
399
|
+
let recDetail = 'Further analysis recommended before committing resources.';
|
|
400
|
+
if (successProb >= 0.9) {
|
|
163
401
|
recommendation = 'PROCEED';
|
|
164
|
-
|
|
402
|
+
recDetail = `Proceed with full implementation targeting ${primarySystem} integration.`;
|
|
403
|
+
}
|
|
404
|
+
else if (successProb >= 0.7) {
|
|
165
405
|
recommendation = 'CONDITIONAL PROCEED';
|
|
166
|
-
|
|
167
|
-
|
|
406
|
+
recDetail = `Proceed with a scoped pilot to validate core assumptions before full commitment.`;
|
|
407
|
+
}
|
|
408
|
+
// ADR-PIPELINE-024: Pyramid Principle — lead with the answer
|
|
168
409
|
const lines = [
|
|
169
410
|
'# Executive Summary',
|
|
170
411
|
'',
|
|
171
412
|
`**Date:** ${now}`,
|
|
172
|
-
`**Assessment:** ${problemStatement}`,
|
|
173
|
-
`**Confidence Level:** ${confidence}`,
|
|
174
|
-
`**Recommendation:** ${recommendation}`,
|
|
175
413
|
'',
|
|
176
414
|
'---',
|
|
177
415
|
'',
|
|
178
|
-
'##
|
|
179
|
-
'',
|
|
180
|
-
`This enterprise feasibility analysis evaluates the proposed initiative: **${problemStatement}**.`,
|
|
181
|
-
'',
|
|
182
|
-
`| Metric | Value |`,
|
|
183
|
-
`|--------|-------|`,
|
|
184
|
-
`| Success Probability | ${(successProb * 100).toFixed(0)}% |`,
|
|
185
|
-
`| Estimated Timeline | ${timeline || '12-18 weeks (to be refined)'} |`,
|
|
186
|
-
`| Confidence Level | ${confidence} |`,
|
|
416
|
+
'## Recommendation',
|
|
187
417
|
'',
|
|
418
|
+
`**${recommendation}** — ${recDetail}`,
|
|
188
419
|
];
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
420
|
+
if (fin.hasData) {
|
|
421
|
+
const impactParts = [];
|
|
422
|
+
if (fin.budget)
|
|
423
|
+
impactParts.push(`${fin.budget} investment`);
|
|
424
|
+
if (fin.payback)
|
|
425
|
+
impactParts.push(`${fin.payback} payback`);
|
|
426
|
+
if (fin.npv)
|
|
427
|
+
impactParts.push(`${fin.npv} 5-year NPV`);
|
|
428
|
+
if (impactParts.length > 0) {
|
|
429
|
+
lines.push(`Projected impact: ${impactParts.join(', ')}.`);
|
|
199
430
|
}
|
|
200
|
-
lines.push('');
|
|
201
431
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
432
|
+
// ADR-PIPELINE-025 §4: Success probability with decomposition
|
|
433
|
+
const techFeasibility = Math.min(0.95, successProb + 0.04);
|
|
434
|
+
const orgReadiness = Math.max(0.70, successProb - 0.03);
|
|
435
|
+
const dataAvailability = successProb;
|
|
436
|
+
const integrationFeasibility = Math.max(0.75, successProb - 0.01);
|
|
437
|
+
lines.push(`Success probability: **${(successProb * 100).toFixed(0)}%** (Technical ${(techFeasibility * 100).toFixed(0)}%, Organizational ${(orgReadiness * 100).toFixed(0)}%, Data ${(dataAvailability * 100).toFixed(0)}%, Integration ${(integrationFeasibility * 100).toFixed(0)}%) | Timeline: ${timeline}`, '');
|
|
438
|
+
// The Opportunity — cost of inaction
|
|
439
|
+
lines.push('## The Opportunity', '');
|
|
440
|
+
lines.push(`${problemStatement}. Current processes rely on manual review and periodic reporting, ` +
|
|
441
|
+
`limiting the organization's ability to act proactively. Without intervention, inefficiencies ` +
|
|
442
|
+
`compound as operational scale grows across ${extracted.stakeholders.length > 0 ? extracted.stakeholders.slice(0, 2).join(' and ') : 'multiple teams'} ` +
|
|
443
|
+
`and ${primarySystem} data volume increases.`, '');
|
|
444
|
+
// What We Found — key insights with numbers
|
|
445
|
+
lines.push('## Key Findings', '');
|
|
446
|
+
const insights = [];
|
|
447
|
+
if (fin.roi)
|
|
448
|
+
insights.push(`Projected return on investment of ${fin.roi}`);
|
|
449
|
+
if (fin.costSavings)
|
|
450
|
+
insights.push(`Estimated cost savings of ${fin.costSavings}`);
|
|
451
|
+
if (fin.revenue)
|
|
452
|
+
insights.push(`Revenue impact potential of ${fin.revenue}`);
|
|
453
|
+
// Pull agent insights
|
|
454
|
+
const execAgent = extractAgentData(platformResults, 'platform', 'executive-summary');
|
|
455
|
+
if (execAgent) {
|
|
456
|
+
const findings = safeArray(execAgent, 'findings').concat(safeArray(execAgent, 'key_findings'));
|
|
457
|
+
for (const f of findings.slice(0, 3)) {
|
|
458
|
+
insights.push(typeof f === 'string' ? f : safeString(f, 'description') || safeString(f, 'finding') || String(f));
|
|
217
459
|
}
|
|
218
460
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
lines.push(...insights);
|
|
225
|
-
lines.push('');
|
|
226
|
-
}
|
|
461
|
+
insights.push(`${primarySystem} integration is technically feasible with ${extracted.constraints.length > 0 ? 'identified compliance constraints manageable' : 'standard API integration patterns'}`);
|
|
462
|
+
insights.push('Human-in-the-loop governance preserves decision authority while accelerating analysis');
|
|
463
|
+
for (const insight of insights.slice(0, 6)) {
|
|
464
|
+
if (insight)
|
|
465
|
+
lines.push(`- ${insight}`);
|
|
227
466
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
lines.push(
|
|
241
|
-
if (
|
|
242
|
-
lines.push(
|
|
243
|
-
if (
|
|
244
|
-
lines.push(
|
|
245
|
-
if (execPayback)
|
|
246
|
-
lines.push(`- **Payback Period:** ${execPayback}`);
|
|
247
|
-
if (execFinModel) {
|
|
248
|
-
const fmRevenue = safeString(execFinModel, 'projected_revenue') || safeString(execFinModel, 'annual_value');
|
|
249
|
-
if (fmRevenue)
|
|
250
|
-
lines.push(`- **Projected Revenue:** ${fmRevenue}`);
|
|
251
|
-
}
|
|
252
|
-
if (roiAgent) {
|
|
253
|
-
const insights = synthesizeAgentInsights(roiAgent, 'costops-roi');
|
|
254
|
-
lines.push('', ...insights);
|
|
255
|
-
}
|
|
256
|
-
if (forecastAgent) {
|
|
257
|
-
const insights = synthesizeAgentInsights(forecastAgent, 'costops-forecast');
|
|
258
|
-
lines.push('', ...insights);
|
|
259
|
-
}
|
|
467
|
+
lines.push('');
|
|
468
|
+
// Financial Impact
|
|
469
|
+
if (fin.hasData) {
|
|
470
|
+
lines.push('## Financial Impact', '');
|
|
471
|
+
lines.push('| Metric | Value |', '|--------|-------|');
|
|
472
|
+
if (fin.budget)
|
|
473
|
+
lines.push(`| Total Investment | ${fin.budget} |`);
|
|
474
|
+
if (fin.roi)
|
|
475
|
+
lines.push(`| Expected ROI | ${fin.roi} |`);
|
|
476
|
+
if (fin.npv)
|
|
477
|
+
lines.push(`| 5-Year NPV | ${fin.npv} |`);
|
|
478
|
+
if (fin.payback)
|
|
479
|
+
lines.push(`| Payback Period | ${fin.payback} |`);
|
|
480
|
+
if (fin.revenue)
|
|
481
|
+
lines.push(`| Revenue Impact | ${fin.revenue} |`);
|
|
482
|
+
if (fin.costSavings)
|
|
483
|
+
lines.push(`| Cost Savings | ${fin.costSavings} |`);
|
|
260
484
|
lines.push('');
|
|
261
485
|
}
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
lines.push('---', '');
|
|
271
|
-
if (execSources.length > 0) {
|
|
272
|
-
lines.push(`*Sources: ${execSources.join(', ')}*`);
|
|
486
|
+
// Risk Profile — never empty
|
|
487
|
+
lines.push('## Risk Profile', '');
|
|
488
|
+
const topRisks = risks.slice(0, 3);
|
|
489
|
+
const maxScore = topRisks[0]?.score ?? 0;
|
|
490
|
+
const riskLevel = maxScore >= 6 ? 'MEDIUM' : maxScore >= 3 ? 'LOW-MEDIUM' : 'LOW';
|
|
491
|
+
lines.push(`**Overall Risk Level: ${riskLevel}** — ${topRisks.length} risks require active management.`, '');
|
|
492
|
+
for (const r of topRisks) {
|
|
493
|
+
lines.push(`- **${r.category}** (${r.likelihood} likelihood, ${r.impact} impact): ${r.risk}`);
|
|
273
494
|
}
|
|
495
|
+
lines.push('');
|
|
496
|
+
// Recommended Next Steps — time-bound
|
|
497
|
+
lines.push('## Recommended Next Steps', '');
|
|
498
|
+
const steps = buildDomainNextSteps(recommendation, extracted);
|
|
499
|
+
for (const step of steps) {
|
|
500
|
+
lines.push(step);
|
|
501
|
+
}
|
|
502
|
+
lines.push('');
|
|
503
|
+
// Provenance
|
|
504
|
+
const sources = platformResults.filter(r => r.status >= 200 && r.status < 300).map(r => `${r.domain}/${r.agent}`);
|
|
505
|
+
lines.push('---', '');
|
|
506
|
+
lines.push(`*Sources: ${sources.slice(0, 8).join(', ')}${sources.length > 8 ? ` + ${sources.length - 8} more` : ''}*`);
|
|
274
507
|
lines.push(`*Generated: ${now}*`);
|
|
275
508
|
return lines.join('\n');
|
|
276
509
|
}
|
|
@@ -282,140 +515,109 @@ export function renderDecisionMemo(query, simulationResult, platformResults) {
|
|
|
282
515
|
const simPayload = extractSignalPayload(simulationResult);
|
|
283
516
|
const simData = simPayload.data ?? {};
|
|
284
517
|
const successProb = extractSuccessProbability(simData);
|
|
285
|
-
const
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
const
|
|
290
|
-
const riskScoreData = extractAgentData(platformResults, 'platform', 'risk-score');
|
|
518
|
+
const extracted = extractScenarioFromQuery(query);
|
|
519
|
+
const problemStatement = distillProblemStatement(query);
|
|
520
|
+
const fin = synthesizeFinancials(simData, platformResults, query);
|
|
521
|
+
const risks = generateDomainRisks(query, extracted);
|
|
522
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'the enterprise platform';
|
|
291
523
|
const plannerData = extractAgentData(platformResults, 'copilot', 'planner');
|
|
292
524
|
let recommendation = 'DEFER';
|
|
293
|
-
let rationale = '
|
|
525
|
+
let rationale = 'Further analysis recommended before committing resources.';
|
|
294
526
|
if (successProb >= 0.9) {
|
|
295
527
|
recommendation = 'PROCEED';
|
|
296
|
-
rationale = 'High probability of success with manageable risks.';
|
|
528
|
+
rationale = 'High probability of success with manageable risks. Full deployment recommended.';
|
|
297
529
|
}
|
|
298
530
|
else if (successProb >= 0.7) {
|
|
299
531
|
recommendation = 'CONDITIONAL PROCEED';
|
|
300
|
-
rationale = 'Moderate confidence
|
|
532
|
+
rationale = 'Moderate confidence. A scoped pilot is recommended to validate core assumptions before committing to full deployment.';
|
|
301
533
|
}
|
|
302
|
-
//
|
|
303
|
-
const extracted = extractScenarioFromQuery(query);
|
|
304
|
-
const problemStatement = distillProblemStatement(query);
|
|
534
|
+
// ADR-PIPELINE-024: Structured decision package
|
|
305
535
|
const lines = [
|
|
306
536
|
'# Decision Memo',
|
|
307
537
|
'',
|
|
308
538
|
`**Date:** ${now}`,
|
|
309
539
|
`**Subject:** ${problemStatement}`,
|
|
540
|
+
`**Decision Requested:** Approval to proceed with ${successProb >= 0.7 ? 'scoped pilot' : 'feasibility investigation'}`,
|
|
310
541
|
'',
|
|
311
542
|
'---',
|
|
312
543
|
'',
|
|
313
544
|
'## Recommendation',
|
|
314
545
|
'',
|
|
315
|
-
|
|
546
|
+
`**${recommendation}** — ${rationale}`,
|
|
547
|
+
'',
|
|
548
|
+
`Success probability: ${(successProb * 100).toFixed(0)}%`,
|
|
316
549
|
'',
|
|
317
|
-
|
|
550
|
+
// Business Context
|
|
551
|
+
'## Business Context', '',
|
|
552
|
+
`The organization requires ${problemStatement.toLowerCase()}. ` +
|
|
553
|
+
`Current manual processes limit visibility and create operational inefficiency across ` +
|
|
554
|
+
`${extracted.stakeholders.slice(0, 3).join(', ') || 'multiple business units'}. ` +
|
|
555
|
+
`${primarySystem} serves as the system of record, and any solution must integrate non-disruptively.`,
|
|
318
556
|
'',
|
|
319
|
-
|
|
557
|
+
// Options Considered
|
|
558
|
+
'## Options Considered', '',
|
|
559
|
+
'| Option | Description | Investment | Timeline | Risk Level | Recommendation |',
|
|
560
|
+
'|--------|-------------|-----------|----------|------------|---------------|',
|
|
561
|
+
`| **A. Scoped Pilot** | Validate with subset of data and ${extracted.domain_entities.slice(0, 2).join(', ') || 'core entities'} | ${fin.budget || 'TBD'} (pilot scope) | 8-12 weeks | Low | **Recommended** |`,
|
|
562
|
+
`| **B. Full Deployment** | Enterprise-wide rollout across all ${extracted.systems.length > 0 ? extracted.systems.join(', ') : 'systems'} | ${fin.budget || 'TBD'} | ${safeString(simData, 'timeline_estimate') || '16-24 weeks'} | Medium | After pilot validation |`,
|
|
563
|
+
`| **C. Do Nothing** | Continue with manual processes | $0 | N/A | High | Not recommended — costs compound |`,
|
|
320
564
|
'',
|
|
321
565
|
];
|
|
322
|
-
//
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (hasFinancialData) {
|
|
339
|
-
lines.push('## Financial Impact', '');
|
|
340
|
-
// Simulation-sourced financial data
|
|
341
|
-
const budget = simBudget || fmBudget;
|
|
342
|
-
const roi = simRoi || fmRoi;
|
|
343
|
-
const npv = simNpv || fmNpv;
|
|
344
|
-
const payback = simPayback || fmPayback;
|
|
345
|
-
if (budget)
|
|
346
|
-
lines.push(`- **Total Investment:** ${budget}`);
|
|
347
|
-
if (roi)
|
|
348
|
-
lines.push(`- **Expected ROI:** ${roi}`);
|
|
349
|
-
if (npv)
|
|
350
|
-
lines.push(`- **5-Year NPV:** ${npv}`);
|
|
351
|
-
if (payback)
|
|
352
|
-
lines.push(`- **Payback Period:** ${payback}`);
|
|
353
|
-
if (simRevenue)
|
|
354
|
-
lines.push(`- **Projected Revenue Impact:** ${simRevenue}`);
|
|
355
|
-
if (simCostSavings)
|
|
356
|
-
lines.push(`- **Cost Savings:** ${simCostSavings}`);
|
|
357
|
-
// Enrich with costops agent data if available
|
|
358
|
-
if (roiData) {
|
|
359
|
-
const agentRoi = safeString(roiData, 'roi') || safeString(roiData, 'expected_roi');
|
|
360
|
-
const agentPayback = safeString(roiData, 'payback_period') || safeString(roiData, 'payback');
|
|
361
|
-
const agentCost = safeString(roiData, 'total_cost') || safeString(roiData, 'estimated_cost');
|
|
362
|
-
if (agentRoi && !roi)
|
|
363
|
-
lines.push(`- **Expected ROI:** ${agentRoi}`);
|
|
364
|
-
if (agentPayback && !payback)
|
|
365
|
-
lines.push(`- **Payback Period:** ${agentPayback}`);
|
|
366
|
-
if (agentCost && !budget)
|
|
367
|
-
lines.push(`- **Estimated Cost:** ${agentCost}`);
|
|
368
|
-
const insights = synthesizeAgentInsights(roiData, 'costops-roi');
|
|
369
|
-
if (insights.length > 0 && insights.some(l => l.trim().length > 0))
|
|
370
|
-
lines.push('', ...insights);
|
|
371
|
-
}
|
|
372
|
-
if (tradeoffData) {
|
|
373
|
-
lines.push('', '**Cost-Quality Tradeoff:**');
|
|
374
|
-
const insights = synthesizeAgentInsights(tradeoffData, 'costops-tradeoff');
|
|
375
|
-
lines.push(...insights);
|
|
376
|
-
}
|
|
566
|
+
// Financial Impact
|
|
567
|
+
lines.push('## Financial Impact', '');
|
|
568
|
+
if (fin.hasData) {
|
|
569
|
+
lines.push('| Metric | Value |', '|--------|-------|');
|
|
570
|
+
if (fin.budget)
|
|
571
|
+
lines.push(`| Total Investment | ${fin.budget} |`);
|
|
572
|
+
if (fin.roi)
|
|
573
|
+
lines.push(`| Expected ROI | ${fin.roi} |`);
|
|
574
|
+
if (fin.npv)
|
|
575
|
+
lines.push(`| 5-Year NPV | ${fin.npv} |`);
|
|
576
|
+
if (fin.payback)
|
|
577
|
+
lines.push(`| Payback Period | ${fin.payback} |`);
|
|
578
|
+
if (fin.revenue)
|
|
579
|
+
lines.push(`| Revenue Impact | ${fin.revenue} |`);
|
|
580
|
+
if (fin.costSavings)
|
|
581
|
+
lines.push(`| Cost Savings | ${fin.costSavings} |`);
|
|
377
582
|
lines.push('');
|
|
378
583
|
}
|
|
379
|
-
lines.push('## Risk Summary', '');
|
|
380
|
-
// Combine simulation risks with platform risk-score agent
|
|
381
|
-
const allRisks = [...riskFactors];
|
|
382
|
-
if (riskScoreData) {
|
|
383
|
-
const agentRisks = safeArray(riskScoreData, 'risk_factors')
|
|
384
|
-
.concat(safeArray(riskScoreData, 'risks'))
|
|
385
|
-
.concat(safeArray(riskScoreData, 'factors'));
|
|
386
|
-
allRisks.push(...agentRisks);
|
|
387
|
-
}
|
|
388
|
-
if (allRisks.length > 0) {
|
|
389
|
-
for (const risk of allRisks) {
|
|
390
|
-
lines.push(`- ${typeof risk === 'string' ? risk : JSON.stringify(risk)}`);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
584
|
else {
|
|
394
|
-
lines.push('
|
|
585
|
+
lines.push('Financial model to be developed during discovery phase. Key inputs: implementation cost, operational savings, and revenue impact.', '');
|
|
395
586
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
587
|
+
// Risk Assessment Summary — never empty
|
|
588
|
+
lines.push('## Risk Assessment', '');
|
|
589
|
+
lines.push('| # | Risk | Category | Likelihood | Impact | Mitigation |');
|
|
590
|
+
lines.push('|---|------|----------|-----------|--------|------------|');
|
|
591
|
+
for (let i = 0; i < Math.min(risks.length, 5); i++) {
|
|
592
|
+
const r = risks[i];
|
|
593
|
+
lines.push(`| ${i + 1} | ${r.risk} | ${r.category} | ${r.likelihood} | ${r.impact} | ${r.mitigation} |`);
|
|
400
594
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
lines.push(`- ${typeof rec === 'object' && rec !== null ? JSON.stringify(rec) : String(rec)}`);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
// ADR-PIPELINE-020: Implementation approach from copilot/planner
|
|
595
|
+
lines.push('');
|
|
596
|
+
// Implementation Approach
|
|
597
|
+
lines.push('## Implementation Approach', '');
|
|
408
598
|
if (plannerData) {
|
|
409
|
-
lines.push('', '## Implementation Approach', '');
|
|
410
599
|
const insights = synthesizeAgentInsights(plannerData, 'copilot-planner');
|
|
411
600
|
lines.push(...insights);
|
|
412
601
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
lines.push(`*Sources: ${sources.join(', ')}*`);
|
|
602
|
+
else {
|
|
603
|
+
const steps = buildDomainNextSteps(recommendation, extracted);
|
|
604
|
+
for (const step of steps)
|
|
605
|
+
lines.push(step);
|
|
418
606
|
}
|
|
607
|
+
lines.push('');
|
|
608
|
+
// Stakeholder Impact
|
|
609
|
+
lines.push('## Stakeholder Impact', '');
|
|
610
|
+
lines.push('| Stakeholder | Impact | Action Required |');
|
|
611
|
+
lines.push('|-------------|--------|----------------|');
|
|
612
|
+
lines.push(`| Executive Sponsor | Budget approval, strategic alignment | Review financial model and approve pilot scope |`);
|
|
613
|
+
lines.push(`| ${extracted.stakeholders[0] || 'Technology Lead'} | Architecture sign-off | Validate ${primarySystem} integration approach |`);
|
|
614
|
+
lines.push(`| ${extracted.stakeholders[1] || 'Operations Team'} | Process change, pilot participation | Participate in pilot; provide domain expertise |`);
|
|
615
|
+
lines.push(`| ${extracted.stakeholders[2] || 'Compliance / Risk'} | Governance review | Validate audit trail and approval workflow design |`);
|
|
616
|
+
lines.push('');
|
|
617
|
+
// Provenance
|
|
618
|
+
const sources = platformResults.filter(r => r.status >= 200 && r.status < 300).map(r => `${r.domain}/${r.agent}`);
|
|
619
|
+
lines.push('---', '');
|
|
620
|
+
lines.push(`*Sources: ${sources.slice(0, 6).join(', ')}${sources.length > 6 ? ` + ${sources.length - 6} more` : ''}*`);
|
|
419
621
|
lines.push(`*Generated: ${now}*`);
|
|
420
622
|
return lines.join('\n');
|
|
421
623
|
}
|
|
@@ -906,9 +1108,9 @@ export function buildRoadmapArtifact(query, simulationResult, platformResults) {
|
|
|
906
1108
|
// Extract phases from timeline or generate domain-specific structure
|
|
907
1109
|
const phases = safeArray(simData, 'timeline', 'phases');
|
|
908
1110
|
const extracted = extractScenarioFromQuery(query);
|
|
909
|
-
const systemList = extracted.systems.length > 0 ? extracted.systems.join(', ') : 'target platform';
|
|
1111
|
+
const systemList = extracted.systems.length > 0 ? extracted.systems.join(', ') : (extractSystemFromQuery(query) ?? 'target platform');
|
|
910
1112
|
const entityList = extracted.domain_entities.slice(0, 3).join(', ') || 'core domain models';
|
|
911
|
-
const primarySystem = extracted.systems[0] ?? 'enterprise platform';
|
|
1113
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'enterprise platform';
|
|
912
1114
|
const defaultPhases = phases.length > 0 ? phases : [
|
|
913
1115
|
{
|
|
914
1116
|
id: 'phase-1',
|
|
@@ -969,15 +1171,27 @@ export function buildRoadmapArtifact(query, simulationResult, platformResults) {
|
|
|
969
1171
|
},
|
|
970
1172
|
phases: defaultPhases,
|
|
971
1173
|
success_criteria: safeArray(simData, 'recommendations').map(r => String(r)),
|
|
972
|
-
|
|
1174
|
+
// ADR-PIPELINE-024: Always include domain-appropriate risks
|
|
1175
|
+
risk_factors: generateDomainRisks(query, extracted).map(r => ({
|
|
1176
|
+
description: r.risk,
|
|
1177
|
+
category: r.category,
|
|
1178
|
+
likelihood: r.likelihood,
|
|
1179
|
+
impact: r.impact,
|
|
1180
|
+
mitigation: r.mitigation,
|
|
1181
|
+
})),
|
|
973
1182
|
platform_insights: platformResults
|
|
974
1183
|
.filter(r => r.agent === 'executive-summary' && r.status >= 200 && r.status < 300)
|
|
975
1184
|
.map(r => extractSignalPayload(r.response).data)
|
|
976
1185
|
.filter(Boolean),
|
|
977
|
-
// ADR-PIPELINE-020: Enrich roadmap with planner and costops data
|
|
978
1186
|
cost_estimate: extractAgentData(platformResults, 'costops', 'forecast'),
|
|
979
1187
|
implementation_plan: extractAgentData(platformResults, 'copilot', 'planner'),
|
|
980
1188
|
resource_requirements: extractAgentData(platformResults, 'costops', 'budget'),
|
|
1189
|
+
// ADR-PIPELINE-024: Risk gates between phases
|
|
1190
|
+
phase_gates: [
|
|
1191
|
+
{ after_phase: 'phase-1', gate: 'Discovery Complete', criteria: `${primarySystem} API validated, requirements signed off, team resourced`, decision: 'Proceed to build or pivot' },
|
|
1192
|
+
{ after_phase: 'phase-2', gate: 'Prototype Validated', criteria: 'Core functionality demonstrated, integration tested, stakeholder approval', decision: 'Proceed to validation or iterate' },
|
|
1193
|
+
{ after_phase: 'phase-3', gate: 'Pilot Ready', criteria: 'All tests passing, security review complete, operations runbook approved', decision: 'Deploy to production or address gaps' },
|
|
1194
|
+
],
|
|
981
1195
|
};
|
|
982
1196
|
}
|
|
983
1197
|
// ============================================================================
|
|
@@ -988,81 +1202,80 @@ export function buildRiskAssessment(query, simulationResult, platformResults) {
|
|
|
988
1202
|
const simPayload = extractSignalPayload(simulationResult);
|
|
989
1203
|
const simData = simPayload.data ?? {};
|
|
990
1204
|
const successProb = extractSuccessProbability(simData);
|
|
991
|
-
const
|
|
992
|
-
//
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
1205
|
+
const extracted = extractScenarioFromQuery(query);
|
|
1206
|
+
// ADR-PIPELINE-024: Generate domain risks — never return empty
|
|
1207
|
+
const domainRisks = generateDomainRisks(query, extracted);
|
|
1208
|
+
// Enrich with agent-sourced risks
|
|
1209
|
+
const riskAgentData = extractAgentData(platformResults, 'platform', 'risk-score');
|
|
996
1210
|
const sentinelAgents = extractAgentsByDomain(platformResults, 'sentinel');
|
|
997
1211
|
const shieldAgents = extractAgentsByDomain(platformResults, 'shield');
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1212
|
+
const agentRiskDescriptions = [];
|
|
1213
|
+
if (riskAgentData) {
|
|
1214
|
+
for (const r of safeArray(riskAgentData, 'risk_factors').concat(safeArray(riskAgentData, 'risks'))) {
|
|
1215
|
+
agentRiskDescriptions.push(typeof r === 'string' ? r : safeString(r, 'description') || JSON.stringify(r));
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
for (const r of safeArray(simData, 'risk_factors')) {
|
|
1219
|
+
agentRiskDescriptions.push(typeof r === 'string' ? r : JSON.stringify(r));
|
|
1220
|
+
}
|
|
1007
1221
|
for (const agent of sentinelAgents) {
|
|
1008
1222
|
const data = extractSignalPayload(agent.response).data;
|
|
1009
1223
|
if (data) {
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
allRiskFactors.push(typeof finding === 'string' ? finding : finding);
|
|
1224
|
+
for (const f of safeArray(data, 'findings').concat(safeArray(data, 'anomalies'))) {
|
|
1225
|
+
agentRiskDescriptions.push(typeof f === 'string' ? f : JSON.stringify(f));
|
|
1013
1226
|
}
|
|
1014
1227
|
}
|
|
1015
1228
|
}
|
|
1016
|
-
//
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1229
|
+
// Merge agent risks into domain risks (avoid duplicates)
|
|
1230
|
+
for (const desc of agentRiskDescriptions) {
|
|
1231
|
+
if (desc && !domainRisks.some(r => r.risk.toLowerCase().includes(desc.toLowerCase().slice(0, 30)))) {
|
|
1232
|
+
domainRisks.push({
|
|
1233
|
+
risk: desc, category: 'Agent-Identified', likelihood: 'Medium', impact: 'Medium', score: 4,
|
|
1234
|
+
mitigation: 'To be assessed during discovery phase',
|
|
1235
|
+
});
|
|
1023
1236
|
}
|
|
1024
1237
|
}
|
|
1025
|
-
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1238
|
+
const maxScore = domainRisks[0]?.score ?? 0;
|
|
1239
|
+
const overallRisk = maxScore >= 6 ? 'MEDIUM' : maxScore >= 3 ? 'LOW-MEDIUM' : 'LOW';
|
|
1240
|
+
// Security findings
|
|
1241
|
+
const securityFindings = [];
|
|
1242
|
+
for (const agent of shieldAgents) {
|
|
1028
1243
|
const data = extractSignalPayload(agent.response).data;
|
|
1029
|
-
if (data)
|
|
1030
|
-
|
|
1031
|
-
healthFindings.push(...findings);
|
|
1032
|
-
}
|
|
1244
|
+
if (data)
|
|
1245
|
+
securityFindings.push(...safeArray(data, 'findings').concat(safeArray(data, 'vulnerabilities')));
|
|
1033
1246
|
}
|
|
1034
1247
|
return {
|
|
1035
|
-
metadata: {
|
|
1036
|
-
title: `Risk Assessment: ${query}`,
|
|
1037
|
-
version: '1.0.0',
|
|
1038
|
-
created: now,
|
|
1039
|
-
},
|
|
1248
|
+
metadata: { title: `Risk Assessment: ${distillProblemStatement(query)}`, version: '2.0.0', created: now },
|
|
1040
1249
|
overall_risk_level: overallRisk,
|
|
1041
1250
|
success_probability: successProb,
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1251
|
+
risk_register: domainRisks.map((r, i) => ({
|
|
1252
|
+
id: `RISK-${String(i + 1).padStart(3, '0')}`,
|
|
1253
|
+
description: r.risk,
|
|
1254
|
+
category: r.category,
|
|
1255
|
+
likelihood: r.likelihood,
|
|
1256
|
+
impact: r.impact,
|
|
1257
|
+
score: r.score,
|
|
1258
|
+
mitigation: r.mitigation,
|
|
1259
|
+
owner: r.category === 'Technical' ? 'Engineering Lead' : r.category === 'Organizational' ? 'Program Manager' : r.category === 'Regulatory' ? 'Compliance Officer' : r.category === 'Financial' ? 'Finance Lead' : 'Project Manager',
|
|
1260
|
+
status: 'open',
|
|
1261
|
+
})),
|
|
1262
|
+
risk_heat_map: {
|
|
1263
|
+
high_likelihood_high_impact: domainRisks.filter(r => r.likelihood === 'High' && r.impact === 'High').map(r => r.risk),
|
|
1264
|
+
high_likelihood_medium_impact: domainRisks.filter(r => r.likelihood === 'High' && r.impact === 'Medium').map(r => r.risk),
|
|
1265
|
+
medium_likelihood_high_impact: domainRisks.filter(r => r.likelihood === 'Medium' && r.impact === 'High').map(r => r.risk),
|
|
1266
|
+
medium_likelihood_medium_impact: domainRisks.filter(r => r.likelihood === 'Medium' && r.impact === 'Medium').map(r => r.risk),
|
|
1267
|
+
low_likelihood_high_impact: domainRisks.filter(r => r.likelihood === 'Low' && r.impact === 'High').map(r => r.risk),
|
|
1268
|
+
},
|
|
1049
1269
|
security_findings: securityFindings.length > 0 ? securityFindings : null,
|
|
1050
|
-
health_findings: healthFindings.length > 0 ? healthFindings : null,
|
|
1051
|
-
sentinel_analysis: sentinelAgents.length > 0 ? sentinelAgents.map(a => ({
|
|
1052
|
-
agent: a.agent,
|
|
1053
|
-
data: extractSignalPayload(a.response).data,
|
|
1054
|
-
})) : null,
|
|
1055
1270
|
mitigation_strategy: {
|
|
1056
|
-
immediate:
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
ongoing: ['Continuous monitoring during implementation', 'Regular stakeholder reviews'],
|
|
1271
|
+
immediate: domainRisks.filter(r => r.score >= 6).map(r => r.mitigation),
|
|
1272
|
+
short_term: domainRisks.filter(r => r.score >= 3 && r.score < 6).map(r => r.mitigation),
|
|
1273
|
+
ongoing: ['Continuous risk monitoring during implementation', 'Quarterly risk review with stakeholders', 'Automated alerting for technical risks'],
|
|
1060
1274
|
},
|
|
1061
1275
|
sources: [
|
|
1062
1276
|
riskAgentData && 'platform/risk-score',
|
|
1063
1277
|
...sentinelAgents.map(a => `sentinel/${a.agent}`),
|
|
1064
1278
|
...shieldAgents.map(a => `shield/${a.agent}`),
|
|
1065
|
-
...observatoryAgents.map(a => `observatory/${a.agent}`),
|
|
1066
1279
|
].filter(Boolean),
|
|
1067
1280
|
};
|
|
1068
1281
|
}
|
|
@@ -1072,16 +1285,17 @@ export function buildRiskAssessment(query, simulationResult, platformResults) {
|
|
|
1072
1285
|
export function renderFinancialAnalysis(query, simulationResult, platformResults) {
|
|
1073
1286
|
const now = new Date().toISOString();
|
|
1074
1287
|
const problemStatement = distillProblemStatement(query);
|
|
1075
|
-
// Extract from simulation result first (primary source)
|
|
1076
1288
|
const simPayload = extractSignalPayload(simulationResult);
|
|
1077
1289
|
const simData = simPayload.data ?? {};
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1290
|
+
const fin = synthesizeFinancials(simData, platformResults, query);
|
|
1291
|
+
const successProb = extractSuccessProbability(simData);
|
|
1292
|
+
const extracted = extractScenarioFromQuery(query);
|
|
1080
1293
|
const roiData = extractAgentData(platformResults, 'costops', 'roi');
|
|
1081
1294
|
const forecastData = extractAgentData(platformResults, 'costops', 'forecast');
|
|
1082
1295
|
const attributionData = extractAgentData(platformResults, 'costops', 'attribution');
|
|
1083
1296
|
const budgetData = extractAgentData(platformResults, 'costops', 'budget');
|
|
1084
1297
|
const tradeoffData = extractAgentData(platformResults, 'costops', 'tradeoff');
|
|
1298
|
+
// ADR-PIPELINE-024: Never output "pending" — always produce a model
|
|
1085
1299
|
const lines = [
|
|
1086
1300
|
'# Financial Analysis',
|
|
1087
1301
|
'',
|
|
@@ -1090,36 +1304,30 @@ export function renderFinancialAnalysis(query, simulationResult, platformResults
|
|
|
1090
1304
|
'',
|
|
1091
1305
|
'---',
|
|
1092
1306
|
'',
|
|
1307
|
+
`## Investment Summary${fin.isEstimated ? ' (Estimated from Organization Profile)' : ''}`, '',
|
|
1308
|
+
'| Metric | Value |',
|
|
1309
|
+
'|--------|-------|',
|
|
1310
|
+
`| Total Investment | ${fin.budget || 'To be quantified during discovery'} |`,
|
|
1311
|
+
`| Expected ROI | ${fin.roi || `Based on ${(successProb * 100).toFixed(0)}% success probability`} |`,
|
|
1312
|
+
`| 5-Year NPV | ${fin.npv || 'To be modeled based on pilot results'} |`,
|
|
1313
|
+
`| Payback Period | ${fin.payback || 'Projected 12-18 months (industry benchmark)'} |`,
|
|
1314
|
+
`| Revenue / Savings Impact | ${fin.revenue || fin.costSavings || 'To be quantified'} |`,
|
|
1315
|
+
'',
|
|
1093
1316
|
];
|
|
1094
|
-
// Simulation-sourced financial summary
|
|
1095
|
-
const simBudget = safeString(simData, 'budget') || safeString(simData, 'investment') || (financialModel ? safeString(financialModel, 'budget') || safeString(financialModel, 'total_investment') : '');
|
|
1096
|
-
const simRoi = safeString(simData, 'roi') || safeString(simData, 'expected_roi') || (financialModel ? safeString(financialModel, 'roi') : '');
|
|
1097
|
-
const simNpv = safeString(simData, 'npv') || safeString(simData, 'net_present_value') || (financialModel ? safeString(financialModel, 'npv') : '');
|
|
1098
|
-
const simPaybackPeriod = safeString(simData, 'payback_period') || safeString(simData, 'payback') || (financialModel ? safeString(financialModel, 'payback_period') : '');
|
|
1099
|
-
if (simBudget || simRoi || simNpv || simPaybackPeriod) {
|
|
1100
|
-
lines.push('## Financial Summary (from Simulation)', '');
|
|
1101
|
-
lines.push('| Metric | Value |', '|--------|-------|');
|
|
1102
|
-
if (simBudget)
|
|
1103
|
-
lines.push(`| Total Investment | ${simBudget} |`);
|
|
1104
|
-
if (simRoi)
|
|
1105
|
-
lines.push(`| Expected ROI | ${simRoi} |`);
|
|
1106
|
-
if (simNpv)
|
|
1107
|
-
lines.push(`| 5-Year NPV | ${simNpv} |`);
|
|
1108
|
-
if (simPaybackPeriod)
|
|
1109
|
-
lines.push(`| Payback Period | ${simPaybackPeriod} |`);
|
|
1110
|
-
lines.push('');
|
|
1111
|
-
}
|
|
1112
1317
|
// ROI Analysis
|
|
1113
1318
|
lines.push('## Return on Investment', '');
|
|
1114
1319
|
if (roiData) {
|
|
1115
1320
|
const insights = synthesizeAgentInsights(roiData, 'costops-roi');
|
|
1116
1321
|
lines.push(...insights);
|
|
1117
1322
|
}
|
|
1118
|
-
else if (
|
|
1119
|
-
lines.push(`
|
|
1323
|
+
else if (fin.roi || fin.payback) {
|
|
1324
|
+
lines.push(`The projected ROI of ${fin.roi || 'this initiative'} is based on simulation modeling ` +
|
|
1325
|
+
`with ${(successProb * 100).toFixed(0)}% confidence. ${fin.payback ? `Expected payback period: ${fin.payback}.` : ''} ` +
|
|
1326
|
+
`These projections should be validated against actual operational data during the pilot phase.`, '');
|
|
1120
1327
|
}
|
|
1121
1328
|
else {
|
|
1122
|
-
lines.push(
|
|
1329
|
+
lines.push(`ROI to be quantified during the discovery phase based on actual ${extracted.systems[0] || 'operational'} data. ` +
|
|
1330
|
+
`Industry benchmarks for ${extracted.scenario_type.replace(/-/g, ' ')} initiatives suggest 12-24 month payback periods.`, '');
|
|
1123
1331
|
}
|
|
1124
1332
|
// Cost Forecast
|
|
1125
1333
|
lines.push('## Cost Forecast', '');
|
|
@@ -1128,7 +1336,7 @@ export function renderFinancialAnalysis(query, simulationResult, platformResults
|
|
|
1128
1336
|
lines.push(...insights);
|
|
1129
1337
|
}
|
|
1130
1338
|
else {
|
|
1131
|
-
lines.push(
|
|
1339
|
+
lines.push(`Cost projections to be developed during discovery phase based on ${extracted.systems[0] || 'platform'} implementation scope and resource requirements.`, '');
|
|
1132
1340
|
}
|
|
1133
1341
|
// Cost Attribution
|
|
1134
1342
|
lines.push('## Cost Attribution', '');
|
|
@@ -1137,7 +1345,7 @@ export function renderFinancialAnalysis(query, simulationResult, platformResults
|
|
|
1137
1345
|
lines.push(...insights);
|
|
1138
1346
|
}
|
|
1139
1347
|
else {
|
|
1140
|
-
lines.push('
|
|
1348
|
+
lines.push('Typical cost distribution for enterprise implementations: Development (35-40%), Infrastructure (20-25%), Integration (15-20%), Operations & Support (15-20%). Actual breakdown to be refined during planning.', '');
|
|
1141
1349
|
}
|
|
1142
1350
|
// Budget Allocation
|
|
1143
1351
|
lines.push('## Budget Allocation', '');
|
|
@@ -1146,7 +1354,7 @@ export function renderFinancialAnalysis(query, simulationResult, platformResults
|
|
|
1146
1354
|
lines.push(...insights);
|
|
1147
1355
|
}
|
|
1148
1356
|
else {
|
|
1149
|
-
lines.push('
|
|
1357
|
+
lines.push('Budget allocation to be determined based on pilot scope and organizational resource availability. Recommended approach: 60% development, 25% infrastructure, 15% contingency.', '');
|
|
1150
1358
|
}
|
|
1151
1359
|
// Cost-Quality Tradeoff
|
|
1152
1360
|
lines.push('## Cost-Quality Tradeoff Analysis', '');
|
|
@@ -1155,11 +1363,26 @@ export function renderFinancialAnalysis(query, simulationResult, platformResults
|
|
|
1155
1363
|
lines.push(...insights);
|
|
1156
1364
|
}
|
|
1157
1365
|
else {
|
|
1158
|
-
lines.push('
|
|
1366
|
+
lines.push('Cost-quality tradeoff analysis to be conducted during pilot phase. Key tradeoff dimensions: implementation speed vs. feature completeness, custom development vs. platform capabilities, pilot scope vs. enterprise coverage.', '');
|
|
1159
1367
|
}
|
|
1160
1368
|
const sources = [roiData && 'costops/roi', forecastData && 'costops/forecast', attributionData && 'costops/attribution', budgetData && 'costops/budget', tradeoffData && 'costops/tradeoff'].filter(Boolean);
|
|
1161
1369
|
lines.push('---', '');
|
|
1162
|
-
|
|
1370
|
+
// Scenario Analysis
|
|
1371
|
+
lines.push('## Scenario Analysis', '');
|
|
1372
|
+
lines.push('| Scenario | Assumption | Investment | ROI | Probability |');
|
|
1373
|
+
lines.push('|----------|-----------|-----------|-----|------------|');
|
|
1374
|
+
if (fin.hasData) {
|
|
1375
|
+
lines.push(`| **Base Case** | Plan executes as modeled | ${fin.budget || 'TBD'} | ${fin.roi || 'Projected'} | 50% |`);
|
|
1376
|
+
lines.push(`| Optimistic | Faster adoption, lower integration costs | ${fin.budget ? fin.budget + ' (-20%)' : 'TBD'} | ${fin.roi ? fin.roi + ' (+30%)' : 'Above base'} | 25% |`);
|
|
1377
|
+
lines.push(`| Pessimistic | Slower adoption, scope growth | ${fin.budget ? fin.budget + ' (+40%)' : 'TBD'} | ${fin.roi ? 'Reduced' : 'Below base'} | 25% |`);
|
|
1378
|
+
}
|
|
1379
|
+
else {
|
|
1380
|
+
lines.push('| Base Case | Standard implementation | To be quantified | Industry benchmark | 50% |');
|
|
1381
|
+
lines.push('| Optimistic | Accelerated timeline | -20% of base | Above benchmark | 25% |');
|
|
1382
|
+
lines.push('| Pessimistic | Extended timeline | +40% of base | Below benchmark | 25% |');
|
|
1383
|
+
}
|
|
1384
|
+
lines.push('', '*Scenario analysis assumes ±20% cost variance and ±30% timeline variance from base case.*', '');
|
|
1385
|
+
lines.push(`*Sources: ${sources.length > 0 ? sources.join(', ') : 'simulation data + industry benchmarks'}*`);
|
|
1163
1386
|
lines.push(`*Generated: ${now}*`);
|
|
1164
1387
|
return lines.join('\n');
|
|
1165
1388
|
}
|
|
@@ -1171,16 +1394,32 @@ export function renderSecurityAssessment(query, _simulationResult, platformResul
|
|
|
1171
1394
|
const problemStatement = distillProblemStatement(query);
|
|
1172
1395
|
const shieldAgents = extractAgentsByDomain(platformResults, 'shield');
|
|
1173
1396
|
const govAgents = extractAgentsByDomain(platformResults, 'governance-dashboard');
|
|
1397
|
+
// ADR-PIPELINE-025 §7c: Detect applicable regulations from query context
|
|
1398
|
+
const regulations = detectApplicableRegulations(query);
|
|
1399
|
+
const extracted = extractScenarioFromQuery(query);
|
|
1400
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'the enterprise platform';
|
|
1174
1401
|
const lines = [
|
|
1175
1402
|
'# Security & Compliance Assessment',
|
|
1176
1403
|
'',
|
|
1177
1404
|
`**Date:** ${now}`,
|
|
1178
1405
|
`**Subject:** ${problemStatement}`,
|
|
1406
|
+
`**Target System:** ${primarySystem}`,
|
|
1179
1407
|
'',
|
|
1180
1408
|
'---',
|
|
1181
1409
|
'',
|
|
1182
|
-
'## Security Posture', '',
|
|
1183
1410
|
];
|
|
1411
|
+
// Applicable Regulations — always populated from query context
|
|
1412
|
+
lines.push('## Applicable Regulatory Frameworks', '');
|
|
1413
|
+
if (regulations.length > 0) {
|
|
1414
|
+
lines.push('| Framework | Applicability | Key Requirements | Impact on Solution |');
|
|
1415
|
+
lines.push('|-----------|--------------|------------------|-------------------|');
|
|
1416
|
+
for (const reg of regulations) {
|
|
1417
|
+
lines.push(`| ${reg.framework} | ${reg.applicability} | ${reg.requirements} | ${reg.impact} |`);
|
|
1418
|
+
}
|
|
1419
|
+
lines.push('');
|
|
1420
|
+
}
|
|
1421
|
+
// Security Posture
|
|
1422
|
+
lines.push('## Security Posture', '');
|
|
1184
1423
|
if (shieldAgents.length > 0) {
|
|
1185
1424
|
for (const agent of shieldAgents) {
|
|
1186
1425
|
const data = extractSignalPayload(agent.response).data;
|
|
@@ -1192,8 +1431,20 @@ export function renderSecurityAssessment(query, _simulationResult, platformResul
|
|
|
1192
1431
|
}
|
|
1193
1432
|
}
|
|
1194
1433
|
else {
|
|
1195
|
-
lines.push('
|
|
1434
|
+
lines.push('### Data Classification', '');
|
|
1435
|
+
lines.push(`- **Internal data**: Operational telemetry, performance metrics, efficiency analysis`);
|
|
1436
|
+
lines.push(`- **Confidential data**: ${primarySystem} transaction records, financial data, employee information`);
|
|
1437
|
+
lines.push(`- **Regulated data**: ${regulations.length > 0 ? regulations.map(r => r.dataType).join(', ') : 'Subject to applicable industry regulations'}`);
|
|
1438
|
+
lines.push('');
|
|
1439
|
+
lines.push('### Recommended Security Controls', '');
|
|
1440
|
+
lines.push('- Role-based access control (RBAC) with separation of duties');
|
|
1441
|
+
lines.push('- Encryption at rest and in transit (TLS 1.3)');
|
|
1442
|
+
lines.push('- Audit trail for all data access and decision actions');
|
|
1443
|
+
lines.push('- Secret management via cloud provider vault (no hardcoded credentials)');
|
|
1444
|
+
lines.push(`- PII redaction in ${primarySystem} data pipeline`);
|
|
1445
|
+
lines.push('');
|
|
1196
1446
|
}
|
|
1447
|
+
// Governance & Compliance
|
|
1197
1448
|
lines.push('## Governance & Compliance', '');
|
|
1198
1449
|
if (govAgents.length > 0) {
|
|
1199
1450
|
for (const agent of govAgents) {
|
|
@@ -1206,14 +1457,105 @@ export function renderSecurityAssessment(query, _simulationResult, platformResul
|
|
|
1206
1457
|
}
|
|
1207
1458
|
}
|
|
1208
1459
|
else {
|
|
1209
|
-
lines.push('
|
|
1460
|
+
lines.push('### Decision Governance Model', '');
|
|
1461
|
+
lines.push('- Human-in-the-loop: No automated execution — all recommendations require human approval');
|
|
1462
|
+
lines.push('- Approval workflow: Submit → Review → Approve/Reject → Execute (governance-gated)');
|
|
1463
|
+
lines.push(`- ${primarySystem} writeback: Only approved actions propagate to the ERP system`);
|
|
1464
|
+
lines.push('- Audit trail: Append-only log with actor, timestamp, action, correlation ID');
|
|
1465
|
+
lines.push('');
|
|
1210
1466
|
}
|
|
1211
1467
|
const sources = [...shieldAgents.map(a => `shield/${a.agent}`), ...govAgents.map(a => `governance-dashboard/${a.agent}`)];
|
|
1212
1468
|
lines.push('---', '');
|
|
1213
|
-
lines.push(`*Sources: ${sources.length > 0 ? sources.join(', ') : '
|
|
1469
|
+
lines.push(`*Sources: ${sources.length > 0 ? sources.join(', ') : 'domain context analysis + regulatory framework detection'}*`);
|
|
1214
1470
|
lines.push(`*Generated: ${now}*`);
|
|
1215
1471
|
return lines.join('\n');
|
|
1216
1472
|
}
|
|
1473
|
+
function detectApplicableRegulations(query) {
|
|
1474
|
+
const regs = [];
|
|
1475
|
+
const lower = query.toLowerCase();
|
|
1476
|
+
// Pharmaceutical / Life Sciences
|
|
1477
|
+
if (lower.includes('pharma') || lower.includes('drug') || lower.includes('gxp') || lower.includes('clinical') || lower.includes('temperature-sensitive') || lower.includes('cold chain')) {
|
|
1478
|
+
regs.push({
|
|
1479
|
+
framework: 'FDA 21 CFR Part 11',
|
|
1480
|
+
applicability: 'Required — pharmaceutical electronic records',
|
|
1481
|
+
requirements: 'Audit trails, electronic signatures, data integrity, system validation',
|
|
1482
|
+
impact: 'All decision records must be tamper-evident with full audit trail',
|
|
1483
|
+
dataType: 'pharmaceutical production and distribution records',
|
|
1484
|
+
});
|
|
1485
|
+
regs.push({
|
|
1486
|
+
framework: 'EU GxP Annex 11',
|
|
1487
|
+
applicability: 'Required for EU operations — computerized systems',
|
|
1488
|
+
requirements: 'System validation, data integrity, access controls, change management',
|
|
1489
|
+
impact: 'System must be validated before production use; all changes documented',
|
|
1490
|
+
dataType: 'GxP-regulated process data',
|
|
1491
|
+
});
|
|
1492
|
+
regs.push({
|
|
1493
|
+
framework: 'GDP (Good Distribution Practice)',
|
|
1494
|
+
applicability: 'Required — pharmaceutical distribution',
|
|
1495
|
+
requirements: 'Temperature monitoring, transportation qualification, deviation management',
|
|
1496
|
+
impact: 'Cold chain monitoring and deviation alerts are compliance-critical',
|
|
1497
|
+
dataType: 'temperature and logistics records',
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
// Healthcare
|
|
1501
|
+
if (lower.includes('health') || lower.includes('patient') || lower.includes('clinical') || lower.includes('hipaa') || lower.includes('medical')) {
|
|
1502
|
+
regs.push({
|
|
1503
|
+
framework: 'HIPAA',
|
|
1504
|
+
applicability: 'Required — protected health information',
|
|
1505
|
+
requirements: 'PHI encryption, access controls, breach notification, BAAs',
|
|
1506
|
+
impact: 'All patient data must be encrypted; access logged; minimum necessary principle',
|
|
1507
|
+
dataType: 'protected health information (PHI)',
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
// Financial
|
|
1511
|
+
if (lower.includes('financial') || lower.includes('banking') || lower.includes('insurance') || lower.includes('sox') || lower.includes('accounting')) {
|
|
1512
|
+
regs.push({
|
|
1513
|
+
framework: 'SOX (Sarbanes-Oxley)',
|
|
1514
|
+
applicability: 'Required — financial reporting controls',
|
|
1515
|
+
requirements: 'Internal controls, audit trail, segregation of duties, change management',
|
|
1516
|
+
impact: 'Financial data transformations must have full audit trail and dual approval',
|
|
1517
|
+
dataType: 'financial records and reporting data',
|
|
1518
|
+
});
|
|
1519
|
+
regs.push({
|
|
1520
|
+
framework: 'PCI-DSS',
|
|
1521
|
+
applicability: 'If payment data is processed',
|
|
1522
|
+
requirements: 'Encryption, access controls, vulnerability management, monitoring',
|
|
1523
|
+
impact: 'Payment data must never be stored unencrypted; quarterly vulnerability scans',
|
|
1524
|
+
dataType: 'payment card and transaction data',
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
// EU operations → GDPR
|
|
1528
|
+
if (lower.includes('europe') || lower.includes('eu ') || lower.includes('gdpr') || lower.includes('european')) {
|
|
1529
|
+
regs.push({
|
|
1530
|
+
framework: 'GDPR',
|
|
1531
|
+
applicability: 'Required — EU personal data processing',
|
|
1532
|
+
requirements: 'Data minimization, consent management, right to erasure, DPIAs, data residency',
|
|
1533
|
+
impact: 'EU employee/customer data must stay in EU region; privacy by design required',
|
|
1534
|
+
dataType: 'personal data of EU data subjects',
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
// Multi-region → data residency
|
|
1538
|
+
if ((lower.includes('north america') && lower.includes('europe')) || (lower.includes('asia') && lower.includes('europe')) || lower.includes('global') || lower.includes('multi-region')) {
|
|
1539
|
+
regs.push({
|
|
1540
|
+
framework: 'Data Residency Requirements',
|
|
1541
|
+
applicability: 'Required — multi-jurisdictional operations',
|
|
1542
|
+
requirements: 'Data must be stored in the jurisdiction where it was generated unless transfer mechanisms exist',
|
|
1543
|
+
impact: 'Regional deployment or data partitioning may be required; cross-border transfer assessments',
|
|
1544
|
+
dataType: 'regionally-generated operational and personal data',
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
// Sustainability / ESG
|
|
1548
|
+
if (lower.includes('sustainability') || lower.includes('emission') || lower.includes('carbon') || lower.includes('esg') || lower.includes('environmental')) {
|
|
1549
|
+
regs.push({
|
|
1550
|
+
framework: 'CSRD / EU Taxonomy',
|
|
1551
|
+
applicability: 'Required for EU-operating large enterprises — sustainability reporting',
|
|
1552
|
+
requirements: 'Auditable emissions data, double materiality assessment, ESRS compliance',
|
|
1553
|
+
impact: 'Sustainability metrics must be auditable with documented methodology',
|
|
1554
|
+
dataType: 'emissions, energy, waste, and sustainability performance data',
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
return regs;
|
|
1558
|
+
}
|
|
1217
1559
|
// ============================================================================
|
|
1218
1560
|
// ADR-PIPELINE-020: Integration Assessment Renderer (connector-hub agents)
|
|
1219
1561
|
// ============================================================================
|
|
@@ -1228,7 +1570,7 @@ export function renderIntegrationAssessment(query, _simulationResult, platformRe
|
|
|
1228
1570
|
'',
|
|
1229
1571
|
`**Date:** ${now}`,
|
|
1230
1572
|
`**Subject:** ${problemStatement}`,
|
|
1231
|
-
`**Target Systems:** ${extracted.systems.join(', ') || '
|
|
1573
|
+
`**Target Systems:** ${extracted.systems.join(', ') || extractSystemFromQuery(query) || 'See scenario description'}`,
|
|
1232
1574
|
'',
|
|
1233
1575
|
'---',
|
|
1234
1576
|
'',
|
|
@@ -1245,7 +1587,8 @@ export function renderIntegrationAssessment(query, _simulationResult, platformRe
|
|
|
1245
1587
|
}
|
|
1246
1588
|
}
|
|
1247
1589
|
else {
|
|
1248
|
-
|
|
1590
|
+
const intSystem = extracted.systems[0] || extractSystemFromQuery(query) || 'the target platform';
|
|
1591
|
+
lines.push(`Integration with ${intSystem} requires API authentication, rate limits assessment, data format compatibility validation, error handling strategy, and retry logic. A 1-2 week technical spike is recommended to validate these integration points before full development.`, '');
|
|
1249
1592
|
}
|
|
1250
1593
|
lines.push('## Edge & Resilience Patterns', '');
|
|
1251
1594
|
if (edgeAgents.length > 0) {
|
|
@@ -1259,7 +1602,7 @@ export function renderIntegrationAssessment(query, _simulationResult, platformRe
|
|
|
1259
1602
|
}
|
|
1260
1603
|
}
|
|
1261
1604
|
else {
|
|
1262
|
-
lines.push('
|
|
1605
|
+
lines.push('Resilience patterns (circuit breakers, retry with backoff, failover) to be implemented during the build phase based on integration assessment findings.', '');
|
|
1263
1606
|
}
|
|
1264
1607
|
const sources = [...connectorAgents.map(a => `connector-hub/${a.agent}`), ...edgeAgents.map(a => `edge/${a.agent}`)];
|
|
1265
1608
|
lines.push('---', '');
|