agentic-qe 3.8.10 → 3.8.12
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/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +40 -0
- package/dist/cli/bundle.js +1345 -1003
- package/dist/cli/command-registry.js +5 -1
- package/dist/cli/commands/pipeline.d.ts +16 -0
- package/dist/cli/commands/pipeline.js +314 -0
- package/dist/cli/commands/ruvector-commands.js +17 -0
- package/dist/cli/commands/token-usage.js +24 -1
- package/dist/cli/handlers/heartbeat-handler.d.ts +26 -0
- package/dist/cli/handlers/heartbeat-handler.js +382 -0
- package/dist/cli/handlers/index.d.ts +2 -0
- package/dist/cli/handlers/index.js +2 -0
- package/dist/cli/handlers/routing-handler.d.ts +22 -0
- package/dist/cli/handlers/routing-handler.js +227 -0
- package/dist/cli/index.js +2 -0
- package/dist/coordination/deterministic-actions.d.ts +36 -0
- package/dist/coordination/deterministic-actions.js +257 -0
- package/dist/coordination/workflow-orchestrator.d.ts +18 -1
- package/dist/coordination/workflow-orchestrator.js +113 -3
- package/dist/coordination/workflow-types.d.ts +19 -1
- package/dist/coordination/workflow-types.js +3 -0
- package/dist/coordination/yaml-pipeline-loader.d.ts +1 -0
- package/dist/coordination/yaml-pipeline-loader.js +34 -0
- package/dist/domains/code-intelligence/coordinator-gnn.d.ts +21 -0
- package/dist/domains/code-intelligence/coordinator-gnn.js +102 -0
- package/dist/domains/contract-testing/coordinator.js +13 -0
- package/dist/domains/coverage-analysis/coordinator.js +5 -0
- package/dist/domains/defect-intelligence/coordinator.d.ts +1 -0
- package/dist/domains/defect-intelligence/coordinator.js +43 -0
- package/dist/domains/quality-assessment/coordinator.js +26 -0
- package/dist/domains/test-generation/coordinator.js +14 -0
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts +11 -0
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js +44 -1
- package/dist/integrations/rl-suite/algorithms/eprop.d.ts +79 -0
- package/dist/integrations/rl-suite/algorithms/eprop.js +284 -0
- package/dist/integrations/rl-suite/algorithms/index.d.ts +2 -1
- package/dist/integrations/rl-suite/algorithms/index.js +2 -1
- package/dist/integrations/rl-suite/index.d.ts +2 -2
- package/dist/integrations/rl-suite/index.js +2 -2
- package/dist/integrations/rl-suite/interfaces.d.ts +3 -3
- package/dist/integrations/rl-suite/interfaces.js +1 -1
- package/dist/integrations/rl-suite/orchestrator.d.ts +2 -2
- package/dist/integrations/rl-suite/orchestrator.js +3 -2
- package/dist/integrations/rl-suite/reward-signals.d.ts +1 -1
- package/dist/integrations/rl-suite/reward-signals.js +1 -1
- package/dist/integrations/ruvector/coherence-gate-cohomology.d.ts +41 -0
- package/dist/integrations/ruvector/coherence-gate-cohomology.js +47 -0
- package/dist/integrations/ruvector/coherence-gate-core.d.ts +200 -0
- package/dist/integrations/ruvector/coherence-gate-core.js +294 -0
- package/dist/integrations/ruvector/coherence-gate-energy.d.ts +136 -0
- package/dist/integrations/ruvector/coherence-gate-energy.js +373 -0
- package/dist/integrations/ruvector/coherence-gate-vector.d.ts +38 -0
- package/dist/integrations/ruvector/coherence-gate-vector.js +76 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +10 -311
- package/dist/integrations/ruvector/coherence-gate.js +10 -652
- package/dist/integrations/ruvector/cold-tier-trainer.d.ts +103 -0
- package/dist/integrations/ruvector/cold-tier-trainer.js +377 -0
- package/dist/integrations/ruvector/cusum-detector.d.ts +70 -0
- package/dist/integrations/ruvector/cusum-detector.js +142 -0
- package/dist/integrations/ruvector/delta-tracker.d.ts +122 -0
- package/dist/integrations/ruvector/delta-tracker.js +311 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +79 -1
- package/dist/integrations/ruvector/domain-transfer.js +158 -2
- package/dist/integrations/ruvector/eprop-learner.d.ts +135 -0
- package/dist/integrations/ruvector/eprop-learner.js +351 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +177 -0
- package/dist/integrations/ruvector/feature-flags.js +145 -0
- package/dist/integrations/ruvector/graphmae-encoder.d.ts +88 -0
- package/dist/integrations/ruvector/graphmae-encoder.js +360 -0
- package/dist/integrations/ruvector/hdc-fingerprint.d.ts +127 -0
- package/dist/integrations/ruvector/hdc-fingerprint.js +222 -0
- package/dist/integrations/ruvector/hopfield-memory.d.ts +97 -0
- package/dist/integrations/ruvector/hopfield-memory.js +238 -0
- package/dist/integrations/ruvector/index.d.ts +13 -2
- package/dist/integrations/ruvector/index.js +46 -2
- package/dist/integrations/ruvector/mincut-wrapper.d.ts +7 -0
- package/dist/integrations/ruvector/mincut-wrapper.js +54 -2
- package/dist/integrations/ruvector/reservoir-replay.d.ts +172 -0
- package/dist/integrations/ruvector/reservoir-replay.js +335 -0
- package/dist/integrations/ruvector/solver-adapter.d.ts +93 -0
- package/dist/integrations/ruvector/solver-adapter.js +299 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +33 -0
- package/dist/integrations/ruvector/sona-persistence.js +47 -0
- package/dist/integrations/ruvector/spectral-sparsifier.d.ts +154 -0
- package/dist/integrations/ruvector/spectral-sparsifier.js +389 -0
- package/dist/integrations/ruvector/temporal-causality.d.ts +63 -0
- package/dist/integrations/ruvector/temporal-causality.js +317 -0
- package/dist/learning/pattern-promotion.d.ts +63 -0
- package/dist/learning/pattern-promotion.js +235 -1
- package/dist/learning/pattern-store.d.ts +2 -0
- package/dist/learning/pattern-store.js +187 -1
- package/dist/learning/sqlite-persistence.d.ts +2 -0
- package/dist/learning/sqlite-persistence.js +4 -0
- package/dist/mcp/bundle.js +477 -380
- package/dist/mcp/handlers/heartbeat-handlers.d.ts +67 -0
- package/dist/mcp/handlers/heartbeat-handlers.js +180 -0
- package/dist/mcp/handlers/index.d.ts +2 -1
- package/dist/mcp/handlers/index.js +5 -1
- package/dist/mcp/handlers/task-handlers.d.ts +28 -0
- package/dist/mcp/handlers/task-handlers.js +39 -0
- package/dist/mcp/protocol-server.js +45 -1
- package/dist/mcp/server.js +41 -1
- package/dist/optimization/index.d.ts +2 -0
- package/dist/optimization/index.js +1 -0
- package/dist/optimization/session-cache.d.ts +80 -0
- package/dist/optimization/session-cache.js +227 -0
- package/dist/optimization/token-optimizer-service.d.ts +10 -0
- package/dist/optimization/token-optimizer-service.js +51 -0
- package/dist/routing/economic-routing.d.ts +126 -0
- package/dist/routing/economic-routing.js +290 -0
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.js +2 -0
- package/dist/routing/routing-feedback.d.ts +29 -0
- package/dist/routing/routing-feedback.js +75 -0
- package/dist/shared/utils/index.d.ts +1 -0
- package/dist/shared/utils/index.js +1 -0
- package/dist/shared/utils/xorshift128.d.ts +24 -0
- package/dist/shared/utils/xorshift128.js +50 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { LoggerFactory } from '../../logging/index.js';
|
|
8
8
|
import { QEGNNIndexFactory, initGNN, } from '../../integrations/ruvector/wrappers';
|
|
9
|
+
import { isGraphMAEEnabled, isColdTierGNNEnabled, createGraphMAEEncoder, createColdTierTrainer, InMemoryGraph, } from '../../integrations/ruvector/index.js';
|
|
9
10
|
/**
|
|
10
11
|
* Initialize GNN for code graph embeddings
|
|
11
12
|
*/
|
|
@@ -55,6 +56,73 @@ export async function indexCodeEmbeddings(gnnIndex, fileReader, paths) {
|
|
|
55
56
|
logger.error('Failed to index code embeddings:', error instanceof Error ? error : undefined);
|
|
56
57
|
}
|
|
57
58
|
}
|
|
59
|
+
// R4: GraphMAE singleton encoder (lazy-initialized)
|
|
60
|
+
let graphMAEEncoder = null;
|
|
61
|
+
/**
|
|
62
|
+
* R4: Generate graph-aware embeddings for a set of files using GraphMAE.
|
|
63
|
+
* Builds a dependency graph from import relationships and uses masked
|
|
64
|
+
* autoencoders to produce structure-aware embeddings.
|
|
65
|
+
* Falls back to feature-hash embeddings when GraphMAE is disabled.
|
|
66
|
+
*/
|
|
67
|
+
export async function generateGraphMAEEmbeddings(fileReader, paths) {
|
|
68
|
+
const embeddings = new Map();
|
|
69
|
+
if (!isGraphMAEEnabled() || paths.length < 2) {
|
|
70
|
+
// Fallback: use feature-hash for each file independently
|
|
71
|
+
for (const p of paths) {
|
|
72
|
+
const result = await fileReader.readFile(p);
|
|
73
|
+
if (result.success && result.value) {
|
|
74
|
+
embeddings.set(p, await generateCodeEmbedding(p, result.value));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return embeddings;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
// Build QEGraph: nodes are files, edges are import dependencies
|
|
81
|
+
const nodes = new Map();
|
|
82
|
+
const edges = new Map();
|
|
83
|
+
const contentCache = new Map();
|
|
84
|
+
for (const p of paths) {
|
|
85
|
+
const result = await fileReader.readFile(p);
|
|
86
|
+
if (result.success && result.value) {
|
|
87
|
+
contentCache.set(p, result.value);
|
|
88
|
+
const featureVec = await generateCodeEmbedding(p, result.value);
|
|
89
|
+
nodes.set(p, new Float32Array(featureVec.slice(0, 32)));
|
|
90
|
+
// Extract import targets as edges
|
|
91
|
+
const imports = result.value.match(/from\s+['"]([^'"]+)['"]/g) ?? [];
|
|
92
|
+
const targets = imports
|
|
93
|
+
.map(m => m.replace(/from\s+['"]/, '').replace(/['"]$/, ''))
|
|
94
|
+
.filter(t => paths.some(pp => pp.includes(t.replace(/^\.\//, ''))));
|
|
95
|
+
edges.set(p, targets.length > 0 ? targets : []);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (nodes.size < 2) {
|
|
99
|
+
// Not enough nodes for meaningful graph learning
|
|
100
|
+
for (const [p, content] of contentCache) {
|
|
101
|
+
embeddings.set(p, await generateCodeEmbedding(p, content));
|
|
102
|
+
}
|
|
103
|
+
return embeddings;
|
|
104
|
+
}
|
|
105
|
+
const graph = { nodes, edges };
|
|
106
|
+
if (!graphMAEEncoder) {
|
|
107
|
+
graphMAEEncoder = createGraphMAEEncoder({ embeddingDim: 128, numHeads: 4 });
|
|
108
|
+
}
|
|
109
|
+
const graphEmbeddings = graphMAEEncoder.embed(graph);
|
|
110
|
+
for (const [nodeId, emb] of graphEmbeddings) {
|
|
111
|
+
embeddings.set(nodeId, Array.from(emb));
|
|
112
|
+
}
|
|
113
|
+
logger.info(`[R4] GraphMAE generated ${graphEmbeddings.size} graph-aware embeddings`);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
logger.error('[R4] GraphMAE embedding failed, using feature-hash fallback:', error instanceof Error ? error : undefined);
|
|
117
|
+
for (const p of paths) {
|
|
118
|
+
const result = await fileReader.readFile(p);
|
|
119
|
+
if (result.success && result.value) {
|
|
120
|
+
embeddings.set(p, await generateCodeEmbedding(p, result.value));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return embeddings;
|
|
125
|
+
}
|
|
58
126
|
/**
|
|
59
127
|
* Generate code embedding using semantic features
|
|
60
128
|
*/
|
|
@@ -171,6 +239,40 @@ export function mergeSearchResults(semanticResults, gnnResults) {
|
|
|
171
239
|
.sort((a, b) => b.score - a.score)
|
|
172
240
|
.slice(0, 20);
|
|
173
241
|
}
|
|
242
|
+
// R6: ColdTier trainer singleton (lazy-initialized)
|
|
243
|
+
let coldTierTrainer = null;
|
|
244
|
+
/**
|
|
245
|
+
* R6: Train GNN embeddings with memory-bounded cold-tier training.
|
|
246
|
+
* Uses LRU hotset caching when the graph exceeds hotsetSize, falling
|
|
247
|
+
* back to full in-memory training for small graphs.
|
|
248
|
+
*/
|
|
249
|
+
export function trainWithColdTier(nodeFeatures, adjacency, options = {}) {
|
|
250
|
+
if (!isColdTierGNNEnabled()) {
|
|
251
|
+
return { embeddings: new Map(), loss: 0, usedColdTier: false };
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
if (!coldTierTrainer) {
|
|
255
|
+
coldTierTrainer = createColdTierTrainer({
|
|
256
|
+
hotsetSize: options.hotsetSize ?? 10000,
|
|
257
|
+
epochs: options.epochs ?? 10,
|
|
258
|
+
hiddenDim: options.hiddenDim ?? 64,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
const graph = new InMemoryGraph(nodeFeatures, adjacency);
|
|
262
|
+
const result = coldTierTrainer.train(graph);
|
|
263
|
+
logger.info(`[R6] ColdTier training: ${graph.nodeCount} nodes, ` +
|
|
264
|
+
`loss=${result.loss.toFixed(4)}, mode=${result.usedInMemoryMode ? 'in-memory' : 'cold-tier'}`);
|
|
265
|
+
return {
|
|
266
|
+
embeddings: result.embeddings,
|
|
267
|
+
loss: result.loss,
|
|
268
|
+
usedColdTier: !result.usedInMemoryMode,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
logger.error('[R6] ColdTier training failed:', error instanceof Error ? error : undefined);
|
|
273
|
+
return { embeddings: new Map(), loss: 0, usedColdTier: false };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
174
276
|
/**
|
|
175
277
|
* Simple hash function for strings
|
|
176
278
|
*/
|
|
@@ -16,6 +16,8 @@ import { ContractValidatorService } from './services/contract-validator.js';
|
|
|
16
16
|
import { ApiCompatibilityService } from './services/api-compatibility.js';
|
|
17
17
|
import { SARSAAlgorithm } from '../../integrations/rl-suite/algorithms/sarsa.js';
|
|
18
18
|
import { createPersistentSONAEngine } from '../../integrations/ruvector/sona-persistence.js';
|
|
19
|
+
// Three-loop feature flag for instantAdapt protocol
|
|
20
|
+
import { isSONAThreeLoopEnabled } from '../../integrations/ruvector/feature-flags.js';
|
|
19
21
|
import { createDomainFinding, } from '../../coordination/consensus/domain-findings.js';
|
|
20
22
|
// CQ-002: Base domain coordinator
|
|
21
23
|
import { BaseDomainCoordinator, } from '../base-domain-coordinator.js';
|
|
@@ -960,6 +962,17 @@ export class ContractTestingCoordinator extends BaseDomainCoordinator {
|
|
|
960
962
|
contract.endpoints.filter((e) => e.responseSchema).length / 100,
|
|
961
963
|
],
|
|
962
964
|
};
|
|
965
|
+
// Three-loop protocol: instantAdapt must precede recordOutcome
|
|
966
|
+
if (isSONAThreeLoopEnabled() && this.qesona.isThreeLoopEnabled()) {
|
|
967
|
+
this.qesona.instantAdapt([
|
|
968
|
+
contract.endpoints.length / 100,
|
|
969
|
+
contract.consumers.length / 50,
|
|
970
|
+
contract.schemas.length / 50,
|
|
971
|
+
quality,
|
|
972
|
+
validationSuccess ? 1 : 0,
|
|
973
|
+
contract.version.major / 10,
|
|
974
|
+
]);
|
|
975
|
+
}
|
|
963
976
|
const action = {
|
|
964
977
|
type: validationSuccess ? 'validate' : 'reject',
|
|
965
978
|
value: quality,
|
|
@@ -69,6 +69,11 @@ export class CoverageAnalysisCoordinator extends BaseDomainCoordinator {
|
|
|
69
69
|
async onInitialize() {
|
|
70
70
|
// Services are stateless, no domain-specific initialization needed
|
|
71
71
|
// ADR-059: Ghost coverage analyzer is lazy-initialized on first use
|
|
72
|
+
// NOTE: EWC++ three-loop integration not available in this coordinator.
|
|
73
|
+
// Coverage-analysis uses Q-Learning (not SONA), so the three-loop engine
|
|
74
|
+
// is not accessible here. To enable EWC++ for coverage patterns, this
|
|
75
|
+
// coordinator would need a PersistentSONAEngine instance wired the same
|
|
76
|
+
// way as quality-assessment and test-generation coordinators.
|
|
72
77
|
}
|
|
73
78
|
async onDispose() {
|
|
74
79
|
// No domain-specific cleanup needed
|
|
@@ -54,6 +54,7 @@ export declare class DefectIntelligenceCoordinator extends BaseDomainCoordinator
|
|
|
54
54
|
private readonly predictor;
|
|
55
55
|
private readonly patternLearner;
|
|
56
56
|
private readonly rootCauseAnalyzer;
|
|
57
|
+
private readonly testExecutionHistory;
|
|
57
58
|
constructor(eventBus: EventBus, memory: MemoryBackend, agentCoordinator: AgentCoordinator, config?: Partial<CoordinatorConfig>);
|
|
58
59
|
protected onInitialize(): Promise<void>;
|
|
59
60
|
protected onDispose(): Promise<void>;
|
|
@@ -17,6 +17,9 @@ import { PatternLearnerService, } from './services/pattern-learner';
|
|
|
17
17
|
import { RootCauseAnalyzerService, } from './services/root-cause-analyzer';
|
|
18
18
|
// CQ-002: Base domain coordinator
|
|
19
19
|
import { BaseDomainCoordinator, } from '../base-domain-coordinator.js';
|
|
20
|
+
// R12: Granger Causality for temporal failure prediction (ADR-087)
|
|
21
|
+
import { getRuVectorFeatureFlags } from '../../integrations/ruvector/feature-flags.js';
|
|
22
|
+
import { GrangerAnalyzer } from '../../integrations/ruvector/temporal-causality.js';
|
|
20
23
|
const DEFAULT_CONFIG = {
|
|
21
24
|
maxConcurrentWorkflows: 5,
|
|
22
25
|
defaultTimeout: 60000,
|
|
@@ -49,6 +52,8 @@ export class DefectIntelligenceCoordinator extends BaseDomainCoordinator {
|
|
|
49
52
|
predictor;
|
|
50
53
|
patternLearner;
|
|
51
54
|
rootCauseAnalyzer;
|
|
55
|
+
// R12: Accumulated test execution history for Granger causality (ADR-087)
|
|
56
|
+
testExecutionHistory = new Map();
|
|
52
57
|
constructor(eventBus, memory, agentCoordinator, config = {}) {
|
|
53
58
|
const fullConfig = { ...DEFAULT_CONFIG, ...config };
|
|
54
59
|
super(eventBus, 'defect-intelligence', fullConfig, {
|
|
@@ -160,6 +165,44 @@ export class DefectIntelligenceCoordinator extends BaseDomainCoordinator {
|
|
|
160
165
|
if (this.config.enablePatternLearning) {
|
|
161
166
|
await this.autoAnalyzeHighRisk(enhancedResult);
|
|
162
167
|
}
|
|
168
|
+
// R12: Accumulate test history and run Granger causality (ADR-087)
|
|
169
|
+
if (getRuVectorFeatureFlags().useGrangerCausality) {
|
|
170
|
+
try {
|
|
171
|
+
const now = Date.now();
|
|
172
|
+
// Accumulate prediction outcomes into per-test history
|
|
173
|
+
for (const p of enhancedResult.predictions) {
|
|
174
|
+
const history = this.testExecutionHistory.get(p.file) ?? { timestamps: [], outcomes: [] };
|
|
175
|
+
history.timestamps.push(now);
|
|
176
|
+
history.outcomes.push(p.probability > 0.5 ? 0 : 1);
|
|
177
|
+
// Cap history at 500 points per test
|
|
178
|
+
if (history.timestamps.length > 500) {
|
|
179
|
+
history.timestamps.splice(0, history.timestamps.length - 500);
|
|
180
|
+
history.outcomes.splice(0, history.outcomes.length - 500);
|
|
181
|
+
}
|
|
182
|
+
this.testExecutionHistory.set(p.file, history);
|
|
183
|
+
}
|
|
184
|
+
// Run Granger analysis when we have enough accumulated history
|
|
185
|
+
const eligibleSeries = Array.from(this.testExecutionHistory.entries())
|
|
186
|
+
.filter(([, h]) => h.timestamps.length >= 30)
|
|
187
|
+
.map(([testId, h]) => ({ testId, timestamps: h.timestamps, outcomes: h.outcomes }));
|
|
188
|
+
if (eligibleSeries.length >= 3) {
|
|
189
|
+
const analyzer = new GrangerAnalyzer({ maxLag: 5, alpha: 0.05 });
|
|
190
|
+
const causalLinks = analyzer.analyzeCausality(eligibleSeries);
|
|
191
|
+
if (causalLinks.length > 0) {
|
|
192
|
+
logger.info('Granger causality found causal links in defect predictions', {
|
|
193
|
+
linkCount: causalLinks.length,
|
|
194
|
+
historyDepth: eligibleSeries[0].timestamps.length,
|
|
195
|
+
topLink: `${causalLinks[0].sourceTestId} → ${causalLinks[0].targetTestId} (lag=${causalLinks[0].lag}, p=${causalLinks[0].pValue.toFixed(4)})`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (grangerErr) {
|
|
201
|
+
logger.warn('Granger causality analysis failed (non-fatal)', {
|
|
202
|
+
error: grangerErr instanceof Error ? grangerErr.message : 'unknown',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
163
206
|
// Stop the agent
|
|
164
207
|
await this.agentCoordinator.stop(agentResult.value);
|
|
165
208
|
return ok(enhancedResult);
|
|
@@ -28,6 +28,8 @@ import * as ClaimVerifierHelpers from './coordinator-claim-verifier.js';
|
|
|
28
28
|
import * as GateEvalHelpers from './coordinator-gate-evaluation.js';
|
|
29
29
|
// ADR-070: Witness Chain audit trail
|
|
30
30
|
import { getWitnessChain } from '../../audit/witness-chain.js';
|
|
31
|
+
// Three-loop feature flag for instantAdapt protocol
|
|
32
|
+
import { isSONAThreeLoopEnabled } from '../../integrations/ruvector/feature-flags.js';
|
|
31
33
|
// CQ-002: Base domain coordinator
|
|
32
34
|
import { BaseDomainCoordinator, } from '../base-domain-coordinator.js';
|
|
33
35
|
const DEFAULT_CONFIG = {
|
|
@@ -227,6 +229,20 @@ export class QualityAssessmentCoordinator extends BaseDomainCoordinator {
|
|
|
227
229
|
}
|
|
228
230
|
// Success path
|
|
229
231
|
this.completeWorkflow(workflowId);
|
|
232
|
+
// Three-loop protocol: instantAdapt must precede recordOutcome
|
|
233
|
+
if (isSONAThreeLoopEnabled() && this.qesona?.isThreeLoopEnabled()) {
|
|
234
|
+
const m = effectiveRequest.metrics;
|
|
235
|
+
this.qesona.instantAdapt([
|
|
236
|
+
m.coverage / 100,
|
|
237
|
+
m.testsPassing / 100,
|
|
238
|
+
m.criticalBugs / 10,
|
|
239
|
+
m.codeSmells / 100,
|
|
240
|
+
m.securityVulnerabilities / 10,
|
|
241
|
+
m.technicalDebt / 100,
|
|
242
|
+
m.duplications / 100,
|
|
243
|
+
finalResult.overallScore / 100,
|
|
244
|
+
]);
|
|
245
|
+
}
|
|
230
246
|
// Store quality pattern in SONA if enabled
|
|
231
247
|
if (this.config.enableSONAPatternLearning && this.qesona) {
|
|
232
248
|
await this.storeQualityPattern(effectiveRequest, finalResult);
|
|
@@ -322,6 +338,16 @@ export class QualityAssessmentCoordinator extends BaseDomainCoordinator {
|
|
|
322
338
|
result.value = enhanced;
|
|
323
339
|
}
|
|
324
340
|
}
|
|
341
|
+
// Three-loop protocol: instantAdapt must precede recordOutcome
|
|
342
|
+
if (isSONAThreeLoopEnabled() && this.qesona?.isThreeLoopEnabled()) {
|
|
343
|
+
const score = result.value.score;
|
|
344
|
+
this.qesona.instantAdapt([
|
|
345
|
+
score.overall / 100,
|
|
346
|
+
result.value.metrics.length / 20,
|
|
347
|
+
result.value.trends.length / 10,
|
|
348
|
+
result.value.recommendations.length / 10,
|
|
349
|
+
]);
|
|
350
|
+
}
|
|
325
351
|
// Store quality pattern in SONA
|
|
326
352
|
if (this.config.enableSONAPatternLearning && this.qesona) {
|
|
327
353
|
await this.storeQualityAnalysisPattern(request, result.value);
|
|
@@ -24,6 +24,8 @@ import { createDomainFinding, } from '../../coordination/consensus/domain-findin
|
|
|
24
24
|
import { createPersistentSONAEngine, } from '../../integrations/ruvector/sona-persistence.js';
|
|
25
25
|
import { createQEFlashAttention, } from '../../integrations/ruvector/wrappers.js';
|
|
26
26
|
import { DecisionTransformerAlgorithm, } from '../../integrations/rl-suite/algorithms/decision-transformer.js';
|
|
27
|
+
// Three-loop feature flag for instantAdapt protocol
|
|
28
|
+
import { isSONAThreeLoopEnabled } from '../../integrations/ruvector/feature-flags.js';
|
|
27
29
|
// Coherence Gate Integration (ADR-052)
|
|
28
30
|
import { createTestGenerationCoherenceGate, } from './services/coherence-gate-service.js';
|
|
29
31
|
const DEFAULT_CONFIG = {
|
|
@@ -307,6 +309,18 @@ export class TestGenerationCoordinator extends BaseDomainCoordinator {
|
|
|
307
309
|
await this.publishTestGenerated(test, request.framework ?? 'vitest');
|
|
308
310
|
}
|
|
309
311
|
}
|
|
312
|
+
// Three-loop protocol: instantAdapt must precede recordOutcome
|
|
313
|
+
if (isSONAThreeLoopEnabled() && this.qesona?.isThreeLoopEnabled()) {
|
|
314
|
+
const tests = result.value;
|
|
315
|
+
this.qesona.instantAdapt([
|
|
316
|
+
tests.tests.length / 20,
|
|
317
|
+
tests.coverageEstimate / 100,
|
|
318
|
+
tests.patternsUsed.length / 10,
|
|
319
|
+
request.sourceFiles.length / 20,
|
|
320
|
+
(request.coverageTarget ?? 80) / 100,
|
|
321
|
+
tests.tests.filter(t => t.type === 'unit').length / 20,
|
|
322
|
+
]);
|
|
323
|
+
}
|
|
310
324
|
// Learn from successful generation using QESONA
|
|
311
325
|
if (this.config.enableQESONA && this.qesona) {
|
|
312
326
|
await this.storeTestGenerationPattern(result.value, request);
|
|
@@ -117,6 +117,7 @@ export declare class ExperienceReplay {
|
|
|
117
117
|
private experienceIdToHnswId;
|
|
118
118
|
private nextHnswId;
|
|
119
119
|
private recentExperiences;
|
|
120
|
+
private reservoirBuffer;
|
|
120
121
|
private stats;
|
|
121
122
|
constructor(config?: Partial<ExperienceReplayConfig>);
|
|
122
123
|
/**
|
|
@@ -197,6 +198,16 @@ export declare class ExperienceReplay {
|
|
|
197
198
|
hnswIndexSize: number;
|
|
198
199
|
recentBufferSize: number;
|
|
199
200
|
};
|
|
201
|
+
/**
|
|
202
|
+
* Get reservoir buffer stats (R10, ADR-087).
|
|
203
|
+
* Returns null if the reservoir is not enabled.
|
|
204
|
+
*/
|
|
205
|
+
getReservoirStats(): {
|
|
206
|
+
size: number;
|
|
207
|
+
totalAdmitted: number;
|
|
208
|
+
totalRejected: number;
|
|
209
|
+
tierCounts: Record<string, number>;
|
|
210
|
+
} | null;
|
|
200
211
|
/**
|
|
201
212
|
* Dispose and cleanup
|
|
202
213
|
*/
|
|
@@ -19,6 +19,8 @@ import { CircularBuffer } from '../../../shared/utils/circular-buffer.js';
|
|
|
19
19
|
import { HNSWEmbeddingIndex } from '../../embeddings/index/HNSWIndex.js';
|
|
20
20
|
import { safeJsonParse } from '../../../shared/safe-json.js';
|
|
21
21
|
import { ExperienceConsolidator } from '../../../learning/experience-consolidation.js';
|
|
22
|
+
import { getRuVectorFeatureFlags } from '../../ruvector/feature-flags.js';
|
|
23
|
+
import { ReservoirReplayBuffer } from '../../ruvector/reservoir-replay.js';
|
|
22
24
|
const DEFAULT_CONFIG = {
|
|
23
25
|
minQualityThreshold: 0.6,
|
|
24
26
|
maxExperiencesPerDomain: 500,
|
|
@@ -64,6 +66,8 @@ export class ExperienceReplay {
|
|
|
64
66
|
nextHnswId = 0;
|
|
65
67
|
// Recent experiences buffer
|
|
66
68
|
recentExperiences;
|
|
69
|
+
// Reservoir replay buffer (R10, ADR-087) — coherence-gated admission
|
|
70
|
+
reservoirBuffer = null;
|
|
67
71
|
// Statistics
|
|
68
72
|
stats = {
|
|
69
73
|
experiencesStored: 0,
|
|
@@ -99,6 +103,11 @@ export class ExperienceReplay {
|
|
|
99
103
|
this.prepareStatements();
|
|
100
104
|
// Load embeddings into memory index
|
|
101
105
|
await this.loadEmbeddingIndex();
|
|
106
|
+
// Initialize reservoir buffer if feature flag is enabled (R10, ADR-087)
|
|
107
|
+
if (getRuVectorFeatureFlags().useReservoirReplay) {
|
|
108
|
+
this.reservoirBuffer = new ReservoirReplayBuffer({ capacity: 10_000 });
|
|
109
|
+
console.log('[ExperienceReplay] Reservoir replay buffer enabled');
|
|
110
|
+
}
|
|
102
111
|
this.initialized = true;
|
|
103
112
|
console.log('[ExperienceReplay] Initialized');
|
|
104
113
|
}
|
|
@@ -333,6 +342,10 @@ export class ExperienceReplay {
|
|
|
333
342
|
}
|
|
334
343
|
// Add to recent buffer
|
|
335
344
|
this.recentExperiences.push(experience);
|
|
345
|
+
// Admit to reservoir buffer with coherence gating (R10, ADR-087)
|
|
346
|
+
if (this.reservoirBuffer) {
|
|
347
|
+
this.reservoirBuffer.admit(experience.id, experience, experience.qualityScore);
|
|
348
|
+
}
|
|
336
349
|
this.stats.experiencesStored++;
|
|
337
350
|
// Auto-consolidate if enabled (replaces destructive auto-prune)
|
|
338
351
|
if (this.config.autoPrune) {
|
|
@@ -349,8 +362,23 @@ export class ExperienceReplay {
|
|
|
349
362
|
*/
|
|
350
363
|
async getGuidance(task, domain) {
|
|
351
364
|
this.ensureInitialized();
|
|
352
|
-
// Find similar experiences
|
|
365
|
+
// Find similar experiences via HNSW
|
|
353
366
|
const similar = await this.findSimilarExperiences(task, domain);
|
|
367
|
+
// Blend in high-coherence experiences from reservoir buffer (R10, ADR-087)
|
|
368
|
+
if (this.reservoirBuffer && this.reservoirBuffer.size() > 0) {
|
|
369
|
+
const reservoirSamples = this.reservoirBuffer.sample(Math.max(2, Math.floor(this.config.topK / 2)), 0.6);
|
|
370
|
+
for (const entry of reservoirSamples) {
|
|
371
|
+
const exp = entry.data;
|
|
372
|
+
// Skip if already in HNSW results
|
|
373
|
+
if (similar.some(s => s.experience.id === exp.id))
|
|
374
|
+
continue;
|
|
375
|
+
// Skip if domain filter doesn't match
|
|
376
|
+
if (domain && exp.domain !== domain)
|
|
377
|
+
continue;
|
|
378
|
+
// Add with a coherence-based similarity score
|
|
379
|
+
similar.push({ experience: exp, similarity: entry.coherenceScore * 0.8 });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
354
382
|
if (similar.length === 0) {
|
|
355
383
|
return null;
|
|
356
384
|
}
|
|
@@ -564,6 +592,21 @@ export class ExperienceReplay {
|
|
|
564
592
|
recentBufferSize: this.recentExperiences.length,
|
|
565
593
|
};
|
|
566
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* Get reservoir buffer stats (R10, ADR-087).
|
|
597
|
+
* Returns null if the reservoir is not enabled.
|
|
598
|
+
*/
|
|
599
|
+
getReservoirStats() {
|
|
600
|
+
if (!this.reservoirBuffer)
|
|
601
|
+
return null;
|
|
602
|
+
const stats = this.reservoirBuffer.getStats();
|
|
603
|
+
return {
|
|
604
|
+
size: stats.size,
|
|
605
|
+
totalAdmitted: stats.totalAdmitted,
|
|
606
|
+
totalRejected: stats.totalRejected,
|
|
607
|
+
tierCounts: stats.tierCounts,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
567
610
|
/**
|
|
568
611
|
* Dispose and cleanup
|
|
569
612
|
*/
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - E-prop Online Learning Algorithm (ADR-087 Milestone 4)
|
|
3
|
+
*
|
|
4
|
+
* RL algorithm #10: Eligibility propagation for online learning.
|
|
5
|
+
* Uses 12 bytes/synapse with no backprop required.
|
|
6
|
+
*
|
|
7
|
+
* Application: Online adaptive test strategies — learns in real time
|
|
8
|
+
* from test execution feedback without storing replay buffers.
|
|
9
|
+
*/
|
|
10
|
+
import { BaseRLAlgorithm } from '../base-algorithm';
|
|
11
|
+
import type { RLState, RLPrediction, RLTrainingStats, RLExperience, RLAlgorithmInfo, RewardSignal } from '../interfaces';
|
|
12
|
+
interface EpropAlgorithmConfig {
|
|
13
|
+
/** Number of state features */
|
|
14
|
+
stateSize: number;
|
|
15
|
+
/** Hidden layer size for the E-prop network */
|
|
16
|
+
hiddenSize: number;
|
|
17
|
+
/** Number of discrete actions */
|
|
18
|
+
actionSize: number;
|
|
19
|
+
/** E-prop learning rate */
|
|
20
|
+
epropLearningRate: number;
|
|
21
|
+
/** Eligibility trace decay */
|
|
22
|
+
eligibilityDecay: number;
|
|
23
|
+
/** Use feedback alignment */
|
|
24
|
+
feedbackAlignment: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* E-prop online learning algorithm for adaptive test strategies.
|
|
28
|
+
*
|
|
29
|
+
* Unlike batch RL algorithms, E-prop learns from each experience
|
|
30
|
+
* immediately using eligibility traces — no replay buffer needed.
|
|
31
|
+
*
|
|
32
|
+
* Key advantages:
|
|
33
|
+
* - Online: updates weights after every step
|
|
34
|
+
* - Memory-efficient: 12 bytes/synapse (vs kilobytes for replay-based)
|
|
35
|
+
* - Biologically plausible: no weight transport (feedback alignment)
|
|
36
|
+
* - Fast: no backward pass through the full network
|
|
37
|
+
*/
|
|
38
|
+
export declare class EpropAlgorithm extends BaseRLAlgorithm {
|
|
39
|
+
private network;
|
|
40
|
+
private epropConfig;
|
|
41
|
+
private actions;
|
|
42
|
+
constructor(config?: Partial<EpropAlgorithmConfig>, rewardSignals?: RewardSignal[]);
|
|
43
|
+
/**
|
|
44
|
+
* Predict best action for a given state.
|
|
45
|
+
* Runs the E-prop network forward pass and selects the action
|
|
46
|
+
* with highest output activation.
|
|
47
|
+
*/
|
|
48
|
+
predict(state: RLState): Promise<RLPrediction>;
|
|
49
|
+
/**
|
|
50
|
+
* Train with a single experience — the core online learning step.
|
|
51
|
+
*
|
|
52
|
+
* Unlike batch algorithms, E-prop processes each experience immediately:
|
|
53
|
+
* 1. Forward pass (already done during predict)
|
|
54
|
+
* 2. Online update: dw = eta * eligibility * reward
|
|
55
|
+
*/
|
|
56
|
+
train(experience: RLExperience): Promise<RLTrainingStats>;
|
|
57
|
+
/**
|
|
58
|
+
* Core training logic for batch experiences.
|
|
59
|
+
* E-prop processes each experience online (sequentially).
|
|
60
|
+
*/
|
|
61
|
+
protected trainCore(experiences: RLExperience[]): Promise<RLTrainingStats>;
|
|
62
|
+
/**
|
|
63
|
+
* Get algorithm-specific info.
|
|
64
|
+
*/
|
|
65
|
+
protected getAlgorithmInfo(): RLAlgorithmInfo;
|
|
66
|
+
protected exportCustomData(): Promise<Record<string, unknown>>;
|
|
67
|
+
protected importCustomData(data: Record<string, unknown>): Promise<void>;
|
|
68
|
+
protected resetAlgorithm(): Promise<void>;
|
|
69
|
+
private prepareState;
|
|
70
|
+
private argmax;
|
|
71
|
+
private calculateConfidence;
|
|
72
|
+
private generateReasoning;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Create a new E-prop RL algorithm instance.
|
|
76
|
+
*/
|
|
77
|
+
export declare function createEpropAlgorithm(config?: Partial<EpropAlgorithmConfig>, rewardSignals?: RewardSignal[]): EpropAlgorithm;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=eprop.d.ts.map
|