agentic-qe 3.8.5 → 3.8.7

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.
Files changed (67) hide show
  1. package/.claude/skills/skills-manifest.json +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/assets/governance/constitution.md +1 -1
  4. package/assets/governance/shards/chaos-resilience.shard.md +1 -1
  5. package/assets/governance/shards/code-intelligence.shard.md +1 -1
  6. package/assets/governance/shards/contract-testing.shard.md +1 -1
  7. package/assets/governance/shards/coverage-analysis.shard.md +1 -1
  8. package/assets/governance/shards/defect-intelligence.shard.md +1 -1
  9. package/assets/governance/shards/learning-optimization.shard.md +1 -1
  10. package/assets/governance/shards/quality-assessment.shard.md +1 -1
  11. package/assets/governance/shards/requirements-validation.shard.md +1 -1
  12. package/assets/governance/shards/security-compliance.shard.md +1 -1
  13. package/assets/governance/shards/test-execution.shard.md +1 -1
  14. package/assets/governance/shards/test-generation.shard.md +1 -1
  15. package/assets/governance/shards/visual-accessibility.shard.md +1 -1
  16. package/dist/cli/bundle.js +715 -643
  17. package/dist/cli/command-registry.js +3 -1
  18. package/dist/cli/completions/index.d.ts +17 -0
  19. package/dist/cli/completions/index.js +49 -1
  20. package/dist/cli/handlers/hypergraph-handler.d.ts +27 -0
  21. package/dist/cli/handlers/hypergraph-handler.js +248 -0
  22. package/dist/cli/handlers/index.d.ts +1 -0
  23. package/dist/cli/handlers/index.js +1 -0
  24. package/dist/coordination/mincut/phase-executor.d.ts +27 -0
  25. package/dist/coordination/mincut/phase-executor.js +70 -0
  26. package/dist/coordination/mincut/time-crystal-analysis.d.ts +35 -0
  27. package/dist/coordination/mincut/time-crystal-analysis.js +237 -0
  28. package/dist/coordination/mincut/time-crystal-persistence.d.ts +35 -0
  29. package/dist/coordination/mincut/time-crystal-persistence.js +81 -0
  30. package/dist/coordination/mincut/time-crystal-scheduling.d.ts +34 -0
  31. package/dist/coordination/mincut/time-crystal-scheduling.js +213 -0
  32. package/dist/coordination/mincut/time-crystal-types.d.ts +278 -0
  33. package/dist/coordination/mincut/time-crystal-types.js +67 -0
  34. package/dist/coordination/mincut/time-crystal.d.ts +8 -438
  35. package/dist/coordination/mincut/time-crystal.js +87 -905
  36. package/dist/coordination/protocols/code-intelligence-index.js +2 -11
  37. package/dist/domains/code-intelligence/coordinator-hypergraph.js +1 -1
  38. package/dist/domains/code-intelligence/coordinator.d.ts +5 -0
  39. package/dist/domains/code-intelligence/coordinator.js +35 -3
  40. package/dist/init/phases/06-code-intelligence.d.ts +8 -3
  41. package/dist/init/phases/06-code-intelligence.js +70 -32
  42. package/dist/learning/agent-routing.d.ts +53 -0
  43. package/dist/learning/agent-routing.js +142 -0
  44. package/dist/learning/embedding-utils.d.ts +34 -0
  45. package/dist/learning/embedding-utils.js +95 -0
  46. package/dist/learning/pattern-promotion.d.ts +63 -0
  47. package/dist/learning/pattern-promotion.js +187 -0
  48. package/dist/learning/pretrained-patterns.d.ts +14 -0
  49. package/dist/learning/pretrained-patterns.js +726 -0
  50. package/dist/learning/qe-reasoning-bank-types.d.ts +174 -0
  51. package/dist/learning/qe-reasoning-bank-types.js +24 -0
  52. package/dist/learning/qe-reasoning-bank.d.ts +9 -192
  53. package/dist/learning/qe-reasoning-bank.js +48 -1093
  54. package/dist/mcp/bundle.js +1084 -1083
  55. package/dist/mcp/handlers/hypergraph-handler.d.ts +27 -0
  56. package/dist/mcp/handlers/hypergraph-handler.js +140 -0
  57. package/dist/mcp/handlers/index.d.ts +1 -0
  58. package/dist/mcp/handlers/index.js +2 -0
  59. package/dist/mcp/server.js +19 -0
  60. package/dist/mcp/tool-scoping.js +5 -0
  61. package/dist/shared/code-index-extractor.d.ts +23 -0
  62. package/dist/shared/code-index-extractor.js +101 -0
  63. package/dist/shared/security/command-validator.js +2 -2
  64. package/dist/shared/security/input-sanitizer.js +1 -1
  65. package/dist/shared/security/path-traversal-validator.js +1 -1
  66. package/dist/shared/security/regex-safety-validator.js +7 -7
  67. package/package.json +1 -1
@@ -17,28 +17,17 @@ import { LoggerFactory } from '../logging/index.js';
17
17
  const logger = LoggerFactory.create('QEReasoningBank');
18
18
  import { ok, err } from '../shared/types/index.js';
19
19
  import { toError, toErrorMessage } from '../shared/error-utils.js';
20
- import { detectQEDomains, QE_DOMAIN_LIST, shouldPromotePattern, } from './qe-patterns.js';
20
+ import { detectQEDomains, QE_DOMAIN_LIST, } from './qe-patterns.js';
21
21
  import { getGuidance, getCombinedGuidance, generateGuidanceContext, checkAntiPatterns, } from './qe-guidance.js';
22
22
  import { createPatternStore, } from './pattern-store.js';
23
23
  import { createSQLitePatternStore, } from './sqlite-persistence.js';
24
24
  import { getWitnessChain } from '../audit/witness-chain.js';
25
- /**
26
- * Default configuration
27
- */
28
- export const DEFAULT_QE_REASONING_BANK_CONFIG = {
29
- enableLearning: true,
30
- enableGuidance: true,
31
- enableRouting: true,
32
- embeddingDimension: 384, // Native all-MiniLM-L6-v2 dimension — no interpolation needed
33
- useONNXEmbeddings: true, // ADR-051: Enable ONNX embeddings by default
34
- maxRoutingCandidates: 10,
35
- routingWeights: {
36
- similarity: 0.3,
37
- performance: 0.4,
38
- capabilities: 0.3,
39
- },
40
- coherenceThreshold: 0.4, // ADR-052: Coherence gate threshold
41
- };
25
+ // Import extracted modules
26
+ import { DEFAULT_QE_REASONING_BANK_CONFIG } from './qe-reasoning-bank-types.js';
27
+ import { PRETRAINED_PATTERNS } from './pretrained-patterns.js';
28
+ import { AGENT_CAPABILITIES, calculateAgentScores, } from './agent-routing.js';
29
+ import { resizeEmbedding, hashEmbedding } from './embedding-utils.js';
30
+ import { checkPatternPromotionWithCoherence, promotePattern as promotePatternFn, seedCrossDomainPatterns as seedCrossDomainPatternsFn, } from './pattern-promotion.js';
42
31
  // ============================================================================
43
32
  // QEReasoningBank Implementation
44
33
  // ============================================================================
@@ -103,64 +92,6 @@ export class QEReasoningBank {
103
92
  learningOutcomes: 0,
104
93
  successfulOutcomes: 0,
105
94
  };
106
- // Agent capability mapping (QE agents)
107
- agentCapabilities = {
108
- 'qe-test-generator': {
109
- domains: ['test-generation'],
110
- capabilities: ['test-generation', 'tdd', 'bdd', 'unit-test', 'integration-test'],
111
- performanceScore: 0.85,
112
- },
113
- 'qe-coverage-analyzer': {
114
- domains: ['coverage-analysis'],
115
- capabilities: ['coverage-analysis', 'gap-detection', 'risk-scoring'],
116
- performanceScore: 0.92,
117
- },
118
- 'qe-coverage-specialist': {
119
- domains: ['coverage-analysis'],
120
- capabilities: ['sublinear-analysis', 'branch-coverage', 'mutation-testing'],
121
- performanceScore: 0.88,
122
- },
123
- 'qe-test-architect': {
124
- domains: ['test-generation', 'coverage-analysis'],
125
- capabilities: ['test-strategy', 'test-pyramid', 'architecture'],
126
- performanceScore: 0.9,
127
- },
128
- 'qe-api-contract-validator': {
129
- domains: ['contract-testing'],
130
- capabilities: ['contract-testing', 'openapi', 'graphql', 'pact'],
131
- performanceScore: 0.87,
132
- },
133
- 'qe-security-auditor': {
134
- domains: ['security-compliance'],
135
- capabilities: ['sast', 'dast', 'vulnerability', 'owasp'],
136
- performanceScore: 0.82,
137
- },
138
- 'qe-visual-tester': {
139
- domains: ['visual-accessibility'],
140
- capabilities: ['screenshot', 'visual-regression', 'percy', 'chromatic'],
141
- performanceScore: 0.8,
142
- },
143
- 'qe-a11y-ally': {
144
- domains: ['visual-accessibility'],
145
- capabilities: ['wcag', 'aria', 'screen-reader', 'contrast'],
146
- performanceScore: 0.85,
147
- },
148
- 'qe-performance-tester': {
149
- domains: ['chaos-resilience'],
150
- capabilities: ['load-testing', 'stress-testing', 'k6', 'artillery'],
151
- performanceScore: 0.83,
152
- },
153
- 'qe-flaky-investigator': {
154
- domains: ['test-execution'],
155
- capabilities: ['flaky-detection', 'test-stability', 'retry'],
156
- performanceScore: 0.78,
157
- },
158
- 'qe-chaos-engineer': {
159
- domains: ['chaos-resilience'],
160
- capabilities: ['chaos-testing', 'resilience', 'fault-injection'],
161
- performanceScore: 0.75,
162
- },
163
- };
164
95
  constructor(memory, eventBus, config = {}, coherenceService) {
165
96
  this.memory = memory;
166
97
  this.eventBus = eventBus;
@@ -225,719 +156,8 @@ export class QEReasoningBank {
225
156
  logger.info('Found existing patterns', { totalPatterns: stats.totalPatterns });
226
157
  return;
227
158
  }
228
- // Add foundational patterns
229
- const foundationalPatterns = [
230
- {
231
- patternType: 'test-template',
232
- name: 'AAA Unit Test',
233
- description: 'Arrange-Act-Assert pattern for clear, maintainable unit tests',
234
- template: {
235
- type: 'code',
236
- content: `describe('{{className}}', () => {
237
- describe('{{methodName}}', () => {
238
- it('should {{expectedBehavior}}', {{async}} () => {
239
- // Arrange
240
- {{arrangeCode}}
241
-
242
- // Act
243
- {{actCode}}
244
-
245
- // Assert
246
- {{assertCode}}
247
- });
248
- });
249
- });`,
250
- variables: [
251
- { name: 'className', type: 'string', required: true, description: 'Class under test' },
252
- { name: 'methodName', type: 'string', required: true, description: 'Method under test' },
253
- { name: 'expectedBehavior', type: 'string', required: true, description: 'Expected behavior in plain English' },
254
- { name: 'async', type: 'string', required: false, defaultValue: '', description: 'async keyword if needed' },
255
- { name: 'arrangeCode', type: 'code', required: true, description: 'Setup code' },
256
- { name: 'actCode', type: 'code', required: true, description: 'Action code' },
257
- { name: 'assertCode', type: 'code', required: true, description: 'Assertion code' },
258
- ],
259
- },
260
- context: {
261
- testType: 'unit',
262
- tags: ['unit-test', 'aaa', 'arrange-act-assert', 'best-practice'],
263
- },
264
- },
265
- {
266
- patternType: 'mock-pattern',
267
- name: 'Dependency Mock',
268
- description: 'Pattern for mocking external dependencies in tests',
269
- template: {
270
- type: 'code',
271
- content: `const mock{{DependencyName}} = {
272
- {{mockMethods}}
273
- };
274
-
275
- vi.mock('{{modulePath}}', () => ({
276
- {{DependencyName}}: vi.fn(() => mock{{DependencyName}}),
277
- }));`,
278
- variables: [
279
- { name: 'DependencyName', type: 'string', required: true, description: 'Name of dependency to mock' },
280
- { name: 'modulePath', type: 'string', required: true, description: 'Module path to mock' },
281
- { name: 'mockMethods', type: 'code', required: true, description: 'Mock method implementations' },
282
- ],
283
- },
284
- context: {
285
- framework: 'vitest',
286
- testType: 'unit',
287
- tags: ['mock', 'vitest', 'dependency-injection'],
288
- },
289
- },
290
- {
291
- patternType: 'coverage-strategy',
292
- name: 'Risk-Based Coverage',
293
- description: 'Prioritize coverage by code risk and complexity',
294
- template: {
295
- type: 'prompt',
296
- content: `Analyze coverage gaps for {{targetPath}} with focus on:
297
- 1. Critical business logic paths
298
- 2. Error handling branches
299
- 3. Edge cases and boundary conditions
300
- 4. High-complexity functions (cyclomatic complexity > 10)
301
-
302
- Risk scoring:
303
- - Critical: Business logic, auth, payments
304
- - High: Data validation, external integrations
305
- - Medium: Internal utilities, helpers
306
- - Low: Config, constants`,
307
- variables: [
308
- { name: 'targetPath', type: 'string', required: true, description: 'Path to analyze' },
309
- ],
310
- },
311
- context: {
312
- tags: ['coverage', 'risk-based', 'prioritization'],
313
- },
314
- },
315
- {
316
- patternType: 'flaky-fix',
317
- name: 'Timing-Based Flakiness',
318
- description: 'Fix flaky tests caused by timing issues',
319
- template: {
320
- type: 'prompt',
321
- content: `The test {{testName}} is flaky due to timing issues.
322
-
323
- Common fixes:
324
- 1. Replace setTimeout with explicit waits
325
- 2. Use waitFor or waitForCondition
326
- 3. Mock time-dependent functions
327
- 4. Increase timeouts for async operations
328
- 5. Add retry logic with exponential backoff
329
-
330
- Check for:
331
- - Race conditions in async code
332
- - Missing await keywords
333
- - Shared state between tests
334
- - External service dependencies`,
335
- variables: [
336
- { name: 'testName', type: 'string', required: true, description: 'Name of flaky test' },
337
- ],
338
- },
339
- context: {
340
- tags: ['flaky', 'timing', 'async', 'stability'],
341
- },
342
- },
343
- // ================================================================
344
- // quality-assessment domain seeds
345
- // ================================================================
346
- {
347
- patternType: 'assertion-pattern',
348
- qeDomain: 'quality-assessment',
349
- name: 'Quality Gate Checklist',
350
- description: 'Evaluate deployment readiness against quality gate criteria',
351
- template: {
352
- type: 'prompt',
353
- content: `Quality gate evaluation for {{targetModule}}:
354
-
355
- 1. Test coverage >= {{coverageThreshold}}%
356
- 2. No critical/high severity bugs open
357
- 3. All P1 tests passing
358
- 4. Performance benchmarks within SLA (p95 < {{latencyThresholdMs}}ms)
359
- 5. Security scan clean (no critical CVEs)
360
- 6. Code review approved
361
-
362
- Fail the gate if ANY criterion is unmet. Report which criteria passed/failed.`,
363
- variables: [
364
- { name: 'targetModule', type: 'string', required: true, description: 'Module or service to evaluate' },
365
- { name: 'coverageThreshold', type: 'number', required: false, defaultValue: 80, description: 'Minimum coverage %' },
366
- { name: 'latencyThresholdMs', type: 'number', required: false, defaultValue: 500, description: 'P95 latency limit in ms' },
367
- ],
368
- },
369
- context: {
370
- tags: ['quality-gate', 'deployment', 'readiness', 'sla'],
371
- },
372
- confidence: 0.6,
373
- },
374
- {
375
- patternType: 'assertion-pattern',
376
- qeDomain: 'quality-assessment',
377
- name: 'Metric Threshold Validator',
378
- description: 'Validate quality metrics against configurable thresholds',
379
- template: {
380
- type: 'code',
381
- content: `function validateMetrics(metrics: Record<string, number>, thresholds: Record<string, { min?: number; max?: number }>) {
382
- const violations: string[] = [];
383
- for (const [metric, value] of Object.entries(metrics)) {
384
- const threshold = thresholds[metric];
385
- if (!threshold) continue;
386
- if (threshold.min !== undefined && value < threshold.min) {
387
- violations.push(\`\${metric}: \${value} < min \${threshold.min}\`);
388
- }
389
- if (threshold.max !== undefined && value > threshold.max) {
390
- violations.push(\`\${metric}: \${value} > max \${threshold.max}\`);
391
- }
392
- }
393
- return { pass: violations.length === 0, violations };
394
- }`,
395
- variables: [],
396
- },
397
- context: {
398
- tags: ['metrics', 'threshold', 'validation', 'scoring'],
399
- },
400
- confidence: 0.6,
401
- },
402
- // ================================================================
403
- // defect-intelligence domain seeds
404
- // ================================================================
405
- {
406
- patternType: 'error-handling',
407
- qeDomain: 'defect-intelligence',
408
- name: 'Root Cause Analysis Workflow',
409
- description: 'Systematic root cause analysis for test failures and production incidents',
410
- template: {
411
- type: 'prompt',
412
- content: `Root cause analysis for: {{incidentDescription}}
413
-
414
- 1. **Reproduce**: Confirm the failure is deterministic
415
- 2. **Isolate**: Narrow down to the smallest failing component
416
- 3. **Timeline**: When did it last pass? What changed since?
417
- 4. **Categorize**: Is it a code bug, config issue, data issue, or environment?
418
- 5. **Five Whys**: Ask why at least 5 times to reach the root cause
419
- 6. **Fix**: Propose fix with test to prevent regression
420
- 7. **Verify**: Run the fix and confirm the original failure is resolved`,
421
- variables: [
422
- { name: 'incidentDescription', type: 'string', required: true, description: 'Description of the failure or incident' },
423
- ],
424
- },
425
- context: {
426
- tags: ['root-cause', 'rca', 'debugging', 'incident'],
427
- },
428
- confidence: 0.6,
429
- },
430
- {
431
- patternType: 'error-handling',
432
- qeDomain: 'defect-intelligence',
433
- name: 'Regression Risk Scorer',
434
- description: 'Score change risk for regression based on code complexity and history',
435
- template: {
436
- type: 'prompt',
437
- content: `Regression risk assessment for {{changePath}}:
438
-
439
- Risk factors (score each 0-3):
440
- - Lines changed: {{linesChanged}} → complexity risk
441
- - Files affected: {{filesAffected}} → blast radius
442
- - Previous bugs in area: historical defect density
443
- - Test coverage of changed code: gap risk
444
- - Dependency depth: cascade risk
445
-
446
- Total risk = sum / 15 → Low (<0.3), Medium (0.3-0.6), High (>0.6)`,
447
- variables: [
448
- { name: 'changePath', type: 'string', required: true, description: 'File or module changed' },
449
- { name: 'linesChanged', type: 'number', required: true, description: 'Number of lines changed' },
450
- { name: 'filesAffected', type: 'number', required: true, description: 'Number of files affected' },
451
- ],
452
- },
453
- context: {
454
- tags: ['regression', 'risk', 'prediction', 'change-analysis'],
455
- },
456
- confidence: 0.6,
457
- },
458
- // ================================================================
459
- // requirements-validation domain seeds
460
- // ================================================================
461
- {
462
- patternType: 'test-template',
463
- qeDomain: 'requirements-validation',
464
- name: 'Gherkin BDD Scenario',
465
- description: 'Behavior-driven development scenario in Given/When/Then format',
466
- template: {
467
- type: 'code',
468
- content: `Feature: {{featureName}}
469
-
470
- Scenario: {{scenarioDescription}}
471
- Given {{precondition}}
472
- When {{action}}
473
- Then {{expectedOutcome}}
474
-
475
- Scenario: {{scenarioDescription}} - error case
476
- Given {{precondition}}
477
- When {{errorAction}}
478
- Then {{errorOutcome}}`,
479
- variables: [
480
- { name: 'featureName', type: 'string', required: true, description: 'Feature under test' },
481
- { name: 'scenarioDescription', type: 'string', required: true, description: 'What the scenario validates' },
482
- { name: 'precondition', type: 'string', required: true, description: 'Given precondition' },
483
- { name: 'action', type: 'string', required: true, description: 'When action' },
484
- { name: 'expectedOutcome', type: 'string', required: true, description: 'Then expected result' },
485
- { name: 'errorAction', type: 'string', required: true, description: 'When error action' },
486
- { name: 'errorOutcome', type: 'string', required: true, description: 'Then error result' },
487
- ],
488
- },
489
- context: {
490
- tags: ['bdd', 'gherkin', 'requirements', 'acceptance'],
491
- },
492
- confidence: 0.6,
493
- },
494
- {
495
- patternType: 'test-template',
496
- qeDomain: 'requirements-validation',
497
- name: 'Acceptance Criteria Testability Check',
498
- description: 'Evaluate whether acceptance criteria are testable and complete',
499
- template: {
500
- type: 'prompt',
501
- content: `Testability assessment for: {{requirementId}} - {{requirementTitle}}
502
-
503
- Acceptance criteria:
504
- {{acceptanceCriteria}}
505
-
506
- Evaluate each criterion:
507
- 1. **Observable**: Can we verify the outcome without internal knowledge?
508
- 2. **Measurable**: Is there a quantifiable threshold or clear pass/fail?
509
- 3. **Atomic**: Does each criterion test exactly one thing?
510
- 4. **Achievable**: Can this be tested with available tools?
511
- 5. **Complete**: Are edge cases and error paths covered?
512
-
513
- Flag any untestable or ambiguous criteria with suggested rewrites.`,
514
- variables: [
515
- { name: 'requirementId', type: 'string', required: true, description: 'Requirement identifier' },
516
- { name: 'requirementTitle', type: 'string', required: true, description: 'Requirement title' },
517
- { name: 'acceptanceCriteria', type: 'string', required: true, description: 'The acceptance criteria text' },
518
- ],
519
- },
520
- context: {
521
- tags: ['requirements', 'testability', 'acceptance-criteria', 'validation'],
522
- },
523
- confidence: 0.6,
524
- },
525
- // ================================================================
526
- // code-intelligence domain seeds
527
- // ================================================================
528
- {
529
- patternType: 'refactor-safe',
530
- name: 'Safe Refactoring Checklist',
531
- description: 'Ensure refactoring preserves behavior with systematic safety checks',
532
- template: {
533
- type: 'prompt',
534
- content: `Safe refactoring checklist for {{targetPath}}:
535
-
536
- Before refactoring:
537
- 1. Run existing tests — capture baseline results
538
- 2. Check coverage of code being refactored
539
- 3. Identify all callers and dependents via references
540
-
541
- During refactoring:
542
- 4. Make one semantic change at a time
543
- 5. Keep public API signatures stable (or update all callers)
544
- 6. Preserve error handling contracts
545
-
546
- After refactoring:
547
- 7. Run full test suite — compare to baseline
548
- 8. Check for any new uncovered paths
549
- 9. Verify no unused imports or dead code introduced`,
550
- variables: [
551
- { name: 'targetPath', type: 'string', required: true, description: 'Path to refactor' },
552
- ],
553
- },
554
- context: {
555
- tags: ['refactor', 'safety', 'code-change', 'impact'],
556
- },
557
- confidence: 0.6,
558
- },
559
- {
560
- patternType: 'refactor-safe',
561
- name: 'Dependency Impact Analysis',
562
- description: 'Analyze the blast radius of a code change through the dependency graph',
563
- template: {
564
- type: 'prompt',
565
- content: `Impact analysis for changes in {{changedFile}}:
566
-
567
- 1. Direct dependents: files that import/require {{changedFile}}
568
- 2. Transitive dependents: files that depend on direct dependents
569
- 3. Test coverage: which tests exercise the changed code?
570
- 4. Integration points: does this affect API contracts or events?
571
- 5. Risk tier: Critical (auth/payments) > High (business logic) > Medium (utils)
572
-
573
- Output: sorted list of affected files with risk scores.`,
574
- variables: [
575
- { name: 'changedFile', type: 'string', required: true, description: 'File being changed' },
576
- ],
577
- },
578
- context: {
579
- tags: ['impact', 'dependency', 'blast-radius', 'analysis'],
580
- },
581
- confidence: 0.6,
582
- },
583
- // ================================================================
584
- // security-compliance domain seeds
585
- // ================================================================
586
- {
587
- patternType: 'assertion-pattern',
588
- qeDomain: 'security-compliance',
589
- name: 'OWASP Top 10 Security Audit',
590
- description: 'Check code against OWASP Top 10 vulnerability categories',
591
- template: {
592
- type: 'prompt',
593
- content: `Security audit for {{targetPath}} against OWASP Top 10:
594
-
595
- 1. **A01 Broken Access Control**: Check authorization on every endpoint
596
- 2. **A02 Cryptographic Failures**: No plaintext secrets, proper hashing
597
- 3. **A03 Injection**: Parameterized queries, input sanitization
598
- 4. **A04 Insecure Design**: Threat modeling, least privilege
599
- 5. **A05 Security Misconfiguration**: Default creds, verbose errors
600
- 6. **A06 Vulnerable Components**: Check dependencies for CVEs
601
- 7. **A07 Auth Failures**: Brute force protection, session management
602
- 8. **A08 Data Integrity Failures**: Verify signatures, safe deserialization
603
- 9. **A09 Logging Failures**: Audit trail, no sensitive data in logs
604
- 10. **A10 SSRF**: Validate/allowlist outbound URLs
605
-
606
- Flag findings as Critical/High/Medium/Low with remediation guidance.`,
607
- variables: [
608
- { name: 'targetPath', type: 'string', required: true, description: 'Code path to audit' },
609
- ],
610
- },
611
- context: {
612
- tags: ['owasp', 'security', 'audit', 'vulnerability'],
613
- },
614
- confidence: 0.6,
615
- },
616
- {
617
- patternType: 'assertion-pattern',
618
- qeDomain: 'security-compliance',
619
- name: 'Input Sanitization Pattern',
620
- description: 'Validate and sanitize inputs at system boundaries to prevent injection',
621
- template: {
622
- type: 'code',
623
- content: `// Input validation at system boundary
624
- function validateInput(input: unknown, schema: {
625
- type: 'string' | 'number' | 'boolean';
626
- maxLength?: number;
627
- pattern?: RegExp;
628
- allowedValues?: unknown[];
629
- }): { valid: boolean; sanitized: unknown; errors: string[] } {
630
- const errors: string[] = [];
631
- if (typeof input !== schema.type) {
632
- errors.push(\`Expected \${schema.type}, got \${typeof input}\`);
633
- return { valid: false, sanitized: null, errors };
634
- }
635
- if (schema.type === 'string') {
636
- let str = input as string;
637
- if (schema.maxLength && str.length > schema.maxLength) {
638
- str = str.slice(0, schema.maxLength);
639
- errors.push('Input truncated to max length');
640
- }
641
- if (schema.pattern && !schema.pattern.test(str)) {
642
- errors.push('Input does not match expected pattern');
643
- return { valid: false, sanitized: null, errors };
644
- }
645
- return { valid: errors.length === 0, sanitized: str, errors };
646
- }
647
- return { valid: true, sanitized: input, errors };
648
- }`,
649
- variables: [],
650
- },
651
- context: {
652
- tags: ['security', 'validation', 'sanitization', 'injection-prevention'],
653
- },
654
- confidence: 0.6,
655
- },
656
- // ================================================================
657
- // contract-testing domain seeds
658
- // ================================================================
659
- {
660
- patternType: 'api-contract',
661
- name: 'OpenAPI Contract Validator',
662
- description: 'Validate API responses against OpenAPI/Swagger schema',
663
- template: {
664
- type: 'code',
665
- content: `describe('{{apiEndpoint}} contract', () => {
666
- it('should match the OpenAPI schema for {{operationId}}', async () => {
667
- const response = await request(app).{{httpMethod}}('{{apiEndpoint}}');
668
- expect(response.status).toBe({{expectedStatus}});
669
- expect(response.body).toMatchSchema(openApiSpec.paths['{{apiEndpoint}}'].{{httpMethod}}.responses['{{expectedStatus}}'].content['application/json'].schema);
670
- });
671
-
672
- it('should return proper error for invalid input', async () => {
673
- const response = await request(app).{{httpMethod}}('{{apiEndpoint}}').send({{invalidPayload}});
674
- expect(response.status).toBe(400);
675
- expect(response.body).toHaveProperty('errors');
676
- });
677
- });`,
678
- variables: [
679
- { name: 'apiEndpoint', type: 'string', required: true, description: 'API endpoint path' },
680
- { name: 'operationId', type: 'string', required: true, description: 'OpenAPI operation ID' },
681
- { name: 'httpMethod', type: 'string', required: true, description: 'HTTP method (get/post/put/delete)' },
682
- { name: 'expectedStatus', type: 'number', required: true, description: 'Expected HTTP status code' },
683
- { name: 'invalidPayload', type: 'object', required: true, description: 'Invalid request body for error case' },
684
- ],
685
- },
686
- context: {
687
- tags: ['api', 'contract', 'openapi', 'schema-validation'],
688
- },
689
- confidence: 0.6,
690
- },
691
- {
692
- patternType: 'api-contract',
693
- name: 'Consumer-Driven Contract Test',
694
- description: 'Pact-style consumer-driven contract testing between services',
695
- template: {
696
- type: 'code',
697
- content: `// Consumer side contract
698
- const pact = new Pact({
699
- consumer: '{{consumerName}}',
700
- provider: '{{providerName}}',
701
- });
702
-
703
- describe('{{consumerName}} -> {{providerName}} contract', () => {
704
- beforeAll(() => pact.setup());
705
- afterAll(() => pact.finalize());
706
-
707
- it('should receive {{expectedResource}}', async () => {
708
- await pact.addInteraction({
709
- state: '{{providerState}}',
710
- uponReceiving: '{{interactionDescription}}',
711
- withRequest: { method: '{{httpMethod}}', path: '{{requestPath}}' },
712
- willRespondWith: {
713
- status: 200,
714
- body: like({{expectedShape}}),
715
- },
716
- });
717
-
718
- const result = await client.{{clientMethod}}();
719
- expect(result).toBeDefined();
720
- });
721
- });`,
722
- variables: [
723
- { name: 'consumerName', type: 'string', required: true, description: 'Consumer service name' },
724
- { name: 'providerName', type: 'string', required: true, description: 'Provider service name' },
725
- { name: 'expectedResource', type: 'string', required: true, description: 'Resource being consumed' },
726
- { name: 'providerState', type: 'string', required: true, description: 'Provider state precondition' },
727
- { name: 'interactionDescription', type: 'string', required: true, description: 'Interaction description' },
728
- { name: 'httpMethod', type: 'string', required: true, description: 'HTTP method' },
729
- { name: 'requestPath', type: 'string', required: true, description: 'Request path' },
730
- { name: 'expectedShape', type: 'object', required: true, description: 'Expected response shape' },
731
- { name: 'clientMethod', type: 'string', required: true, description: 'Client method to call' },
732
- ],
733
- },
734
- context: {
735
- tags: ['contract', 'pact', 'consumer-driven', 'microservice'],
736
- },
737
- confidence: 0.6,
738
- },
739
- // ================================================================
740
- // visual-accessibility domain seeds
741
- // ================================================================
742
- {
743
- patternType: 'a11y-check',
744
- name: 'WCAG Contrast and ARIA Check',
745
- description: 'Validate WCAG 2.2 color contrast ratios and ARIA attribute correctness',
746
- template: {
747
- type: 'prompt',
748
- content: `Accessibility audit for {{componentName}}:
749
-
750
- WCAG 2.2 AA Compliance:
751
- 1. **Color Contrast**: Text contrast ratio >= 4.5:1 (normal), >= 3:1 (large)
752
- 2. **ARIA Roles**: All interactive elements have correct role attributes
753
- 3. **ARIA Labels**: Forms and buttons have aria-label or aria-labelledby
754
- 4. **Focus Management**: Tab order is logical, focus visible on all interactive elements
755
- 5. **Screen Reader**: Content is meaningful when linearized
756
- 6. **Keyboard**: All functionality accessible via keyboard alone
757
-
758
- Test with: axe-core, pa11y, or lighthouse --accessibility`,
759
- variables: [
760
- { name: 'componentName', type: 'string', required: true, description: 'UI component to audit' },
761
- ],
762
- },
763
- context: {
764
- tags: ['wcag', 'accessibility', 'aria', 'contrast', 'a11y'],
765
- },
766
- confidence: 0.6,
767
- },
768
- {
769
- patternType: 'visual-baseline',
770
- name: 'Visual Regression Baseline',
771
- description: 'Capture and compare screenshots for visual regression detection',
772
- template: {
773
- type: 'code',
774
- content: `describe('{{pageName}} visual regression', () => {
775
- it('should match baseline screenshot', async () => {
776
- await page.goto('{{pageUrl}}');
777
- await page.waitForLoadState('networkidle');
778
-
779
- const screenshot = await page.screenshot({ fullPage: {{fullPage}} });
780
- expect(screenshot).toMatchSnapshot('{{pageName}}-baseline.png', {
781
- maxDiffPixelRatio: {{maxDiffRatio}},
782
- });
783
- });
784
-
785
- it('should match baseline at mobile viewport', async () => {
786
- await page.setViewportSize({ width: 375, height: 812 });
787
- await page.goto('{{pageUrl}}');
788
- const screenshot = await page.screenshot({ fullPage: {{fullPage}} });
789
- expect(screenshot).toMatchSnapshot('{{pageName}}-mobile-baseline.png', {
790
- maxDiffPixelRatio: {{maxDiffRatio}},
791
- });
792
- });
793
- });`,
794
- variables: [
795
- { name: 'pageName', type: 'string', required: true, description: 'Page name for snapshot naming' },
796
- { name: 'pageUrl', type: 'string', required: true, description: 'Page URL to capture' },
797
- { name: 'fullPage', type: 'boolean', required: false, defaultValue: true, description: 'Capture full page' },
798
- { name: 'maxDiffRatio', type: 'number', required: false, defaultValue: 0.01, description: 'Max pixel diff ratio' },
799
- ],
800
- },
801
- context: {
802
- tags: ['visual', 'screenshot', 'regression', 'baseline', 'playwright'],
803
- },
804
- confidence: 0.6,
805
- },
806
- // ================================================================
807
- // chaos-resilience domain seeds
808
- // ================================================================
809
- {
810
- patternType: 'perf-benchmark',
811
- name: 'Load Test Scenario',
812
- description: 'k6-style load test with ramp-up stages and SLA assertions',
813
- template: {
814
- type: 'code',
815
- content: `import http from 'k6/http';
816
- import { check, sleep } from 'k6';
817
-
818
- export const options = {
819
- stages: [
820
- { duration: '{{rampUpDuration}}', target: {{peakUsers}} },
821
- { duration: '{{steadyDuration}}', target: {{peakUsers}} },
822
- { duration: '{{rampDownDuration}}', target: 0 },
823
- ],
824
- thresholds: {
825
- http_req_duration: ['p(95)<{{p95ThresholdMs}}'],
826
- http_req_failed: ['rate<{{errorRateThreshold}}'],
827
- },
828
- };
829
-
830
- export default function () {
831
- const res = http.get('{{targetUrl}}');
832
- check(res, {
833
- 'status is 200': (r) => r.status === 200,
834
- 'response time < {{p95ThresholdMs}}ms': (r) => r.timings.duration < {{p95ThresholdMs}},
835
- });
836
- sleep(1);
837
- }`,
838
- variables: [
839
- { name: 'targetUrl', type: 'string', required: true, description: 'Target URL to load test' },
840
- { name: 'peakUsers', type: 'number', required: true, description: 'Peak concurrent users' },
841
- { name: 'rampUpDuration', type: 'string', required: false, defaultValue: '2m', description: 'Ramp-up duration' },
842
- { name: 'steadyDuration', type: 'string', required: false, defaultValue: '5m', description: 'Steady state duration' },
843
- { name: 'rampDownDuration', type: 'string', required: false, defaultValue: '1m', description: 'Ramp-down duration' },
844
- { name: 'p95ThresholdMs', type: 'number', required: false, defaultValue: 500, description: 'P95 latency threshold in ms' },
845
- { name: 'errorRateThreshold', type: 'number', required: false, defaultValue: 0.01, description: 'Max error rate (0-1)' },
846
- ],
847
- },
848
- context: {
849
- tags: ['load-test', 'k6', 'performance', 'sla'],
850
- },
851
- confidence: 0.6,
852
- },
853
- {
854
- patternType: 'perf-benchmark',
855
- name: 'Circuit Breaker Resilience Test',
856
- description: 'Test circuit breaker behavior under fault injection',
857
- template: {
858
- type: 'prompt',
859
- content: `Resilience test for {{serviceName}} circuit breaker:
860
-
861
- 1. **Closed state**: Verify normal requests pass through
862
- 2. **Fault injection**: Inject {{faultType}} faults at {{faultRate}}% rate
863
- 3. **Trip threshold**: Verify breaker opens after {{failureThreshold}} consecutive failures
864
- 4. **Open state**: Verify requests fail fast (no upstream calls)
865
- 5. **Half-open probe**: After {{cooldownMs}}ms, verify single probe request
866
- 6. **Recovery**: On probe success, verify breaker closes and traffic resumes
867
- 7. **Metrics**: Verify circuit state changes are logged and observable`,
868
- variables: [
869
- { name: 'serviceName', type: 'string', required: true, description: 'Service with circuit breaker' },
870
- { name: 'faultType', type: 'string', required: false, defaultValue: 'latency', description: 'Type of fault (latency/error/timeout)' },
871
- { name: 'faultRate', type: 'number', required: false, defaultValue: 50, description: 'Fault injection rate %' },
872
- { name: 'failureThreshold', type: 'number', required: false, defaultValue: 5, description: 'Failures to trip breaker' },
873
- { name: 'cooldownMs', type: 'number', required: false, defaultValue: 30000, description: 'Cooldown before half-open' },
874
- ],
875
- },
876
- context: {
877
- tags: ['chaos', 'circuit-breaker', 'resilience', 'fault-injection'],
878
- },
879
- confidence: 0.6,
880
- },
881
- // ================================================================
882
- // learning-optimization domain seeds
883
- // ================================================================
884
- {
885
- patternType: 'coverage-strategy',
886
- qeDomain: 'learning-optimization',
887
- name: 'Cross-Domain Pattern Transfer',
888
- description: 'Strategy for transferring useful patterns from one QE domain to related domains',
889
- template: {
890
- type: 'prompt',
891
- content: `Pattern transfer from {{sourceDomain}} to {{targetDomain}}:
892
-
893
- 1. Identify generalizable patterns in source domain (success rate > 70%)
894
- 2. Check domain compatibility (related domains get 80% relevance, unrelated 50%)
895
- 3. Adapt pattern context: replace domain-specific terms with target equivalents
896
- 4. Set transferred pattern confidence to source * compatibility factor
897
- 5. Store in target domain with provenance metadata
898
- 6. Track transfer outcomes to learn which transfers are valuable`,
899
- variables: [
900
- { name: 'sourceDomain', type: 'string', required: true, description: 'Source QE domain' },
901
- { name: 'targetDomain', type: 'string', required: true, description: 'Target QE domain' },
902
- ],
903
- },
904
- context: {
905
- tags: ['transfer', 'cross-domain', 'learning', 'pattern-reuse'],
906
- },
907
- confidence: 0.6,
908
- },
909
- {
910
- patternType: 'coverage-strategy',
911
- qeDomain: 'learning-optimization',
912
- name: 'Pattern Quality Promotion Pipeline',
913
- description: 'Pipeline for promoting short-term patterns to long-term based on success evidence',
914
- template: {
915
- type: 'prompt',
916
- content: `Pattern promotion evaluation for {{patternName}}:
917
-
918
- Criteria (all must pass):
919
- 1. Usage count >= 3 successful applications
920
- 2. Success rate >= 70%
921
- 3. Confidence score >= 0.6
922
- 4. Coherence check: no contradiction with existing long-term patterns
923
- 5. Age: pattern has existed for at least 24 hours (not rushed)
924
-
925
- On promotion:
926
- - Move from short-term to long-term tier
927
- - Boost confidence by 10%
928
- - Enable for cross-domain transfer to related domains
929
- - Mark as reusable for token savings optimization`,
930
- variables: [
931
- { name: 'patternName', type: 'string', required: true, description: 'Pattern to evaluate for promotion' },
932
- ],
933
- },
934
- context: {
935
- tags: ['promotion', 'learning', 'quality', 'pipeline'],
936
- },
937
- confidence: 0.6,
938
- },
939
- ];
940
- for (const options of foundationalPatterns) {
159
+ // Add foundational patterns from extracted module
160
+ for (const options of PRETRAINED_PATTERNS) {
941
161
  try {
942
162
  await this.patternStore.create(options);
943
163
  }
@@ -945,7 +165,7 @@ On promotion:
945
165
  logger.warn('Failed to load pattern', { name: options.name, error });
946
166
  }
947
167
  }
948
- logger.info('Loaded foundational patterns', { count: foundationalPatterns.length });
168
+ logger.info('Loaded foundational patterns', { count: PRETRAINED_PATTERNS.length });
949
169
  }
950
170
  /**
951
171
  * Seed cross-domain patterns by transferring generalizable patterns
@@ -958,88 +178,11 @@ On promotion:
958
178
  if (!this.initialized) {
959
179
  await this.initialize();
960
180
  }
961
- const stats = await this.patternStore.getStats();
962
- let transferred = 0;
963
- let skipped = 0;
964
- // Domain compatibility matrix (same as TransferSpecialistService)
965
- const relatedDomains = {
966
- 'test-generation': ['test-execution', 'coverage-analysis', 'requirements-validation'],
967
- 'test-execution': ['test-generation', 'coverage-analysis', 'quality-assessment'],
968
- 'coverage-analysis': ['test-generation', 'test-execution', 'quality-assessment'],
969
- 'quality-assessment': ['test-execution', 'coverage-analysis', 'defect-intelligence'],
970
- 'defect-intelligence': ['quality-assessment', 'code-intelligence'],
971
- 'requirements-validation': ['test-generation', 'quality-assessment'],
972
- 'code-intelligence': ['defect-intelligence', 'security-compliance'],
973
- 'security-compliance': ['code-intelligence', 'quality-assessment'],
974
- 'contract-testing': ['test-generation', 'test-execution'],
975
- 'visual-accessibility': ['quality-assessment'],
976
- 'chaos-resilience': ['test-execution', 'quality-assessment'],
977
- 'learning-optimization': ['test-generation', 'test-execution', 'coverage-analysis', 'quality-assessment', 'defect-intelligence'],
978
- };
979
- // For each domain that has patterns, transfer to related domains
980
- for (const [sourceDomainStr, targetDomains] of Object.entries(relatedDomains)) {
981
- const sourceDomain = sourceDomainStr;
982
- const sourceCount = stats.byDomain[sourceDomain] || 0;
983
- if (sourceCount === 0)
984
- continue;
985
- // Get source domain patterns
986
- const sourceResult = await this.searchPatterns('', {
987
- domain: sourceDomain,
988
- limit: 50,
989
- });
990
- if (!sourceResult.success)
991
- continue;
992
- for (const targetDomain of targetDomains) {
993
- // Only transfer to domains that have few patterns (less than source)
994
- const targetCount = stats.byDomain[targetDomain] || 0;
995
- if (targetCount >= sourceCount) {
996
- skipped++;
997
- continue;
998
- }
999
- // Transfer each source pattern to the target domain
1000
- for (const { pattern: sourcePattern } of sourceResult.value) {
1001
- // Check if a similar pattern already exists in target domain
1002
- const existingCheck = await this.searchPatterns(sourcePattern.name, {
1003
- domain: targetDomain,
1004
- limit: 1,
1005
- });
1006
- if (existingCheck.success && existingCheck.value.length > 0) {
1007
- const bestMatch = existingCheck.value[0];
1008
- if (bestMatch.score > 0.8) {
1009
- skipped++;
1010
- continue; // Similar pattern already exists
1011
- }
1012
- }
1013
- // Create transferred pattern with reduced confidence
1014
- const transferredConfidence = Math.max(0.3, (sourcePattern.confidence || 0.5) * 0.8);
1015
- const transferResult = await this.storePattern({
1016
- patternType: sourcePattern.patternType,
1017
- qeDomain: targetDomain,
1018
- name: `${sourcePattern.name} (from ${sourceDomain})`,
1019
- description: `${sourcePattern.description} [Transferred from ${sourceDomain} domain]`,
1020
- template: sourcePattern.template,
1021
- context: {
1022
- ...sourcePattern.context,
1023
- relatedDomains: [sourceDomain, targetDomain],
1024
- tags: [
1025
- ...sourcePattern.context.tags,
1026
- 'cross-domain-transfer',
1027
- `source:${sourceDomain}`,
1028
- ],
1029
- },
1030
- confidence: transferredConfidence,
1031
- });
1032
- if (transferResult.success) {
1033
- transferred++;
1034
- }
1035
- else {
1036
- skipped++;
1037
- }
1038
- }
1039
- }
1040
- }
1041
- logger.info('Cross-domain transfer complete', { transferred, skipped });
1042
- return { transferred, skipped };
181
+ return seedCrossDomainPatternsFn({
182
+ searchPatterns: this.searchPatterns.bind(this),
183
+ storePattern: this.storePattern.bind(this),
184
+ patternStore: this.patternStore,
185
+ });
1043
186
  }
1044
187
  /**
1045
188
  * Store a new pattern
@@ -1140,121 +283,28 @@ On promotion:
1140
283
  getWitnessChain().then(wc => wc.append('PATTERN_UPDATE', { patternId: outcome.patternId, success: outcome.success }, 'reasoning-bank')).catch((e) => { logger.warn('Witness chain PATTERN_UPDATE failed', { error: toErrorMessage(e) }); });
1141
284
  // Check if pattern should be promoted (with coherence gate)
1142
285
  const pattern = await this.getPattern(outcome.patternId);
1143
- if (pattern && await this.checkPatternPromotionWithCoherence(pattern)) {
1144
- await this.promotePattern(outcome.patternId);
286
+ const deps = this.getPromotionDeps();
287
+ if (pattern && await checkPatternPromotionWithCoherence(pattern, deps)) {
288
+ await promotePatternFn(outcome.patternId, deps);
1145
289
  logger.info('Pattern promoted to long-term', { name: pattern.name });
1146
290
  }
1147
291
  }
1148
292
  return result;
1149
293
  }
1150
294
  /**
1151
- * Check if a pattern should be promoted with coherence gate (ADR-052)
1152
- *
1153
- * This method implements a two-stage promotion check:
1154
- * 1. Basic criteria (usage and quality) - cheap to check
1155
- * 2. Coherence criteria (only if basic passes) - expensive, requires coherence service
1156
- *
1157
- * @param pattern - Pattern to evaluate for promotion
1158
- * @returns true if pattern should be promoted, false otherwise
295
+ * Get promotion dependencies for the extracted promotion module
1159
296
  */
1160
- async checkPatternPromotionWithCoherence(pattern) {
1161
- // 1. Check basic criteria first (cheap)
1162
- const basicCheck = shouldPromotePattern(pattern);
1163
- if (!basicCheck.meetsUsageCriteria || !basicCheck.meetsQualityCriteria) {
1164
- return false;
1165
- }
1166
- // 2. Check coherence with existing long-term patterns (expensive, only if basic passes)
1167
- if (this.coherenceService && this.coherenceService.isInitialized()) {
1168
- const longTermPatterns = await this.getLongTermPatterns();
1169
- // Create coherence check with candidate pattern added to long-term set
1170
- const allPatterns = [...longTermPatterns, pattern];
1171
- const coherenceNodes = allPatterns.map(p => ({
1172
- id: p.id,
1173
- embedding: p.embedding || [],
1174
- weight: p.confidence,
1175
- metadata: { name: p.name, domain: p.qeDomain },
1176
- }));
1177
- const coherenceResult = await this.coherenceService.checkCoherence(coherenceNodes);
1178
- if (coherenceResult.energy >= (this.config.coherenceThreshold || 0.4)) {
1179
- // Promotion blocked due to coherence violation
1180
- const event = {
1181
- patternId: pattern.id,
1182
- patternName: pattern.name,
1183
- reason: 'coherence_violation',
1184
- energy: coherenceResult.energy,
1185
- existingPatternConflicts: coherenceResult.contradictions?.map(c => c.nodeIds).flat(),
1186
- };
1187
- // Publish event if eventBus is available
1188
- if (this.eventBus) {
1189
- await this.eventBus.publish({
1190
- id: `pattern-promotion-blocked-${pattern.id}`,
1191
- type: 'pattern:promotion_blocked',
1192
- timestamp: new Date(),
1193
- source: 'learning-optimization',
1194
- payload: event,
1195
- });
1196
- }
1197
- logger.info('Pattern promotion blocked due to coherence violation', {
1198
- name: pattern.name,
1199
- energy: coherenceResult.energy,
1200
- });
1201
- return false;
1202
- }
1203
- }
1204
- return true;
1205
- }
1206
- /**
1207
- * Get all long-term patterns for coherence checking
1208
- *
1209
- * @returns Array of long-term patterns
1210
- */
1211
- async getLongTermPatterns() {
1212
- const result = await this.searchPatterns('', { tier: 'long-term', limit: 1000 });
1213
- return result.success ? result.value.map(r => r.pattern) : [];
1214
- }
1215
- /**
1216
- * Promote a pattern to long-term storage
1217
- *
1218
- * @param patternId - Pattern ID to promote
1219
- */
1220
- async promotePattern(patternId) {
1221
- const result = await this.patternStore.promote(patternId);
1222
- if (result.success) {
1223
- // Persist promotion to SQLite
1224
- try {
1225
- this.getSqliteStore().promotePattern(patternId);
1226
- }
1227
- catch (e) {
1228
- logger.warn('SQLite pattern promotion persist failed', { error: toErrorMessage(e) });
1229
- }
1230
- // Phase 3: Best-effort RVF dual-write on promotion (re-sync embedding)
1231
- if (this.rvfDualWriter) {
1232
- try {
1233
- const promoted = await this.getPattern(patternId);
1234
- if (promoted?.embedding && promoted.embedding.length > 0) {
1235
- this.rvfDualWriter.writePattern(patternId, promoted.embedding);
1236
- }
1237
- }
1238
- catch (rvfErr) {
1239
- logger.warn('RVF dual-write on promote failed (non-fatal)', { patternId, error: toErrorMessage(rvfErr) });
1240
- }
1241
- }
1242
- // ADR-070: Record pattern promotion in witness chain
1243
- getWitnessChain().then(wc => wc.append('PATTERN_PROMOTE', { patternId }, 'reasoning-bank')).catch((e) => { logger.warn('Witness chain PATTERN_PROMOTE failed', { error: toErrorMessage(e) }); });
1244
- logger.info('Promoted pattern to long-term', { patternId });
1245
- if (this.eventBus) {
1246
- await this.eventBus.publish({
1247
- id: `pattern-promoted-${patternId}`,
1248
- type: 'pattern:promoted',
1249
- timestamp: new Date(),
1250
- source: 'learning-optimization',
1251
- payload: { patternId, newTier: 'long-term' },
1252
- });
1253
- }
1254
- }
1255
- else {
1256
- logger.error('Failed to promote pattern', result.error, { patternId });
1257
- }
297
+ getPromotionDeps() {
298
+ return {
299
+ patternStore: this.patternStore,
300
+ coherenceService: this.coherenceService,
301
+ eventBus: this.eventBus,
302
+ coherenceThreshold: this.config.coherenceThreshold || 0.4,
303
+ getSqliteStore: this.getSqliteStore.bind(this),
304
+ rvfDualWriter: this.rvfDualWriter,
305
+ searchPatterns: this.searchPatterns.bind(this),
306
+ getPattern: this.getPattern.bind(this),
307
+ };
1258
308
  }
1259
309
  /**
1260
310
  * Route a task to optimal agent
@@ -1285,44 +335,16 @@ On promotion:
1285
335
  const patterns = patternResults.success
1286
336
  ? patternResults.value.map((r) => r.pattern)
1287
337
  : [];
1288
- // 3. Calculate agent scores
1289
- const agentScores = [];
1290
- for (const [agentType, profile] of Object.entries(this.agentCapabilities)) {
1291
- let score = 0;
1292
- const reasoning = [];
1293
- // Domain match (0-0.4)
1294
- const domainMatch = detectedDomains.filter((d) => profile.domains.includes(d)).length;
1295
- const domainScore = domainMatch > 0 ? (domainMatch / detectedDomains.length) * 0.4 : 0;
1296
- score += domainScore * this.config.routingWeights.similarity;
1297
- if (domainScore > 0) {
1298
- reasoning.push(`Domain match: ${(domainScore * 100).toFixed(0)}%`);
1299
- }
1300
- // Capability match (0-0.3)
1301
- if (request.capabilities && request.capabilities.length > 0) {
1302
- const capMatch = request.capabilities.filter((c) => profile.capabilities.some((pc) => pc.toLowerCase().includes(c.toLowerCase()))).length;
1303
- const capScore = capMatch > 0 ? (capMatch / request.capabilities.length) * 0.3 : 0;
1304
- score += capScore * this.config.routingWeights.capabilities;
1305
- if (capScore > 0) {
1306
- reasoning.push(`Capability match: ${(capScore * 100).toFixed(0)}%`);
1307
- }
1308
- }
1309
- else {
1310
- score += 0.15 * this.config.routingWeights.capabilities;
1311
- }
1312
- // Historical performance (0-0.3)
1313
- score += profile.performanceScore * 0.3 * this.config.routingWeights.performance;
1314
- reasoning.push(`Performance score: ${(profile.performanceScore * 100).toFixed(0)}%`);
1315
- // Pattern similarity boost
1316
- const agentPatterns = patterns.filter((p) => profile.domains.includes(p.qeDomain));
1317
- if (agentPatterns.length > 0) {
1318
- const patternBoost = Math.min(0.1, agentPatterns.length * 0.02);
1319
- score += patternBoost;
1320
- reasoning.push(`Pattern matches: ${agentPatterns.length}`);
338
+ // 3. Build agent-to-pattern-count map for scoring
339
+ const agentDomainPatternCounts = new Map();
340
+ for (const [agentType, profile] of Object.entries(AGENT_CAPABILITIES)) {
341
+ const count = patterns.filter((p) => profile.domains.includes(p.qeDomain)).length;
342
+ if (count > 0) {
343
+ agentDomainPatternCounts.set(agentType, count);
1321
344
  }
1322
- agentScores.push({ agent: agentType, score, reasoning });
1323
345
  }
1324
- // Sort by score
1325
- agentScores.sort((a, b) => b.score - a.score);
346
+ // 4. Calculate agent scores using extracted function
347
+ const agentScores = calculateAgentScores(detectedDomains, request.capabilities, agentDomainPatternCounts, this.config.routingWeights);
1326
348
  const recommended = agentScores[0];
1327
349
  const alternatives = agentScores.slice(1, 4);
1328
350
  // Generate guidance
@@ -1384,7 +406,7 @@ On promotion:
1384
406
  const embedding = await computeRealEmbedding(text);
1385
407
  // Resize to configured dimension if needed (384 -> 128)
1386
408
  if (embedding.length !== this.config.embeddingDimension) {
1387
- return this.resizeEmbedding(embedding, this.config.embeddingDimension);
409
+ return resizeEmbedding(embedding, this.config.embeddingDimension);
1388
410
  }
1389
411
  return embedding;
1390
412
  }
@@ -1399,81 +421,7 @@ On promotion:
1399
421
  }
1400
422
  }
1401
423
  // Hash-based fallback (always works, including ARM64)
1402
- return this.hashEmbedding(text);
1403
- }
1404
- /**
1405
- * Resize embedding to target dimension using averaging or truncation
1406
- */
1407
- resizeEmbedding(embedding, targetDim) {
1408
- if (embedding.length === targetDim) {
1409
- return embedding;
1410
- }
1411
- if (embedding.length > targetDim) {
1412
- // Average adjacent values to reduce dimension
1413
- const ratio = embedding.length / targetDim;
1414
- const result = new Array(targetDim).fill(0);
1415
- for (let i = 0; i < targetDim; i++) {
1416
- const start = Math.floor(i * ratio);
1417
- const end = Math.floor((i + 1) * ratio);
1418
- let sum = 0;
1419
- for (let j = start; j < end; j++) {
1420
- sum += embedding[j];
1421
- }
1422
- result[i] = sum / (end - start);
1423
- }
1424
- // Normalize
1425
- const magnitude = Math.sqrt(result.reduce((sum, val) => sum + val * val, 0));
1426
- if (magnitude > 0) {
1427
- for (let i = 0; i < targetDim; i++) {
1428
- result[i] /= magnitude;
1429
- }
1430
- }
1431
- return result;
1432
- }
1433
- else {
1434
- // Interpolate to increase dimension (less common)
1435
- const result = new Array(targetDim).fill(0);
1436
- const ratio = (embedding.length - 1) / (targetDim - 1);
1437
- for (let i = 0; i < targetDim; i++) {
1438
- const pos = i * ratio;
1439
- const lower = Math.floor(pos);
1440
- const upper = Math.min(lower + 1, embedding.length - 1);
1441
- const weight = pos - lower;
1442
- result[i] = embedding[lower] * (1 - weight) + embedding[upper] * weight;
1443
- }
1444
- // Normalize
1445
- const magnitude = Math.sqrt(result.reduce((sum, val) => sum + val * val, 0));
1446
- if (magnitude > 0) {
1447
- for (let i = 0; i < targetDim; i++) {
1448
- result[i] /= magnitude;
1449
- }
1450
- }
1451
- return result;
1452
- }
1453
- }
1454
- /**
1455
- * Simple hash-based embedding fallback
1456
- */
1457
- hashEmbedding(text) {
1458
- const dimension = this.config.embeddingDimension;
1459
- const embedding = new Array(dimension).fill(0);
1460
- const normalized = text.toLowerCase().trim();
1461
- // Use multiple hash passes for better distribution
1462
- for (let pass = 0; pass < 3; pass++) {
1463
- for (let i = 0; i < normalized.length; i++) {
1464
- const charCode = normalized.charCodeAt(i);
1465
- const idx = (charCode * (i + 1) * (pass + 1)) % dimension;
1466
- embedding[idx] += Math.sin(charCode * (pass + 1)) / (i + 1);
1467
- }
1468
- }
1469
- // Normalize to unit vector
1470
- const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
1471
- if (magnitude > 0) {
1472
- for (let i = 0; i < dimension; i++) {
1473
- embedding[i] /= magnitude;
1474
- }
1475
- }
1476
- return embedding;
424
+ return hashEmbedding(text, this.config.embeddingDimension);
1477
425
  }
1478
426
  /**
1479
427
  * Get statistics
@@ -1530,7 +478,14 @@ export function createQEReasoningBank(memory, eventBus, config, coherenceService
1530
478
  return new QEReasoningBank(memory, eventBus, config, coherenceService);
1531
479
  }
1532
480
  // ============================================================================
1533
- // Convenience Exports
481
+ // Re-exports from extracted modules
482
+ // ============================================================================
483
+ // Re-export all types/interfaces/constants so existing import paths work unchanged
484
+ export { DEFAULT_QE_REASONING_BANK_CONFIG } from './qe-reasoning-bank-types.js';
485
+ export { PRETRAINED_PATTERNS } from './pretrained-patterns.js';
486
+ export { AGENT_CAPABILITIES, RELATED_DOMAINS, calculateAgentScores, } from './agent-routing.js';
487
+ // ============================================================================
488
+ // Convenience Exports (preserved from original)
1534
489
  // ============================================================================
1535
490
  export { detectQEDomain, detectQEDomains, mapQEDomainToAQE, applyPatternTemplate, } from './qe-patterns.js';
1536
491
  export { getGuidance, getCombinedGuidance, generateGuidanceContext, } from './qe-guidance.js';