agentic-qe 3.7.15 → 3.7.16

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.
@@ -38,6 +38,17 @@ function exportBrain() {
38
38
  }
39
39
 
40
40
  try {
41
+ // Checkpoint WAL to ensure all pending writes are flushed to main DB
42
+ try {
43
+ execSync(`sqlite3 "${DB_PATH}" "PRAGMA wal_checkpoint(TRUNCATE);"`, {
44
+ timeout: 10000,
45
+ encoding: 'utf-8',
46
+ });
47
+ log('WAL checkpoint completed');
48
+ } catch (walErr) {
49
+ log(`WAL checkpoint warning: ${walErr.message}`);
50
+ }
51
+
41
52
  // Remove existing to avoid LockHeld errors
42
53
  if (fs.existsSync(RVF_PATH)) {
43
54
  fs.unlinkSync(RVF_PATH);
@@ -904,7 +904,7 @@
904
904
  },
905
905
  "metadata": {
906
906
  "generatedBy": "Agentic QE Fleet",
907
- "fleetVersion": "3.7.15",
907
+ "fleetVersion": "3.7.16",
908
908
  "manifestVersion": "1.3.0",
909
909
  "lastUpdated": "2026-02-04T00:00:00.000Z",
910
910
  "contributors": [
package/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to the Agentic QE project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.7.16] - 2026-03-10
9
+
10
+ ### Added
11
+
12
+ - **Tier 3 baseline collection and instrumentation** — Collect benchmark baselines for all Tier 3 features and add Priority 2 instrumentation: routing tier tags, HNSW/FTS5 search latency tracking, token tracker auto-save, and pipeline step timers.
13
+ - **MCP persistence pipeline fix** — Wire `recordDomainFeedback` into the feedback loop (handler-factory Step 5d) so `test_outcomes` and `coverage_sessions` receive data from live MCP tool calls.
14
+ - **Quality feedback loop singleton** — `getQualityFeedbackLoop()` provides cross-module access to the feedback loop instance.
15
+ - **Experience embedding on capture** — Embedding computation now runs automatically when experiences are captured.
16
+ - **Routing tier tracking** — New `model_tier` column in `routing_outcomes` with tier inference for cost analysis.
17
+ - **Search latency instrumentation** — `performance.now()` timing added to HNSW search and FTS5 `searchFTS` for benchmarking.
18
+ - **Token metrics auto-persistence** — `TokenMetricsCollector` now saves to DB automatically on initialization.
19
+ - **Pipeline step latencies** — `TestSchedulingPipeline` results now include per-step timing data.
20
+
21
+ ### Fixed
22
+
23
+ - **Critical: Test DB isolation** — Fixed `goap-planner.test.ts` and `q-value-persistence.test.ts` using relative `.agentic-qe/memory.db` paths that deleted the production database during test cleanup. Tests now use `os.tmpdir()`.
24
+ - **Critical: Project root cache leak** — `resetUnifiedMemory()` now calls `clearProjectRootCache()` to prevent stale path cache from redirecting tests to the production DB.
25
+ - **DB path safety redirect** — `UnifiedMemoryManager._doInitialize()` now detects and redirects when a test process (with `AQE_PROJECT_ROOT` set) tries to open a production `.agentic-qe/memory.db`.
26
+ - **`process.cwd()` DB path bypasses** — `pull-agent.ts` and `brain-handler.ts` now use `findProjectRoot()` instead of `process.cwd()` to resolve the DB path, respecting `AQE_PROJECT_ROOT`.
27
+ - **Optional native module graceful degradation** — FlashAttention and DecisionTransformer now degrade gracefully when native modules are unavailable.
28
+ - **WAL checkpoint before RVF export** — Brain checkpoint now runs a WAL checkpoint before RVF export to ensure data consistency.
29
+
8
30
  ## [3.7.15] - 2026-03-09
9
31
 
10
32
  ### Added
@@ -1876,7 +1876,19 @@ var init_hnsw_adapter = __esm({
1876
1876
  this.backend.add(id, vector, metadata);
1877
1877
  }
1878
1878
  search(query, k68) {
1879
- return this.backend.search(query, k68);
1879
+ const start = performance.now();
1880
+ const results = this.backend.search(query, k68);
1881
+ const elapsed = performance.now() - start;
1882
+ if (elapsed > 50) {
1883
+ console.warn(`[HNSW] search took ${elapsed.toFixed(1)}ms (k=${k68}, results=${results.length})`);
1884
+ }
1885
+ this._lastSearchLatencyMs = elapsed;
1886
+ return results;
1887
+ }
1888
+ /** Last search latency in ms, for instrumentation */
1889
+ _lastSearchLatencyMs = 0;
1890
+ get lastSearchLatencyMs() {
1891
+ return this._lastSearchLatencyMs;
1880
1892
  }
1881
1893
  remove(id) {
1882
1894
  return this.backend.remove(id);
@@ -3073,10 +3085,12 @@ var init_unified_memory_schemas = __esm({
3073
3085
  quality_score REAL NOT NULL,
3074
3086
  duration_ms REAL NOT NULL,
3075
3087
  error TEXT,
3088
+ model_tier TEXT,
3076
3089
  created_at TEXT DEFAULT (datetime('now'))
3077
3090
  );
3078
3091
  CREATE INDEX IF NOT EXISTS idx_routing_outcomes_agent ON routing_outcomes(used_agent);
3079
3092
  CREATE INDEX IF NOT EXISTS idx_routing_outcomes_created ON routing_outcomes(created_at);
3093
+ CREATE INDEX IF NOT EXISTS idx_routing_outcomes_tier ON routing_outcomes(model_tier);
3080
3094
 
3081
3095
  -- Coverage sessions (ADR-023: Coverage Learning)
3082
3096
  CREATE TABLE IF NOT EXISTS coverage_sessions (
@@ -4070,6 +4084,7 @@ __export(unified_memory_exports, {
4070
4084
  validateTableName: () => validateTableName
4071
4085
  });
4072
4086
  import * as fs from "fs";
4087
+ import * as os from "os";
4073
4088
  import * as path from "path";
4074
4089
  function clearProjectRootCache() {
4075
4090
  _cachedProjectRoot = null;
@@ -4226,6 +4241,7 @@ var init_unified_memory = __esm({
4226
4241
  _UnifiedMemoryManager.instance = null;
4227
4242
  }
4228
4243
  _UnifiedMemoryManager.instancePromise = null;
4244
+ clearProjectRootCache();
4229
4245
  }
4230
4246
  async initialize() {
4231
4247
  if (this.initialized) return;
@@ -4237,6 +4253,17 @@ var init_unified_memory = __esm({
4237
4253
  async _doInitialize() {
4238
4254
  if (this.initialized) return;
4239
4255
  try {
4256
+ const envRoot = process.env.AQE_PROJECT_ROOT;
4257
+ if (envRoot) {
4258
+ const resolvedPath = path.resolve(this.config.dbPath);
4259
+ const resolvedRoot = path.resolve(envRoot);
4260
+ if (!resolvedPath.startsWith(resolvedRoot) && !resolvedPath.startsWith(os.tmpdir()) && resolvedPath.includes(".agentic-qe")) {
4261
+ console.error(
4262
+ `[UnifiedMemory] WARNING: DB path "${this.config.dbPath}" points to a production .agentic-qe/ while AQE_PROJECT_ROOT="${envRoot}". Redirecting to test-safe path.`
4263
+ );
4264
+ this.config.dbPath = path.join(envRoot, ".agentic-qe", "memory.db");
4265
+ }
4266
+ }
4240
4267
  const dir = path.dirname(this.config.dbPath);
4241
4268
  if (!fs.existsSync(dir)) {
4242
4269
  fs.mkdirSync(dir, { recursive: true });
@@ -24571,6 +24598,7 @@ var init_pipeline = __esm({
24571
24598
  const startTime = Date.now();
24572
24599
  let selectedTests = [];
24573
24600
  let ranAllTests = this.config.runAllTests ?? false;
24601
+ const selectionStart = performance.now();
24574
24602
  if (!ranAllTests) {
24575
24603
  const selectionResult = await this.selector.selectAffectedTests();
24576
24604
  if (selectionResult.runAllTests) {
@@ -24584,19 +24612,26 @@ var init_pipeline = __esm({
24584
24612
  console.log(`[TestSchedulingPipeline] Selected ${selectedTests.length} affected tests`);
24585
24613
  }
24586
24614
  }
24615
+ const selectionMs = performance.now() - selectionStart;
24616
+ const executionStart = performance.now();
24587
24617
  let phaseResults;
24588
24618
  if (ranAllTests) {
24589
24619
  phaseResults = await this.scheduler.run();
24590
24620
  } else {
24591
24621
  phaseResults = await this.runWithSelectedTests(selectedTests);
24592
24622
  }
24623
+ const executionMs = performance.now() - executionStart;
24624
+ const analysisStart = performance.now();
24593
24625
  const flakyAnalysis = this.flakyTracker.analyze();
24594
24626
  if (this.config.flakyHistoryPath) {
24595
24627
  await saveFlakyTracker(this.flakyTracker, this.config.flakyHistoryPath);
24596
24628
  }
24629
+ const analysisMs = performance.now() - analysisStart;
24630
+ const reportingStart = performance.now();
24597
24631
  if (this.ciEnvironment.isCI) {
24598
24632
  await this.reporter.writeOutput(phaseResults);
24599
24633
  }
24634
+ const reportingMs = performance.now() - reportingStart;
24600
24635
  const totalDurationMs = Date.now() - startTime;
24601
24636
  return {
24602
24637
  phaseResults,
@@ -24604,7 +24639,8 @@ var init_pipeline = __esm({
24604
24639
  ranAllTests,
24605
24640
  flakyAnalysis,
24606
24641
  ciEnvironment: this.ciEnvironment,
24607
- totalDurationMs
24642
+ totalDurationMs,
24643
+ stepLatencies: { selectionMs, executionMs, analysisMs, reportingMs }
24608
24644
  };
24609
24645
  }
24610
24646
  /**
@@ -26769,6 +26805,7 @@ var init_sqlite_persistence = __esm({
26769
26805
  if (!this.db) throw new Error("Database not initialized");
26770
26806
  if (!query.trim()) return [];
26771
26807
  const sanitized = '"' + query.replace(/"/g, '""') + '"';
26808
+ const start = performance.now();
26772
26809
  try {
26773
26810
  const rows = this.db.prepare(`
26774
26811
  SELECT p.id, rank AS fts_score
@@ -26778,6 +26815,11 @@ var init_sqlite_persistence = __esm({
26778
26815
  ORDER BY rank
26779
26816
  LIMIT ?
26780
26817
  `).all(sanitized, limit);
26818
+ const elapsed = performance.now() - start;
26819
+ if (elapsed > 50) {
26820
+ console.warn(`[FTS5] searchFTS took ${elapsed.toFixed(1)}ms (results=${rows.length})`);
26821
+ }
26822
+ this._lastFtsLatencyMs = elapsed;
26781
26823
  const maxAbsScore = Math.max(...rows.map((r54) => Math.abs(r54.fts_score)), 1);
26782
26824
  return rows.map((r54) => ({
26783
26825
  id: r54.id,
@@ -26787,6 +26829,11 @@ var init_sqlite_persistence = __esm({
26787
26829
  return [];
26788
26830
  }
26789
26831
  }
26832
+ /** Last FTS5 search latency in ms, for instrumentation */
26833
+ _lastFtsLatencyMs = 0;
26834
+ get lastFtsLatencyMs() {
26835
+ return this._lastFtsLatencyMs;
26836
+ }
26790
26837
  /**
26791
26838
  * Ghost pattern check: find patterns in SQLite that have no embeddings.
26792
26839
  * Used by aqe_health to detect data integrity issues.
@@ -44853,7 +44900,7 @@ function resolveRequest(request) {
44853
44900
  import { execSync } from "child_process";
44854
44901
  import * as fs3 from "fs";
44855
44902
  import * as path3 from "path";
44856
- import * as os from "os";
44903
+ import * as os2 from "os";
44857
44904
  var COMPILE_COMMANDS = {
44858
44905
  typescript: { check: "npx tsc --noEmit --strict", fileExt: ".ts" },
44859
44906
  java: { check: "javac -d /dev/null", fileExt: ".java" },
@@ -44874,7 +44921,7 @@ var CompilationValidator = class {
44874
44921
  suggestions: [`No compilation check available for ${language} -- syntax validation skipped`]
44875
44922
  };
44876
44923
  }
44877
- const tmpDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "aqe-compile-"));
44924
+ const tmpDir = fs3.mkdtempSync(path3.join(os2.tmpdir(), "aqe-compile-"));
44878
44925
  const tmpFile = path3.join(tmpDir, `test_validation${config.fileExt}`);
44879
44926
  try {
44880
44927
  fs3.writeFileSync(tmpFile, code, "utf-8");
@@ -61036,8 +61083,8 @@ var TestGenerationCoordinator = class extends BaseDomainCoordinator {
61036
61083
  );
61037
61084
  console.log("[TestGenerationCoordinator] QEFlashAttention initialized for test-similarity");
61038
61085
  } catch (error) {
61039
- console.error("[TestGenerationCoordinator] Failed to initialize QEFlashAttention:", error);
61040
- throw new Error(`QEFlashAttention initialization failed: ${toErrorMessage(error)}`);
61086
+ console.warn("[TestGenerationCoordinator] QEFlashAttention unavailable (optional native module), continuing without it:", toErrorMessage(error));
61087
+ this.flashAttention = null;
61041
61088
  }
61042
61089
  }
61043
61090
  if (this.config.enableDecisionTransformer) {
@@ -61048,8 +61095,8 @@ var TestGenerationCoordinator = class extends BaseDomainCoordinator {
61048
61095
  });
61049
61096
  console.log("[TestGenerationCoordinator] DecisionTransformer created for test case selection");
61050
61097
  } catch (error) {
61051
- console.error("[TestGenerationCoordinator] Failed to create DecisionTransformer:", error);
61052
- throw new Error(`DecisionTransformer creation failed: ${toErrorMessage(error)}`);
61098
+ console.warn("[TestGenerationCoordinator] DecisionTransformer unavailable (optional native module), continuing without it:", toErrorMessage(error));
61099
+ this.decisionTransformer = null;
61053
61100
  }
61054
61101
  }
61055
61102
  this.subscribeToEvents();
@@ -76635,7 +76682,7 @@ function getCodeMetricsAnalyzer() {
76635
76682
  }
76636
76683
 
76637
76684
  // src/shared/metrics/system-metrics.ts
76638
- import * as os2 from "os";
76685
+ import * as os3 from "os";
76639
76686
  var SystemMetricsCollector = class {
76640
76687
  lastCpuUsage = null;
76641
76688
  lastCpuTime = 0;
@@ -76667,8 +76714,8 @@ var SystemMetricsCollector = class {
76667
76714
  * Collect CPU metrics
76668
76715
  */
76669
76716
  collectCpuMetrics() {
76670
- const cpus2 = os2.cpus();
76671
- const loadAverage = os2.loadavg();
76717
+ const cpus2 = os3.cpus();
76718
+ const loadAverage = os3.loadavg();
76672
76719
  let totalIdle = 0;
76673
76720
  let totalTick = 0;
76674
76721
  for (const cpu of cpus2) {
@@ -76688,8 +76735,8 @@ var SystemMetricsCollector = class {
76688
76735
  * Collect memory metrics
76689
76736
  */
76690
76737
  collectMemoryMetrics() {
76691
- const total = os2.totalmem();
76692
- const free = os2.freemem();
76738
+ const total = os3.totalmem();
76739
+ const free = os3.freemem();
76693
76740
  const used = total - free;
76694
76741
  const usage = used / total * 100;
76695
76742
  return {
@@ -76712,7 +76759,7 @@ var SystemMetricsCollector = class {
76712
76759
  const cpuPercent = totalCpuUs / elapsedUs * 100;
76713
76760
  this.lastCpuUsage = cpuUsage;
76714
76761
  this.lastCpuTime = now;
76715
- const totalMem = os2.totalmem();
76762
+ const totalMem = os3.totalmem();
76716
76763
  const memPercent = memUsage.rss / totalMem * 100;
76717
76764
  return {
76718
76765
  cpuUsage: Math.min(100, Math.round(cpuPercent * 100) / 100),
@@ -132739,6 +132786,9 @@ var TokenMetricsCollectorImpl = class _TokenMetricsCollectorImpl {
132739
132786
  if (config) {
132740
132787
  this.costConfig = { ...DEFAULT_COST_CONFIG, ...config };
132741
132788
  }
132789
+ this.initializeDb().catch(() => {
132790
+ });
132791
+ this.startAutoSave();
132742
132792
  }
132743
132793
  /**
132744
132794
  * Set cost configuration for token pricing.
@@ -132794,6 +132844,7 @@ var TokenMetricsCollectorImpl = class _TokenMetricsCollectorImpl {
132794
132844
  this.totalTokensSaved += tokensSaved;
132795
132845
  }
132796
132846
  this.isDirty = true;
132847
+ this.maybePersistToKv();
132797
132848
  }
132798
132849
  /**
132799
132850
  * Record pattern reuse for a task (tokens saved by skipping LLM call).
@@ -134400,7 +134451,7 @@ init_safe_json();
134400
134451
  import { randomUUID as randomUUID17 } from "crypto";
134401
134452
  import * as fs18 from "fs";
134402
134453
  import * as path19 from "path";
134403
- import * as os3 from "os";
134454
+ import * as os4 from "os";
134404
134455
  init_safe_json();
134405
134456
  init_error_utils();
134406
134457
  var DEFAULT_CONFIG_DIR = ".aqe";
@@ -134427,7 +134478,7 @@ var PersistentScheduler = class {
134427
134478
  * Prevents writing to arbitrary filesystem locations
134428
134479
  */
134429
134480
  validateSchedulesPath(schedulesPath) {
134430
- const homeDir = os3.homedir();
134481
+ const homeDir = os4.homedir();
134431
134482
  const resolvedPath = path19.resolve(schedulesPath);
134432
134483
  const resolvedHome = path19.resolve(homeDir);
134433
134484
  if (resolvedPath.startsWith(resolvedHome + path19.sep) || resolvedPath === resolvedHome) {
@@ -134438,7 +134489,7 @@ var PersistentScheduler = class {
134438
134489
  if (resolvedPath.startsWith(resolvedCwd + path19.sep) || resolvedPath === resolvedCwd) {
134439
134490
  return;
134440
134491
  }
134441
- const tmpDir = os3.tmpdir();
134492
+ const tmpDir = os4.tmpdir();
134442
134493
  const resolvedTmp = path19.resolve(tmpDir);
134443
134494
  if (resolvedPath.startsWith(resolvedTmp + path19.sep) || resolvedPath === resolvedTmp) {
134444
134495
  return;
@@ -134451,7 +134502,7 @@ var PersistentScheduler = class {
134451
134502
  * Get the default path for schedules.json
134452
134503
  */
134453
134504
  getDefaultSchedulesPath() {
134454
- const homeDir = os3.homedir();
134505
+ const homeDir = os4.homedir();
134455
134506
  return path19.join(homeDir, DEFAULT_CONFIG_DIR, SCHEDULES_FILE);
134456
134507
  }
134457
134508
  /**
@@ -134901,7 +134952,7 @@ var ALL_DOMAINS2 = [
134901
134952
  "enterprise-integration"
134902
134953
  ];
134903
134954
  function getAQEVersion() {
134904
- return true ? "3.7.15" : "3.0.0";
134955
+ return true ? "3.7.16" : "3.0.0";
134905
134956
  }
134906
134957
  function createDefaultConfig(projectName, projectRoot) {
134907
134958
  return {
@@ -143106,6 +143157,7 @@ function createProtocolHandler(cleanupAndExit2, ensureInitialized2) {
143106
143157
  }
143107
143158
 
143108
143159
  // src/cli/handlers/brain-handler.ts
143160
+ init_unified_memory();
143109
143161
  import path20 from "path";
143110
143162
  import chalk9 from "chalk";
143111
143163
 
@@ -144373,7 +144425,7 @@ Examples:
144373
144425
  }
144374
144426
  };
144375
144427
  function defaultDbPath() {
144376
- return path20.join(process.cwd(), ".agentic-qe", "memory.db");
144428
+ return path20.join(findProjectRoot(), ".agentic-qe", "memory.db");
144377
144429
  }
144378
144430
  function formatBytes(bytes) {
144379
144431
  if (bytes < 1024) return `${bytes} B`;
@@ -156941,6 +156993,7 @@ async function syncIncrementalToCloud(since, config) {
156941
156993
  init_esm_node();
156942
156994
  import * as fs29 from "fs";
156943
156995
  import * as path29 from "path";
156996
+ init_unified_memory();
156944
156997
  init_error_utils();
156945
156998
  init_logging();
156946
156999
  var logger21 = LoggerFactory.create("pull-agent");
@@ -157162,7 +157215,7 @@ var PullSyncAgent = class {
157162
157215
  if (this.config.targetDb) {
157163
157216
  return path29.resolve(this.config.targetDb);
157164
157217
  }
157165
- return path29.resolve(process.cwd(), ".agentic-qe/memory.db");
157218
+ return path29.resolve(findProjectRoot(), ".agentic-qe/memory.db");
157166
157219
  }
157167
157220
  backupLocalDb() {
157168
157221
  if (this.config.dryRun) return;
@@ -164260,7 +164313,7 @@ async function cleanupAndExit(code = 0) {
164260
164313
  process.exit(code);
164261
164314
  }
164262
164315
  var program = new Command21();
164263
- var VERSION = true ? "3.7.15" : "0.0.0-dev";
164316
+ var VERSION = true ? "3.7.16" : "0.0.0-dev";
164264
164317
  program.name("aqe").description("Agentic QE - Domain-Driven Quality Engineering").version(VERSION);
164265
164318
  var registry2 = createCommandRegistry(context, cleanupAndExit, ensureInitialized, ensureInitializedStrict);
164266
164319
  registry2.registerAll(program);
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import path from 'path';
8
8
  import chalk from 'chalk';
9
+ import { findProjectRoot } from '../../kernel/unified-memory.js';
9
10
  import { exportBrain, importBrain, brainInfo, witnessBackfill, } from '../brain-commands.js';
10
11
  // ============================================================================
11
12
  // Brain Handler
@@ -270,7 +271,7 @@ Examples:
270
271
  // Helpers
271
272
  // ============================================================================
272
273
  function defaultDbPath() {
273
- return path.join(process.cwd(), '.agentic-qe', 'memory.db');
274
+ return path.join(findProjectRoot(), '.agentic-qe', 'memory.db');
274
275
  }
275
276
  function formatBytes(bytes) {
276
277
  if (bytes < 1024)
@@ -140,8 +140,9 @@ export class TestGenerationCoordinator extends BaseDomainCoordinator {
140
140
  console.log('[TestGenerationCoordinator] QEFlashAttention initialized for test-similarity');
141
141
  }
142
142
  catch (error) {
143
- console.error('[TestGenerationCoordinator] Failed to initialize QEFlashAttention:', error);
144
- throw new Error(`QEFlashAttention initialization failed: ${toErrorMessage(error)}`);
143
+ // Graceful degradation: native module may not be available on all platforms
144
+ console.warn('[TestGenerationCoordinator] QEFlashAttention unavailable (optional native module), continuing without it:', toErrorMessage(error));
145
+ this.flashAttention = null;
145
146
  }
146
147
  }
147
148
  // Initialize Decision Transformer for test case selection
@@ -155,8 +156,9 @@ export class TestGenerationCoordinator extends BaseDomainCoordinator {
155
156
  console.log('[TestGenerationCoordinator] DecisionTransformer created for test case selection');
156
157
  }
157
158
  catch (error) {
158
- console.error('[TestGenerationCoordinator] Failed to create DecisionTransformer:', error);
159
- throw new Error(`DecisionTransformer creation failed: ${toErrorMessage(error)}`);
159
+ // Graceful degradation: native module may not be available on all platforms
160
+ console.warn('[TestGenerationCoordinator] DecisionTransformer unavailable (optional native module), continuing without it:', toErrorMessage(error));
161
+ this.decisionTransformer = null;
160
162
  }
161
163
  }
162
164
  // Subscribe to relevant events
@@ -176,4 +176,9 @@ export declare function createQualityFeedbackLoop(config?: Partial<FeedbackConfi
176
176
  * Create and initialize a quality feedback loop with DB persistence
177
177
  */
178
178
  export declare function createInitializedFeedbackLoop(config?: Partial<FeedbackConfig>): Promise<QualityFeedbackLoop>;
179
+ /**
180
+ * Get the initialized feedback loop singleton.
181
+ * Returns null if not yet initialized (call createInitializedFeedbackLoop first).
182
+ */
183
+ export declare function getQualityFeedbackLoop(): QualityFeedbackLoop | null;
179
184
  //# sourceMappingURL=feedback-loop.d.ts.map
@@ -295,6 +295,18 @@ export function createQualityFeedbackLoop(config) {
295
295
  export async function createInitializedFeedbackLoop(config) {
296
296
  const loop = new QualityFeedbackLoop(config);
297
297
  await loop.initialize();
298
+ _feedbackLoopInstance = loop;
298
299
  return loop;
299
300
  }
301
+ // ============================================================================
302
+ // Singleton accessor for cross-module integration
303
+ // ============================================================================
304
+ let _feedbackLoopInstance = null;
305
+ /**
306
+ * Get the initialized feedback loop singleton.
307
+ * Returns null if not yet initialized (call createInitializedFeedbackLoop first).
308
+ */
309
+ export function getQualityFeedbackLoop() {
310
+ return _feedbackLoopInstance;
311
+ }
300
312
  //# sourceMappingURL=feedback-loop.js.map
@@ -11,7 +11,7 @@ export { CoverageLearner, createCoverageLearner, } from './coverage-learner.js';
11
11
  export { QualityScoreCalculator, createQualityScoreCalculator, } from './quality-score-calculator.js';
12
12
  export { PatternPromotionManager, createPatternPromotionManager, } from './pattern-promotion.js';
13
13
  export type { PatternMetrics } from './pattern-promotion.js';
14
- export { QualityFeedbackLoop, createQualityFeedbackLoop, createInitializedFeedbackLoop, } from './feedback-loop.js';
14
+ export { QualityFeedbackLoop, createQualityFeedbackLoop, createInitializedFeedbackLoop, getQualityFeedbackLoop, } from './feedback-loop.js';
15
15
  export type { FeedbackLoopStats, RoutingAnalysis, RoutingOutcomeInput, } from './feedback-loop.js';
16
16
  export { RoutingFeedbackCollector, createRoutingFeedbackCollector, } from '../routing/routing-feedback.js';
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -14,7 +14,7 @@ export { QualityScoreCalculator, createQualityScoreCalculator, } from './quality
14
14
  // Pattern Promotion Manager
15
15
  export { PatternPromotionManager, createPatternPromotionManager, } from './pattern-promotion.js';
16
16
  // Main Feedback Loop Integrator
17
- export { QualityFeedbackLoop, createQualityFeedbackLoop, createInitializedFeedbackLoop, } from './feedback-loop.js';
17
+ export { QualityFeedbackLoop, createQualityFeedbackLoop, createInitializedFeedbackLoop, getQualityFeedbackLoop, } from './feedback-loop.js';
18
18
  // Re-export routing feedback for direct access
19
19
  export { RoutingFeedbackCollector, createRoutingFeedbackCollector, } from '../routing/routing-feedback.js';
20
20
  //# sourceMappingURL=index.js.map
@@ -31,6 +31,9 @@ export declare class HnswAdapter implements IHnswIndexProvider {
31
31
  constructor(name: string, config?: Partial<HnswConfig>);
32
32
  add(id: number, vector: Float32Array, metadata?: Record<string, unknown>): void;
33
33
  search(query: Float32Array, k: number): SearchResult[];
34
+ /** Last search latency in ms, for instrumentation */
35
+ private _lastSearchLatencyMs;
36
+ get lastSearchLatencyMs(): number;
34
37
  remove(id: number): boolean;
35
38
  size(): number;
36
39
  dimensions(): number;
@@ -76,8 +76,18 @@ export class HnswAdapter {
76
76
  this.backend.add(id, vector, metadata);
77
77
  }
78
78
  search(query, k) {
79
- return this.backend.search(query, k);
79
+ const start = performance.now();
80
+ const results = this.backend.search(query, k);
81
+ const elapsed = performance.now() - start;
82
+ if (elapsed > 50) {
83
+ console.warn(`[HNSW] search took ${elapsed.toFixed(1)}ms (k=${k}, results=${results.length})`);
84
+ }
85
+ this._lastSearchLatencyMs = elapsed;
86
+ return results;
80
87
  }
88
+ /** Last search latency in ms, for instrumentation */
89
+ _lastSearchLatencyMs = 0;
90
+ get lastSearchLatencyMs() { return this._lastSearchLatencyMs; }
81
91
  remove(id) {
82
92
  return this.backend.remove(id);
83
93
  }
@@ -17,6 +17,6 @@ export declare const QE_PATTERNS_SCHEMA = "\n -- QE Patterns table (unified fro
17
17
  export declare const MINCUT_SCHEMA = "\n -- MinCut Graph Snapshots (ADR-047)\n CREATE TABLE IF NOT EXISTS mincut_snapshots (\n id TEXT PRIMARY KEY,\n timestamp TEXT NOT NULL DEFAULT (datetime('now')),\n vertex_count INTEGER NOT NULL,\n edge_count INTEGER NOT NULL,\n total_weight REAL NOT NULL DEFAULT 0.0,\n is_connected INTEGER NOT NULL DEFAULT 1,\n component_count INTEGER NOT NULL DEFAULT 1,\n vertices_json TEXT NOT NULL,\n edges_json TEXT NOT NULL,\n created_at TEXT DEFAULT (datetime('now'))\n );\n\n -- MinCut History (time-series MinCut values)\n CREATE TABLE IF NOT EXISTS mincut_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp TEXT NOT NULL DEFAULT (datetime('now')),\n mincut_value REAL NOT NULL,\n vertex_count INTEGER NOT NULL,\n edge_count INTEGER NOT NULL,\n algorithm TEXT NOT NULL DEFAULT 'weighted-degree',\n duration_ms INTEGER,\n snapshot_id TEXT,\n created_at TEXT DEFAULT (datetime('now')),\n FOREIGN KEY (snapshot_id) REFERENCES mincut_snapshots(id) ON DELETE SET NULL\n );\n\n -- MinCut Weak Vertices (detected bottlenecks)\n CREATE TABLE IF NOT EXISTS mincut_weak_vertices (\n id TEXT PRIMARY KEY,\n vertex_id TEXT NOT NULL,\n weighted_degree REAL NOT NULL,\n risk_score REAL NOT NULL,\n reason TEXT NOT NULL,\n domain TEXT,\n vertex_type TEXT NOT NULL,\n suggestions_json TEXT,\n detected_at TEXT NOT NULL DEFAULT (datetime('now')),\n resolved_at TEXT,\n snapshot_id TEXT,\n created_at TEXT DEFAULT (datetime('now')),\n FOREIGN KEY (snapshot_id) REFERENCES mincut_snapshots(id) ON DELETE SET NULL\n );\n\n -- MinCut Alerts\n CREATE TABLE IF NOT EXISTS mincut_alerts (\n id TEXT PRIMARY KEY,\n severity TEXT NOT NULL,\n message TEXT NOT NULL,\n mincut_value REAL NOT NULL,\n threshold REAL NOT NULL,\n affected_vertices_json TEXT,\n remediations_json TEXT,\n acknowledged INTEGER DEFAULT 0,\n acknowledged_at TEXT,\n acknowledged_by TEXT,\n timestamp TEXT NOT NULL DEFAULT (datetime('now')),\n created_at TEXT DEFAULT (datetime('now'))\n );\n\n -- MinCut Healing Actions (self-healing history)\n CREATE TABLE IF NOT EXISTS mincut_healing_actions (\n id TEXT PRIMARY KEY,\n action_type TEXT NOT NULL,\n action_params_json TEXT NOT NULL,\n success INTEGER NOT NULL,\n mincut_before REAL NOT NULL,\n mincut_after REAL NOT NULL,\n improvement REAL NOT NULL DEFAULT 0.0,\n error_message TEXT,\n duration_ms INTEGER NOT NULL,\n triggered_by TEXT,\n snapshot_before_id TEXT,\n snapshot_after_id TEXT,\n created_at TEXT DEFAULT (datetime('now')),\n FOREIGN KEY (snapshot_before_id) REFERENCES mincut_snapshots(id) ON DELETE SET NULL,\n FOREIGN KEY (snapshot_after_id) REFERENCES mincut_snapshots(id) ON DELETE SET NULL\n );\n\n -- MinCut Strange Loop Observations (P1: self-organizing)\n CREATE TABLE IF NOT EXISTS mincut_observations (\n id TEXT PRIMARY KEY,\n iteration INTEGER NOT NULL,\n mincut_value REAL NOT NULL,\n weak_vertex_count INTEGER NOT NULL DEFAULT 0,\n weak_vertices_json TEXT,\n snapshot_id TEXT,\n prediction_json TEXT,\n actual_vs_predicted_diff REAL,\n timestamp TEXT NOT NULL DEFAULT (datetime('now')),\n FOREIGN KEY (snapshot_id) REFERENCES mincut_snapshots(id) ON DELETE SET NULL\n );\n\n -- MinCut Indexes\n CREATE INDEX IF NOT EXISTS idx_mincut_history_timestamp ON mincut_history(timestamp DESC);\n CREATE INDEX IF NOT EXISTS idx_mincut_history_value ON mincut_history(mincut_value);\n CREATE INDEX IF NOT EXISTS idx_mincut_weak_vertex ON mincut_weak_vertices(vertex_id);\n CREATE INDEX IF NOT EXISTS idx_mincut_weak_risk ON mincut_weak_vertices(risk_score DESC);\n CREATE INDEX IF NOT EXISTS idx_mincut_weak_resolved ON mincut_weak_vertices(resolved_at);\n CREATE INDEX IF NOT EXISTS idx_mincut_alerts_severity ON mincut_alerts(severity);\n CREATE INDEX IF NOT EXISTS idx_mincut_alerts_ack ON mincut_alerts(acknowledged);\n CREATE INDEX IF NOT EXISTS idx_mincut_healing_type ON mincut_healing_actions(action_type);\n CREATE INDEX IF NOT EXISTS idx_mincut_healing_success ON mincut_healing_actions(success);\n CREATE INDEX IF NOT EXISTS idx_mincut_observations_iter ON mincut_observations(iteration);\n";
18
18
  export declare const SONA_PATTERNS_SCHEMA = "\n -- SONA Patterns table (ADR-046: Pattern Persistence for Neural Backbone)\n CREATE TABLE IF NOT EXISTS sona_patterns (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n domain TEXT NOT NULL,\n state_embedding BLOB,\n action_embedding BLOB,\n action_type TEXT NOT NULL,\n action_value TEXT,\n outcome_reward REAL NOT NULL DEFAULT 0.0,\n outcome_success INTEGER NOT NULL DEFAULT 0,\n outcome_quality REAL NOT NULL DEFAULT 0.0,\n confidence REAL DEFAULT 0.5,\n usage_count INTEGER DEFAULT 0,\n success_count INTEGER DEFAULT 0,\n failure_count INTEGER DEFAULT 0,\n metadata TEXT,\n created_at TEXT DEFAULT (datetime('now')),\n updated_at TEXT DEFAULT (datetime('now')),\n last_used_at TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_sona_patterns_type ON sona_patterns(type);\n CREATE INDEX IF NOT EXISTS idx_sona_patterns_domain ON sona_patterns(domain);\n CREATE INDEX IF NOT EXISTS idx_sona_patterns_confidence ON sona_patterns(confidence DESC);\n CREATE INDEX IF NOT EXISTS idx_sona_patterns_updated ON sona_patterns(updated_at DESC);\n";
19
19
  export declare const WITNESS_CHAIN_SCHEMA = "\n -- Witness Chain (ADR-070: Cryptographic audit trail for QE decisions)\n CREATE TABLE IF NOT EXISTS witness_chain (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n prev_hash TEXT NOT NULL,\n action_hash TEXT NOT NULL,\n action_type TEXT NOT NULL,\n action_data TEXT,\n timestamp TEXT NOT NULL,\n actor TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_witness_action_type ON witness_chain(action_type);\n CREATE INDEX IF NOT EXISTS idx_witness_timestamp ON witness_chain(timestamp);\n";
20
- export declare const FEEDBACK_SCHEMA = "\n -- Test outcomes (ADR-023: Quality Feedback Loop)\n CREATE TABLE IF NOT EXISTS test_outcomes (\n id TEXT PRIMARY KEY,\n test_id TEXT NOT NULL,\n test_name TEXT NOT NULL,\n generated_by TEXT NOT NULL,\n pattern_id TEXT,\n framework TEXT NOT NULL,\n language TEXT NOT NULL,\n domain TEXT NOT NULL,\n passed INTEGER NOT NULL,\n error_message TEXT,\n coverage_lines REAL DEFAULT 0,\n coverage_branches REAL DEFAULT 0,\n coverage_functions REAL DEFAULT 0,\n mutation_score REAL,\n execution_time_ms REAL NOT NULL,\n flaky INTEGER DEFAULT 0,\n flakiness_score REAL,\n maintainability_score REAL NOT NULL,\n complexity REAL,\n lines_of_code INTEGER,\n assertion_count INTEGER,\n file_path TEXT,\n source_file_path TEXT,\n metadata_json TEXT,\n created_at TEXT DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_pattern ON test_outcomes(pattern_id);\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_agent ON test_outcomes(generated_by);\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_domain ON test_outcomes(domain);\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_created ON test_outcomes(created_at);\n\n -- Routing outcomes (ADR-022: Adaptive QE Agent Routing)\n CREATE TABLE IF NOT EXISTS routing_outcomes (\n id TEXT PRIMARY KEY,\n task_json TEXT NOT NULL,\n decision_json TEXT NOT NULL,\n used_agent TEXT NOT NULL,\n followed_recommendation INTEGER NOT NULL,\n success INTEGER NOT NULL,\n quality_score REAL NOT NULL,\n duration_ms REAL NOT NULL,\n error TEXT,\n created_at TEXT DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS idx_routing_outcomes_agent ON routing_outcomes(used_agent);\n CREATE INDEX IF NOT EXISTS idx_routing_outcomes_created ON routing_outcomes(created_at);\n\n -- Coverage sessions (ADR-023: Coverage Learning)\n CREATE TABLE IF NOT EXISTS coverage_sessions (\n id TEXT PRIMARY KEY,\n target_path TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n technique TEXT NOT NULL,\n before_lines REAL DEFAULT 0,\n before_branches REAL DEFAULT 0,\n before_functions REAL DEFAULT 0,\n after_lines REAL DEFAULT 0,\n after_branches REAL DEFAULT 0,\n after_functions REAL DEFAULT 0,\n tests_generated INTEGER DEFAULT 0,\n tests_passed INTEGER DEFAULT 0,\n gaps_json TEXT,\n duration_ms REAL NOT NULL,\n started_at TEXT NOT NULL,\n completed_at TEXT NOT NULL,\n context_json TEXT,\n created_at TEXT DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS idx_coverage_sessions_technique ON coverage_sessions(technique);\n CREATE INDEX IF NOT EXISTS idx_coverage_sessions_agent ON coverage_sessions(agent_id);\n CREATE INDEX IF NOT EXISTS idx_coverage_sessions_created ON coverage_sessions(created_at);\n";
20
+ export declare const FEEDBACK_SCHEMA = "\n -- Test outcomes (ADR-023: Quality Feedback Loop)\n CREATE TABLE IF NOT EXISTS test_outcomes (\n id TEXT PRIMARY KEY,\n test_id TEXT NOT NULL,\n test_name TEXT NOT NULL,\n generated_by TEXT NOT NULL,\n pattern_id TEXT,\n framework TEXT NOT NULL,\n language TEXT NOT NULL,\n domain TEXT NOT NULL,\n passed INTEGER NOT NULL,\n error_message TEXT,\n coverage_lines REAL DEFAULT 0,\n coverage_branches REAL DEFAULT 0,\n coverage_functions REAL DEFAULT 0,\n mutation_score REAL,\n execution_time_ms REAL NOT NULL,\n flaky INTEGER DEFAULT 0,\n flakiness_score REAL,\n maintainability_score REAL NOT NULL,\n complexity REAL,\n lines_of_code INTEGER,\n assertion_count INTEGER,\n file_path TEXT,\n source_file_path TEXT,\n metadata_json TEXT,\n created_at TEXT DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_pattern ON test_outcomes(pattern_id);\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_agent ON test_outcomes(generated_by);\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_domain ON test_outcomes(domain);\n CREATE INDEX IF NOT EXISTS idx_test_outcomes_created ON test_outcomes(created_at);\n\n -- Routing outcomes (ADR-022: Adaptive QE Agent Routing)\n CREATE TABLE IF NOT EXISTS routing_outcomes (\n id TEXT PRIMARY KEY,\n task_json TEXT NOT NULL,\n decision_json TEXT NOT NULL,\n used_agent TEXT NOT NULL,\n followed_recommendation INTEGER NOT NULL,\n success INTEGER NOT NULL,\n quality_score REAL NOT NULL,\n duration_ms REAL NOT NULL,\n error TEXT,\n model_tier TEXT,\n created_at TEXT DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS idx_routing_outcomes_agent ON routing_outcomes(used_agent);\n CREATE INDEX IF NOT EXISTS idx_routing_outcomes_created ON routing_outcomes(created_at);\n CREATE INDEX IF NOT EXISTS idx_routing_outcomes_tier ON routing_outcomes(model_tier);\n\n -- Coverage sessions (ADR-023: Coverage Learning)\n CREATE TABLE IF NOT EXISTS coverage_sessions (\n id TEXT PRIMARY KEY,\n target_path TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n technique TEXT NOT NULL,\n before_lines REAL DEFAULT 0,\n before_branches REAL DEFAULT 0,\n before_functions REAL DEFAULT 0,\n after_lines REAL DEFAULT 0,\n after_branches REAL DEFAULT 0,\n after_functions REAL DEFAULT 0,\n tests_generated INTEGER DEFAULT 0,\n tests_passed INTEGER DEFAULT 0,\n gaps_json TEXT,\n duration_ms REAL NOT NULL,\n started_at TEXT NOT NULL,\n completed_at TEXT NOT NULL,\n context_json TEXT,\n created_at TEXT DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS idx_coverage_sessions_technique ON coverage_sessions(technique);\n CREATE INDEX IF NOT EXISTS idx_coverage_sessions_agent ON coverage_sessions(agent_id);\n CREATE INDEX IF NOT EXISTS idx_coverage_sessions_created ON coverage_sessions(created_at);\n";
21
21
  export declare const STATS_TABLES: string[];
22
22
  //# sourceMappingURL=unified-memory-schemas.d.ts.map
@@ -551,10 +551,12 @@ export const FEEDBACK_SCHEMA = `
551
551
  quality_score REAL NOT NULL,
552
552
  duration_ms REAL NOT NULL,
553
553
  error TEXT,
554
+ model_tier TEXT,
554
555
  created_at TEXT DEFAULT (datetime('now'))
555
556
  );
556
557
  CREATE INDEX IF NOT EXISTS idx_routing_outcomes_agent ON routing_outcomes(used_agent);
557
558
  CREATE INDEX IF NOT EXISTS idx_routing_outcomes_created ON routing_outcomes(created_at);
559
+ CREATE INDEX IF NOT EXISTS idx_routing_outcomes_tier ON routing_outcomes(model_tier);
558
560
 
559
561
  -- Coverage sessions (ADR-023: Coverage Learning)
560
562
  CREATE TABLE IF NOT EXISTS coverage_sessions (
@@ -25,6 +25,7 @@ import Database from 'better-sqlite3';
25
25
  import { safeJsonParse } from '../shared/safe-json.js';
26
26
  import { toErrorMessage } from '../shared/error-utils.js';
27
27
  import * as fs from 'fs';
28
+ import * as os from 'os';
28
29
  import * as path from 'path';
29
30
  import { MEMORY_CONSTANTS } from './constants.js';
30
31
  import { LoggerFactory } from '../logging/index.js';
@@ -207,6 +208,11 @@ export class UnifiedMemoryManager {
207
208
  UnifiedMemoryManager.instance = null;
208
209
  }
209
210
  UnifiedMemoryManager.instancePromise = null;
211
+ // CRITICAL: Clear the cached project root so the next getInstance() call
212
+ // re-reads AQE_PROJECT_ROOT from the environment. Without this, tests that
213
+ // call resetUnifiedMemory() would still use the stale cached path, potentially
214
+ // opening the production database instead of the test database.
215
+ clearProjectRootCache();
210
216
  }
211
217
  async initialize() {
212
218
  if (this.initialized)
@@ -220,6 +226,25 @@ export class UnifiedMemoryManager {
220
226
  if (this.initialized)
221
227
  return;
222
228
  try {
229
+ // SAFETY: Detect test processes trying to open the production DB.
230
+ // If AQE_PROJECT_ROOT is set to a temp dir (vitest isolation) but the
231
+ // dbPath resolves to the REAL project's .agentic-qe/memory.db, redirect.
232
+ // Only redirect production paths — leave explicitly-set temp paths alone.
233
+ const envRoot = process.env.AQE_PROJECT_ROOT;
234
+ if (envRoot) {
235
+ const resolvedPath = path.resolve(this.config.dbPath);
236
+ const resolvedRoot = path.resolve(envRoot);
237
+ // Only redirect if path points OUTSIDE the env root AND into a real
238
+ // .agentic-qe directory (not another temp test dir)
239
+ if (!resolvedPath.startsWith(resolvedRoot) &&
240
+ !resolvedPath.startsWith(os.tmpdir()) &&
241
+ resolvedPath.includes('.agentic-qe')) {
242
+ console.error(`[UnifiedMemory] WARNING: DB path "${this.config.dbPath}" points to a ` +
243
+ `production .agentic-qe/ while AQE_PROJECT_ROOT="${envRoot}". ` +
244
+ `Redirecting to test-safe path.`);
245
+ this.config.dbPath = path.join(envRoot, '.agentic-qe', 'memory.db');
246
+ }
247
+ }
223
248
  const dir = path.dirname(this.config.dbPath);
224
249
  if (!fs.existsSync(dir)) {
225
250
  fs.mkdirSync(dir, { recursive: true });
@@ -268,11 +268,35 @@ async function persistExperience(context, outcome) {
268
268
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
269
269
  `);
270
270
  stmt.run(outcome.id, context.task, context.agent, context.domain, outcome.success ? 1 : 0, outcome.quality, outcome.durationMs, context.modelTier || null, safeJsonStringify(context.routing), safeJsonStringify(outcome.steps), safeJsonStringify(outcome.result), outcome.error || null, context.startedAt.toISOString(), 'middleware');
271
+ // Fire-and-forget: compute and store embedding for this experience
272
+ computeExperienceEmbedding(db, outcome.id, context.task, context.domain).catch(() => { });
271
273
  }
272
274
  catch (error) {
273
275
  console.error('[ExperienceCaptureMiddleware] Failed to persist experience:', error);
274
276
  }
275
277
  }
278
+ /**
279
+ * Compute and store embedding for a captured experience (async, non-blocking).
280
+ * Uses the shared ONNX transformer model (all-MiniLM-L6-v2, 384-dim).
281
+ */
282
+ async function computeExperienceEmbedding(db, experienceId, task, domain) {
283
+ try {
284
+ const { computeRealEmbedding } = await import('./real-embeddings.js');
285
+ const text = `${domain}: ${task}`.slice(0, 512); // Cap input length
286
+ const embedding = await computeRealEmbedding(text);
287
+ if (!embedding || embedding.length === 0)
288
+ return;
289
+ const buffer = Buffer.from(new Float32Array(embedding).buffer);
290
+ db.prepare(`
291
+ UPDATE captured_experiences
292
+ SET embedding = ?, embedding_dimension = ?
293
+ WHERE id = ? AND embedding IS NULL
294
+ `).run(buffer, embedding.length, experienceId);
295
+ }
296
+ catch {
297
+ // Non-critical — embedding will be backfilled later if needed
298
+ }
299
+ }
276
300
  /**
277
301
  * Persist to sona_patterns for learning integration
278
302
  */
@@ -93,6 +93,9 @@ export declare class SQLitePatternStore {
93
93
  id: string;
94
94
  ftsScore: number;
95
95
  }>;
96
+ /** Last FTS5 search latency in ms, for instrumentation */
97
+ private _lastFtsLatencyMs;
98
+ get lastFtsLatencyMs(): number;
96
99
  /**
97
100
  * Ghost pattern check: find patterns in SQLite that have no embeddings.
98
101
  * Used by aqe_health to detect data integrity issues.