import { describe, expect, it } from "vitest"; import { CausalReasoner } from "./causal-reasoner.js"; describe("CausalReasoner", () => { it("builds stable causal links without duplicating graph structure across repeated observations", () => { const reasoner = new CausalReasoner(); reasoner.observeCorrelation("action_recovery", "stability", "A→B"); reasoner.observeCorrelation("action_recovery", "stability", "A→B"); reasoner.observeCorrelation("action_recovery", "stability", "A→B"); const plan = reasoner.reasonAboutIntervention("action_recovery"); const stats = reasoner.getStats(); expect(stats.edges).toBe(1); expect(plan.expectedEffects).toHaveLength(1); expect(plan.expectedEffects[0]?.variable).toBe("stability"); expect(plan.expectedEffects[0]?.confidence).toBe(1); }); it("propagates indirect effects with decaying confidence and surfaces backed confounders as backfire risk", () => { const reasoner = new CausalReasoner(); reasoner.observeCorrelation("background_load", "action_recovery", "A→B"); reasoner.observeCorrelation("background_load", "action_recovery", "A→B"); reasoner.observeCorrelation("background_load", "action_recovery", "A→B"); reasoner.observeCorrelation("action_recovery", "stability", "A→B"); reasoner.observeCorrelation("action_recovery", "stability", "A→B"); reasoner.observeCorrelation("action_recovery", "stability", "A→B"); reasoner.observeCorrelation("stability", "throughput", "A→B"); reasoner.observeCorrelation("stability", "throughput", "A→B"); reasoner.observeCorrelation("stability", "throughput", "A→B"); reasoner.detectConfounders(); const plan = reasoner.reasonAboutIntervention("action_recovery"); const direct = plan.expectedEffects.find((effect) => effect.variable === "stability"); const indirect = plan.expectedEffects.find((effect) => effect.variable === "throughput"); expect(direct?.confidence).toBe(1); expect(indirect?.confidence).toBeLessThan(0.8); expect(plan.potentialBackfires.some((risk) => risk.includes("background_load"))).toBe(true); }); });