mindforge-cc 5.6.0 → 5.10.0

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.
@@ -2,11 +2,14 @@
2
2
  * MindForge — WaveFeedbackLoop (Pillar VI: Proactive Equilibrium)
3
3
  * Monitors divergence during wave execution and triggers self-healing.
4
4
  */
5
+ const fs = require('fs');
6
+ const path = require('path');
5
7
 
6
8
  class WaveFeedbackLoop {
7
9
  constructor(config = {}) {
8
10
  this.failureThreshold = config.failureThreshold || 0.20; // 20% failure rate
9
11
  this.divergenceWeight = config.divergenceWeight || 1.5; // Bias for rapid divergence
12
+ this.statsPath = config.statsPath || path.join(__dirname, '..', 'models', 'performance-stats.json');
10
13
  this.waveState = {
11
14
  completed: 0,
12
15
  failed: 0,
@@ -16,19 +19,51 @@ class WaveFeedbackLoop {
16
19
  }
17
20
 
18
21
  /**
19
- * Updates the feedback loop with a task result.
22
+ * Updates the feedback loop with a task result and records performance stats.
20
23
  */
21
24
  update(result) {
22
25
  this.waveState.total++;
26
+ const provider = result.providerId || 'unknown';
27
+ const taskType = result.taskType || 'default';
28
+
23
29
  if (result.status === 'completed') {
24
30
  this.waveState.completed++;
31
+ this.recordPerformance(provider, taskType, true);
25
32
  } else if (result.status === 'failed') {
26
33
  this.waveState.failed++;
34
+ this.recordPerformance(provider, taskType, false);
27
35
  } else {
28
36
  this.waveState.skipped++;
29
37
  }
30
38
  }
31
39
 
40
+ /**
41
+ * Records performance metrics to persistent storage.
42
+ */
43
+ recordPerformance(provider, taskType, isSuccess) {
44
+ if (provider === 'unknown') return;
45
+
46
+ try {
47
+ let stats = {};
48
+ if (fs.existsSync(this.statsPath)) {
49
+ stats = JSON.parse(fs.readFileSync(this.statsPath, 'utf8'));
50
+ }
51
+
52
+ if (!stats[provider]) stats[provider] = {};
53
+ if (!stats[provider][taskType]) stats[provider][taskType] = { success: 0, failure: 0 };
54
+
55
+ if (isSuccess) {
56
+ stats[provider][taskType].success++;
57
+ } else {
58
+ stats[provider][taskType].failure++;
59
+ }
60
+
61
+ fs.writeFileSync(this.statsPath, JSON.stringify(stats, null, 2));
62
+ } catch (e) {
63
+ console.warn(`[MCA-WARN] Failed to record performance stats: ${e.message}`);
64
+ }
65
+ }
66
+
32
67
  /**
33
68
  * Calculates the current divergence rate.
34
69
  * @returns {number} - 0.0 to 1.0 divergence rate.
@@ -45,7 +45,7 @@ class NexusTracer {
45
45
  /**
46
46
  * Start a new ART span.
47
47
  */
48
- startSpan(name, attributes = {}, parentSpanId = null) {
48
+ async startSpan(name, attributes = {}, parentSpanId = null) {
49
49
  const spanId = `sp_${crypto.randomBytes(6).toString('hex')}`;
50
50
  const startTime = new Date().toISOString();
51
51
 
@@ -59,6 +59,8 @@ class NexusTracer {
59
59
  attributes: {
60
60
  ...attributes,
61
61
  service: 'mindforge-nexus',
62
+ host: require('os').hostname(),
63
+ pid: process.pid
62
64
  }
63
65
  };
64
66
 
@@ -81,7 +83,7 @@ class NexusTracer {
81
83
  }
82
84
 
83
85
  // Record span start in AUDIT.jsonl
84
- this._recordEvent('span_started', {
86
+ await this._recordEvent('span_started', {
85
87
  span_id: spanId,
86
88
  parent_span_id: parentSpanId,
87
89
  span_name: name,
@@ -95,7 +97,7 @@ class NexusTracer {
95
97
  /**
96
98
  * End an active span.
97
99
  */
98
- endSpan(spanId, status = 'success', metadata = {}) {
100
+ async endSpan(spanId, status = 'success', metadata = {}) {
99
101
  const span = this.activeSpans.get(spanId);
100
102
  if (!span) return;
101
103
 
@@ -107,7 +109,7 @@ class NexusTracer {
107
109
  this.sreManager.terminateEnclave(span.attributes.enclave_id);
108
110
  }
109
111
 
110
- this._recordEvent('span_completed', {
112
+ await this._recordEvent('span_completed', {
111
113
  span_id: spanId,
112
114
  status,
113
115
  ...metadata
@@ -119,19 +121,31 @@ class NexusTracer {
119
121
  /**
120
122
  * Record a Reasoning Trace event (ART granularity).
121
123
  */
122
- recordReasoning(spanId, agent, thought, resolution = 'none') {
124
+ async recordReasoning(spanId, agent, thought, resolution = 'none') {
123
125
  const span = this.activeSpans.get(spanId);
124
126
  let sanitizedThought = thought;
125
127
 
126
128
  if (span && span.attributes.enclave_id) {
127
- sanitizedThought = this.sreManager.sanitizeThoughtChain(thought, span.attributes.enclave_id);
129
+ const result = this.sreManager.sanitizeThoughtChain(thought, span.attributes.enclave_id);
130
+
131
+ if (result.status === 'SRE-ISOLATED') {
132
+ // Log the ZK proof instead of the raw thought
133
+ await this._recordEvent('sre_proof_logged', {
134
+ span_id: spanId,
135
+ agent,
136
+ certificate: result,
137
+ resolution
138
+ });
139
+ return; // Skip standard reasoning trace for isolated content
140
+ }
141
+ sanitizedThought = result.content || thought;
128
142
  }
129
143
 
130
144
  // v5 Pillar III: PES (Proactive Equilibrium Scoring)
131
145
  const entropy = this.calculateEntropy(spanId, sanitizedThought);
132
146
  const isStagnant = entropy > this.RES_THRESHOLD;
133
147
 
134
- this._recordEvent('reasoning_trace', {
148
+ await this._recordEvent('reasoning_trace', {
135
149
  span_id: spanId,
136
150
  agent,
137
151
  thought: sanitizedThought,
@@ -145,7 +159,7 @@ class NexusTracer {
145
159
  const stagnationCount = history.filter(h => h.entropy > this.RES_THRESHOLD).length;
146
160
 
147
161
  if (stagnationCount >= 3) {
148
- this._recordEvent('vulnerability_detected', {
162
+ await this._recordEvent('vulnerability_detected', {
149
163
  span_id: spanId,
150
164
  type: 'REASONING_LOOP',
151
165
  severity: 'HIGH',
@@ -154,7 +168,7 @@ class NexusTracer {
154
168
  });
155
169
 
156
170
  // Signal proactive recovery
157
- this.recordSelfHeal(spanId, {
171
+ await this.recordSelfHeal(spanId, {
158
172
  type: 'PROACTIVE_RCA',
159
173
  cause: 'REASONING_STAGNATION',
160
174
  suggestion: 'Entropy threshold exceeded. Switch reasoning strategy.'
@@ -200,7 +214,7 @@ class NexusTracer {
200
214
  /**
201
215
  * Internal AUDIT writer.
202
216
  */
203
- _recordEvent(event, data) {
217
+ async _recordEvent(event, data) {
204
218
  const entry = {
205
219
  id: crypto.randomUUID(),
206
220
  timestamp: new Date().toISOString(),
@@ -215,7 +229,7 @@ class NexusTracer {
215
229
  entry.did = this.did;
216
230
  // Sign the stringified entry WITHOUT the signature field itself
217
231
  const payload = JSON.stringify(entry);
218
- entry.signature = ztai.signData(this.did, payload);
232
+ entry.signature = await ztai.signData(this.did, payload);
219
233
  } catch (err) {
220
234
  console.warn(`[NexusTracer] ZTAI signing failed: ${err.message}`);
221
235
  }
@@ -234,8 +248,8 @@ class NexusTracer {
234
248
  /**
235
249
  * Records a FinOps budget decision (Pillar V).
236
250
  */
237
- recordFinOps(spanId, decision) {
238
- this._recordEvent('finops_decision', {
251
+ async recordFinOps(spanId, decision) {
252
+ await this._recordEvent('finops_decision', {
239
253
  span_id: spanId,
240
254
  ...decision
241
255
  });
@@ -244,8 +258,8 @@ class NexusTracer {
244
258
  /**
245
259
  * Records a Self-Healing trigger event (Pillar VI).
246
260
  */
247
- recordSelfHeal(spanId, report) {
248
- this._recordEvent('self_heal_trigger', {
261
+ async recordSelfHeal(spanId, report) {
262
+ await this._recordEvent('self_heal_trigger', {
249
263
  span_id: spanId,
250
264
  ...report
251
265
  });
@@ -254,7 +268,7 @@ class NexusTracer {
254
268
  /**
255
269
  * Finalize and export the Agentic SBOM (Pillar IV).
256
270
  */
257
- exportSBOM(outputPath = null) {
271
+ async exportSBOM(outputPath = null) {
258
272
  const finalPath = outputPath || path.join(process.cwd(), '.planning', 'MANIFEST.sbom.json');
259
273
  const manifest = {
260
274
  ...this.sbom,
@@ -269,7 +283,7 @@ class NexusTracer {
269
283
  }
270
284
  fs.writeFileSync(finalPath, JSON.stringify(manifest, null, 2));
271
285
 
272
- this._recordEvent('sbom_exported', { path: finalPath });
286
+ await this._recordEvent('sbom_exported', { path: finalPath });
273
287
  return finalPath;
274
288
  } catch (err) {
275
289
  console.error(`[NexusTracer] Failed to export SBOM: ${err.message}`);
@@ -278,4 +292,9 @@ class NexusTracer {
278
292
  }
279
293
  }
280
294
 
281
- module.exports = NexusTracer;
295
+ // Global Singleton Instance for easy mesh-wide access
296
+ const globalTracer = new NexusTracer();
297
+
298
+ // Export both the class and the global instance
299
+ module.exports = globalTracer;
300
+ module.exports.NexusTracer = NexusTracer;
@@ -6,6 +6,10 @@
6
6
 
7
7
  const crypto = require('crypto');
8
8
 
9
+ // Simulated System DID for Enclave Proofs (Tier 3)
10
+ const ENCLAVE_PRIVATE_KEY = 'tier3-enclave-secret-key-sim'; // In production, this would be a TEE-bound private key
11
+ const SYSTEM_DID = 'did:mindforge:enclave:0xbeast';
12
+
9
13
  class SREManager {
10
14
  constructor() {
11
15
  this.activeEnclaves = new Map();
@@ -26,7 +30,8 @@ class SREManager {
26
30
  startedAt: new Date().toISOString(),
27
31
  principal: context.did,
28
32
  hasIP: true,
29
- isolationLevel: 'Hardware-Enclave (Simulated)'
33
+ isolationLevel: 'Hardware-Enclave (Simulated)',
34
+ cumulativeHash: null // Root of the proof chain
30
35
  });
31
36
 
32
37
  console.log(`[SRE-INIT] Initialized Sovereign Reason Enclave: ${enclaveId} for ${context.did}`);
@@ -34,19 +39,68 @@ class SREManager {
34
39
  }
35
40
 
36
41
  /**
37
- * Sanitizes a thought chain for global audit logging.
38
- * Ensures that sensitive IP or "zero-visibility" thoughts are isolated.
42
+ * Sanitizes a thought chain and generates a ZK-Proof Compliance Certificate.
43
+ * Ensures that sensitive IP or "zero-visibility" thoughts are isolated while proving audit-eligibility.
39
44
  * @param {string} thoughtChain - The raw agentic thought chain.
40
- * @returns {string} - Sanitized/Isolated thought chain.
45
+ * @param {string} enclaveId - The active enclave ID.
46
+ * @param {Object} policyResult - Whether the content passed internal policy checks.
47
+ * @returns {Object} - ZK-Proof compliance certificate.
41
48
  */
42
- sanitizeThoughtChain(thoughtChain, enclaveId) {
43
- if (!this.activeEnclaves.has(enclaveId)) return thoughtChain;
49
+ sanitizeThoughtChain(thoughtChain, enclaveId, policyResult = { passed: true }) {
50
+ if (!this.activeEnclaves.has(enclaveId)) {
51
+ return { status: 'PLAINTEXT', content: thoughtChain };
52
+ }
44
53
 
45
- // Zero-Visibility Protocol: In SRE mode, the global audit log only receives a hash
46
- // or a redacted summary. The full chain is kept in volatile enclave memory.
54
+ // v5 Pillar VI: Merkle-style Cumulative Hash Chain
55
+ const enclaveData = this.activeEnclaves.get(enclaveId);
56
+ const prevHash = enclaveData.cumulativeHash;
47
57
  const digest = crypto.createHash('sha256').update(thoughtChain).digest('hex');
48
58
 
49
- return `[SRE-ISOLATED] Confidential reasoning executed in enclave ${enclaveId}. Verification Digest: ${digest}. Original trace is isolated from persistence.`;
59
+ // Generate a simulated ZK-Proof Compliance Certificate
60
+ const proofPayload = {
61
+ enclaveId: enclaveId,
62
+ digest: digest,
63
+ prevHash: prevHash, // Links the chain
64
+ policyPassed: policyResult.passed,
65
+ timestamp: new Date().toISOString(),
66
+ principal: enclaveData.principal
67
+ };
68
+
69
+ // Sign the proof with the Enclave Private Key
70
+ const signature = crypto.createHmac('sha256', ENCLAVE_PRIVATE_KEY)
71
+ .update(JSON.stringify(proofPayload))
72
+ .digest('hex');
73
+
74
+ // Update the cumulative hash for the next block
75
+ const proofHash = crypto.createHash('sha256').update(signature).digest('hex');
76
+ enclaveData.cumulativeHash = proofHash;
77
+
78
+ const certificate = {
79
+ status: 'SRE-ISOLATED',
80
+ proof: proofPayload,
81
+ signature: signature,
82
+ proofHash: proofHash,
83
+ verificationDid: SYSTEM_DID,
84
+ message: `[SRE-ZK-PROOF] Confidential reasoning (sha256:${digest.substring(0, 8)}...) verified by Enclave Auditor.`
85
+ };
86
+
87
+ return certificate;
88
+ }
89
+
90
+ /**
91
+ * Verifies an SRE Compliance Certificate without seeing the original content.
92
+ */
93
+ verifyZKProof(certificate) {
94
+ if (certificate.status !== 'SRE-ISOLATED') return false;
95
+
96
+ const expectedSignature = crypto.createHmac('sha256', ENCLAVE_PRIVATE_KEY)
97
+ .update(JSON.stringify(certificate.proof))
98
+ .digest('hex');
99
+
100
+ const isValid = (expectedSignature === certificate.signature);
101
+ const policyPassed = certificate.proof.policyPassed;
102
+
103
+ return isValid && policyPassed;
50
104
  }
51
105
 
52
106
  /**
@@ -3,33 +3,97 @@
3
3
  * Dynamically routes tasks across multiple cloud providers (Vertex, Bedrock, Azure).
4
4
  */
5
5
  'use strict';
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
6
9
 
7
10
  class CloudBroker {
8
11
  constructor(config = {}) {
9
12
  this.providers = config.providers || ['anthropic', 'google', 'aws', 'azure'];
13
+ this.statsPath = config.statsPath || path.join(__dirname, 'performance-stats.json');
14
+ this.blacklist = new Map(); // provider -> expiry (Date)
15
+ this.failureWindow = new Map(); // provider:taskType -> count
10
16
  this.state = {
11
17
  'anthropic': { latency: 450, costMultiplier: 1.0, healthy: true },
12
18
  'google': { latency: 600, costMultiplier: 0.85, healthy: true },
13
19
  'aws': { latency: 550, costMultiplier: 0.95, healthy: true },
14
20
  'azure': { latency: 650, costMultiplier: 1.1, healthy: true }
15
21
  };
22
+ this.reloadStats();
16
23
  }
17
24
 
18
25
  /**
19
- * Selects the optimal provider based on weighted latency and cost.
20
- * @param {Object} options - Task requirements (maxLatency, budgetConstraint)
26
+ * Loads performance stats from persistent storage and applies decay (0.95 factor).
27
+ */
28
+ reloadStats() {
29
+ try {
30
+ if (fs.existsSync(this.statsPath)) {
31
+ const raw = JSON.parse(fs.readFileSync(this.statsPath, 'utf8'));
32
+
33
+ // v5 Pillar V: Metrics Decay (Hyper-Enterprise Hardening)
34
+ // Ensure historical data doesn't anchor the model if recent performance shifts.
35
+ this.performanceStats = {};
36
+ for (const [provider, tasks] of Object.entries(raw)) {
37
+ this.performanceStats[provider] = {};
38
+ for (const [task, stats] of Object.entries(tasks)) {
39
+ this.performanceStats[provider][task] = {
40
+ success: stats.success * 0.95,
41
+ failure: stats.failure * 0.95
42
+ };
43
+ }
44
+ }
45
+ } else {
46
+ this.performanceStats = {};
47
+ }
48
+ } catch (e) {
49
+ console.warn(`[MCA-WARN] Failed to load performance stats: ${e.message}`);
50
+ this.performanceStats = {};
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Selects the optimal provider based on weighted latency, cost, and historical success.
56
+ * @param {Object} requirements - Task requirements (maxLatency, budgetConstraint, taskType)
21
57
  * @returns {string} - Best provider ID
22
58
  */
23
59
  getBestProvider(requirements = {}) {
60
+ this.reloadStats(); // Just-In-Time refresh for latest feedback loop data
61
+ const taskType = requirements.taskType || 'default';
62
+
24
63
  const scored = Object.entries(this.state)
25
- .filter(([_, data]) => data.healthy)
64
+ .filter(([id, data]) => {
65
+ // v5 Pillar V: Circuit Breaker Verification
66
+ if (!data.healthy) return false;
67
+ const blacklistExpiry = this.blacklist.get(id);
68
+ if (blacklistExpiry && blacklistExpiry > new Date()) {
69
+ return false; // Circuit is OPEN (Blacklisted)
70
+ }
71
+ return true;
72
+ })
26
73
  .map(([id, data]) => {
27
- // Score = (Latency * 0.4) + (Cost * 0.6) — Lower is better
28
- const score = (data.latency * 0.4) + (data.costMultiplier * 1000 * 0.6);
29
- return { id, score };
74
+ // Calculate Success Probability for this task
75
+ const stats = this.performanceStats[id]?.[taskType] || { success: 1, failure: 0 };
76
+ const totalTasks = stats.success + stats.failure;
77
+ const successProb = totalTasks > 0 ? (stats.success / totalTasks) : 0.5;
78
+
79
+ // Score Calculation (The "Affinity" Algorithm)
80
+ const latencyWeight = 0.2;
81
+ const costWeight = 0.3;
82
+ const affinityWeight = 0.5;
83
+
84
+ const score = (data.latency * latencyWeight) +
85
+ (data.costMultiplier * 1000 * costWeight) +
86
+ ((1.0 - successProb) * 2000 * affinityWeight);
87
+
88
+ return { id, score, successProb: successProb.toFixed(2) };
30
89
  });
31
90
 
32
91
  scored.sort((a, b) => a.score - b.score);
92
+
93
+ if (scored.length > 0) {
94
+ console.log(`[MCA-ROUTE] Selected provider: ${scored[0].id} (Affinity: ${scored[0].successProb} for '${taskType}')`);
95
+ }
96
+
33
97
  return scored[0]?.id || 'anthropic';
34
98
  }
35
99
 
@@ -53,20 +117,34 @@ class CloudBroker {
53
117
 
54
118
  /**
55
119
  * Retrieves provider-specific model mapping.
56
- * @param {string} provider - Provider ID
57
- * @param {string} modelGroup - e.g., 'sonnet', 'opus', 'haiku'
58
120
  */
59
121
  mapToProviderModel(provider, modelGroup) {
60
122
  const mappings = {
61
123
  'anthropic': { 'sonnet': 'claude-3-5-sonnet', 'opus': 'claude-3-opus', 'haiku': 'claude-3-haiku' },
62
- 'google': { 'sonnet': 'gemini-1.5-pro', 'opus': 'gemini-1.5-pro', 'haiku': 'gemini-1.5-flash' },
63
- 'aws': { 'sonnet': 'anthropic.claude-3-5-sonnet-v2:0', 'opus': 'anthropic.claude-3-opus-v1:0', 'haiku': 'anthropic.claude-3-haiku-v1:0' },
64
- 'azure': { 'sonnet': 'gpt-4o', 'opus': 'gpt-4-turbo', 'haiku': 'gpt-35-turbo' }
124
+ 'google': { 'sonnet': 'gemini-1.5-pro', 'haiku': 'gemini-1.5-flash' },
125
+ 'aws': { 'sonnet': 'anthropic.claude-3-5-sonnet-v2:0', 'haiku': 'anthropic.claude-3-haiku-v1:0' },
126
+ 'azure': { 'sonnet': 'gpt-4o', 'haiku': 'gpt-35-turbo' }
65
127
  };
66
128
 
67
129
  return mappings[provider]?.[modelGroup] || mappings[provider]?.['sonnet'];
68
130
  }
69
131
 
132
+ /**
133
+ * Records a task failure and manages the circuit breaker.
134
+ */
135
+ recordFailure(provider, taskType = 'default') {
136
+ const key = `${provider}:${taskType}`;
137
+ const failures = (this.failureWindow.get(key) || 0) + 1;
138
+ this.failureWindow.set(key, failures);
139
+
140
+ if (failures >= 3) {
141
+ const expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 min blacklist
142
+ this.blacklist.set(provider, expiry);
143
+ console.warn(`[MCA-CIRCUIT-OPEN] Provider '${provider}' blacklisted for 10 min due to consecutive failures on '${taskType}'.`);
144
+ this.failureWindow.set(key, 0); // Reset window upon blacklisting
145
+ }
146
+ }
147
+
70
148
  /**
71
149
  * Hardening: Simulate provider failures to verify Fallback Protocol.
72
150
  */
@@ -0,0 +1,22 @@
1
+ {
2
+ "anthropic": {
3
+ "refactor": { "success": 45, "failure": 2 },
4
+ "test": { "success": 30, "failure": 1 },
5
+ "architect": { "success": 12, "failure": 0 }
6
+ },
7
+ "google": {
8
+ "refactor": { "success": 25, "failure": 8 },
9
+ "test": { "success": 50, "failure": 2 },
10
+ "architect": { "success": 8, "failure": 4 }
11
+ },
12
+ "aws": {
13
+ "refactor": { "success": 20, "failure": 5 },
14
+ "test": { "success": 15, "failure": 5 },
15
+ "architect": { "success": 5, "failure": 2 }
16
+ },
17
+ "azure": {
18
+ "refactor": { "success": 10, "failure": 10 },
19
+ "test": { "success": 20, "failure": 5 },
20
+ "architect": { "success": 3, "failure": 5 }
21
+ }
22
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * MindForge v5.10.0 — AgRevOps Governance Debt Monitor
3
+ * Calculates Security Health Score and Governance Debt.
4
+ */
5
+ 'use strict';
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ class DebtMonitor {
11
+ constructor() {
12
+ this.auditPath = path.join(process.cwd(), '.planning', 'AUDIT.jsonl');
13
+ }
14
+
15
+ /**
16
+ * Monitor governance debt and security health.
17
+ * @param {Object} metrics - From MetricsAggregator
18
+ */
19
+ monitor(metrics) {
20
+ const auditEntries = metrics.auditEntries || [];
21
+
22
+ // 1. Identify high-risk events
23
+ const criticalFindings = auditEntries.filter(e => e.event === 'security_finding' && e.severity === 'critical');
24
+ const tier3Approvals = auditEntries.filter(e => e.event === 'approval_granted' && e.tier === 3);
25
+ const policyBypasses = auditEntries.filter(e => e.event === 'policy_bypass');
26
+
27
+ // 2. Calculate Health Score (starts at 100)
28
+ let score = 100;
29
+ score -= (criticalFindings.length * 10);
30
+ score -= (tier3Approvals.length * 5);
31
+ score -= (policyBypasses.length * 15);
32
+
33
+ const healthScore = Math.max(0, score);
34
+
35
+ // 3. Determine status
36
+ let status = 'Excellent';
37
+ if (healthScore < 90) status = 'Good';
38
+ if (healthScore < 75) status = 'Warning';
39
+ if (healthScore < 50) status = 'Critical';
40
+
41
+ return {
42
+ security_health_score: healthScore,
43
+ governance_status: status,
44
+ critical_findings: criticalFindings.length,
45
+ tier3_approvals: tier3Approvals.length,
46
+ policy_bypasses: policyBypasses.length,
47
+ debt_level: this.getDebtLevel(healthScore),
48
+ timestamp: new Date().toISOString()
49
+ };
50
+ }
51
+
52
+ getDebtLevel(score) {
53
+ if (score >= 95) return 'Minimal';
54
+ if (score >= 80) return 'Managing';
55
+ if (score >= 60) return 'Moderate';
56
+ return 'High';
57
+ }
58
+ }
59
+
60
+ module.exports = new DebtMonitor();
@@ -0,0 +1,60 @@
1
+ /**
2
+ * MindForge v5.10.0 — AgRevOps ROI Engine
3
+ * Calculates $ Value Saved vs $ Token Cost.
4
+ */
5
+ 'use strict';
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ class ROIEngine {
11
+ constructor() {
12
+ this.hourlyRate = 100; // Average Enterprise Dev Hourly Rate (USD)
13
+ this.taskValues = {
14
+ 'refactor': 1.5, // 1.5 hours saved
15
+ 'test': 0.75, // 0.75 hours saved
16
+ 'architect': 4.0, // 4 hours saved
17
+ 'default': 0.5 // 0.5 hours saved
18
+ };
19
+ }
20
+
21
+ /**
22
+ * Calculate ROI from current session metrics.
23
+ * @param {Object} metrics - From MetricsAggregator
24
+ */
25
+ calculate(metrics) {
26
+ const tokenCost = metrics.costs?.reduce((sum, c) => sum + (c.cost || 0), 0) || 0;
27
+
28
+ // map successful tasks to dev hours
29
+ const tasks = metrics.auditEntries?.filter(e => e.event === 'task_completed') || [];
30
+ let hoursSaved = 0;
31
+
32
+ tasks.forEach(t => {
33
+ const type = this.detectTaskType(t.message || t.task || '');
34
+ hoursSaved += this.taskValues[type] || this.taskValues.default;
35
+ });
36
+
37
+ const grossValue = hoursSaved * this.hourlyRate;
38
+ const netValue = grossValue - tokenCost;
39
+ const roi = tokenCost > 0 ? (netValue / tokenCost) * 100 : 0;
40
+
41
+ return {
42
+ token_cost: parseFloat(tokenCost.toFixed(4)),
43
+ hours_saved: parseFloat(hoursSaved.toFixed(2)),
44
+ gross_value: parseFloat(grossValue.toFixed(2)),
45
+ net_value: parseFloat(netValue.toFixed(2)),
46
+ roi_percentage: parseFloat(roi.toFixed(1)),
47
+ currency: 'USD'
48
+ };
49
+ }
50
+
51
+ detectTaskType(msg) {
52
+ const m = msg.toLowerCase();
53
+ if (m.includes('refactor')) return 'refactor';
54
+ if (m.includes('test') || m.includes('verify')) return 'test';
55
+ if (m.includes('architect') || m.includes('design') || m.includes('pillar')) return 'architect';
56
+ return 'default';
57
+ }
58
+ }
59
+
60
+ module.exports = new ROIEngine();
@@ -0,0 +1,59 @@
1
+ /**
2
+ * MindForge v5.10.0 — AgRevOps Velocity Forecaster
3
+ * Predicts milestone completion ETA based on historical velocity.
4
+ */
5
+ 'use strict';
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ class VelocityForecaster {
11
+ constructor() {
12
+ this.auditPath = path.join(process.cwd(), '.planning', 'AUDIT.jsonl');
13
+ }
14
+
15
+ /**
16
+ * Predict completion ETA for remaining tasks.
17
+ * @param {Object} metrics - From MetricsAggregator
18
+ */
19
+ predict(metrics) {
20
+ const completedTasks = metrics.auditEntries?.filter(e => e.event === 'task_completed') || [];
21
+ if (completedTasks.length < 2) {
22
+ return {
23
+ avg_seconds_per_task: 0,
24
+ tasks_remaining: metrics.tasks_total - metrics.tasks_completed,
25
+ eta: 'Inconclusive (Not enough data)'
26
+ };
27
+ }
28
+
29
+ // Sort by timestamp
30
+ const sorted = [...completedTasks].sort((a,b) => new Date(a.timestamp) - new Date(b.timestamp));
31
+
32
+ // Calculate historical duration between tasks
33
+ let totalSeconds = 0;
34
+ for (let i = 1; i < sorted.length; i++) {
35
+ const delta = (new Date(sorted[i].timestamp) - new Date(sorted[i - 1].timestamp)) / 1000;
36
+ // ignore outliers (gaps longer than 1 hour)
37
+ if (delta < 3600) {
38
+ totalSeconds += delta;
39
+ }
40
+ }
41
+
42
+ const avgSeconds = totalSeconds / (sorted.length - 1) || 0;
43
+ const remainingTasks = Math.max(0, metrics.tasks_total - metrics.tasks_completed);
44
+ const etaSeconds = avgSeconds * remainingTasks;
45
+
46
+ const etaDate = new Date(Date.now() + (etaSeconds * 1000));
47
+
48
+ return {
49
+ avg_seconds_per_task: parseFloat(avgSeconds.toFixed(1)),
50
+ tasks_completed: metrics.tasks_completed,
51
+ tasks_remaining: remainingTasks,
52
+ eta: remainingTasks > 0 ? etaDate.toLocaleString() : 'Done',
53
+ confidence: completedTasks.length > 5 ? 'High (85%)' : 'Medium (60%)',
54
+ timestamp: new Date().toISOString()
55
+ };
56
+ }
57
+ }
58
+
59
+ module.exports = new VelocityForecaster();