Files
Profa 2253d05618 feat: flatten project structure — archive v2, promote v3 to root
Remove the nested v3/ subdirectory by moving all active source code,
tests, configs, and assets directly to the project root. Archive v2/
(dead code, 847MB) to v2-archive.tar.gz.

Key changes:
- Move v3/src/ → src/, v3/tests/ → tests/, v3/packages/ → packages/
- Move v3/tsconfig.json, v3/vitest.config.ts to root
- Merge v3/package.json into root package.json (ESM, scripts, deps, exports)
- Merge v3/scripts/ into scripts/, v3/implementation/ into docs/implementation/
- Update all CI workflows to remove "cd v3 &&" and v3/ path prefixes
- Fix all init installer path resolution (3-level-up → 2-level-up)
- Fix hooks to use "node ./dist/cli/bundle.js" instead of bare "aqe"
- Update settings.json ADR/DDD directories to docs/implementation/
- Fix helper scripts (statusline, adr-compliance, ddd-tracker, guidance-hooks)
- Update 58 agent definitions: aqe/v3/ → aqe/ memory namespaces
- Update 15 guidance shards: v3/src/ → src/ path references
- Fix sync interfaces: remove v3/ and v2/ path references
- Fix test path calculations (process.cwd() + '..' no longer needed)
- Fix flaky perf test threshold (500ms → 1000ms for CI environments)
- Fix complexity-analyzer test to match ADR-051 keyword-based eligibility

Build passes, 17,901 tests pass, CLI and MCP bundles verified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:35:41 +00:00

8.4 KiB

SPEC-035-C: Causal Discovery Engine

Field Value
Specification ID SPEC-035-C
Parent ADR ADR-035
Version 1.0
Status Draft
Last Updated 2026-01-20
Author Architecture Team

Overview

This specification defines the Causal Discovery Engine that orchestrates event observation, weight learning, and root cause analysis. It provides the main API for integrating causal discovery with defect intelligence.


CausalDiscoveryEngine Implementation

export class CausalDiscoveryEngine {
  private weightMatrix: CausalWeightMatrix;
  private eventHistory: TestEvent[] = [];
  private config: CausalDiscoveryConfig;

  constructor(config: CausalDiscoveryConfig = DEFAULT_CAUSAL_CONFIG) {
    this.config = config;
    this.weightMatrix = new CausalWeightMatrix(config);
  }

  /** Observe a test event */
  observe(event: TestEvent): void {
    this.weightMatrix.updateWeights(event);
    this.eventHistory.push(event);

    // Prune old events
    const cutoff = event.timestamp - this.config.timeWindow * 100;
    this.eventHistory = this.eventHistory.filter(e => e.timestamp > cutoff);
  }

  /** Observe multiple events */
  observeBatch(events: TestEvent[]): void {
    for (const event of events) {
      this.observe(event);
    }
  }

  /** Perform root cause analysis for a target event */
  analyzeRootCause(targetEvent: TestEventType): RootCauseAnalysis {
    const graph = this.weightMatrix.extractCausalGraph();

    // Find direct causes
    const directCauses = graph.edgesTo(targetEvent)
      .map(e => ({ event: e.source, strength: e.strength }))
      .sort((a, b) => b.strength - a.strength);

    // Find indirect causes via transitive closure
    const closedGraph = graph.transitiveClosure();
    const indirectCauses: Array<{ event: TestEventType; strength: number; path: TestEventType[] }> = [];

    for (const edge of closedGraph.edgesTo(targetEvent)) {
      // Skip if it's a direct cause
      if (directCauses.some(dc => dc.event === edge.source)) continue;

      indirectCauses.push({
        event: edge.source,
        strength: edge.strength,
        path: this.findPath(graph, edge.source, targetEvent),
      });
    }

    indirectCauses.sort((a, b) => b.strength - a.strength);

    // Find optimal intervention points
    const interventionPoints = this.findInterventionPoints(graph, targetEvent);

    // Calculate confidence based on evidence strength
    const totalEvidence = [...directCauses, ...indirectCauses]
      .reduce((sum, c) => sum + c.strength, 0);
    const confidence = Math.min(totalEvidence / 5, 1);

    return {
      targetEvent,
      directCauses,
      indirectCauses,
      interventionPoints,
      confidence,
    };
  }

  /** Find path from source to target */
  private findPath(graph: CausalGraph, source: TestEventType, target: TestEventType): TestEventType[] {
    const visited = new Set<TestEventType>();
    const parent = new Map<TestEventType, TestEventType>();
    const queue: TestEventType[] = [source];

    while (queue.length > 0) {
      const current = queue.shift()!;
      if (current === target) break;
      if (visited.has(current)) continue;
      visited.add(current);

      for (const edge of graph.edgesFrom(current)) {
        if (!visited.has(edge.target)) {
          parent.set(edge.target, current);
          queue.push(edge.target);
        }
      }
    }

    // Reconstruct path
    const path: TestEventType[] = [];
    let current: TestEventType | undefined = target;
    while (current && current !== source) {
      path.unshift(current);
      current = parent.get(current);
    }
    if (current === source) {
      path.unshift(source);
    }

    return path;
  }

  /** Find optimal intervention points (mincut on causal graph) */
  private findInterventionPoints(graph: CausalGraph, target: TestEventType): TestEventType[] {
    // Heuristic: nodes with high out-degree that can reach target
    const reachability = new Map<TestEventType, number>();

    for (const node of graph.nodes) {
      const reachable = graph.reachableFrom(node);
      if (reachable.has(target)) {
        const outDegree = graph.edgesFrom(node).length;
        reachability.set(node, outDegree);
      }
    }

    // Sort by out-degree and return top 5
    return Array.from(reachability.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, 5)
      .map(([node]) => node);
  }

  /** Get all learned causal relationships */
  getCausalGraph(): CausalGraph {
    return this.weightMatrix.extractCausalGraph();
  }

  /** Get summary statistics */
  getSummary(): CausalSummary {
    const graph = this.weightMatrix.extractCausalGraph();

    let causesCount = 0;
    let preventsCount = 0;
    let totalStrength = 0;

    for (const edge of graph.edges) {
      totalStrength += edge.strength;
      if (edge.relation === 'causes') causesCount++;
      if (edge.relation === 'prevents') preventsCount++;
    }

    return {
      numRelationships: graph.edges.length,
      causesCount,
      preventsCount,
      avgStrength: graph.edges.length > 0 ? totalStrength / graph.edges.length : 0,
      eventsObserved: this.eventHistory.length,
    };
  }

  /** Apply decay to learned weights */
  decay(): void {
    this.weightMatrix.decay();
  }

  /** Reset all learned relationships */
  reset(): void {
    this.weightMatrix = new CausalWeightMatrix(this.config);
    this.eventHistory = [];
  }
}

Defect Intelligence Integration

// In defect-intelligence domain
import { CausalDiscoveryEngine, RootCauseAnalysis } from '../causal-discovery';

export class DefectIntelligenceService {
  private causalEngine: CausalDiscoveryEngine;

  constructor() {
    this.causalEngine = new CausalDiscoveryEngine();
  }

  /** Called when any test event occurs */
  onTestEvent(event: TestEvent): void {
    this.causalEngine.observe(event);
  }

  /** Analyze root cause of test failures */
  analyzeTestFailure(failedTestId: string): RootCauseAnalysis {
    const analysis = this.causalEngine.analyzeRootCause('test_failed');

    console.log(`[Defect Intelligence] Root cause analysis for ${failedTestId}:`);
    console.log(`  Direct causes: ${analysis.directCauses.map(c => c.event).join(', ')}`);
    console.log(`  Intervention points: ${analysis.interventionPoints.join(', ')}`);

    return analysis;
  }

  /** Predict which events might cause future failures */
  predictFailures(): TestEventType[] {
    const graph = this.causalEngine.getCausalGraph();

    return graph.edgesTo('test_failed')
      .filter(e => e.strength > 0.5)
      .map(e => e.source);
  }
}

Implementation Plan

Phase 1: Core Causal Discovery (Days 1-2)

v3/src/causal-discovery/
├── index.ts
├── types.ts
├── weight-matrix.ts         # CausalWeightMatrix
├── causal-graph.ts          # CausalGraph implementation
└── discovery-engine.ts      # CausalDiscoveryEngine

Phase 2: Root Cause Analysis (Days 3-4)

├── root-cause-analyzer.ts   # RootCauseAnalysis logic
├── intervention-finder.ts   # Optimal intervention points
└── path-finder.ts           # Causal path discovery

Phase 3: Integration (Day 5)

  • Integrate with defect-intelligence domain
  • Add MCP tool for causal analysis
  • Create visualization dashboard

Validation Rules

Rule ID Description Severity
SPEC-035-C-001 Events must have valid timestamps Error
SPEC-035-C-002 Event type must be valid TestEventType Error
SPEC-035-C-003 Batch events should be sorted by timestamp Warning

Spec ID Title Relationship
SPEC-035-A Causal Weight Matrix Core learning component
SPEC-035-B Causal Graph Graph representation

Changelog

Version Date Author Changes
1.0 2026-01-20 Architecture Team Initial specification

References